Files
Chris Tate 7e3a3157d0 feat(core): support streaming fetch responses (#300)
* feat(core): support streaming fetch responses

- Add a typed line-streaming Cmd.fetch overload and carry it through the command wire and runtime host.

- Keep stream lifecycle deterministic with loud cancellation and duplicate-key rejection.

- Cover the feature with contract, conformance, runtime, compiled-core, harness, example, and documentation updates.

* feat: stream AI chat through Vercel gateway

- Render chat-completion SSE deltas as they arrive.
- Pin the example to Vercel AI Gateway with official key config.
- Cover streaming, failure, and replay paths end to end.

* feat: add streaming fetch and chatbot example

* feat(chatbot): refine streaming chat experience

- Add a compact live model picker and immediate Stop action.

- Improve conversation layout, prompt focus, and caret retention.

- Expand chatbot documentation and end-to-end regression coverage.

* fix: harden streaming fetch limits

* fix: harden streaming fetch and textarea behavior

* fix(canvas): render lifted rich text
2026-08-09 21:02:29 -05:00

44 lines
2.3 KiB
Plaintext

# The chat client's core-logic loop, headless: replay with
# native dev --core --script dev-script.ndjson
# Msgs dispatch into update; each Gateway SSE line is an ordinary Msg, so
# deltas and terminals are fed back by hand exactly as the transcript's
# `cmd fetch ...` lines invite — the same loop the native app runs, with
# you standing in for the network.
# The API key arrives through the env channel as an ordinary Msg (under
# the real app the generated wiring dispatches it from the environment
# at install). The endpoint and openai/gpt-5.6-luna default are fixed in
# core.ts; nothing dials out under the core host.
{"kind":"key_set","value":{"$bytes":"example-gateway-key"}}
# Type a message (the composer runs the SDK byte-splice text engine) and
# send. The transcript shows the streaming fetch command whole: POST, the
# Gateway endpoint, SSE accept and runtime-built authorization headers,
# and the JSON body — system prompt first, then history and stream:true.
{"kind":"draft_edit","edit":{"kind":"insert_text","text":{"$bytes":"Say hi in two words"}}}
{"kind":"send"}
# Two Gateway deltas grow the pending assistant response immediately. The
# explicit [DONE] marker and HTTP terminal then commit the whole turn.
{"kind":"chat_line","line":{"$bytes":"data: {\"choices\":[{\"delta\":{\"content\":\"Hi\\n\"}}]}"}}
{"kind":"chat_line","line":{"$bytes":"data: {\"choices\":[{\"delta\":{\"content\":\"there!\"}}]}"}}
{"kind":"chat_line","line":{"$bytes":"data: [DONE]"}}
{"kind":"chat_done","status":200}
# A second turn grows the history: watch the request body carry both
# earlier turns before the new question.
{"kind":"draft_edit","edit":{"kind":"insert_text","text":{"$bytes":"And a follow-up?"}}}
{"kind":"send"}
# This time the Gateway fails with its own JSON error line — the failed
# state keeps the history and surfaces error.message as the reason.
{"kind":"chat_line","line":{"$bytes":"{\"error\":{\"message\":\"model overloaded\",\"type\":\"server_error\"}}"}}
{"kind":"chat_done","status":500}
# Retry re-sends the SAME conversation (no new turn); a success resolves
# it into the fourth turn.
{"kind":"retry"}
{"kind":"chat_line","line":{"$bytes":"data: {\"choices\":[{\"delta\":{\"content\":\"Certainly.\"}}]}"}}
{"kind":"chat_line","line":{"$bytes":"data: [DONE]"}}
{"kind":"chat_done","status":200}