Rthur2003 commited on
Commit
b4ae825
·
1 Parent(s): dc559f6

feat: add parsing functions for CLAP and FST results in score fusion

Browse files
Files changed (1) hide show
  1. app/services/score_fusion.py +69 -19
app/services/score_fusion.py CHANGED
@@ -200,64 +200,114 @@ def _parse_external_ses_analizi(data: Optional[dict]) -> Optional[_ExternalScore
200
  return None
201
 
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  # ═══════════════════════════════════════════════════════════════════════
204
  # PRIVATE — Score fusion
205
  # ═══════════════════════════════════════════════════════════════════════
206
 
207
  def _fuse(
208
  local: float,
 
 
209
  ext_mai: Optional[_ExternalScore],
210
  ext_ses: Optional[_ExternalScore],
211
  ) -> tuple[float, str, str]:
212
  """
213
- Fuse local + external scores.
 
 
 
 
 
 
 
214
 
215
  Returns: (confidence, decision_source, analysis_mode)
216
  """
217
- scores: list[tuple[float, float, str]] = [] # (score, weight, source)
 
 
 
 
 
 
 
218
 
219
- # Local always present
220
- scores.append((local, 0.5, "auris_local"))
 
221
 
 
222
  if ext_mai and ext_mai.available:
223
- scores.append((ext_mai.confidence, 0.3, "music_ai"))
224
 
225
  if ext_ses and ext_ses.available:
226
- scores.append((ext_ses.confidence, 0.2, "ses_analizi"))
227
 
228
  # Normalize weights
229
  total_weight = sum(w for _, w, _ in scores)
230
  confidence = sum(s * (w / total_weight) for s, w, _ in scores)
231
 
232
- # Determine primary decision source
233
- if ext_mai and ext_mai.available:
234
- decision_source = "auris_fusion"
235
- mode = "production"
236
- elif ext_ses and ext_ses.available:
237
  decision_source = "auris_fusion"
238
- mode = "production"
 
 
 
239
  else:
240
  decision_source = "auris_local"
241
- mode = "production" # Still production — we have real feature analysis
242
 
243
- # Clamp to valid range (never show 0.50 or 1.00)
 
 
244
  confidence = max(0.51, min(0.97, confidence))
245
  confidence = round(confidence, 4)
246
 
247
- # Agreement bonus: if all sources agree, boost confidence toward edges
248
  if len(scores) > 1:
249
  all_ai = all(s > 0.5 for s, _, _ in scores)
250
  all_human = all(s <= 0.5 for s, _, _ in scores)
251
  if all_ai:
252
- confidence = min(0.97, confidence * 1.05)
 
 
253
  elif all_human:
254
- confidence = max(0.51, confidence * 0.95)
 
255
 
256
  confidence = round(max(0.51, min(0.97, confidence)), 4)
257
 
 
258
  logger.info(
259
- f"Fusion: {len(scores)} sources → confidence={confidence}, "
260
- f"source={decision_source}, mode={mode}"
261
  )
262
 
263
  return confidence, decision_source, mode
 
200
  return None
201
 
202
 
203
+ def _parse_clap_result(
204
+ result: Optional[CLAPResult],
205
+ ) -> Optional[_ExternalScore]:
206
+ """Convert CLAPResult to internal score format."""
207
+ if not result or not result.available:
208
+ return None
209
+ return _ExternalScore(
210
+ available=True,
211
+ is_ai=result.is_ai,
212
+ confidence=result.confidence,
213
+ )
214
+
215
+
216
+ def _parse_fst_result(
217
+ result: Optional[FSTResult],
218
+ ) -> Optional[_ExternalScore]:
219
+ """Convert FSTResult to internal score format."""
220
+ if not result or not result.available:
221
+ return None
222
+ return _ExternalScore(
223
+ available=True,
224
+ is_ai=result.is_ai,
225
+ confidence=result.confidence,
226
+ )
227
+
228
+
229
  # ═══════════════════════════════════════════════════════════════════════
230
  # PRIVATE — Score fusion
231
  # ═══════════════════════════════════════════════════════════════════════
232
 
233
  def _fuse(
234
  local: float,
235
+ clap: Optional[_ExternalScore],
236
+ fst: Optional[_ExternalScore],
237
  ext_mai: Optional[_ExternalScore],
238
  ext_ses: Optional[_ExternalScore],
239
  ) -> tuple[float, str, str]:
240
  """
241
+ Fuse local + CLAP + FST + legacy external scores.
242
+
243
+ Weight allocation (normalized when layers are missing):
244
+ Layer 1 (local features+vocals): 0.40
245
+ Layer 2 (CLAP embedding): 0.20
246
+ Layer 3 (FST external): 0.25
247
+ Legacy (music_ai): 0.10
248
+ Legacy (ses_analizi): 0.05
249
 
250
  Returns: (confidence, decision_source, analysis_mode)
251
  """
252
+ scores: list[tuple[float, float, str]] = []
253
+
254
+ # Layer 1 — always present
255
+ scores.append((local, 0.40, "auris_local"))
256
+
257
+ # Layer 2 — CLAP embeddings
258
+ if clap and clap.available:
259
+ scores.append((clap.confidence, 0.20, "clap"))
260
 
261
+ # Layer 3 — FST external (highest individual accuracy)
262
+ if fst and fst.available:
263
+ scores.append((fst.confidence, 0.25, "fst"))
264
 
265
+ # Legacy external services
266
  if ext_mai and ext_mai.available:
267
+ scores.append((ext_mai.confidence, 0.10, "music_ai"))
268
 
269
  if ext_ses and ext_ses.available:
270
+ scores.append((ext_ses.confidence, 0.05, "ses_analizi"))
271
 
272
  # Normalize weights
273
  total_weight = sum(w for _, w, _ in scores)
274
  confidence = sum(s * (w / total_weight) for s, w, _ in scores)
275
 
276
+ # Determine decision source based on available layers
277
+ n_layers = sum(1 for _, _, src in scores if src != "auris_local")
278
+ if n_layers >= 2:
 
 
279
  decision_source = "auris_fusion"
280
+ elif n_layers == 1:
281
+ # Name includes the single extra layer
282
+ extra = [src for _, _, src in scores if src != "auris_local"][0]
283
+ decision_source = f"auris_fusion_{extra}"
284
  else:
285
  decision_source = "auris_local"
 
286
 
287
+ mode: str = "production"
288
+
289
+ # Clamp to valid range
290
  confidence = max(0.51, min(0.97, confidence))
291
  confidence = round(confidence, 4)
292
 
293
+ # Agreement bonus
294
  if len(scores) > 1:
295
  all_ai = all(s > 0.5 for s, _, _ in scores)
296
  all_human = all(s <= 0.5 for s, _, _ in scores)
297
  if all_ai:
298
+ # More layers agreeing → bigger boost
299
+ boost = 1.0 + 0.02 * len(scores)
300
+ confidence = min(0.97, confidence * boost)
301
  elif all_human:
302
+ dampen = 1.0 - 0.02 * len(scores)
303
+ confidence = max(0.51, confidence * dampen)
304
 
305
  confidence = round(max(0.51, min(0.97, confidence)), 4)
306
 
307
+ sources_str = ", ".join(f"{src}={s:.3f}" for s, _, src in scores)
308
  logger.info(
309
+ f"Fusion: {len(scores)} sources [{sources_str}] "
310
+ f"-> confidence={confidence}, source={decision_source}"
311
  )
312
 
313
  return confidence, decision_source, mode