Repository navigation
Stream every AI answer live, token by token - #149
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Live drafts show result text while the request still runs, so specs now wait for the draft to close before reading rows. Also fix the Library form selectors these specs still used from before the design shell. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Jam recorder wraps window.fetch and keeps an aborted response streaming, so Cancel left the page waiting for the whole answer. The live reader now cancels itself on abort. The live proxy records the answering model from the first chunk and stops the upstream call when its caller hangs up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ded sources Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reachedEnable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Next included review available in 8 minutes. View limit details
ℹ️ Recent review info
📝 Walkthrough
Note 🎁 Summarized by CodeRabbit Free
Comment |
…order - Cap a streamed answer's text at the plain reply's 1 MiB; the SSE frames around it get room for their per-token overhead. - A Sparring screen that rejoins a running request gets the draft so far and every later one. - A throttled draft is delivered before the next phase, so Checking never flips back to Writing live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codex review, round 1 (head 2e24502): three findings, all fixed in 0416044.
After the fixes: |
Live drafts and run controllers are module-level, keyed by tool. Sign-out and account switches reset the session store but left them, so the next account could see the previous one's draft. The session reset now aborts every tool run and clears every draft first. (Codex review r2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codex review, round 2 (head 0416044): one finding, fixed in c2c512d. Medium: live drafts survived an account change. The run controllers and drafts in After the fix: |
AskPage already dropped a late answer when the debate scope changed. The account is now part of that scope, so a reply, or its live text, asked by one account never lands in the next one's chat. (Codex review r3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codex review, round 3 (head c2c512d): one finding, fixed in a6ee0c1. Medium: a structured Ask could keep writing into another account's chat. In one-off mode both accounts have the same (empty) chat scope, so an Ask asked by account A stayed current after B signed in, and its next live text recreated A's bubble. The plain final reply had the same gap before this PR. Fixed: the signed-in account is now part of AskPage's request scope. On an account change the page aborts the request and clears the live bubble and loading state. Test:
|
|
Codex review, round 4 (head a6ee0c1): no findings. It checked the Ask account-switch fix and its test, the earlier fixes, the SSE transport, repair retries, cancellation, usage accounting, draft rendering, and the repo standards. Every finding from rounds 1–3 is fixed. |
On mobile, a full-page screenshot could outlast the one-second fake stream before the draft's ARIA snapshot. fake.streamDelay(ms) slows streamed chunks for one test; the next reset restores the default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codex review, round 5 (head baf3f8d, the fake-provider stream-delay control): no findings. That's two clean rounds in a row on the current code. |
Not merging yet: you asked for full send without merge. Video and screenshots are attached below (live mode: real OpenRouter pinned to Muse Spark contributor, real Exa).
live-generation-demo.mp4
Every AI answer in DebateLab now appears while the model writes it, not after the whole reply lands.
What changes for students
ai-stream, unchanged). Stop now cancels these requests as well.How it works
aiEdge Function: a request withstream: truegets Server-Sent Events (supabase/functions/ai/stream.ts):phase,delta(raw model text),reset(a JSON repair retry starts again), and one finalresultwith the status and body a plain request returns. Every check before the feature handler (auth, input, debate context, quota, model routing, source ranking) still answers with plain JSON and its real status.provider.ts: only the main call whose output the user sees streams (stream: true+stream_options.include_usage). Research sub-calls and Jev never stream. The joined text goes through the sameparseProviderContent, so outputs are identical; streamed usage is billed, and marked incomplete if the stream breaks.src/lib/ai/live.tsreads the stream and turns a failedresultinto the sameFunctionsHttpErrora plain request gets, so limit, paywall, and provider-error handling is unchanged. A JSON body is accepted too, so this client works against the currently deployedaifunction until it is redeployed.packages/core/src/ai/partial-json.ts: a tolerant prefix parser (cut strings, escapes, surrogate pairs, partial literals and numbers, fences). Drafts never show a value that may still change.livecallback (section regeneration, Crossfire signals, Workspace practice) are unchanged.Tests
partial-json.test.ts(parser, every prefix of a real answer),src/test/ai/live.test.ts(stream reader: JSON fallback, split events, reset, error mapping, truncated stream),supabase/functions/ai/stream.test.ts(streamed body equals the plain body byte for byte, repair retry resets and bills both attempts, provider failure, pre-checks stay JSON).recipes/live-generation.spec.ts(draft on screen mid-stream, Cancel, every tool's event stream) andexpectStreamedin the practice recipes.features/live-generation.md,SKILL.md, and the feature index are updated. The fake provider streams structured answers in small chunks.airesponses throughe2e/helpers/ai-response.ts(event stream or JSON), including the live, canary, and staging specs.expectResult), since a draft shows result text before the result is saved. Three of them also had stale Library selectors from the design shell; fixed.Screenshots (live mode, mid-stream):
Verification (local; GitHub Actions is blocked by billing)
Every Actions job is refused before it starts ("recent account payments have failed or your spending limit needs to be increased"), so these are local runs, not CI passes.
At the current head:
npm run verify(lint, typecheck, unit, edge, build, service worker, desktop smoke 30/30): pass..superset/db-gate.sh): pgTAP 419/419, browser suite 71 passed, 1 skipped.npm test301 passed,npm run test:edge339 passed.Earlier in the PR (each run again after the later changes it touched):
drive all72 passed, 4 skipped (Workspace autosave needs--collab), desktop and mobile;e2e/full-stack/68 passed; agentic journeys 24 passed;ai-tools,auth,live-generation, full-stackcase-generationrerun at the end.recipes/live-generation6/6 (desktop + mobile, Cancel included),recipes/practice6/6,e2e/live/8/8.Codex review: five rounds. Rounds 1–3 found five issues (frame-size cap, rejoined Sparring drafts, phase order, and two cases of live text crossing an account change); all are fixed with tests, and the replies are in the comments below. Rounds 4 and 5 (current head): no findings.
Found and fixed along the way: the Jam recorder wraps
window.fetchand keeps an aborted response streaming, so Cancel did not stop a run (the request finished in the background and still autosaved its sources). The live reader now cancels itself on abort. Plain requests without live generation still have that limitation; it predates this PR.Not covered: staging and production (the
aiEdge Function is not deployed by a merge), WebKit, voice audio from ElevenLabs.Deploy notes
Merging ships the client only. The client falls back to JSON, so it works against today's
aifunction, just without live drafts. Live generation needs theaiEdge Function deployed (staging, then production).🤖 Generated with Claude Code
Summary by CodeRabbit