[Backend] Compact the transcript prompt — merge same-speaker runs and use block timestamps #341
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: MEDIUM. Found in the August 2026 session lifecycle review (#319). Cheap, architecture-independent, and worth doing regardless of which summarisation design lands.
The opportunity
Roughly 35% of a transcript prompt is per-line overhead. The format
[HH:MM:SS] Name:costs 10-14 tokens per line: about 7 for the bracketed stamp, 2-5 for the name, one newline. At ~1,700 lines centrally, that is ~20k tokens of pure tax out of a ~55k prompt.Proposed fix
Two changes with no information loss that matters to a summary:
Together these recover an estimated 10-15k tokens on every full-transcript prompt. Measure before and after on a real transcript rather than trusting the estimate.
Note the coupling: any format change must update
_TRANSCRIPT_LINE_RE(reminder_tasks.py:2786), the highlights prompt (audio_service.py:933-951), and every prompt that describes the line format. Landing the segment-rows issue first makes this much safer, since the text becomes a render rather than the source of truth.Acceptance criteria
Measured on real transcripts
Run against all seven production transcripts over 20k chars, in the prod backend container (read-only; no transcript text left the container).
chars/token = 3.4, the codebase's own estimate.The estimate was optimistic. The issue projected ~35% of the prompt as per-line overhead and 10-15k tokens recovered. Actual: 10.9% of characters, ~1,900-5,100 tokens per session. Line counts fell 24-30%, not "a large fraction" — speech clusters, but less than assumed. Still worth doing: it is free, lossless, and it is the margin that keeps a borderline session out of the chunked path.
Deviations from the proposed fix
1. Runs are capped at 30 seconds. Merging without a time bound collapses a five-minute GM monologue to one line at one timestamp — every beat drawn from it then shares a start time, so ordering within the monologue is lost and cited evidence can sit minutes from what it describes. Three existing beat tests failed on exactly that shape, which is how it surfaced. The cap costs ~1 percentage point (11.9% → 10.9%).
2. Per-block timestamps are NOT implemented, deliberately. Under block stamps a citation can only say "somewhere in this 30 seconds", and a block holds several speakers — so "this actor spoke this line" degrades to "this actor spoke near this line". That is precisely the verification #332/#333/#334 exist to provide, and it is worth more than the remaining tokens. The issue's own hedge — "keeping exact per-line stamps only where the beat pipeline needs citations" — resolves to everywhere, because any line can be evidence.
If the token pressure ever justifies revisiting this, the safe version is block headers in the prompt plus a per-line stamp retained on every line, which saves nothing — so realistically this half of the issue is closed as not-worth-doing rather than deferred.
Consumers
The format did not change, so
_TRANSCRIPT_LINE_RE, the highlights prompt and the beat prompts needed no edit. Compaction applies to the prompt only —session.transcriptis untouched, and every consumer downstream ofsummarise()reads the same compacted text, since the validator resolves stamps against it and indexing a different rendering than the model saw would fail every citation.beat_service._LINE_REis now publicLINE_RE— it is the shared definition of the line format, and re-declaring it per consumer is the coupling #335 set out to remove.Acceptance criteria: merged ✓, block timestamps ✗ (closed as harmful, reasoning above), measured ✓, consumers unchanged ✓, beat references still resolve ✓ (
test_every_remaining_stamp_still_indexes_to_a_real_line).Verified against the acceptance criteria as amended by the 2026-08-26 comment, which is the right yardstick here.
compact_transcript(audio_service.py:1591-1690), capped at 30 seconds per run so a monologue's internal ordering is not collapsed. Tested (tests/test_transcript_compaction.py:23-115).test_every_remaining_stamp_still_indexes_to_a_real_line.That measurement later paid for itself: it is what made the #345 conditional-range decision arguable from data rather than opinion.
Closing. Part of a full acceptance-criteria pass across the v4.0.0 milestone.