@langchain/langgraph
Version:
1 lines • 11.2 kB
Source Map (JSON)
{"version":3,"file":"convert.cjs","names":[],"sources":["../../src/stream/convert.ts"],"sourcesContent":["/**\n * Protocol event conversion — maps raw `[ns, mode, payload]` stream chunks\n * from graph.stream() to CDDL-aligned ProtocolEvents.\n */\n\nimport type { StreamMode } from \"../pregel/types.js\";\nimport type {\n Namespace,\n ProtocolEvent,\n ToolsEventData,\n UpdatesEventData,\n} from \"./types.js\";\n\n/**\n * The set of stream modes requested by\n * `streamEvents(..., { version: \"v3\" })` — every mode the protocol maps\n * to a channel.\n *\n * The verbose `\"debug\"` mode is intentionally excluded: it was a thin\n * re-wrap of `checkpoints` + `tasks` carrying no new information.\n *\n * The `\"checkpoints\"` mode is likewise excluded from the stream-mode\n * request because the protocol's `checkpoints` channel carries only a\n * lightweight envelope (`id`, `parent_id`, `step`, `source`) emitted as a\n * separate ``[namespace, \"checkpoints\", envelope]`` chunk before each paired\n * `values` chunk — not the full-state shape from Pregel's `checkpoints`\n * stream mode when subscribed via `debug`.\n */\nexport const STREAM_EVENTS_V3_MODES: StreamMode[] = [\n \"values\",\n \"updates\",\n \"messages\",\n \"tools\",\n \"custom\",\n \"tasks\",\n];\n\n/**\n * True when `payload` is a lightweight checkpoint envelope (not a full-state\n * Pregel `checkpoints` debug payload).\n */\nexport function isCheckpointEnvelope(payload: unknown): boolean {\n if (payload == null || typeof payload !== \"object\") return false;\n const p = payload as Record<string, unknown>;\n return (\n typeof p.id === \"string\" &&\n (\"source\" in p || typeof p.step === \"number\") &&\n !(\"values\" in p) &&\n !(\"config\" in p)\n );\n}\n\nexport interface ConvertToProtocolEventOptions {\n namespace: Namespace;\n mode: StreamMode;\n payload: unknown;\n seq: number;\n}\n\nfunction unwrapMessagesPayload(payload: unknown) {\n if (!Array.isArray(payload) || payload.length !== 2) {\n return { data: payload };\n }\n\n const [data, metadata] = payload;\n if (metadata == null || typeof metadata !== \"object\") {\n return { data: payload };\n }\n\n const record = metadata as Record<string, unknown>;\n const node =\n typeof record.langgraph_node === \"string\"\n ? record.langgraph_node\n : undefined;\n const runId = typeof record.run_id === \"string\" ? record.run_id : undefined;\n const dataWithRunId =\n runId != null && data != null && typeof data === \"object\"\n ? { ...(data as Record<string, unknown>), run_id: runId }\n : data;\n\n return { data: dataWithRunId, node };\n}\n\nexport function convertToProtocolEvent({\n namespace: ns,\n mode,\n payload,\n seq,\n}: ConvertToProtocolEventOptions): ProtocolEvent[] {\n const timestamp = Date.now();\n const base = { type: \"event\" as const };\n\n switch (mode) {\n case \"messages\": {\n const { data, node } = unwrapMessagesPayload(payload);\n return [\n {\n ...base,\n seq,\n method: \"messages\",\n params: { namespace: ns, timestamp, ...(node ? { node } : {}), data },\n },\n ];\n }\n\n case \"tools\":\n return [\n {\n ...base,\n seq,\n method: \"tools\",\n params: {\n namespace: ns,\n timestamp,\n data: convertToolsPayload(payload),\n },\n },\n ];\n\n case \"checkpoints\": {\n if (!isCheckpointEnvelope(payload)) {\n return [];\n }\n return [\n {\n ...base,\n seq,\n method: \"checkpoints\",\n params: { namespace: ns, timestamp, data: payload },\n },\n ];\n }\n\n case \"values\": {\n return [\n {\n ...base,\n seq,\n method: \"values\",\n params: { namespace: ns, timestamp, data: payload },\n },\n ];\n }\n\n case \"updates\": {\n const data = convertUpdatesPayload(payload);\n return [\n {\n ...base,\n seq,\n method: \"updates\",\n params: {\n namespace: ns,\n timestamp,\n // Surface the completed node at the top level of `params` so\n // transformers (notably `LifecycleTransformer`) can attribute\n // a finished task to its child namespace without re-parsing\n // the payload. The same value is retained inside `data` for\n // downstream consumers that inspect the event body.\n ...(typeof data.node === \"string\" ? { node: data.node } : {}),\n data,\n },\n },\n ];\n }\n\n case \"custom\": {\n const data =\n typeof payload === \"object\" &&\n payload !== null &&\n !Array.isArray(payload) &&\n \"name\" in payload\n ? payload\n : { payload };\n return [\n {\n ...base,\n seq,\n method: \"custom\",\n params: { namespace: ns, timestamp, data },\n },\n ];\n }\n\n case \"tasks\":\n return [\n {\n ...base,\n seq,\n method: \"tasks\",\n params: { namespace: ns, timestamp, data: payload },\n },\n ];\n\n default:\n return [];\n }\n}\n\n/**\n * Normalises a raw tools-mode payload into a typed {@link ToolsEventData}\n * discriminated union, mapping internal lifecycle events (`on_tool_start`,\n * `on_tool_end`, etc.) to their protocol counterparts.\n *\n * @param payload - The raw payload from a `\"tools\"` stream chunk.\n * @returns A {@link ToolsEventData} object with the appropriate `event`\n * discriminant and associated fields.\n */\nfunction convertToolsPayload(payload: unknown): ToolsEventData {\n if (typeof payload !== \"object\" || payload === null) {\n return {\n event: \"tool-error\",\n tool_call_id: \"\",\n message: \"Unexpected tools payload shape\",\n };\n }\n\n const p = payload as Record<string, unknown>;\n const tool_call_id = String(p.toolCallId ?? \"\");\n\n switch (p.event) {\n case \"on_tool_start\":\n return {\n event: \"tool-started\",\n tool_call_id,\n tool_name: String(p.name ?? \"unknown\"),\n input: p.input,\n };\n\n case \"on_tool_event\": {\n const delta =\n typeof p.data === \"string\" ? p.data : JSON.stringify(p.data ?? \"\");\n return {\n event: \"tool-output-delta\",\n tool_call_id,\n delta,\n };\n }\n\n case \"on_tool_end\":\n return {\n event: \"tool-finished\",\n tool_call_id,\n output: p.output,\n };\n\n case \"on_tool_error\": {\n const err = p.error;\n const errMessage =\n typeof err === \"object\" &&\n err !== null &&\n \"message\" in err &&\n typeof (err as { message: unknown }).message === \"string\"\n ? (err as { message: string }).message\n : String(err ?? \"unknown error\");\n return {\n event: \"tool-error\",\n tool_call_id,\n message: errMessage,\n };\n }\n\n default:\n return {\n event: \"tool-error\",\n tool_call_id: \"\",\n message: `Unknown tool event: ${String(p.event)}`,\n };\n }\n}\n\n/**\n * Extracts the first `{node: delta}` entry from an updates-mode payload and\n * reshapes it into an {@link UpdatesEventData} with explicit `node` and\n * `values` fields. Non-object payloads are coerced to `{ values: {} }`.\n *\n * @param payload - The raw payload from an `\"updates\"` stream chunk,\n * expected to be a `Record<string, unknown>` keyed by node name.\n * @returns An {@link UpdatesEventData} containing the extracted node name\n * and its associated delta values.\n */\nfunction convertUpdatesPayload(payload: unknown): UpdatesEventData {\n if (typeof payload !== \"object\" || payload === null) {\n return { values: {} };\n }\n\n const entries = Object.entries(payload as Record<string, unknown>);\n if (entries.length === 0) {\n return { values: {} };\n }\n\n const [node, values] = entries[0];\n return {\n node,\n values: (typeof values === \"object\" && values !== null\n ? values\n : { value: values }) as Record<string, unknown>,\n };\n}\n"],"mappings":"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"}