@langchain/langgraph 1.3.5 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/stream/index.cjs +1 -0
- package/dist/stream/index.d.cts +2 -1
- package/dist/stream/index.d.ts +2 -1
- package/dist/stream/index.js +1 -0
- package/dist/stream/mux.cjs +13 -3
- package/dist/stream/mux.cjs.map +1 -1
- package/dist/stream/mux.d.cts +2 -2
- package/dist/stream/mux.d.cts.map +1 -1
- package/dist/stream/mux.d.ts +2 -2
- package/dist/stream/mux.d.ts.map +1 -1
- package/dist/stream/mux.js +13 -3
- package/dist/stream/mux.js.map +1 -1
- package/dist/stream/stream-channel.cjs +3 -3
- package/dist/stream/stream-channel.cjs.map +1 -1
- package/dist/stream/stream-channel.d.cts +12 -4
- package/dist/stream/stream-channel.d.cts.map +1 -1
- package/dist/stream/stream-channel.d.ts +12 -4
- package/dist/stream/stream-channel.d.ts.map +1 -1
- package/dist/stream/stream-channel.js +3 -3
- package/dist/stream/stream-channel.js.map +1 -1
- package/dist/stream/subscription.cjs +136 -0
- package/dist/stream/subscription.cjs.map +1 -0
- package/dist/stream/subscription.d.cts +94 -0
- package/dist/stream/subscription.d.cts.map +1 -0
- package/dist/stream/subscription.d.ts +94 -0
- package/dist/stream/subscription.d.ts.map +1 -0
- package/dist/stream/subscription.js +131 -0
- package/dist/stream/subscription.js.map +1 -0
- package/dist/stream/types.cjs.map +1 -1
- package/dist/stream/types.d.cts +3 -3
- package/dist/stream/types.d.ts +3 -3
- package/dist/stream/types.js.map +1 -1
- package/dist/stream.cjs +16 -0
- package/dist/stream.d.cts +5 -0
- package/dist/stream.d.ts +5 -0
- package/dist/stream.js +4 -0
- package/package.json +16 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-channel.js","names":["#items","#wake","#onPush","#done","#error","#waiters"],"sources":["../../src/stream/stream-channel.ts"],"sourcesContent":["/**\n * StreamChannel — projection channel for local or remote streaming.\n *\n * A `StreamChannel<T>` is an append-only async stream with independent\n * cursors. Local channels stay in-process only. Remote channels declare a\n * protocol channel name; when registered with a {@link StreamMux} (via a\n * transformer's `init()` return value), every {@link push} is automatically\n * forwarded as a {@link ProtocolEvent} on the named channel — making the data\n * available both in-process (via `run.extensions`) and to remote clients (via\n * `session.subscribe(\"custom:<channelName>\")`).\n *\n * Lifecycle (`close` / `fail`) is managed by the mux automatically;\n * transformers do not need to call them.\n */\n\n/**\n * Branded symbol placed on every {@link StreamChannel} instance.\n *\n * Uses `Symbol.for` so the same symbol is shared across multiple\n * copies of this package that may coexist in a dependency graph\n * (e.g. when a user app imports `@langchain/langgraph` directly and a\n * wrapping library like `langchain` bundles its own copy). Using a\n * symbol brand instead of `instanceof` lets channels created against\n * one copy of the class be recognised by a mux from another.\n * @internal\n */\nexport const STREAM_CHANNEL_BRAND: unique symbol = Symbol.for(\n \"langgraph.stream_channel\"\n) as typeof STREAM_CHANNEL_BRAND;\n\nexport interface StreamChannelEventStreamOptions<T> {\n /**\n * SSE event name. Defaults to the channel's remote protocol name, if any.\n * Set this for local channels or when exposing the same channel under a\n * route-specific event name.\n */\n event?: string;\n /**\n * Cursor position to start streaming from. Useful for reconnects or\n * secondary subscribers that already consumed the first N buffered items and\n * only need replay from a known offset.\n */\n startAt?: number;\n /**\n * Serialize each item into the SSE `data:` field. Defaults to JSON. Use this\n * when a channel item needs a wire format other than its raw JSON shape, or\n * when the consumer expects line-oriented text payloads.\n */\n serialize?: (item: T) => string;\n}\n\n/**\n * A projection channel for {@link StreamTransformer}s.\n *\n * Implements `AsyncIterable<T>` so it can be iterated directly by\n * in-process consumers via `run.extensions.<key>`. Channels created with\n * {@link StreamChannel.remote} or `new StreamChannel(name)` are also\n * auto-forwarded to remote clients.\n *\n * @typeParam T - The type of items pushed into the channel.\n */\nexport class StreamChannel<T> implements AsyncIterable<T> {\n /** @internal Brand used by {@link StreamChannel.isInstance}. */\n readonly [STREAM_CHANNEL_BRAND] = true as const;\n\n /** Protocol channel name used for auto-forwarded events, if remote. */\n readonly channelName?: string;\n\n #items: T[] = [];\n #waiters: Array<() => void> = [];\n #done = false;\n #error: unknown;\n #onPush?: (item: T) => void;\n\n constructor(name?: string) {\n this.channelName = name;\n }\n\n /**\n * Create an in-process-only channel. Values remain available through\n * `run.extensions.<key>` but are not forwarded to remote clients.\n */\n static local<T>(): StreamChannel<T> {\n return new StreamChannel<T>();\n }\n\n /**\n * Create a channel whose pushes are forwarded to remote clients under\n * the given protocol channel name.\n */\n static remote<T>(name: string): StreamChannel<T> {\n return new StreamChannel<T>(name);\n }\n\n /**\n * Brand-based type guard that recognises any {@link StreamChannel}\n * instance, even ones originating from a different copy of this\n * package. Prefer this over `instanceof StreamChannel` when code\n * may observe channels that were constructed elsewhere.\n */\n static isInstance(value: unknown): value is StreamChannel<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n STREAM_CHANNEL_BRAND in value &&\n (value as { [STREAM_CHANNEL_BRAND]: unknown })[STREAM_CHANNEL_BRAND] ===\n true\n );\n }\n\n /**\n * Append an item to the channel. If this is a remote channel wired to a\n * mux, the item is also injected into the main protocol event stream under\n * {@link channelName}.\n */\n push(item: T): void {\n this.#items.push(item);\n this.#wake();\n this.#onPush?.(item);\n }\n\n /**\n * Returns an async iterator starting at position {@link startAt}. Each call\n * returns an independent cursor so multiple consumers can iterate the same\n * channel concurrently.\n */\n iterate(startAt = 0): AsyncIterator<T> {\n let cursor = startAt;\n return {\n next: async (): Promise<IteratorResult<T>> => {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (cursor < this.#items.length) {\n return { value: this.#items[cursor++], done: false };\n }\n if (this.#done) {\n if (this.#error) throw this.#error;\n return { value: undefined as unknown as T, done: true };\n }\n await new Promise<void>((resolve) => this.#waiters.push(resolve));\n }\n },\n };\n }\n\n /**\n * Creates an {@link AsyncIterable} backed by this channel, starting from\n * {@link startAt}.\n */\n toAsyncIterable(startAt = 0): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator]: () => this.iterate(startAt),\n };\n }\n\n /**\n * Creates a web {@link ReadableStream} that emits channel items as\n * Server-Sent Events. Useful for returning a channel directly from\n * `new Response(channel.toEventStream())`.\n */\n toEventStream(\n options: StreamChannelEventStreamOptions<T> = {}\n ): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n const iterator = this.iterate(options.startAt);\n const event = options.event ?? this.channelName;\n const serialize =\n options.serialize ?? ((item: T) => JSON.stringify(item) ?? \"null\");\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const next = await iterator.next();\n if (next.done) {\n controller.close();\n return;\n }\n\n const lines: string[] = [];\n if (event != null) {\n lines.push(`event: ${event}`);\n }\n for (const line of serialize(next.value).split(/\\r\\n|\\r|\\n/)) {\n lines.push(`data: ${line}`);\n }\n\n controller.enqueue(encoder.encode(`${lines.join(\"\\n\")}\\n\\n`));\n } catch (error) {\n controller.error(error);\n }\n },\n async cancel() {\n await iterator.return?.();\n },\n });\n }\n\n /**\n * Returns the item at the given zero-based index.\n *\n * @throws {RangeError} If the index is out of bounds.\n */\n get(index: number): T {\n if (index < 0 || index >= this.#items.length) {\n throw new RangeError(\n `StreamChannel index ${index} out of bounds (size=${this.#items.length})`\n );\n }\n return this.#items[index];\n }\n\n /** The number of items currently buffered in the channel. */\n get size(): number {\n return this.#items.length;\n }\n\n /** Whether the channel has been closed or failed. */\n get done(): boolean {\n return this.#done;\n }\n\n /** Mark the channel as complete after all buffered items are consumed. */\n close(): void {\n this.#done = true;\n this.#wake();\n }\n\n /** Mark the channel as failed after all buffered items are consumed. */\n fail(err: unknown): void {\n this.#error = err;\n this.#done = true;\n this.#wake();\n }\n\n /** @internal Called by the mux to wire auto-forwarding. */\n _wire(fn: (item: T) => void): void {\n this.#onPush = fn;\n }\n\n /** @internal Called by the mux on normal completion. */\n _close(): void {\n this.close();\n }\n\n /** @internal Called by the mux on failure. */\n _fail(err: unknown): void {\n this.fail(err);\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this.iterate();\n }\n\n #wake(): void {\n const waiters = this.#waiters.splice(0);\n for (const w of waiters) w();\n }\n}\n\n/**\n * Type guard that tests whether a value is a {@link StreamChannel}.\n *\n * Uses a symbol brand rather than `instanceof` so channels built\n * against a different copy of this package (e.g. one bundled by the\n * `langchain` umbrella package) are still recognised.\n */\nexport function isStreamChannel(\n value: unknown\n): value is StreamChannel<unknown> {\n return StreamChannel.isInstance(value);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stream-channel.js","names":["#items","#wake","#onPush","#done","#error","#waiters"],"sources":["../../src/stream/stream-channel.ts"],"sourcesContent":["/**\n * StreamChannel — projection channel for local or remote streaming.\n *\n * A `StreamChannel<T>` is an append-only async stream with independent\n * cursors. Local channels stay in-process only. Remote channels declare a\n * protocol channel name; when registered with a {@link StreamMux} (via a\n * transformer's `init()` return value), every {@link push} is automatically\n * forwarded as a {@link ProtocolEvent} on `custom:<channelName>` — making the\n * data available both in-process (via `run.extensions`) and to remote clients\n * (via `session.subscribe(\"custom:<channelName>\")`).\n *\n * Lifecycle (`close` / `fail`) is managed by the mux automatically;\n * transformers do not need to call them.\n */\n\n/**\n * Branded symbol placed on every {@link StreamChannel} instance.\n *\n * Uses `Symbol.for` so the same symbol is shared across multiple\n * copies of this package that may coexist in a dependency graph\n * (e.g. when a user app imports `@langchain/langgraph` directly and a\n * wrapping library like `langchain` bundles its own copy). Using a\n * symbol brand instead of `instanceof` lets channels created against\n * one copy of the class be recognised by a mux from another.\n * @internal\n */\nexport const STREAM_CHANNEL_BRAND: unique symbol = Symbol.for(\n \"langgraph.stream_channel\"\n) as typeof STREAM_CHANNEL_BRAND;\n\nexport interface StreamChannelEventStreamOptions<T> {\n /**\n * SSE event name. Defaults to the channel's remote protocol name, if any.\n * Set this for local channels or when exposing the same channel under a\n * route-specific event name.\n */\n event?: string;\n /**\n * Cursor position to start streaming from. Useful for reconnects or\n * secondary subscribers that already consumed the first N buffered items and\n * only need replay from a known offset.\n */\n startAt?: number;\n /**\n * Serialize each item into the SSE `data:` field. Defaults to JSON. Use this\n * when a channel item needs a wire format other than its raw JSON shape, or\n * when the consumer expects line-oriented text payloads.\n */\n serialize?: (item: T) => string;\n}\n\n/**\n * A projection channel for {@link StreamTransformer}s.\n *\n * Implements `AsyncIterable<T>` so it can be iterated directly by\n * in-process consumers via `run.extensions.<key>`. Channels created with\n * {@link StreamChannel.remote} or `new StreamChannel(name)` are also\n * auto-forwarded to remote clients.\n *\n * @typeParam T - The type of items pushed into the channel.\n */\nexport class StreamChannel<T> implements AsyncIterable<T> {\n /** @internal Brand used by {@link StreamChannel.isInstance}. */\n readonly [STREAM_CHANNEL_BRAND] = true as const;\n\n /** Protocol channel name used for auto-forwarded events, if remote. */\n readonly channelName?: string;\n\n #items: T[] = [];\n #waiters: Array<() => void> = [];\n #done = false;\n #error: unknown;\n #onPush?: (item: T) => void;\n\n constructor(name?: string) {\n this.channelName = name;\n }\n\n /**\n * Create an in-process-only channel. Values remain available through\n * `run.extensions.<key>` but are not forwarded to remote clients.\n */\n static local<T>(): StreamChannel<T> {\n return new StreamChannel<T>();\n }\n\n /**\n * Create a channel whose pushes are forwarded to remote clients under\n * the given protocol channel name.\n */\n static remote<T>(name: string): StreamChannel<T> {\n return new StreamChannel<T>(name);\n }\n\n /**\n * Brand-based type guard that recognises any {@link StreamChannel}\n * instance, even ones originating from a different copy of this\n * package. Prefer this over `instanceof StreamChannel` when code\n * may observe channels that were constructed elsewhere.\n */\n static isInstance(value: unknown): value is StreamChannel<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n STREAM_CHANNEL_BRAND in value &&\n (value as { [STREAM_CHANNEL_BRAND]: unknown })[STREAM_CHANNEL_BRAND] ===\n true\n );\n }\n\n /**\n * Append an item to the channel. If this is a remote channel wired to a\n * mux, the item is also injected into the main protocol event stream under\n * {@link channelName}.\n */\n push(item: T): void {\n this.#items.push(item);\n this.#wake();\n this.#onPush?.(item);\n }\n\n /**\n * Returns an async iterator starting at position {@link startAt}. Each call\n * returns an independent cursor so multiple consumers can iterate the same\n * channel concurrently.\n */\n iterate(startAt = 0): AsyncIterator<T> {\n let cursor = startAt;\n return {\n next: async (): Promise<IteratorResult<T>> => {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (cursor < this.#items.length) {\n return { value: this.#items[cursor++], done: false };\n }\n if (this.#done) {\n if (this.#error) throw this.#error;\n return { value: undefined as unknown as T, done: true };\n }\n await new Promise<void>((resolve) => this.#waiters.push(resolve));\n }\n },\n };\n }\n\n /**\n * Creates an {@link AsyncIterable} backed by this channel, starting from\n * {@link startAt}.\n */\n toAsyncIterable(startAt = 0): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator]: () => this.iterate(startAt),\n };\n }\n\n /**\n * Creates a web {@link ReadableStream} that emits channel items as\n * Server-Sent Events. Useful for returning a channel directly from\n * `new Response(channel.toEventStream())`.\n */\n toEventStream(\n options: StreamChannelEventStreamOptions<T> = {}\n ): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n const iterator = this.iterate(options.startAt);\n const event = options.event ?? this.channelName;\n const serialize =\n options.serialize ?? ((item: T) => JSON.stringify(item) ?? \"null\");\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const next = await iterator.next();\n if (next.done) {\n controller.close();\n return;\n }\n\n const lines: string[] = [];\n if (event != null) {\n lines.push(`event: ${event}`);\n }\n for (const line of serialize(next.value).split(/\\r\\n|\\r|\\n/)) {\n lines.push(`data: ${line}`);\n }\n\n controller.enqueue(encoder.encode(`${lines.join(\"\\n\")}\\n\\n`));\n } catch (error) {\n controller.error(error);\n }\n },\n async cancel() {\n await iterator.return?.();\n },\n });\n }\n\n /**\n * Returns the item at the given zero-based index.\n *\n * @throws {RangeError} If the index is out of bounds.\n */\n get(index: number): T {\n if (index < 0 || index >= this.#items.length) {\n throw new RangeError(\n `StreamChannel index ${index} out of bounds (size=${this.#items.length})`\n );\n }\n return this.#items[index];\n }\n\n /** The number of items currently buffered in the channel. */\n get size(): number {\n return this.#items.length;\n }\n\n /** Whether the channel has been closed or failed. */\n get done(): boolean {\n return this.#done;\n }\n\n /** Mark the channel as complete after all buffered items are consumed. */\n close(): void {\n this.#done = true;\n this.#wake();\n }\n\n /** Mark the channel as failed after all buffered items are consumed. */\n fail(err: unknown): void {\n this.#error = err;\n this.#done = true;\n this.#wake();\n }\n\n /** @internal Called by the mux to wire auto-forwarding. */\n _wire(fn: (item: T) => void): void {\n this.#onPush = fn;\n }\n\n /** @internal Called by the mux on normal completion. */\n _close(): void {\n this.close();\n }\n\n /** @internal Called by the mux on failure. */\n _fail(err: unknown): void {\n this.fail(err);\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this.iterate();\n }\n\n #wake(): void {\n const waiters = this.#waiters.splice(0);\n for (const w of waiters) w();\n }\n}\n\n/**\n * Type guard that tests whether a value is a {@link StreamChannel}.\n *\n * Uses a symbol brand rather than `instanceof` so channels built\n * against a different copy of this package (e.g. one bundled by the\n * `langchain` umbrella package) are still recognised.\n */\nexport function isStreamChannel(\n value: unknown\n): value is StreamChannel<unknown> {\n return StreamChannel.isInstance(value);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
//#region src/stream/subscription.ts
|
|
2
|
+
/**
|
|
3
|
+
* Strip dynamic suffixes (after `:`) from a namespace segment.
|
|
4
|
+
*
|
|
5
|
+
* Server-emitted namespaces contain runtime-generated suffixes like
|
|
6
|
+
* `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
|
|
7
|
+
* static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
|
|
8
|
+
* `api/langgraph_api/protocol/namespace.py`.
|
|
9
|
+
*
|
|
10
|
+
* @param segment - Raw namespace segment.
|
|
11
|
+
* @returns The stable graph-oriented portion of the segment.
|
|
12
|
+
*/
|
|
13
|
+
function normalizeNamespaceSegment(segment) {
|
|
14
|
+
const idx = segment.indexOf(":");
|
|
15
|
+
return idx === -1 ? segment : segment.slice(0, idx);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Whether `namespace` starts with `prefix`.
|
|
19
|
+
*
|
|
20
|
+
* Segments are compared literally first; if the prefix segment itself contains
|
|
21
|
+
* no `:`, the candidate segment is also compared after its dynamic suffix is
|
|
22
|
+
* stripped (see {@link normalizeNamespaceSegment}). This mirrors
|
|
23
|
+
* `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
|
|
24
|
+
* filtering and client-side per-subscription narrowing stay consistent.
|
|
25
|
+
*
|
|
26
|
+
* @param namespace - Event namespace to test.
|
|
27
|
+
* @param prefix - Subscription namespace prefix.
|
|
28
|
+
*/
|
|
29
|
+
function isPrefixMatch(namespace, prefix) {
|
|
30
|
+
if (prefix.length > namespace.length) return false;
|
|
31
|
+
for (let i = 0; i < prefix.length; i += 1) {
|
|
32
|
+
const segment = prefix[i];
|
|
33
|
+
const candidate = namespace[i];
|
|
34
|
+
if (candidate === segment) continue;
|
|
35
|
+
if (segment.includes(":")) return false;
|
|
36
|
+
if (normalizeNamespaceSegment(candidate) === segment) continue;
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function namespaceMatches(eventNamespace, prefixes, depth) {
|
|
42
|
+
if (!prefixes || prefixes.length === 0) return true;
|
|
43
|
+
return prefixes.some((prefix) => {
|
|
44
|
+
if (!isPrefixMatch(eventNamespace, prefix)) return false;
|
|
45
|
+
if (depth === void 0) return true;
|
|
46
|
+
return eventNamespace.length - prefix.length <= depth;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The base protocol subscription channels, excluding the templated
|
|
51
|
+
* `custom:<name>` form. This is the runtime counterpart to the `Channel`
|
|
52
|
+
* union from `@langchain/protocol` and mirrors the channel set a server
|
|
53
|
+
* recognizes when filtering its event sinks.
|
|
54
|
+
*/
|
|
55
|
+
const SUPPORTED_CHANNELS = new Set([
|
|
56
|
+
"values",
|
|
57
|
+
"updates",
|
|
58
|
+
"messages",
|
|
59
|
+
"tools",
|
|
60
|
+
"lifecycle",
|
|
61
|
+
"input",
|
|
62
|
+
"checkpoints",
|
|
63
|
+
"tasks",
|
|
64
|
+
"custom"
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* Whether `value` names a protocol subscription channel — either a base
|
|
68
|
+
* channel (`"messages"`, `"values"`, …) or a named custom channel
|
|
69
|
+
* (`"custom:<name>"`). Unknown/future method names return `false`,
|
|
70
|
+
* mirroring {@link inferChannel}.
|
|
71
|
+
*
|
|
72
|
+
* @param value - Candidate channel name.
|
|
73
|
+
*/
|
|
74
|
+
function isSupportedChannel(value) {
|
|
75
|
+
return SUPPORTED_CHANNELS.has(value) || value.startsWith("custom:");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Maps a protocol event method to its subscription {@link Channel}.
|
|
79
|
+
*
|
|
80
|
+
* Returns `undefined` for unrecognized methods so that new server-side
|
|
81
|
+
* channels (e.g. from extension transformers) don't break existing
|
|
82
|
+
* subscribers. The `custom` method resolves to the named `custom:<name>`
|
|
83
|
+
* channel when the payload carries a `name`, otherwise the bare `custom`
|
|
84
|
+
* channel. Both `"input"` and the wire-level `"input.requested"` map to the
|
|
85
|
+
* `input` channel.
|
|
86
|
+
*
|
|
87
|
+
* @param event - Event whose method should be mapped to a channel.
|
|
88
|
+
*/
|
|
89
|
+
function inferChannel(event) {
|
|
90
|
+
switch (event.method) {
|
|
91
|
+
case "values": return "values";
|
|
92
|
+
case "checkpoints": return "checkpoints";
|
|
93
|
+
case "updates": return "updates";
|
|
94
|
+
case "messages": return "messages";
|
|
95
|
+
case "tools": return "tools";
|
|
96
|
+
case "custom": {
|
|
97
|
+
const data = event.params.data;
|
|
98
|
+
return data?.name != null ? `custom:${data.name}` : "custom";
|
|
99
|
+
}
|
|
100
|
+
case "lifecycle": return "lifecycle";
|
|
101
|
+
case "input":
|
|
102
|
+
case "input.requested": return "input";
|
|
103
|
+
case "tasks": return "tasks";
|
|
104
|
+
default: return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Returns whether an event should be delivered for a subscription definition.
|
|
109
|
+
*
|
|
110
|
+
* When the definition carries a `since` replay cursor, events at or before
|
|
111
|
+
* that sequence number are excluded — letting the same predicate drive both
|
|
112
|
+
* live fan-out and buffered replay over a {@link StreamChannel}.
|
|
113
|
+
*
|
|
114
|
+
* @param event - Event being checked for delivery.
|
|
115
|
+
* @param definition - Subscription filter definition to evaluate against.
|
|
116
|
+
* The optional `since` field (a `seq` cursor) is read leniently because it
|
|
117
|
+
* is not a declared field on the base {@link SubscribeParams} shape.
|
|
118
|
+
*/
|
|
119
|
+
function matchesSubscription(event, definition) {
|
|
120
|
+
const since = definition.since;
|
|
121
|
+
if (typeof since === "number" && (event.seq ?? 0) <= since) return false;
|
|
122
|
+
const channel = inferChannel(event);
|
|
123
|
+
if (channel === void 0) return false;
|
|
124
|
+
const channels = definition.channels;
|
|
125
|
+
if (!(channels.includes(channel) || channel.startsWith("custom:") && channels.includes("custom"))) return false;
|
|
126
|
+
return namespaceMatches(event.params.namespace, definition.namespaces, definition.depth);
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
exports.SUPPORTED_CHANNELS = SUPPORTED_CHANNELS;
|
|
130
|
+
exports.inferChannel = inferChannel;
|
|
131
|
+
exports.isPrefixMatch = isPrefixMatch;
|
|
132
|
+
exports.isSupportedChannel = isSupportedChannel;
|
|
133
|
+
exports.matchesSubscription = matchesSubscription;
|
|
134
|
+
exports.normalizeNamespaceSegment = normalizeNamespaceSegment;
|
|
135
|
+
|
|
136
|
+
//# sourceMappingURL=subscription.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.cjs","names":[],"sources":["../../src/stream/subscription.ts"],"sourcesContent":["/**\n * Subscription matching and channel inference for the v2 streaming protocol.\n *\n * These helpers are the building blocks a custom transport / server needs to\n * fan protocol events out to subscribers: map an event to its logical\n * {@link Channel} ({@link inferChannel}) and decide whether a buffered event\n * should be delivered for a given {@link SubscribeParams} filter\n * ({@link matchesSubscription}). They are typed against the minimal\n * {@link MatchableEvent} shape so the same predicate works on the core\n * {@link ProtocolEvent} produced by {@link convertToProtocolEvent} /\n * {@link StreamChannel} and on the wire-level `Event` from\n * `@langchain/protocol`.\n */\n\nimport type { Channel, SubscribeParams } from \"@langchain/protocol\";\nimport type { Namespace } from \"./types.js\";\n\n/**\n * Minimal protocol-event shape consumed by {@link inferChannel} and\n * {@link matchesSubscription}.\n *\n * Both the core {@link ProtocolEvent} and the wire-level `Event` from\n * `@langchain/protocol` structurally satisfy this contract, so the same\n * predicates can drive in-process fan-out, buffered replay, and server-side\n * (SSE / WebSocket) event-sink filtering without coupling to a single event\n * type.\n */\nexport interface MatchableEvent {\n /** Logical stream channel; see {@link inferChannel}. */\n readonly method: string;\n\n /** Monotonic sequence number, when present. Used by the `since` cursor. */\n readonly seq?: number;\n\n readonly params: {\n /** Namespace of the node or scope that emitted this event. */\n readonly namespace: Namespace;\n\n /** Opaque channel payload; shape depends on `method`. */\n readonly data?: unknown;\n };\n}\n\n/**\n * Strip dynamic suffixes (after `:`) from a namespace segment.\n *\n * Server-emitted namespaces contain runtime-generated suffixes like\n * `\"fetcher:abc-uuid\"`, while user-supplied subscription filters are typically\n * static names (`\"fetcher\"`). Mirrors `normalize_namespace_segment` in\n * `api/langgraph_api/protocol/namespace.py`.\n *\n * @param segment - Raw namespace segment.\n * @returns The stable graph-oriented portion of the segment.\n */\nexport function normalizeNamespaceSegment(segment: string): string {\n const idx = segment.indexOf(\":\");\n return idx === -1 ? segment : segment.slice(0, idx);\n}\n\n/**\n * Whether `namespace` starts with `prefix`.\n *\n * Segments are compared literally first; if the prefix segment itself contains\n * no `:`, the candidate segment is also compared after its dynamic suffix is\n * stripped (see {@link normalizeNamespaceSegment}). This mirrors\n * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side\n * filtering and client-side per-subscription narrowing stay consistent.\n *\n * @param namespace - Event namespace to test.\n * @param prefix - Subscription namespace prefix.\n */\nexport function isPrefixMatch(\n namespace: Namespace,\n prefix: Namespace\n): boolean {\n if (prefix.length > namespace.length) return false;\n for (let i = 0; i < prefix.length; i += 1) {\n const segment = prefix[i]!;\n const candidate = namespace[i]!;\n if (candidate === segment) continue;\n if (segment.includes(\":\")) return false;\n if (normalizeNamespaceSegment(candidate) === segment) continue;\n return false;\n }\n return true;\n}\n\nfunction namespaceMatches(\n eventNamespace: Namespace,\n prefixes: Namespace[] | undefined,\n depth: number | undefined\n): boolean {\n if (!prefixes || prefixes.length === 0) {\n return true;\n }\n\n return prefixes.some((prefix) => {\n if (!isPrefixMatch(eventNamespace, prefix)) return false;\n if (depth === undefined) return true;\n return eventNamespace.length - prefix.length <= depth;\n });\n}\n\n/**\n * The base protocol subscription channels, excluding the templated\n * `custom:<name>` form. This is the runtime counterpart to the `Channel`\n * union from `@langchain/protocol` and mirrors the channel set a server\n * recognizes when filtering its event sinks.\n */\nexport const SUPPORTED_CHANNELS = new Set<Channel>([\n \"values\",\n \"updates\",\n \"messages\",\n \"tools\",\n \"lifecycle\",\n \"input\",\n \"checkpoints\",\n \"tasks\",\n \"custom\",\n]);\n\n/**\n * Whether `value` names a protocol subscription channel — either a base\n * channel (`\"messages\"`, `\"values\"`, …) or a named custom channel\n * (`\"custom:<name>\"`). Unknown/future method names return `false`,\n * mirroring {@link inferChannel}.\n *\n * @param value - Candidate channel name.\n */\nexport function isSupportedChannel(value: string): value is Channel {\n return (\n SUPPORTED_CHANNELS.has(value as Channel) || value.startsWith(\"custom:\")\n );\n}\n\n/**\n * Maps a protocol event method to its subscription {@link Channel}.\n *\n * Returns `undefined` for unrecognized methods so that new server-side\n * channels (e.g. from extension transformers) don't break existing\n * subscribers. The `custom` method resolves to the named `custom:<name>`\n * channel when the payload carries a `name`, otherwise the bare `custom`\n * channel. Both `\"input\"` and the wire-level `\"input.requested\"` map to the\n * `input` channel.\n *\n * @param event - Event whose method should be mapped to a channel.\n */\nexport function inferChannel(event: MatchableEvent): Channel | undefined {\n switch (event.method) {\n case \"values\":\n return \"values\";\n case \"checkpoints\":\n return \"checkpoints\";\n case \"updates\":\n return \"updates\";\n case \"messages\":\n return \"messages\";\n case \"tools\":\n return \"tools\";\n case \"custom\": {\n const data = event.params.data as { name?: string } | undefined;\n return data?.name != null ? `custom:${data.name}` : \"custom\";\n }\n case \"lifecycle\":\n return \"lifecycle\";\n case \"input\":\n case \"input.requested\":\n return \"input\";\n case \"tasks\":\n return \"tasks\";\n default:\n return undefined;\n }\n}\n\n/**\n * Returns whether an event should be delivered for a subscription definition.\n *\n * When the definition carries a `since` replay cursor, events at or before\n * that sequence number are excluded — letting the same predicate drive both\n * live fan-out and buffered replay over a {@link StreamChannel}.\n *\n * @param event - Event being checked for delivery.\n * @param definition - Subscription filter definition to evaluate against.\n * The optional `since` field (a `seq` cursor) is read leniently because it\n * is not a declared field on the base {@link SubscribeParams} shape.\n */\nexport function matchesSubscription(\n event: MatchableEvent,\n definition: SubscribeParams\n): boolean {\n const since = (definition as { since?: unknown }).since;\n if (typeof since === \"number\" && (event.seq ?? 0) <= since) {\n return false;\n }\n\n const channel = inferChannel(event);\n if (channel === undefined) return false;\n\n const channels = definition.channels;\n const channelMatched =\n channels.includes(channel) ||\n (channel.startsWith(\"custom:\") && channels.includes(\"custom\"));\n if (!channelMatched) {\n return false;\n }\n\n return namespaceMatches(\n event.params.namespace,\n definition.namespaces,\n definition.depth\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsDA,SAAgB,0BAA0B,SAAyB;CACjE,MAAM,MAAM,QAAQ,QAAQ,IAAI;AAChC,QAAO,QAAQ,KAAK,UAAU,QAAQ,MAAM,GAAG,IAAI;;;;;;;;;;;;;;AAerD,SAAgB,cACd,WACA,QACS;AACT,KAAI,OAAO,SAAS,UAAU,OAAQ,QAAO;AAC7C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;EACzC,MAAM,UAAU,OAAO;EACvB,MAAM,YAAY,UAAU;AAC5B,MAAI,cAAc,QAAS;AAC3B,MAAI,QAAQ,SAAS,IAAI,CAAE,QAAO;AAClC,MAAI,0BAA0B,UAAU,KAAK,QAAS;AACtD,SAAO;;AAET,QAAO;;AAGT,SAAS,iBACP,gBACA,UACA,OACS;AACT,KAAI,CAAC,YAAY,SAAS,WAAW,EACnC,QAAO;AAGT,QAAO,SAAS,MAAM,WAAW;AAC/B,MAAI,CAAC,cAAc,gBAAgB,OAAO,CAAE,QAAO;AACnD,MAAI,UAAU,KAAA,EAAW,QAAO;AAChC,SAAO,eAAe,SAAS,OAAO,UAAU;GAChD;;;;;;;;AASJ,MAAa,qBAAqB,IAAI,IAAa;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;AAUF,SAAgB,mBAAmB,OAAiC;AAClE,QACE,mBAAmB,IAAI,MAAiB,IAAI,MAAM,WAAW,UAAU;;;;;;;;;;;;;;AAgB3E,SAAgB,aAAa,OAA4C;AACvE,SAAQ,MAAM,QAAd;EACE,KAAK,SACH,QAAO;EACT,KAAK,cACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,UAAU;GACb,MAAM,OAAO,MAAM,OAAO;AAC1B,UAAO,MAAM,QAAQ,OAAO,UAAU,KAAK,SAAS;;EAEtD,KAAK,YACH,QAAO;EACT,KAAK;EACL,KAAK,kBACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,QACE;;;;;;;;;;;;;;;AAgBN,SAAgB,oBACd,OACA,YACS;CACT,MAAM,QAAS,WAAmC;AAClD,KAAI,OAAO,UAAU,aAAa,MAAM,OAAO,MAAM,MACnD,QAAO;CAGT,MAAM,UAAU,aAAa,MAAM;AACnC,KAAI,YAAY,KAAA,EAAW,QAAO;CAElC,MAAM,WAAW,WAAW;AAI5B,KAAI,EAFF,SAAS,SAAS,QAAQ,IACzB,QAAQ,WAAW,UAAU,IAAI,SAAS,SAAS,SAAS,EAE7D,QAAO;AAGT,QAAO,iBACL,MAAM,OAAO,WACb,WAAW,YACX,WAAW,MACZ"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Namespace } from "./types.cjs";
|
|
2
|
+
import { Channel, SubscribeParams } from "@langchain/protocol";
|
|
3
|
+
|
|
4
|
+
//#region src/stream/subscription.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Minimal protocol-event shape consumed by {@link inferChannel} and
|
|
7
|
+
* {@link matchesSubscription}.
|
|
8
|
+
*
|
|
9
|
+
* Both the core {@link ProtocolEvent} and the wire-level `Event` from
|
|
10
|
+
* `@langchain/protocol` structurally satisfy this contract, so the same
|
|
11
|
+
* predicates can drive in-process fan-out, buffered replay, and server-side
|
|
12
|
+
* (SSE / WebSocket) event-sink filtering without coupling to a single event
|
|
13
|
+
* type.
|
|
14
|
+
*/
|
|
15
|
+
interface MatchableEvent {
|
|
16
|
+
/** Logical stream channel; see {@link inferChannel}. */
|
|
17
|
+
readonly method: string;
|
|
18
|
+
/** Monotonic sequence number, when present. Used by the `since` cursor. */
|
|
19
|
+
readonly seq?: number;
|
|
20
|
+
readonly params: {
|
|
21
|
+
/** Namespace of the node or scope that emitted this event. */readonly namespace: Namespace; /** Opaque channel payload; shape depends on `method`. */
|
|
22
|
+
readonly data?: unknown;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Strip dynamic suffixes (after `:`) from a namespace segment.
|
|
27
|
+
*
|
|
28
|
+
* Server-emitted namespaces contain runtime-generated suffixes like
|
|
29
|
+
* `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
|
|
30
|
+
* static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
|
|
31
|
+
* `api/langgraph_api/protocol/namespace.py`.
|
|
32
|
+
*
|
|
33
|
+
* @param segment - Raw namespace segment.
|
|
34
|
+
* @returns The stable graph-oriented portion of the segment.
|
|
35
|
+
*/
|
|
36
|
+
declare function normalizeNamespaceSegment(segment: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Whether `namespace` starts with `prefix`.
|
|
39
|
+
*
|
|
40
|
+
* Segments are compared literally first; if the prefix segment itself contains
|
|
41
|
+
* no `:`, the candidate segment is also compared after its dynamic suffix is
|
|
42
|
+
* stripped (see {@link normalizeNamespaceSegment}). This mirrors
|
|
43
|
+
* `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
|
|
44
|
+
* filtering and client-side per-subscription narrowing stay consistent.
|
|
45
|
+
*
|
|
46
|
+
* @param namespace - Event namespace to test.
|
|
47
|
+
* @param prefix - Subscription namespace prefix.
|
|
48
|
+
*/
|
|
49
|
+
declare function isPrefixMatch(namespace: Namespace, prefix: Namespace): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The base protocol subscription channels, excluding the templated
|
|
52
|
+
* `custom:<name>` form. This is the runtime counterpart to the `Channel`
|
|
53
|
+
* union from `@langchain/protocol` and mirrors the channel set a server
|
|
54
|
+
* recognizes when filtering its event sinks.
|
|
55
|
+
*/
|
|
56
|
+
declare const SUPPORTED_CHANNELS: Set<Channel>;
|
|
57
|
+
/**
|
|
58
|
+
* Whether `value` names a protocol subscription channel — either a base
|
|
59
|
+
* channel (`"messages"`, `"values"`, …) or a named custom channel
|
|
60
|
+
* (`"custom:<name>"`). Unknown/future method names return `false`,
|
|
61
|
+
* mirroring {@link inferChannel}.
|
|
62
|
+
*
|
|
63
|
+
* @param value - Candidate channel name.
|
|
64
|
+
*/
|
|
65
|
+
declare function isSupportedChannel(value: string): value is Channel;
|
|
66
|
+
/**
|
|
67
|
+
* Maps a protocol event method to its subscription {@link Channel}.
|
|
68
|
+
*
|
|
69
|
+
* Returns `undefined` for unrecognized methods so that new server-side
|
|
70
|
+
* channels (e.g. from extension transformers) don't break existing
|
|
71
|
+
* subscribers. The `custom` method resolves to the named `custom:<name>`
|
|
72
|
+
* channel when the payload carries a `name`, otherwise the bare `custom`
|
|
73
|
+
* channel. Both `"input"` and the wire-level `"input.requested"` map to the
|
|
74
|
+
* `input` channel.
|
|
75
|
+
*
|
|
76
|
+
* @param event - Event whose method should be mapped to a channel.
|
|
77
|
+
*/
|
|
78
|
+
declare function inferChannel(event: MatchableEvent): Channel | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Returns whether an event should be delivered for a subscription definition.
|
|
81
|
+
*
|
|
82
|
+
* When the definition carries a `since` replay cursor, events at or before
|
|
83
|
+
* that sequence number are excluded — letting the same predicate drive both
|
|
84
|
+
* live fan-out and buffered replay over a {@link StreamChannel}.
|
|
85
|
+
*
|
|
86
|
+
* @param event - Event being checked for delivery.
|
|
87
|
+
* @param definition - Subscription filter definition to evaluate against.
|
|
88
|
+
* The optional `since` field (a `seq` cursor) is read leniently because it
|
|
89
|
+
* is not a declared field on the base {@link SubscribeParams} shape.
|
|
90
|
+
*/
|
|
91
|
+
declare function matchesSubscription(event: MatchableEvent, definition: SubscribeParams): boolean;
|
|
92
|
+
//#endregion
|
|
93
|
+
export { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
|
|
94
|
+
//# sourceMappingURL=subscription.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.d.cts","names":[],"sources":["../../src/stream/subscription.ts"],"mappings":";;;;;AAsDA;;;;;AAiBA;;;;UA5CiB,cAAA;EA6Cf;EAAA,SA3CS,MAAA;EA4CT;EAAA,SAzCS,GAAA;EAAA,SAEA,MAAA;IA2EE,uEAzEA,SAAA,EAAW,SAAA,EAyEO;IAAA,SAtElB,IAAA;EAAA;AAAA;;;;;;;;;AA4Gb;;;iBA7FgB,yBAAA,CAA0B,OAAA;;;;;;AAqI1C;;;;;;;iBApHgB,aAAA,CACd,SAAA,EAAW,SAAA,EACX,MAAA,EAAQ,SAAA;;;;;;;cAoCG,kBAAA,EAAkB,GAAA,CAAA,OAAA;;;;;;;;;iBAoBf,kBAAA,CAAmB,KAAA,WAAgB,KAAA,IAAS,OAAA;;;;;;;;;;;;;iBAkB5C,YAAA,CAAa,KAAA,EAAO,cAAA,GAAiB,OAAA;;;;;;;;;;;;;iBAwCrC,mBAAA,CACd,KAAA,EAAO,cAAA,EACP,UAAA,EAAY,eAAA"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Namespace } from "./types.js";
|
|
2
|
+
import { Channel, SubscribeParams } from "@langchain/protocol";
|
|
3
|
+
|
|
4
|
+
//#region src/stream/subscription.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Minimal protocol-event shape consumed by {@link inferChannel} and
|
|
7
|
+
* {@link matchesSubscription}.
|
|
8
|
+
*
|
|
9
|
+
* Both the core {@link ProtocolEvent} and the wire-level `Event` from
|
|
10
|
+
* `@langchain/protocol` structurally satisfy this contract, so the same
|
|
11
|
+
* predicates can drive in-process fan-out, buffered replay, and server-side
|
|
12
|
+
* (SSE / WebSocket) event-sink filtering without coupling to a single event
|
|
13
|
+
* type.
|
|
14
|
+
*/
|
|
15
|
+
interface MatchableEvent {
|
|
16
|
+
/** Logical stream channel; see {@link inferChannel}. */
|
|
17
|
+
readonly method: string;
|
|
18
|
+
/** Monotonic sequence number, when present. Used by the `since` cursor. */
|
|
19
|
+
readonly seq?: number;
|
|
20
|
+
readonly params: {
|
|
21
|
+
/** Namespace of the node or scope that emitted this event. */readonly namespace: Namespace; /** Opaque channel payload; shape depends on `method`. */
|
|
22
|
+
readonly data?: unknown;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Strip dynamic suffixes (after `:`) from a namespace segment.
|
|
27
|
+
*
|
|
28
|
+
* Server-emitted namespaces contain runtime-generated suffixes like
|
|
29
|
+
* `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
|
|
30
|
+
* static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
|
|
31
|
+
* `api/langgraph_api/protocol/namespace.py`.
|
|
32
|
+
*
|
|
33
|
+
* @param segment - Raw namespace segment.
|
|
34
|
+
* @returns The stable graph-oriented portion of the segment.
|
|
35
|
+
*/
|
|
36
|
+
declare function normalizeNamespaceSegment(segment: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Whether `namespace` starts with `prefix`.
|
|
39
|
+
*
|
|
40
|
+
* Segments are compared literally first; if the prefix segment itself contains
|
|
41
|
+
* no `:`, the candidate segment is also compared after its dynamic suffix is
|
|
42
|
+
* stripped (see {@link normalizeNamespaceSegment}). This mirrors
|
|
43
|
+
* `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
|
|
44
|
+
* filtering and client-side per-subscription narrowing stay consistent.
|
|
45
|
+
*
|
|
46
|
+
* @param namespace - Event namespace to test.
|
|
47
|
+
* @param prefix - Subscription namespace prefix.
|
|
48
|
+
*/
|
|
49
|
+
declare function isPrefixMatch(namespace: Namespace, prefix: Namespace): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The base protocol subscription channels, excluding the templated
|
|
52
|
+
* `custom:<name>` form. This is the runtime counterpart to the `Channel`
|
|
53
|
+
* union from `@langchain/protocol` and mirrors the channel set a server
|
|
54
|
+
* recognizes when filtering its event sinks.
|
|
55
|
+
*/
|
|
56
|
+
declare const SUPPORTED_CHANNELS: Set<Channel>;
|
|
57
|
+
/**
|
|
58
|
+
* Whether `value` names a protocol subscription channel — either a base
|
|
59
|
+
* channel (`"messages"`, `"values"`, …) or a named custom channel
|
|
60
|
+
* (`"custom:<name>"`). Unknown/future method names return `false`,
|
|
61
|
+
* mirroring {@link inferChannel}.
|
|
62
|
+
*
|
|
63
|
+
* @param value - Candidate channel name.
|
|
64
|
+
*/
|
|
65
|
+
declare function isSupportedChannel(value: string): value is Channel;
|
|
66
|
+
/**
|
|
67
|
+
* Maps a protocol event method to its subscription {@link Channel}.
|
|
68
|
+
*
|
|
69
|
+
* Returns `undefined` for unrecognized methods so that new server-side
|
|
70
|
+
* channels (e.g. from extension transformers) don't break existing
|
|
71
|
+
* subscribers. The `custom` method resolves to the named `custom:<name>`
|
|
72
|
+
* channel when the payload carries a `name`, otherwise the bare `custom`
|
|
73
|
+
* channel. Both `"input"` and the wire-level `"input.requested"` map to the
|
|
74
|
+
* `input` channel.
|
|
75
|
+
*
|
|
76
|
+
* @param event - Event whose method should be mapped to a channel.
|
|
77
|
+
*/
|
|
78
|
+
declare function inferChannel(event: MatchableEvent): Channel | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Returns whether an event should be delivered for a subscription definition.
|
|
81
|
+
*
|
|
82
|
+
* When the definition carries a `since` replay cursor, events at or before
|
|
83
|
+
* that sequence number are excluded — letting the same predicate drive both
|
|
84
|
+
* live fan-out and buffered replay over a {@link StreamChannel}.
|
|
85
|
+
*
|
|
86
|
+
* @param event - Event being checked for delivery.
|
|
87
|
+
* @param definition - Subscription filter definition to evaluate against.
|
|
88
|
+
* The optional `since` field (a `seq` cursor) is read leniently because it
|
|
89
|
+
* is not a declared field on the base {@link SubscribeParams} shape.
|
|
90
|
+
*/
|
|
91
|
+
declare function matchesSubscription(event: MatchableEvent, definition: SubscribeParams): boolean;
|
|
92
|
+
//#endregion
|
|
93
|
+
export { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
|
|
94
|
+
//# sourceMappingURL=subscription.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.d.ts","names":[],"sources":["../../src/stream/subscription.ts"],"mappings":";;;;;AAsDA;;;;;AAiBA;;;;UA5CiB,cAAA;EA6Cf;EAAA,SA3CS,MAAA;EA4CT;EAAA,SAzCS,GAAA;EAAA,SAEA,MAAA;IA2EE,uEAzEA,SAAA,EAAW,SAAA,EAyEO;IAAA,SAtElB,IAAA;EAAA;AAAA;;;;;;;;;AA4Gb;;;iBA7FgB,yBAAA,CAA0B,OAAA;;;;;;AAqI1C;;;;;;;iBApHgB,aAAA,CACd,SAAA,EAAW,SAAA,EACX,MAAA,EAAQ,SAAA;;;;;;;cAoCG,kBAAA,EAAkB,GAAA,CAAA,OAAA;;;;;;;;;iBAoBf,kBAAA,CAAmB,KAAA,WAAgB,KAAA,IAAS,OAAA;;;;;;;;;;;;;iBAkB5C,YAAA,CAAa,KAAA,EAAO,cAAA,GAAiB,OAAA;;;;;;;;;;;;;iBAwCrC,mBAAA,CACd,KAAA,EAAO,cAAA,EACP,UAAA,EAAY,eAAA"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
//#region src/stream/subscription.ts
|
|
2
|
+
/**
|
|
3
|
+
* Strip dynamic suffixes (after `:`) from a namespace segment.
|
|
4
|
+
*
|
|
5
|
+
* Server-emitted namespaces contain runtime-generated suffixes like
|
|
6
|
+
* `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
|
|
7
|
+
* static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
|
|
8
|
+
* `api/langgraph_api/protocol/namespace.py`.
|
|
9
|
+
*
|
|
10
|
+
* @param segment - Raw namespace segment.
|
|
11
|
+
* @returns The stable graph-oriented portion of the segment.
|
|
12
|
+
*/
|
|
13
|
+
function normalizeNamespaceSegment(segment) {
|
|
14
|
+
const idx = segment.indexOf(":");
|
|
15
|
+
return idx === -1 ? segment : segment.slice(0, idx);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Whether `namespace` starts with `prefix`.
|
|
19
|
+
*
|
|
20
|
+
* Segments are compared literally first; if the prefix segment itself contains
|
|
21
|
+
* no `:`, the candidate segment is also compared after its dynamic suffix is
|
|
22
|
+
* stripped (see {@link normalizeNamespaceSegment}). This mirrors
|
|
23
|
+
* `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
|
|
24
|
+
* filtering and client-side per-subscription narrowing stay consistent.
|
|
25
|
+
*
|
|
26
|
+
* @param namespace - Event namespace to test.
|
|
27
|
+
* @param prefix - Subscription namespace prefix.
|
|
28
|
+
*/
|
|
29
|
+
function isPrefixMatch(namespace, prefix) {
|
|
30
|
+
if (prefix.length > namespace.length) return false;
|
|
31
|
+
for (let i = 0; i < prefix.length; i += 1) {
|
|
32
|
+
const segment = prefix[i];
|
|
33
|
+
const candidate = namespace[i];
|
|
34
|
+
if (candidate === segment) continue;
|
|
35
|
+
if (segment.includes(":")) return false;
|
|
36
|
+
if (normalizeNamespaceSegment(candidate) === segment) continue;
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function namespaceMatches(eventNamespace, prefixes, depth) {
|
|
42
|
+
if (!prefixes || prefixes.length === 0) return true;
|
|
43
|
+
return prefixes.some((prefix) => {
|
|
44
|
+
if (!isPrefixMatch(eventNamespace, prefix)) return false;
|
|
45
|
+
if (depth === void 0) return true;
|
|
46
|
+
return eventNamespace.length - prefix.length <= depth;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The base protocol subscription channels, excluding the templated
|
|
51
|
+
* `custom:<name>` form. This is the runtime counterpart to the `Channel`
|
|
52
|
+
* union from `@langchain/protocol` and mirrors the channel set a server
|
|
53
|
+
* recognizes when filtering its event sinks.
|
|
54
|
+
*/
|
|
55
|
+
const SUPPORTED_CHANNELS = new Set([
|
|
56
|
+
"values",
|
|
57
|
+
"updates",
|
|
58
|
+
"messages",
|
|
59
|
+
"tools",
|
|
60
|
+
"lifecycle",
|
|
61
|
+
"input",
|
|
62
|
+
"checkpoints",
|
|
63
|
+
"tasks",
|
|
64
|
+
"custom"
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* Whether `value` names a protocol subscription channel — either a base
|
|
68
|
+
* channel (`"messages"`, `"values"`, …) or a named custom channel
|
|
69
|
+
* (`"custom:<name>"`). Unknown/future method names return `false`,
|
|
70
|
+
* mirroring {@link inferChannel}.
|
|
71
|
+
*
|
|
72
|
+
* @param value - Candidate channel name.
|
|
73
|
+
*/
|
|
74
|
+
function isSupportedChannel(value) {
|
|
75
|
+
return SUPPORTED_CHANNELS.has(value) || value.startsWith("custom:");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Maps a protocol event method to its subscription {@link Channel}.
|
|
79
|
+
*
|
|
80
|
+
* Returns `undefined` for unrecognized methods so that new server-side
|
|
81
|
+
* channels (e.g. from extension transformers) don't break existing
|
|
82
|
+
* subscribers. The `custom` method resolves to the named `custom:<name>`
|
|
83
|
+
* channel when the payload carries a `name`, otherwise the bare `custom`
|
|
84
|
+
* channel. Both `"input"` and the wire-level `"input.requested"` map to the
|
|
85
|
+
* `input` channel.
|
|
86
|
+
*
|
|
87
|
+
* @param event - Event whose method should be mapped to a channel.
|
|
88
|
+
*/
|
|
89
|
+
function inferChannel(event) {
|
|
90
|
+
switch (event.method) {
|
|
91
|
+
case "values": return "values";
|
|
92
|
+
case "checkpoints": return "checkpoints";
|
|
93
|
+
case "updates": return "updates";
|
|
94
|
+
case "messages": return "messages";
|
|
95
|
+
case "tools": return "tools";
|
|
96
|
+
case "custom": {
|
|
97
|
+
const data = event.params.data;
|
|
98
|
+
return data?.name != null ? `custom:${data.name}` : "custom";
|
|
99
|
+
}
|
|
100
|
+
case "lifecycle": return "lifecycle";
|
|
101
|
+
case "input":
|
|
102
|
+
case "input.requested": return "input";
|
|
103
|
+
case "tasks": return "tasks";
|
|
104
|
+
default: return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Returns whether an event should be delivered for a subscription definition.
|
|
109
|
+
*
|
|
110
|
+
* When the definition carries a `since` replay cursor, events at or before
|
|
111
|
+
* that sequence number are excluded — letting the same predicate drive both
|
|
112
|
+
* live fan-out and buffered replay over a {@link StreamChannel}.
|
|
113
|
+
*
|
|
114
|
+
* @param event - Event being checked for delivery.
|
|
115
|
+
* @param definition - Subscription filter definition to evaluate against.
|
|
116
|
+
* The optional `since` field (a `seq` cursor) is read leniently because it
|
|
117
|
+
* is not a declared field on the base {@link SubscribeParams} shape.
|
|
118
|
+
*/
|
|
119
|
+
function matchesSubscription(event, definition) {
|
|
120
|
+
const since = definition.since;
|
|
121
|
+
if (typeof since === "number" && (event.seq ?? 0) <= since) return false;
|
|
122
|
+
const channel = inferChannel(event);
|
|
123
|
+
if (channel === void 0) return false;
|
|
124
|
+
const channels = definition.channels;
|
|
125
|
+
if (!(channels.includes(channel) || channel.startsWith("custom:") && channels.includes("custom"))) return false;
|
|
126
|
+
return namespaceMatches(event.params.namespace, definition.namespaces, definition.depth);
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
export { SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=subscription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.js","names":[],"sources":["../../src/stream/subscription.ts"],"sourcesContent":["/**\n * Subscription matching and channel inference for the v2 streaming protocol.\n *\n * These helpers are the building blocks a custom transport / server needs to\n * fan protocol events out to subscribers: map an event to its logical\n * {@link Channel} ({@link inferChannel}) and decide whether a buffered event\n * should be delivered for a given {@link SubscribeParams} filter\n * ({@link matchesSubscription}). They are typed against the minimal\n * {@link MatchableEvent} shape so the same predicate works on the core\n * {@link ProtocolEvent} produced by {@link convertToProtocolEvent} /\n * {@link StreamChannel} and on the wire-level `Event` from\n * `@langchain/protocol`.\n */\n\nimport type { Channel, SubscribeParams } from \"@langchain/protocol\";\nimport type { Namespace } from \"./types.js\";\n\n/**\n * Minimal protocol-event shape consumed by {@link inferChannel} and\n * {@link matchesSubscription}.\n *\n * Both the core {@link ProtocolEvent} and the wire-level `Event` from\n * `@langchain/protocol` structurally satisfy this contract, so the same\n * predicates can drive in-process fan-out, buffered replay, and server-side\n * (SSE / WebSocket) event-sink filtering without coupling to a single event\n * type.\n */\nexport interface MatchableEvent {\n /** Logical stream channel; see {@link inferChannel}. */\n readonly method: string;\n\n /** Monotonic sequence number, when present. Used by the `since` cursor. */\n readonly seq?: number;\n\n readonly params: {\n /** Namespace of the node or scope that emitted this event. */\n readonly namespace: Namespace;\n\n /** Opaque channel payload; shape depends on `method`. */\n readonly data?: unknown;\n };\n}\n\n/**\n * Strip dynamic suffixes (after `:`) from a namespace segment.\n *\n * Server-emitted namespaces contain runtime-generated suffixes like\n * `\"fetcher:abc-uuid\"`, while user-supplied subscription filters are typically\n * static names (`\"fetcher\"`). Mirrors `normalize_namespace_segment` in\n * `api/langgraph_api/protocol/namespace.py`.\n *\n * @param segment - Raw namespace segment.\n * @returns The stable graph-oriented portion of the segment.\n */\nexport function normalizeNamespaceSegment(segment: string): string {\n const idx = segment.indexOf(\":\");\n return idx === -1 ? segment : segment.slice(0, idx);\n}\n\n/**\n * Whether `namespace` starts with `prefix`.\n *\n * Segments are compared literally first; if the prefix segment itself contains\n * no `:`, the candidate segment is also compared after its dynamic suffix is\n * stripped (see {@link normalizeNamespaceSegment}). This mirrors\n * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side\n * filtering and client-side per-subscription narrowing stay consistent.\n *\n * @param namespace - Event namespace to test.\n * @param prefix - Subscription namespace prefix.\n */\nexport function isPrefixMatch(\n namespace: Namespace,\n prefix: Namespace\n): boolean {\n if (prefix.length > namespace.length) return false;\n for (let i = 0; i < prefix.length; i += 1) {\n const segment = prefix[i]!;\n const candidate = namespace[i]!;\n if (candidate === segment) continue;\n if (segment.includes(\":\")) return false;\n if (normalizeNamespaceSegment(candidate) === segment) continue;\n return false;\n }\n return true;\n}\n\nfunction namespaceMatches(\n eventNamespace: Namespace,\n prefixes: Namespace[] | undefined,\n depth: number | undefined\n): boolean {\n if (!prefixes || prefixes.length === 0) {\n return true;\n }\n\n return prefixes.some((prefix) => {\n if (!isPrefixMatch(eventNamespace, prefix)) return false;\n if (depth === undefined) return true;\n return eventNamespace.length - prefix.length <= depth;\n });\n}\n\n/**\n * The base protocol subscription channels, excluding the templated\n * `custom:<name>` form. This is the runtime counterpart to the `Channel`\n * union from `@langchain/protocol` and mirrors the channel set a server\n * recognizes when filtering its event sinks.\n */\nexport const SUPPORTED_CHANNELS = new Set<Channel>([\n \"values\",\n \"updates\",\n \"messages\",\n \"tools\",\n \"lifecycle\",\n \"input\",\n \"checkpoints\",\n \"tasks\",\n \"custom\",\n]);\n\n/**\n * Whether `value` names a protocol subscription channel — either a base\n * channel (`\"messages\"`, `\"values\"`, …) or a named custom channel\n * (`\"custom:<name>\"`). Unknown/future method names return `false`,\n * mirroring {@link inferChannel}.\n *\n * @param value - Candidate channel name.\n */\nexport function isSupportedChannel(value: string): value is Channel {\n return (\n SUPPORTED_CHANNELS.has(value as Channel) || value.startsWith(\"custom:\")\n );\n}\n\n/**\n * Maps a protocol event method to its subscription {@link Channel}.\n *\n * Returns `undefined` for unrecognized methods so that new server-side\n * channels (e.g. from extension transformers) don't break existing\n * subscribers. The `custom` method resolves to the named `custom:<name>`\n * channel when the payload carries a `name`, otherwise the bare `custom`\n * channel. Both `\"input\"` and the wire-level `\"input.requested\"` map to the\n * `input` channel.\n *\n * @param event - Event whose method should be mapped to a channel.\n */\nexport function inferChannel(event: MatchableEvent): Channel | undefined {\n switch (event.method) {\n case \"values\":\n return \"values\";\n case \"checkpoints\":\n return \"checkpoints\";\n case \"updates\":\n return \"updates\";\n case \"messages\":\n return \"messages\";\n case \"tools\":\n return \"tools\";\n case \"custom\": {\n const data = event.params.data as { name?: string } | undefined;\n return data?.name != null ? `custom:${data.name}` : \"custom\";\n }\n case \"lifecycle\":\n return \"lifecycle\";\n case \"input\":\n case \"input.requested\":\n return \"input\";\n case \"tasks\":\n return \"tasks\";\n default:\n return undefined;\n }\n}\n\n/**\n * Returns whether an event should be delivered for a subscription definition.\n *\n * When the definition carries a `since` replay cursor, events at or before\n * that sequence number are excluded — letting the same predicate drive both\n * live fan-out and buffered replay over a {@link StreamChannel}.\n *\n * @param event - Event being checked for delivery.\n * @param definition - Subscription filter definition to evaluate against.\n * The optional `since` field (a `seq` cursor) is read leniently because it\n * is not a declared field on the base {@link SubscribeParams} shape.\n */\nexport function matchesSubscription(\n event: MatchableEvent,\n definition: SubscribeParams\n): boolean {\n const since = (definition as { since?: unknown }).since;\n if (typeof since === \"number\" && (event.seq ?? 0) <= since) {\n return false;\n }\n\n const channel = inferChannel(event);\n if (channel === undefined) return false;\n\n const channels = definition.channels;\n const channelMatched =\n channels.includes(channel) ||\n (channel.startsWith(\"custom:\") && channels.includes(\"custom\"));\n if (!channelMatched) {\n return false;\n }\n\n return namespaceMatches(\n event.params.namespace,\n definition.namespaces,\n definition.depth\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsDA,SAAgB,0BAA0B,SAAyB;CACjE,MAAM,MAAM,QAAQ,QAAQ,IAAI;AAChC,QAAO,QAAQ,KAAK,UAAU,QAAQ,MAAM,GAAG,IAAI;;;;;;;;;;;;;;AAerD,SAAgB,cACd,WACA,QACS;AACT,KAAI,OAAO,SAAS,UAAU,OAAQ,QAAO;AAC7C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;EACzC,MAAM,UAAU,OAAO;EACvB,MAAM,YAAY,UAAU;AAC5B,MAAI,cAAc,QAAS;AAC3B,MAAI,QAAQ,SAAS,IAAI,CAAE,QAAO;AAClC,MAAI,0BAA0B,UAAU,KAAK,QAAS;AACtD,SAAO;;AAET,QAAO;;AAGT,SAAS,iBACP,gBACA,UACA,OACS;AACT,KAAI,CAAC,YAAY,SAAS,WAAW,EACnC,QAAO;AAGT,QAAO,SAAS,MAAM,WAAW;AAC/B,MAAI,CAAC,cAAc,gBAAgB,OAAO,CAAE,QAAO;AACnD,MAAI,UAAU,KAAA,EAAW,QAAO;AAChC,SAAO,eAAe,SAAS,OAAO,UAAU;GAChD;;;;;;;;AASJ,MAAa,qBAAqB,IAAI,IAAa;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;AAUF,SAAgB,mBAAmB,OAAiC;AAClE,QACE,mBAAmB,IAAI,MAAiB,IAAI,MAAM,WAAW,UAAU;;;;;;;;;;;;;;AAgB3E,SAAgB,aAAa,OAA4C;AACvE,SAAQ,MAAM,QAAd;EACE,KAAK,SACH,QAAO;EACT,KAAK,cACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,UAAU;GACb,MAAM,OAAO,MAAM,OAAO;AAC1B,UAAO,MAAM,QAAQ,OAAO,UAAU,KAAK,SAAS;;EAEtD,KAAK,YACH,QAAO;EACT,KAAK;EACL,KAAK,kBACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,QACE;;;;;;;;;;;;;;;AAgBN,SAAgB,oBACd,OACA,YACS;CACT,MAAM,QAAS,WAAmC;AAClD,KAAI,OAAO,UAAU,aAAa,MAAM,OAAO,MAAM,MACnD,QAAO;CAGT,MAAM,UAAU,aAAa,MAAM;AACnC,KAAI,YAAY,KAAA,EAAW,QAAO;CAElC,MAAM,WAAW,WAAW;AAI5B,KAAI,EAFF,SAAS,SAAS,QAAQ,IACzB,QAAQ,WAAW,UAAU,IAAI,SAAS,SAAS,SAAS,EAE7D,QAAO;AAGT,QAAO,iBACL,MAAM,OAAO,WACb,WAAW,YACX,WAAW,MACZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","names":[],"sources":["../../src/stream/types.ts"],"sourcesContent":["/**\n * Core type definitions for the v2 streaming interface.\n *\n * Channel event data types (`MessagesEventData`, `ToolsEventData`,\n * `UpdatesEventData`, `UsageInfo`, `Checkpoint`, `CheckpointSource`) are\n * re-exported from `@langchain/protocol` — the generated TypeScript\n * bindings for the canonical CDDL schema. Stream-specific types\n * (`StreamTransformer`, `ChatModelStream`, `ToolCallStream`,\n * `InterruptPayload`) are defined here.\n */\n\nimport type { ChatModelStream as CoreChatModelStream } from \"@langchain/core/language_models/stream\";\nimport type { ChatModelStreamEvent as CoreChatModelStreamEvent } from \"@langchain/core/language_models/event\";\nimport type { StreamMode } from \"../pregel/types.js\";\n\n/**\n * Re-exports from `@langchain/protocol`.\n *\n * These are the canonical wire-format types generated from `protocol.cddl`.\n * They are re-exported with local aliases so that consumers of this module\n * do not need a direct dependency on `@langchain/protocol`.\n */\nexport type {\n MessagesData as MessagesEventData,\n ToolsData as ToolsEventData,\n UpdatesData as UpdatesEventData,\n UsageInfo,\n MessageStartData,\n ContentBlockStartData,\n ContentBlockDeltaData,\n ContentBlockFinishData,\n MessageFinishData,\n MessageErrorData,\n ToolStartedData,\n ToolOutputDeltaData,\n ToolFinishedData,\n ToolErrorData,\n Checkpoint,\n CheckpointSource,\n AgentStatus,\n LifecycleData,\n LifecycleCause,\n} from \"@langchain/protocol\";\n\n/**\n * Hierarchical path identifying a position in the agent tree.\n *\n * Each element is one segment; longer arrays mean deeper nesting (e.g.\n * subgraph or multi-agent scopes).\n */\nexport type Namespace = string[];\n\n/**\n * Channels that can appear on a protocol event. Beyond the raw\n * {@link StreamMode} channels emitted by the Pregel stream, the v2\n * protocol layer synthesizes additional channels (e.g. `lifecycle`,\n * `input`) via built-in {@link StreamTransformer}s and exposes\n * user-defined channels created with {@link StreamChannel}.\n */\nexport type ProtocolMethod = StreamMode | \"lifecycle\" | \"input\" | (string & {});\n\n/**\n * Single envelope for a streaming protocol emission: sequence, channel\n * (`method`), and payload (`params`).\n */\nexport interface ProtocolEvent {\n /** Discriminator; always `\"event\"` for this shape. */\n readonly type: \"event\";\n\n /** Monotonic sequence number for ordering and deduplication within a run. */\n readonly seq: number;\n\n /**\n * Logical stream channel. Built-in channels match {@link StreamMode}\n * (e.g. `messages`, `updates`); transformer-synthesized channels\n * include `lifecycle` and `input`; user-defined channels carry their\n * {@link StreamChannel.channelName}.\n */\n readonly method: ProtocolMethod;\n\n /** Channel-specific payload and routing metadata. */\n readonly params: {\n /** Namespace of the node or scope that emitted this event. */\n readonly namespace: Namespace;\n\n /** Wall-clock or logical timestamp for the emission (milliseconds). */\n readonly timestamp: number;\n\n /**\n * Graph node id when the engine can attribute the event to a single node;\n * omitted for run-level or ambiguous emissions.\n */\n readonly node?: string;\n\n /** Opaque channel payload; shape depends on `method`. */\n readonly data: unknown;\n };\n}\n\n/**\n * Infers the merged extensions type from a tuple of transformer factory functions.\n *\n * Given `[() => StreamTransformer<{ a: number }>, () => StreamTransformer<{ b: string }>]`,\n * produces `{ a: number } & { b: string }`.\n */\nexport type InferExtensions<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends ReadonlyArray<() => StreamTransformer<any>>,\n> = T extends readonly []\n ? Record<string, never>\n : T extends readonly [\n () => StreamTransformer<infer P>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...infer Rest extends ReadonlyArray<() => StreamTransformer<any>>,\n ]\n ? P & InferExtensions<Rest>\n : Record<string, unknown>;\n\n/**\n * Observes {@link ProtocolEvent}s during a graph run and builds typed derived\n * projections (secondary event logs, promises, etc.).\n *\n * Data is surfaced to consumers through **projections** returned from\n * `init()`. Projections are merged into `GraphRunStream.extensions` for\n * in-process consumers. Use {@link StreamChannel.local} for local streaming\n * values, {@link StreamChannel.remote} for values that should also be visible\n * to remote clients, or `Promise<T>` for final values.\n *\n * To make projection data available to **remote** clients (SDK consumers\n * over WebSocket / SSE), create a named channel with\n * `StreamChannel.remote(name)`. The {@link StreamMux} detects named\n * `StreamChannel` instances in the `init()` return and auto-forwards every\n * `push()` as a {@link ProtocolEvent} on the channel's named method. Remote\n * clients subscribe via `session.subscribe(\"custom:<name>\")`.\n *\n * `finalize` and `fail` are optional. When a transformer uses\n * `StreamChannel`, the mux auto-closes/fails the channels on run\n * completion — no manual lifecycle management needed. Implement\n * `finalize`/`fail` only for non-channel teardown (e.g. resolving a\n * `Promise`).\n *\n * @typeParam TProjection - Shape returned by {@link init}, merged into\n * `GraphRunStream.extensions`.\n */\nexport interface StreamTransformer<TProjection = unknown> {\n /**\n * Called once before the run starts.\n *\n * @returns Initial projection merged into `GraphRunStream.extensions`.\n * Any named {@link StreamChannel} instances in the return value are\n * automatically wired to the protocol event stream by the mux. Unnamed\n * channels stay in-process-only.\n */\n init(): TProjection;\n\n /**\n * Optional hook invoked by {@link StreamMux.addTransformer} immediately\n * after the transformer is attached to the mux. Receives a limited\n * handle that exposes only {@link StreamEmitter.push} — enough for\n * the transformer to emit synthesized {@link ProtocolEvent}s on any\n * namespace it chooses (e.g. a deepagents `SubagentTransformer`\n * fabricating `lifecycle`/`messages`/`values` events under a\n * `[\"tools:<tool_call_id>\"]` namespace when a `task` tool starts).\n *\n * Transformers that do not synthesize events can omit this hook.\n *\n * The {@link StreamEmitter} handle is only safe to call *from within*\n * {@link StreamTransformer.process}. Emitting from an unrelated async\n * context (e.g. after `process` has returned, from a `setTimeout`,\n * etc.) races with the mux's close/fail cycle and may land events in\n * an already-closed log.\n */\n onRegister?(emitter: StreamEmitter): void;\n\n /**\n * Called for each {@link ProtocolEvent} before it is appended to the main log.\n *\n * @param event - Next protocol envelope for this run.\n * @returns `false` to drop the original event from the main log (use\n * sparingly; prefer keeping events visible and adding derived data\n * alongside).\n */\n process(event: ProtocolEvent): boolean;\n\n /**\n * Called once when the underlying Pregel run completes without throwing.\n * Optional — only needed for non-channel teardown (e.g. resolving promises).\n *\n * May return a `PromiseLike<void>` to defer the main event log close\n * until the async work (e.g. emitting terminal lifecycle events) has\n * completed. The mux awaits all returned promises before closing its\n * event log.\n */\n finalize?(): void | PromiseLike<void>;\n\n /**\n * Called once when the run fails; `err` is the rejection or error value.\n * Optional — only needed for non-channel teardown (e.g. rejecting promises).\n *\n * @param err - Failure reason from the engine or user code.\n */\n fail?(err: unknown): void;\n}\n\n/**\n * Narrow capability handle passed to\n * {@link StreamTransformer.onRegister}. Exposes only the minimal mux\n * surface required for synthetic event emission — intentionally does\n * not expose close/fail/register/etc. to keep the transformer contract\n * small and tamper-resistant.\n */\nexport interface StreamEmitter {\n /**\n * Injects a new {@link ProtocolEvent} into the mux pipeline. The\n * event is routed through every registered transformer (including\n * the emitting transformer — implementers must guard against\n * re-entrant self-processing) and, if not suppressed, appended to\n * the main event log.\n *\n * @param ns - Target namespace for the synthetic event.\n * @param event - The event envelope to inject. ``event.seq`` is\n * overwritten by the mux; callers can pass any placeholder.\n */\n push(ns: Namespace, event: ProtocolEvent): void;\n}\n\nexport type ChatModelStream = Omit<\n CoreChatModelStream,\n typeof Symbol.asyncIterator\n> & {\n /** Namespace of the graph node that produced this stream. */\n readonly namespace: Namespace;\n\n /** Graph node id for this stream, if the runtime attributed it. */\n readonly node: string | undefined;\n\n /**\n * Low-level async iteration over message lifecycle events.\n *\n * @returns Iterator yielding Core-compatible chat model stream events.\n */\n [Symbol.asyncIterator](): AsyncIterator<CoreChatModelStreamEvent>;\n};\n\n/**\n * Public view yielded by `run.messages`.\n *\n * `ChatModelStream` is PromiseLike to mirror Core, but TypeScript applies\n * `Awaited<T>` to values produced by `for await`. Exposing a non-thenable view\n * keeps loop variables typed as the streaming handle instead of `AIMessage`.\n */\nexport type ChatModelStreamHandle = Omit<ChatModelStream, \"then\">;\n\n/**\n * High-level outcome of a single tool call for UI or aggregators.\n */\nexport type ToolCallStatus =\n /** Invocation in flight or output still streaming. */\n | \"running\"\n /** Completed without error. */\n | \"finished\"\n /** Failed or aborted; see {@link ToolCallStream.error}. */\n | \"error\";\n\n/**\n * Stable handle for one tool call: name, arguments, and async results.\n *\n * Emitted when `content-block-finish` delivers a finalized `tool_call` block.\n *\n * @typeParam TName - Registered tool name.\n * @typeParam TInput - Parsed or raw input type for the call.\n * @typeParam TOutput - Successful result type after the tool returns.\n */\nexport interface ToolCallStream<\n TName extends string = string,\n TInput = unknown,\n TOutput = unknown,\n> {\n /** Tool identifier as registered on the graph or model schema. */\n readonly name: TName;\n\n /** Correlates with protocol `toolCallId` when the runtime provides one. */\n readonly callId: string;\n\n /** Arguments passed to the tool (finalized when the call is observable). */\n readonly input: TInput;\n\n /**\n * Resolves to the tool return value on success.\n *\n * @remarks\n * Rejection or hang semantics depend on the runner; pairing with\n * {@link ToolCallStream.status} and {@link ToolCallStream.error} is recommended.\n */\n readonly output: Promise<TOutput>;\n\n /**\n * Resolves to {@link ToolCallStatus} when the call leaves the running state.\n */\n readonly status: Promise<ToolCallStatus>;\n\n /**\n * Resolves to an error message string if {@link ToolCallStream.status} is\n * `\"error\"`, otherwise `undefined`.\n */\n readonly error: Promise<string | undefined>;\n}\n\n/**\n * Marker interface for transformers provided by internal LangChain products\n * (e.g. ReactAgent's ToolCallTransformer, DeepAgent's SubagentTransformer).\n *\n * Native transformers differ from user-defined extension transformers in\n * where their projection lands on the run stream:\n *\n * - **Native** — projections become direct getters on a\n * `GraphRunStream` subclass (e.g. `run.toolCalls`, `run.subagents`).\n * They emit events on protocol-defined channels (`tools`, `lifecycle`,\n * `tasks`, etc.).\n *\n * - **Extension** (user-defined) — projections are merged into\n * `run.extensions`. Events emitted via `emit()` use an\n * application-chosen method name (e.g. `emit(\"a2a\", data)`) and are\n * accessible to remote clients via `session.subscribe(\"custom:<name>\")`.\n *\n * The `__native` brand is used by downstream stream factory functions\n * to distinguish native transformers from extension transformers at\n * registration time. See `docs/native-stream-transformers.md` for the\n * full pattern.\n */\nexport interface NativeStreamTransformer<\n TProjection = unknown,\n> extends StreamTransformer<TProjection> {\n readonly __native: true;\n}\n\n/**\n * Type guard that tests whether a transformer is a {@link NativeStreamTransformer}.\n */\nexport function isNativeTransformer(\n t: StreamTransformer<unknown>\n): t is NativeStreamTransformer {\n return \"__native\" in t && (t as NativeStreamTransformer).__native === true;\n}\n\n/**\n * Human-in-the-loop interrupt: stable id plus opaque payload for resume UIs.\n */\nexport interface InterruptPayload<TPayload = unknown> {\n /** Idempotent key for this interrupt instance within the run. */\n interruptId: string;\n\n /** Arbitrary data supplied by the graph (e.g. questions, draft state). */\n payload: TPayload;\n}\n"],"mappings":";;;;AAmVA,SAAgB,oBACd,GAC8B;AAC9B,QAAO,cAAc,KAAM,EAA8B,aAAa"}
|
|
1
|
+
{"version":3,"file":"types.cjs","names":[],"sources":["../../src/stream/types.ts"],"sourcesContent":["/**\n * Core type definitions for the v2 streaming interface.\n *\n * Channel event data types (`MessagesEventData`, `ToolsEventData`,\n * `UpdatesEventData`, `UsageInfo`, `Checkpoint`, `CheckpointSource`) are\n * re-exported from `@langchain/protocol` — the generated TypeScript\n * bindings for the canonical CDDL schema. Stream-specific types\n * (`StreamTransformer`, `ChatModelStream`, `ToolCallStream`,\n * `InterruptPayload`) are defined here.\n */\n\nimport type { ChatModelStream as CoreChatModelStream } from \"@langchain/core/language_models/stream\";\nimport type { ChatModelStreamEvent as CoreChatModelStreamEvent } from \"@langchain/core/language_models/event\";\nimport type { StreamMode } from \"../pregel/types.js\";\n\n/**\n * Re-exports from `@langchain/protocol`.\n *\n * These are the canonical wire-format types generated from `protocol.cddl`.\n * They are re-exported with local aliases so that consumers of this module\n * do not need a direct dependency on `@langchain/protocol`.\n */\nexport type {\n MessagesData as MessagesEventData,\n ToolsData as ToolsEventData,\n UpdatesData as UpdatesEventData,\n UsageInfo,\n MessageStartData,\n ContentBlockStartData,\n ContentBlockDeltaData,\n ContentBlockFinishData,\n MessageFinishData,\n MessageErrorData,\n ToolStartedData,\n ToolOutputDeltaData,\n ToolFinishedData,\n ToolErrorData,\n Checkpoint,\n CheckpointSource,\n AgentStatus,\n LifecycleData,\n LifecycleCause,\n} from \"@langchain/protocol\";\n\n/**\n * Hierarchical path identifying a position in the agent tree.\n *\n * Each element is one segment; longer arrays mean deeper nesting (e.g.\n * subgraph or multi-agent scopes).\n */\nexport type Namespace = string[];\n\n/**\n * Channels that can appear on a protocol event. Beyond the raw\n * {@link StreamMode} channels emitted by the Pregel stream, the v2\n * protocol layer synthesizes additional channels (e.g. `lifecycle`,\n * `input`) via built-in {@link StreamTransformer}s and exposes\n * user-defined channels created with {@link StreamChannel}.\n */\nexport type ProtocolMethod = StreamMode | \"lifecycle\" | \"input\" | (string & {});\n\n/**\n * Single envelope for a streaming protocol emission: sequence, channel\n * (`method`), and payload (`params`).\n */\nexport interface ProtocolEvent {\n /** Discriminator; always `\"event\"` for this shape. */\n readonly type: \"event\";\n\n /** Monotonic sequence number for ordering and deduplication within a run. */\n readonly seq: number;\n\n /**\n * Logical stream channel. Built-in channels match {@link StreamMode}\n * (e.g. `messages`, `updates`); transformer-synthesized channels\n * include `lifecycle` and `input`; user-defined channels carry their\n * {@link StreamChannel.channelName}.\n */\n readonly method: ProtocolMethod;\n\n /** Channel-specific payload and routing metadata. */\n readonly params: {\n /** Namespace of the node or scope that emitted this event. */\n readonly namespace: Namespace;\n\n /** Wall-clock or logical timestamp for the emission (milliseconds). */\n readonly timestamp: number;\n\n /**\n * Graph node id when the engine can attribute the event to a single node;\n * omitted for run-level or ambiguous emissions.\n */\n readonly node?: string;\n\n /** Opaque channel payload; shape depends on `method`. */\n readonly data: unknown;\n };\n}\n\n/**\n * Infers the merged extensions type from a tuple of transformer factory functions.\n *\n * Given `[() => StreamTransformer<{ a: number }>, () => StreamTransformer<{ b: string }>]`,\n * produces `{ a: number } & { b: string }`.\n */\nexport type InferExtensions<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n T extends ReadonlyArray<() => StreamTransformer<any>>,\n> = T extends readonly []\n ? Record<string, never>\n : T extends readonly [\n () => StreamTransformer<infer P>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...infer Rest extends ReadonlyArray<() => StreamTransformer<any>>,\n ]\n ? P & InferExtensions<Rest>\n : Record<string, unknown>;\n\n/**\n * Observes {@link ProtocolEvent}s during a graph run and builds typed derived\n * projections (secondary event logs, promises, etc.).\n *\n * Data is surfaced to consumers through **projections** returned from\n * `init()`. Projections are merged into `GraphRunStream.extensions` for\n * in-process consumers. Use {@link StreamChannel.local} for local streaming\n * values, {@link StreamChannel.remote} for values that should also be visible\n * to remote clients, or `Promise<T>` for final values.\n *\n * To make projection data available to **remote** clients (SDK consumers\n * over WebSocket / SSE), create a named channel with\n * `StreamChannel.remote(name)`. The {@link StreamMux} detects named\n * `StreamChannel` instances in the `init()` return and auto-forwards every\n * `push()` as a {@link ProtocolEvent} on `custom:<name>`. Remote clients\n * subscribe via `session.subscribe(\"custom:<name>\")`.\n *\n * `finalize` and `fail` are optional. When a transformer uses\n * `StreamChannel`, the mux auto-closes/fails the channels on run\n * completion — no manual lifecycle management needed. Implement\n * `finalize`/`fail` only for non-channel teardown (e.g. resolving a\n * `Promise`).\n *\n * @typeParam TProjection - Shape returned by {@link init}, merged into\n * `GraphRunStream.extensions`.\n */\nexport interface StreamTransformer<TProjection = unknown> {\n /**\n * Called once before the run starts.\n *\n * @returns Initial projection merged into `GraphRunStream.extensions`.\n * Any named {@link StreamChannel} instances in the return value are\n * automatically wired to the protocol event stream by the mux. Unnamed\n * channels stay in-process-only.\n */\n init(): TProjection;\n\n /**\n * Optional hook invoked by {@link StreamMux.addTransformer} immediately\n * after the transformer is attached to the mux. Receives a limited\n * handle that exposes only {@link StreamEmitter.push} — enough for\n * the transformer to emit synthesized {@link ProtocolEvent}s on any\n * namespace it chooses (e.g. a deepagents `SubagentTransformer`\n * fabricating `lifecycle`/`messages`/`values` events under a\n * `[\"tools:<tool_call_id>\"]` namespace when a `task` tool starts).\n *\n * Transformers that do not synthesize events can omit this hook.\n *\n * The {@link StreamEmitter} handle is only safe to call *from within*\n * {@link StreamTransformer.process}. Emitting from an unrelated async\n * context (e.g. after `process` has returned, from a `setTimeout`,\n * etc.) races with the mux's close/fail cycle and may land events in\n * an already-closed log.\n */\n onRegister?(emitter: StreamEmitter): void;\n\n /**\n * Called for each {@link ProtocolEvent} before it is appended to the main log.\n *\n * @param event - Next protocol envelope for this run.\n * @returns `false` to drop the original event from the main log (use\n * sparingly; prefer keeping events visible and adding derived data\n * alongside).\n */\n process(event: ProtocolEvent): boolean;\n\n /**\n * Called once when the underlying Pregel run completes without throwing.\n * Optional — only needed for non-channel teardown (e.g. resolving promises).\n *\n * May return a `PromiseLike<void>` to defer the main event log close\n * until the async work (e.g. emitting terminal lifecycle events) has\n * completed. The mux awaits all returned promises before closing its\n * event log.\n */\n finalize?(): void | PromiseLike<void>;\n\n /**\n * Called once when the run fails; `err` is the rejection or error value.\n * Optional — only needed for non-channel teardown (e.g. rejecting promises).\n *\n * @param err - Failure reason from the engine or user code.\n */\n fail?(err: unknown): void;\n}\n\n/**\n * Narrow capability handle passed to\n * {@link StreamTransformer.onRegister}. Exposes only the minimal mux\n * surface required for synthetic event emission — intentionally does\n * not expose close/fail/register/etc. to keep the transformer contract\n * small and tamper-resistant.\n */\nexport interface StreamEmitter {\n /**\n * Injects a new {@link ProtocolEvent} into the mux pipeline. The\n * event is routed through every registered transformer (including\n * the emitting transformer — implementers must guard against\n * re-entrant self-processing) and, if not suppressed, appended to\n * the main event log.\n *\n * @param ns - Target namespace for the synthetic event.\n * @param event - The event envelope to inject. ``event.seq`` is\n * overwritten by the mux; callers can pass any placeholder.\n */\n push(ns: Namespace, event: ProtocolEvent): void;\n}\n\nexport type ChatModelStream = Omit<\n CoreChatModelStream,\n typeof Symbol.asyncIterator\n> & {\n /** Namespace of the graph node that produced this stream. */\n readonly namespace: Namespace;\n\n /** Graph node id for this stream, if the runtime attributed it. */\n readonly node: string | undefined;\n\n /**\n * Low-level async iteration over message lifecycle events.\n *\n * @returns Iterator yielding Core-compatible chat model stream events.\n */\n [Symbol.asyncIterator](): AsyncIterator<CoreChatModelStreamEvent>;\n};\n\n/**\n * Public view yielded by `run.messages`.\n *\n * `ChatModelStream` is PromiseLike to mirror Core, but TypeScript applies\n * `Awaited<T>` to values produced by `for await`. Exposing a non-thenable view\n * keeps loop variables typed as the streaming handle instead of `AIMessage`.\n */\nexport type ChatModelStreamHandle = Omit<ChatModelStream, \"then\">;\n\n/**\n * High-level outcome of a single tool call for UI or aggregators.\n */\nexport type ToolCallStatus =\n /** Invocation in flight or output still streaming. */\n | \"running\"\n /** Completed without error. */\n | \"finished\"\n /** Failed or aborted; see {@link ToolCallStream.error}. */\n | \"error\";\n\n/**\n * Stable handle for one tool call: name, arguments, and async results.\n *\n * Emitted when `content-block-finish` delivers a finalized `tool_call` block.\n *\n * @typeParam TName - Registered tool name.\n * @typeParam TInput - Parsed or raw input type for the call.\n * @typeParam TOutput - Successful result type after the tool returns.\n */\nexport interface ToolCallStream<\n TName extends string = string,\n TInput = unknown,\n TOutput = unknown,\n> {\n /** Tool identifier as registered on the graph or model schema. */\n readonly name: TName;\n\n /** Correlates with protocol `toolCallId` when the runtime provides one. */\n readonly callId: string;\n\n /** Arguments passed to the tool (finalized when the call is observable). */\n readonly input: TInput;\n\n /**\n * Resolves to the tool return value on success.\n *\n * @remarks\n * Rejection or hang semantics depend on the runner; pairing with\n * {@link ToolCallStream.status} and {@link ToolCallStream.error} is recommended.\n */\n readonly output: Promise<TOutput>;\n\n /**\n * Resolves to {@link ToolCallStatus} when the call leaves the running state.\n */\n readonly status: Promise<ToolCallStatus>;\n\n /**\n * Resolves to an error message string if {@link ToolCallStream.status} is\n * `\"error\"`, otherwise `undefined`.\n */\n readonly error: Promise<string | undefined>;\n}\n\n/**\n * Marker interface for transformers provided by internal LangChain products\n * (e.g. ReactAgent's ToolCallTransformer, DeepAgent's SubagentTransformer).\n *\n * Native transformers differ from user-defined extension transformers in\n * where their projection lands on the run stream:\n *\n * - **Native** — projections become direct getters on a\n * `GraphRunStream` subclass (e.g. `run.toolCalls`, `run.subagents`).\n * They emit events on protocol-defined channels (`tools`, `lifecycle`,\n * `tasks`, etc.).\n *\n * - **Extension** (user-defined) — projections are merged into\n * `run.extensions`. Events emitted via `emit()` use an\n * application-chosen method name (e.g. `emit(\"a2a\", data)`) and are\n * accessible to remote clients via `session.subscribe(\"custom:<name>\")`.\n *\n * The `__native` brand is used by downstream stream factory functions\n * to distinguish native transformers from extension transformers at\n * registration time. See `docs/native-stream-transformers.md` for the\n * full pattern.\n */\nexport interface NativeStreamTransformer<\n TProjection = unknown,\n> extends StreamTransformer<TProjection> {\n readonly __native: true;\n}\n\n/**\n * Type guard that tests whether a transformer is a {@link NativeStreamTransformer}.\n */\nexport function isNativeTransformer(\n t: StreamTransformer<unknown>\n): t is NativeStreamTransformer {\n return \"__native\" in t && (t as NativeStreamTransformer).__native === true;\n}\n\n/**\n * Human-in-the-loop interrupt: stable id plus opaque payload for resume UIs.\n */\nexport interface InterruptPayload<TPayload = unknown> {\n /** Idempotent key for this interrupt instance within the run. */\n interruptId: string;\n\n /** Arbitrary data supplied by the graph (e.g. questions, draft state). */\n payload: TPayload;\n}\n"],"mappings":";;;;AAmVA,SAAgB,oBACd,GAC8B;AAC9B,QAAO,cAAc,KAAM,EAA8B,aAAa"}
|