Streaming
Normative: MODEL.md §10.
A run is one canonical stream of typed events. Every event carries a common envelope plus its type-specific fields:
{ "run_id": "…", "thread_id": "…", "seq": 7, "ns": [], "type": "node_end", … }
seq— monotonic within a run, 1-based, no gaps. A consumer that drops its connection reconnects and skips everything up to its last-seenseq, so a streamed run survives a broken pipe without replaying from the top.ns— the namespace path to the (sub)graph that emitted the event;[]at the root. Always[]today, reserved so subgraphs can tag their events without a breaking envelope change. Filter by graph offns, not the run id.
Event types
Every implementation emits these, in this order:
| Event | When |
|---|---|
run_start | run begins |
step_start { step, tasks } | superstep begins |
node_start { node, task_id } | task begins |
custom { payload } | ctx.emit(...) — delivered live, mid-superstep |
node_end { node, update } | task returns |
node_error { node, error } | task raises |
state { channels } | after REDUCE |
checkpoint { id } | after CHECKPOINT |
interrupt { pending } | run halts on a pause |
run_end { status } | done · interrupted · error · aborted |
interrupt is a distinct event type. Consumers never parse error text or poll
graph state to discover a pause — the defect this design exists to remove.
Two consumption styles
- TypeScript
- .NET (C#)
import { graph, channel, START, END, stream, streamModes } from "@ilmek/core";
import type { ChannelMap, StateOf } from "@ilmek/core";
/** Support state — one append-only channel of messages. */
const SupportState = {
messages: channel.append<string>(),
} satisfies ChannelMap;
type SupportState = StateOf<typeof SupportState>;
const g = graph("support", SupportState)
.node("agent", async (state, ctx) => ({ messages: ["hi"] }))
.edge(START, "agent")
.edge("agent", END)
.compile();
const input = { messages: ["hello"] };
// full event stream
for await (const ev of stream(g, input)) console.log(ev);
// projected into LangGraph-style mode views — see the next page
for await (const part of streamModes(g, input, ["messages", "updates"])) { /* … */ }
using Ilmek;
/// <summary>Support state — one append-only channel of messages.</summary>
sealed class SupportState
{
[Append] public List<string> Messages { get; set; } = new();
}
var g = Graph.Create<SupportState>("support")
.Node("agent", (state, ctx) => Update.For<SupportState>().Append(s => s.Messages, "hi"))
.Edge(Graph.Start, "agent")
.Edge("agent", Graph.End)
.Compile();
var input = Update.For<SupportState>().Append(s => s.Messages, "hello");
// full event stream
await foreach (var ev in IlmekRuntime.Stream(g, input)) Console.WriteLine(ev);
// projected into LangGraph-style mode views — see the next page
await foreach (var part in Streaming.Projected(
IlmekRuntime.Stream(g, input),
new[] { StreamMode.Messages, StreamMode.Updates })) { /* … */ }