Skip to content

wip(web): an earlier voice input path, through zorp-web transcribe - #116

Draft
adityak74 wants to merge 2 commits into
mainfrom
feat/voice-input
Draft

wip(web): an earlier voice input path, through zorp-web transcribe#116
adityak74 wants to merge 2 commits into
mainfrom
feat/voice-input

Conversation

@adityak74

Copy link
Copy Markdown
Contributor

Parked work recovered from an abandoned agent worktree. Draft on
purpose: this is here to be triaged, not reviewed.

Adds zorp-web/src/transcribe.rs and web/src/voice.ts with tests. Very likely superseded: the zorp-voice crate shipped in #107 and solves the same problem a different way.

The worktree it came from was 56 or more commits behind main, so treat
the base as stale. Nothing here has been built or tested against current
main.

https://claude.ai/code/session_01KGPVQ8wUG7h36zashWYCp4

The chat streamed tool activity but not the answer. `complete_with_options`
is a blocking POST, so the browser got one `assistant` event carrying the
finished text. On a hosted model that is a couple of seconds. On
qwen3.8:27b-mlx on a laptop it is a minute of spinner and then a wall of
text, so this had to start at the model layer.

`Model::complete_streaming` is now the agent loop's call. Its default body
runs the buffered path and reports the answer as a single delta, so every
existing Model keeps working and there is one code path in the loop rather
than a streaming branch and a buffered branch that drift. `Renderer` gains
`assistant_delta`, empty by default, so the CLI is untouched. Anthropic
keeps the buffered path; its event protocol is its own work.

The part that makes a naive version worse than none: `extract_think_tags`
strips `<think>...</think>` from content, so a qwen-family model's chain of
thought never reaches the browser today. Streaming raw deltas would put it
on screen as the answer. `ThinkGate` filters it and withholds text that
could still be a tag opening, because tags arrive split across chunks. The
accumulator then rebuilds a buffered-shaped response and reuses
`parse_assistant_completion`, so the two paths cannot disagree.

Four defects found by running it, not by reading it. Two were invisible to
a green suite:

- `requestAnimationFrame` stored as a bare default parameter loses its
  receiver, so the browser threw "Illegal invocation" and nothing ever
  painted. Every test injected its own scheduler, so the default had no
  coverage until it broke in a real browser.
- Ending the streamed message on `working_done` looked right and was
  wrong: it fires before the finished answer is sent, so every answer
  rendered twice.
- Pruning fragments from the SSE backlog panicked the streaming task and
  poisoned the session mutex. That code holds an index into the vector
  across polls and says so in a comment. The backlog is append-only.
- A provider that ignores `stream` and returns a JSON body decoded as an
  empty stream: no error, no text. Now handled and reported as one delta.

All four are mutation-checked: restoring each defect fails the test written
for it. Verified in a browser against a real local model, 25 progressive
renders growing 18 to 377 characters, one message, no console errors, and
the injection suite still yields xssRan false with zero scripts, images,
iframes, handlers or anchors.

Claude-Session: https://claude.ai/code/session_01RntquUN2UV37oo4C4EUBYd
Adds zorp-web/src/transcribe.rs and web/src/voice.ts with tests. This
predates the zorp-voice crate that shipped in #107 and solves the same
problem a different way, so it is very likely superseded.

Parked from an abandoned agent worktree, 57 commits behind main
at the time it was committed. Opened as a draft so it can be triaged
rather than reviewed.

Claude-Session: https://claude.ai/code/session_01KGPVQ8wUG7h36zashWYCp4
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