Add anonymous speaker tracking (online diarization)
Unrecognized speakers now get stable IDs like "unknown_a7f3" instead of None. Uses online clustering of Resemblyzer embeddings: - Matches against tracked anonymous speakers (cosine > 0.70) - Updates running average embedding on re-identification - Creates new ID from SHA-256 hash of quantized embedding - Expires after 1 hour of silence, max 10 tracked simultaneously New API: POST /speakers/promote?anon_id=unknown_a7f3&name=Alex Promotes an anonymous speaker to enrolled using their averaged embedding. Flow: unknown person speaks → "unknown_a7f3" → you ask "who's that?" → promote to "Bob" → now recognized by name going forward. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
headmic.py
13
headmic.py
@@ -868,6 +868,19 @@ async def list_speakers():
|
||||
return {"speakers": speaker_recognizer.list_speakers()}
|
||||
|
||||
|
||||
@app.post("/speakers/promote")
|
||||
async def promote_speaker(anon_id: str, name: str):
|
||||
"""Promote an anonymous speaker (unknown_XXXX) to an enrolled speaker.
|
||||
Uses their accumulated embedding average — no new audio needed."""
|
||||
if speaker_recognizer is None:
|
||||
raise HTTPException(status_code=503, detail="Speaker recognition not available")
|
||||
if not anon_id.startswith("unknown_"):
|
||||
raise HTTPException(status_code=400, detail="anon_id must start with 'unknown_'")
|
||||
if speaker_recognizer.promote_anonymous(anon_id, name):
|
||||
return {"promoted": anon_id, "name": name, "speakers": speaker_recognizer.list_speakers()}
|
||||
raise HTTPException(status_code=404, detail=f"Anonymous speaker '{anon_id}' not found")
|
||||
|
||||
|
||||
@app.delete("/speakers/{name}")
|
||||
async def delete_speaker(name: str):
|
||||
"""Remove a speaker."""
|
||||
|
||||
Reference in New Issue
Block a user