ai.stream.TurnStream
The raw text-delta stream of ONE model turn. Pull-based: nothing is read from the wire until `next()` or `final_turn()` is called.
Signature
class ai.stream.TurnStreamThe raw text-delta stream of ONE model turn. Pull-based: nothing is read
from the wire until next() or final_turn() is called.
next() yields DELTA chunks (each item is only the newly arrived text,
never the accumulated prefix) and returns Done when the turn is over.
final_turn() drains the rest of the stream and returns the completed
turn: the accumulated text as one Text block, the folded stop_reason, and
usage where the wire provided token counts.
Source:<builtin>/ai/ns_stream/stream.bamlbytes 6151–21029
Fields
_event_source
ai.stream.EventSource | null_sse
baml.http.SseStream | null_decode
((string) -> ai.stream.Event[] throws ai.errors.InvokeError) | null_chunks
string[]_cursor
int_pending
_pcursor
int_text
string_stop_reason
ai.content.StopReason | null_input_tokens
int | null_output_tokens
int | null_done
bool_require_terminal
string | null_saw_terminal
bool_calls
_wire_call
ai.events.LLMCall | null_call_started
baml.time.Instant | null_ttft_from
baml.time.Instant | null_capture_sse
boolStatic methods
from_chunks
(chunks: string[]) -> ai.stream.TurnStreamA turn stream fed from literal delta chunks — the scripted counterpart of a live stream, for tests. There is no wire to be cut, so a scripted stream is never strict: the turn completes as StopReason.Complete after the last chunk.
A stream backed by one normalized-event callback. Reliability clients use this to relay a chosen member's deltas and final metadata while retaining TurnStream's ordinary accumulation and termination behavior. The source itself owns any retry/fallback policy; once it returns a TextDelta, that delta is immediately visible to the caller.
from_sse
(A turn stream over a live SSE connection. decode receives the raw
batch string from sse.next() (a JSON array of SseEvent objects; see
decode_sse_batch) and yields the events it contains.
require_terminal opts into the strict termination contract: pass the
provider id ("anthropic", "openai", …) when the protocol has an
explicit end-of-turn event that the decoder yields as TurnDone. The
stream then throws ai.errors.NetworkFailure if the connection ends
before that event arrives, rather than reporting the truncated text as a
completed turn. Leave it null (the default) only for a protocol whose
sole end-of-turn signal IS the socket closing.
Instance methods
_capture_batch
(self, batch: string) -> void(internal) Append a raw SSE batch to the wire record when capture is on. Telemetry must never fail the stream, so a malformed batch is skipped.
(internal) The strict-termination check: a no-op unless this stream required an explicit terminal event and the stream ended without one, in which case the truncated turn is reported as the transport failure it is instead of being passed off as complete. Called on every end-of-stream path.
_finish
(self) -> void(internal) Marks the turn over and releases the SSE connection behind it, if this stream has one.
_stamp_first_delta
(self) -> void(internal) Stamp time-to-first-token on this stream's wire record, once.
final_turn
(Drain the stream and return the completed turn. Safe to call at any point (including after Done); calling twice returns an equal turn.
On a strict stream (from_sse's require_terminal) that ended without the
provider's terminal event this throws ai.errors.NetworkFailure instead of
returning a turn — the stop_reason default below must never turn a
truncated stream into a StopReason.Complete answer.
next
(All READY text deltas, or Done when the turn is over. Metadata events are folded silently; calling next() after Done keeps returning Done.
Drain-ready-then-deliver: every event already decoded (the wire layer returns whole batches — everything that queued while the caller was busy) is returned as ONE batch, and the wire is only pulled when no deliverable text is in hand. Delta boundaries are preserved — every element is one provider text delta, so consumers that want each SSE delta individually iterate the batch. A consumer that keeps up sees one-element batches; a consumer that falls behind (e.g. a partial parse slower than the token interval) gets the whole backlog in one return instead of replaying it delta by delta — the pull-model equivalent of the legacy engine's latest-snapshot watch channel.