Skip to content

A recording is read with its speakers - #721

Merged
WaylandYang merged 1 commit into
devfrom
feat/a-recording-is-read-with-its-speakers
Sep 15, 2026
Merged

WaylandYang merged 1 commit into
devfrom
feat/a-recording-is-read-with-its-speakers

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

This is cut 3 of decision record 0040. Recordings are now read by a diarizing transcription model configured per workspace. A transcript without speaker labels is refused, as the maintainer decided: it degrades like a missing model.

What changes

  • Settings (migration 0060):
    • llm_settings gains transcribe_base_url, transcribe_api_key and transcribe_model. The key is sealed on write and by the startup backfill.
    • GET .../settings returns transcribe_base_url, transcribe_model and has_transcribe_key.
    • New PUT /workspaces/{id}/settings/transcribe. It re-queues the workspace's recordings waiting for transcription, including ones a previous model could not label.
    • POST .../settings/test gains transcribe: it sends one second of silence to the endpoint.
  • Request: OpenAI /audio/transcriptions with response_format=diarized_json, chunking_strategy=auto and a Bearer key.
  • Reading the transcript (utopia_ingest::transcript):
    • Speakers are written into the text. Consecutive segments from one speaker join into a turn that starts Speaker A:, using the model's label, so extraction sees who said what.
    • The whole recording is one provenance segment. After packing, each chunk's anchor is {"start_ms", "end_ms", "speaker": [...]}: the earliest start, the latest end, and the speakers heard in the chunk, in order.
    • A segment with text but no speaker, or a plain text with no segments, returns NoSpeakers.
    • No speech at all returns Unreadable.
  • Degrade: NoSpeakers is handled like NeedsReader. The document ends failed with reader_needed = 'transcribe', the document.needs_reader alert carries the actual reason, and the error is Terminal. The alert observer now takes the reader and the message.
  • Shared Reading: extracted from the MinerU module (utopia_ingest::Reading). Page regions widen the chunk's bbox, and time regions widen its time range and speaker list.
  • 0040 update:
    • records cut 3 as built;
    • the transcribed anchor becomes speaker: [..] and is required, because a chunk spans turns;
    • one recording is one request, so a failure retries the whole file (splitting audio would need a decoder the server doesn't carry).

The interface cut comes next: Models-page cards for the OCR service and transcription model, and the origin shown on evidence.

Verification

  • Checks: cargo test --workspace 824 passed, 0 failed. cargo clippy --workspace --all-targets -D warnings is clean, and pnpm build passes.
  • New tests:
    • transcript unit tests: turns and prefixes, a chunk's times and speakers at two budgets, refusal without speakers, silence is unreadable.
    • a_recording_is_read_with_who_said_what (wiremock endpoint): one request with the key, diarized_json and the model; ready with one transcribed chunk and anchor {0, 7250, ["A","B"]}.
    • a_transcript_that_cannot_say_who_spoke_waits_for_one_that_can: unlabelled means failed + reader_needed + alert + Terminal with no chunks kept. After saving a labelling model and re-queueing, the recording ends ready.
  • Scripted e2e on a real server (copied DB, Python fake endpoint):
    1. No model: the MP3 ends failed, waiting for transcription.
    2. PUT transcribe with whisper-1: answers requeued: 1; the endpoint returns unlabelled segments, and the document ends failed with "did not say who spoke".
    3. PUT with gpt-4o-transcribe-diarize and no key: requeued: 1, the old key is kept (the call still carries Bearer), and the document ends ready with one chunk.
      • anchor {"start_ms": 0, "end_ms": 9750, "speaker": ["A","B"]};
      • text Speaker A: … Who signs for Beta? / Speaker B: I sign for Beta Robotics….
    • Other checks:
      • one alert group with 2 lines, each giving its reason;
      • the settings view never echoes the key, and the stored key starts enc:v1:;
      • the settings test reports transcribe ok;
      • the three process jobs end failed/failed/done, each with attempts=1.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: WaylandYang <wayland0916@gmail.com>
@WaylandYang
WaylandYang merged commit aa0b6bd into dev Sep 15, 2026
4 checks passed
@WaylandYang
WaylandYang deleted the feat/a-recording-is-read-with-its-speakers branch September 15, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant