fix(azad-text): stop number conversion from mangling ordinary speech#3
Open
Montana wants to merge 1 commit into
Open
fix(azad-text): stop number conversion from mangling ordinary speech#3Montana wants to merge 1 commit into
Montana wants to merge 1 commit into
Conversation
- Digit sequences are cardinal-only, so ordinals are no longer consumed:
'give me one second' no longer becomes 'give me 1 2'. Ordinals still
combine into cardinals ('twenty fourth' -> 24).
- Plural scale words stay verbatim: 'hundreds of people' no longer
becomes '100 of people' (generalizes the existing 'seconds' carve-out).
- Number candidates ending in 'and' are rejected so the connective
survives: 'between five and six' no longer becomes 'between 5 six'.
Interior 'and' still parses ('two hundred and sixty six' -> 266).
Adds regression tests for all three cases; each fails on main.
spence
added a commit
that referenced
this pull request
Jul 8, 2026
Goal #3 of the dual-stream rework: let the stronger 560ms model sharpen the live caption in place while the user speaks, without churning it (goal #1). The refined stream is fed back into the live display in dual-stream mode, but `emit_replacement_live_display` now clamps how far back a refined correction can reach via a mode-specific mutable-tail budget: legacy keeps its wide 36-token tail; dual-stream uses a tight 12-token tail. Because the 560ms stream lags the 80ms stream by ~1-2 words and can re-segment, the tight tail freezes the committed prefix so subtle corrections (and filler decisions) stick instead of flip-flopping, while a large divergence can only ever rewrite the volatile tail. Measured headless on a 156s turn vs legacy_stitch: dual applies 64 in-place word-sharpening corrections (for->forty, week->weekly, per->percent, ...), all <=2 tokens, with live-caption rollback max=2 == legacy max=2 — parity on churn, same correction profile, and faster. Replaces the earlier finalize-only swap. `stabilize_live_display_replacement` / `find_live_display_stable_boundary` take the mutable tail as a parameter; callers pass the mode-appropriate budget.
spence
added a commit
that referenced
this pull request
Jul 8, 2026
Make dual-stream the app default now that it beats legacy_stitch headless on all three priorities: zero-churn live caption (rollback max 2 == legacy), fast high-quality finalization (faster on every measured turn, no stitch/bailout), and subtle in-place corrections from the 560ms stream (goal #3). Takes effect on the next install; the running app is untouched. Live smoke test to follow in an idle window before retiring the legacy stitcher machinery.
spence
added a commit
that referenced
this pull request
Jul 9, 2026
…eze-append)
During speech the caption was composed from the refined 560ms re-decode, which
re-segments its trailing window as more audio arrives. Every re-decode painted
straight onto the caption, so on repeated-phrase content it visibly swapped
between equivalent phrasings mid-speech ("sub agents" <-> "subagents",
"they're" <-> "their") and toggled punctuation on words already shown
("possible" <-> "possible,"). The two prior gates were blind to this: they
checked only token count and normalized match-keys, so count-neutral and
growth-riding flips passed freely.
Replace the whole-candidate accept/hold decision with per-token freeze-append
(`splice_live_display_forward`): every already-shown word is immutable. A
candidate may only grow (append net-new trailing words), complete the last
word being typed ("gen" -> "generate"), or otherwise freeze `previous` and
append just its new tail. Interior re-segmentations and cosmetic (case/
punctuation) re-renders of shown words are never repainted live — they settle
in the single coalesced ReplaceLine at finalize, which sees the whole turn and
is dither-proof. New words and the growing edge still stream at refined
quality, so this keeps the live-refinement value without the flicker; only
corrections to already-shown words defer to finalize (goal #1 zero-churn >
goal #3 in-place sharpening).
Finalize output is byte-for-byte unchanged. Investigated via multi-agent
replay + offline simulation over real recorded live-caption event streams
(headless file-replay can't reproduce the churn — the fast feed starves the
refined worker); word- and punctuation-flicker both go 66/129 -> 0 with zero
leading-edge lag. Adds splice unit tests + a match-key golden equivalence
test; removes the retired whole-candidate classifier.
spence
added a commit
that referenced
this pull request
Jul 9, 2026
The lagging 560ms refined stream re-segments its trailing window ("sub agents"
<->"subagents", punctuation toggles) and folds each re-decode into the caption,
so already-shown words visibly swap phrasings mid-speech (goal #1 violation).
The mutable tail bounds how far back a refined replacement may reach; at 12 it
was loose enough to re-write a whole clause of settled text.
Make the tail a PipelineConfig knob (--live-display-mutable-tail) and tune the
default 12 -> 4 from recorded-turn replay: aggregate visible caption churn drops
~60% (42 -> 17 flips across four turns; the churniest turn 24 -> 7) with the
pasted finalize decode byte-identical on every turn (goal #2 untouched). The
only cost is the refined stream landing in-place touch-ups (goal #3, lowest
priority) a little later.
It cannot stall the caption the way the reverted freeze-append did: the
monotonic streaming stream still supersedes a held replacement, so a tighter
tail only enlarges the frozen prefix, never withholds forward progress. Realtime
(wall-clock-paced) replay confirms the caption still reaches full length and
keeps pace (54 frames, 41 words, identical to the loose tail).
Stabilizer mechanics unit-tests are pinned to an explicit tail so the tuning
default is decoupled; a new test guards that 4 freezes a settled swap 12 tokens
back that 12 would let through. caption_churn.py measures churn from the
--events-jsonl stream for future caption-change validation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hey @spence,
Adds regression tests for all three cases; each fails on main.
Cheers,
Michael