Skip to main content

Projection modes

Normative: MODEL.md §10.1.

The single typed event stream is canonical. A consumer may project it into the mode-shaped views popularized by LangGraph's stream_mode — without the engine offering a second stream:

ModeProjected fromYields
valuesstatefull channel state after each superstep
updatesnode_end{ [node]: update } per node that ran
customcustomeach ctx.emit payload
messagescustomjust the payloads that are token chunks
debugevery eventthe event itself
import { graph, channel, START, END, streamModes } from "@ilmek/core";
import type { ChannelMap, StateOf } from "@ilmek/core";

/** A token chunk — the payload shape the messages projection yields. */
type TokenChunk = { type: "token"; text: string };

/** Answer state — the prompt in, the streamed reply appended out. */
const AnswerState = {
prompt: channel.lastWrite<string>(""),
answer: channel.append<string>(),
} satisfies ChannelMap;

type AnswerState = StateOf<typeof AnswerState>;

const g = graph("answer", AnswerState)
.node("responder", async (state, ctx) => {
// Stream the answer token by token; tokens ride the transient channel.
for (const tok of ["Hel", "lo", "!"]) ctx.emitToken(tok);
return { answer: ["Hello!"] };
})
.edge(START, "responder")
.edge("responder", END)
.compile();

const prompt = "say hello";

// token-by-token, plus the committed update, multiplexed through one pass
for await (const part of streamModes(g, { prompt }, ["messages", "updates"])) {
if (part.mode === "messages") process.stdout.write((part.data as TokenChunk).text);
}

Projection adds no information​

Each projected part carries the seq and ns of the event it came from, so reconnect and subgraph-grouping still work after projecting. project(event, modes) is a pure function of one event, so the same filter runs on a live stream, a resumeStream, or a replay of a reconnect buffer.

Modes at a glance: values (full state per superstep) · updates ({ node: update }) · custom (every ctx.emit) · messages (token deltas) · debug (raw events).