UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

145 lines (104 loc) 4.84 kB
--- title: "Streaming" description: "Consume eve client stream events live, reconnect by event index, and aggregate turn results." --- Every `ClientSession.send()` call posts the turn, then reads the session's NDJSON (newline-delimited JSON) event stream. `MessageResponse` gives you two ways to consume that stream, aggregating it with `result()` or iterating it live. ## Aggregate a turn Use `result()` when you only need the final turn summary: ```ts const response = await session.send("Summarize the latest forecast."); const result = await response.result(); console.log(result.status); console.log(result.message); console.log(result.events.length); ``` This consumes the stream until the current turn boundary: - `session.waiting` - `session.completed` - `session.failed` ## Stream events live Use `for await...of` when you want to render progress: ```ts const response = await session.send("Draft a plan and show your work."); for await (const event of response) { if (event.type === "message.appended") { process.stdout.write(event.data.messageDelta); } if (event.type === "message.completed" && event.data.finishReason !== "tool-calls") { console.log("\nfinal:", event.data.message); } } ``` `message.appended` and `reasoning.appended` are incremental delta events. Their completed forms, `message.completed` and `reasoning.completed`, are the compatibility path for clients that don't render deltas. ## Handle event types Import event types from `eve/client` when you want exhaustiveness or helpers: ```ts import type { HandleMessageStreamEvent } from "eve/client"; import { isCurrentTurnBoundaryEvent } from "eve/client"; function handleEvent(event: HandleMessageStreamEvent) { if (isCurrentTurnBoundaryEvent(event)) { console.log("turn settled:", event.type); } } ``` The most common UI events are: | Event | Use | | -------------------- | ---------------------------------------------------------------- | | `message.received` | Confirm the user message landed. | | `reasoning.appended` | Render reasoning deltas when the model provides them. | | `message.appended` | Render assistant text deltas. | | `actions.requested` | Show tool calls requested by the model. | | `action.result` | Show tool call results. | | `input.requested` | Pause the UI for approval or a question answer. | | `result.completed` | Read structured output from an [output schema](./output-schema). | | `session.waiting` | Enable the composer for the next turn. | | `session.completed` | Mark the conversation terminal. | | `session.failed` | Mark the conversation failed. | For the complete event table, see [Sessions, runs & streaming](../../concepts/sessions-runs-and-streaming). ## Reconnection The client reconnects after transient stream disconnects. It resumes from the number of events already consumed in the current session: ```ts const client = new Client({ host: "https://agent.example.com", maxReconnectAttempts: 5, }); ``` `maxReconnectAttempts` is per turn. The default is `3`. ## Open a stream manually Use `session.stream()` when you already have a session cursor and only need to attach to the existing stream: ```ts const session = client.session({ continuationToken: "eve:6c8b1f2e-3d4a-4b9c-8e21-9f0a1b2c3d4e", sessionId: "wrun_01ARYZ6S41TSV4RRFFQ69G5FAV", streamIndex: 10, }); for await (const event of session.stream()) { console.log(event.type); } ``` Pass `startIndex` to override the stored cursor: ```ts for await (const event of session.stream({ startIndex: 0 })) { console.log(event.type); } ``` `stream()` throws if the session has no `sessionId`, because there's no stream to attach to before the first send. ## Abort a request Pass an `AbortSignal` to cancel the POST or stream. Arm the timeout before awaiting `send()` so it covers the POST as well as the stream: ```ts const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 10_000); const response = await session.send({ message: "Run a long analysis.", signal: controller.signal, }); for await (const event of response) { console.log(event.type); } clearTimeout(timeout); ``` Once a response is aborted, create a new send for the next turn. Don't reuse the same `MessageResponse`. ## What to read next - [Messages](./messages): the send APIs that create streams - [Continuations](./continuations): how stream cursors are persisted - [Output schema](./output-schema): consume `result.completed`