Spaces:
Runtime error
Runtime error
Add speaker labels
Browse files- app/agents/claims.py +39 -1
app/agents/claims.py
CHANGED
|
@@ -126,6 +126,40 @@ def run_claim_extractor(transcript: str) -> Dict[str, Any]:
|
|
| 126 |
return {"claims": []}
|
| 127 |
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
# =========================
|
| 130 |
# Public: extract_claims (used by orchestrator)
|
| 131 |
# =========================
|
|
@@ -146,10 +180,14 @@ def extract_claims(segments: List[Dict]) -> List[Claim]:
|
|
| 146 |
text = (c.get("text") or "").strip()
|
| 147 |
if not text:
|
| 148 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
out.append(Claim(
|
| 150 |
id=f"c{i}",
|
| 151 |
text=text,
|
| 152 |
-
speaker=c.get("speaker"),
|
| 153 |
segment_idx=0, # TODO: map to true segment via start/end if available
|
| 154 |
confidence=float(c.get("confidence", 0.6)),
|
| 155 |
))
|
|
|
|
| 126 |
return {"claims": []}
|
| 127 |
|
| 128 |
|
| 129 |
+
# =========================
|
| 130 |
+
# Helper Functions
|
| 131 |
+
# =========================
|
| 132 |
+
|
| 133 |
+
def _find_speaker_for_claim(claim_text: str, segments: List[Dict]) -> str:
|
| 134 |
+
"""
|
| 135 |
+
Find which speaker made a claim by matching claim text to transcript segments.
|
| 136 |
+
Uses fuzzy matching to handle slight variations in wording.
|
| 137 |
+
"""
|
| 138 |
+
claim_words = set(claim_text.lower().split())
|
| 139 |
+
best_match_speaker = None
|
| 140 |
+
best_match_score = 0
|
| 141 |
+
|
| 142 |
+
for segment in segments:
|
| 143 |
+
segment_text = segment.get("text", "").lower()
|
| 144 |
+
segment_words = set(segment_text.split())
|
| 145 |
+
|
| 146 |
+
# Calculate overlap score (Jaccard similarity)
|
| 147 |
+
if segment_words:
|
| 148 |
+
intersection = claim_words.intersection(segment_words)
|
| 149 |
+
union = claim_words.union(segment_words)
|
| 150 |
+
score = len(intersection) / len(union) if union else 0
|
| 151 |
+
|
| 152 |
+
# Also check if claim is a substring (for exact matches)
|
| 153 |
+
if claim_text.lower() in segment_text or any(word in segment_text for word in claim_words if len(word) > 3):
|
| 154 |
+
score += 0.2 # Boost for substring matches
|
| 155 |
+
|
| 156 |
+
if score > best_match_score:
|
| 157 |
+
best_match_score = score
|
| 158 |
+
best_match_speaker = segment.get("speaker")
|
| 159 |
+
|
| 160 |
+
# Only return speaker if we have a reasonable confidence match
|
| 161 |
+
return best_match_speaker if best_match_score > 0.2 else None
|
| 162 |
+
|
| 163 |
# =========================
|
| 164 |
# Public: extract_claims (used by orchestrator)
|
| 165 |
# =========================
|
|
|
|
| 180 |
text = (c.get("text") or "").strip()
|
| 181 |
if not text:
|
| 182 |
continue
|
| 183 |
+
|
| 184 |
+
# Map claim to speaker by finding which segment contains this text
|
| 185 |
+
speaker = _find_speaker_for_claim(text, segments)
|
| 186 |
+
|
| 187 |
out.append(Claim(
|
| 188 |
id=f"c{i}",
|
| 189 |
text=text,
|
| 190 |
+
speaker=speaker or c.get("speaker"),
|
| 191 |
segment_idx=0, # TODO: map to true segment via start/end if available
|
| 192 |
confidence=float(c.get("confidence", 0.6)),
|
| 193 |
))
|