UNPKG

@langchain/langgraph-sdk

Version:

Client library for interacting with the LangGraph API

1 lines • 17.6 kB
{"version":3,"file":"subagents.cjs","names":["#session","#messagesIterable","MultiCursorBuffer","StreamingMessageAssembler","#startProjection","isTerminalLifecycleAt","#toolCallsIterable","ToolCallAssembler","shouldIgnoreScopedTaskToolEvent","toClientAssembledToolCall","#ensureMediaDispatcher","#audioBuffer","#imagesBuffer","#videoBuffer","#filesBuffer","#mediaDispatcherStarted","MediaAssembler","#subgraphsIterable","SubgraphDiscoveryHandle","#source","#queue","#waiters","#pending","#sourcePump","#processEvent","#closed","#start"],"sources":["../../../../src/client/stream/handles/subagents.ts"],"sourcesContent":["import type {\n Channel,\n Event,\n MessagesEvent,\n SubscribeParams,\n ToolsEvent,\n} from \"@langchain/protocol\";\nimport type { SubscriptionHandle } from \"../index.js\";\nimport { MultiCursorBuffer } from \"../multi-cursor-buffer.js\";\nimport { StreamingMessageAssembler } from \"../messages.js\";\nimport type { StreamingMessage, StreamingMessageHandle } from \"../messages.js\";\nimport {\n shouldIgnoreScopedTaskToolEvent,\n ToolCallAssembler,\n toClientAssembledToolCall,\n} from \"./tools.js\";\nimport type { ClientAssembledToolCall } from \"./tools.js\";\nimport { MediaAssembler } from \"../media.js\";\nimport type {\n AudioMedia,\n FileMedia,\n ImageMedia,\n VideoMedia,\n} from \"../media.js\";\nimport type {\n EventForChannel,\n EventForChannels,\n SubscribeOptions,\n YieldForChannel,\n YieldForChannels,\n} from \"../types.js\";\nimport {\n type Subscribable,\n type SubgraphHandle,\n SubgraphDiscoveryHandle,\n isTerminalLifecycleAt,\n} from \"./subgraphs.js\";\n\n/**\n * Discovered subagent within a streaming session. Mirrors the\n * in-process `SubagentRunStream` from DeepAgent.\n *\n * Each subagent is discovered when a `tool-started` event with\n * `tool_name === \"task\"` is observed. The `taskInput` and `output`\n * promises resolve from the task tool's lifecycle events.\n *\n * Use lazy getters (`sub.messages`, `sub.toolCalls`, etc.) for\n * namespace-scoped projections.\n */\nexport class SubagentHandle {\n readonly name: string;\n readonly callId: string;\n readonly taskInput: Promise<string>;\n readonly output: Promise<unknown>;\n readonly namespace: string[];\n readonly #session: Subscribable;\n\n #messagesIterable?: AsyncIterable<StreamingMessage>;\n #toolCallsIterable?: AsyncIterable<ClientAssembledToolCall>;\n #subgraphsIterable?: AsyncIterable<SubgraphHandle>;\n\n #mediaDispatcherStarted = false;\n #audioBuffer?: MultiCursorBuffer<AudioMedia>;\n #imagesBuffer?: MultiCursorBuffer<ImageMedia>;\n #videoBuffer?: MultiCursorBuffer<VideoMedia>;\n #filesBuffer?: MultiCursorBuffer<FileMedia>;\n\n constructor(\n name: string,\n callId: string,\n namespace: string[],\n taskInput: Promise<string>,\n output: Promise<unknown>,\n session: Subscribable\n ) {\n this.name = name;\n this.callId = callId;\n this.namespace = namespace;\n this.taskInput = taskInput;\n this.output = output;\n this.#session = session;\n }\n\n get messages(): AsyncIterable<StreamingMessageHandle> {\n if (this.#messagesIterable) return this.#messagesIterable;\n const buffer = new MultiCursorBuffer<StreamingMessage>();\n this.#messagesIterable = buffer;\n const assembler = new StreamingMessageAssembler();\n void this.#startProjection(\n [\"messages\", \"lifecycle\"],\n (event) => {\n if (isTerminalLifecycleAt(event, this.namespace)) {\n assembler.abandonActive(this.namespace);\n return;\n }\n if (event.method !== \"messages\") return;\n const msg = assembler.consume(event as MessagesEvent);\n if (msg) buffer.push(msg);\n },\n () => {\n assembler.abandonActive();\n buffer.close();\n }\n );\n return buffer;\n }\n\n get toolCalls(): AsyncIterable<ClientAssembledToolCall> {\n if (this.#toolCallsIterable) return this.#toolCallsIterable;\n const buffer = new MultiCursorBuffer<ClientAssembledToolCall>();\n this.#toolCallsIterable = buffer;\n const assembler = new ToolCallAssembler();\n void this.#startProjection(\n [\"tools\"],\n (event) => {\n if (event.method !== \"tools\") return;\n const toolsEvent = event as ToolsEvent;\n if (shouldIgnoreScopedTaskToolEvent(this.namespace, toolsEvent)) return;\n const tc = assembler.consume(toolsEvent);\n if (tc) buffer.push(toClientAssembledToolCall(tc));\n },\n () => buffer.close()\n );\n return buffer;\n }\n\n get audio(): AsyncIterable<AudioMedia> {\n this.#ensureMediaDispatcher();\n return this.#audioBuffer!;\n }\n\n get images(): AsyncIterable<ImageMedia> {\n this.#ensureMediaDispatcher();\n return this.#imagesBuffer!;\n }\n\n get video(): AsyncIterable<VideoMedia> {\n this.#ensureMediaDispatcher();\n return this.#videoBuffer!;\n }\n\n get files(): AsyncIterable<FileMedia> {\n this.#ensureMediaDispatcher();\n return this.#filesBuffer!;\n }\n\n #ensureMediaDispatcher(): void {\n if (this.#mediaDispatcherStarted) return;\n this.#mediaDispatcherStarted = true;\n const audio = new MultiCursorBuffer<AudioMedia>();\n const images = new MultiCursorBuffer<ImageMedia>();\n const video = new MultiCursorBuffer<VideoMedia>();\n const files = new MultiCursorBuffer<FileMedia>();\n this.#audioBuffer = audio;\n this.#imagesBuffer = images;\n this.#videoBuffer = video;\n this.#filesBuffer = files;\n const assembler = new MediaAssembler({\n onAudio: (m: AudioMedia) => audio.push(m),\n onImage: (m: ImageMedia) => images.push(m),\n onVideo: (m: VideoMedia) => video.push(m),\n onFile: (m: FileMedia) => files.push(m),\n });\n void this.#startProjection(\n [\"messages\"],\n (event) => {\n if (event.method !== \"messages\") return;\n assembler.consume(event as MessagesEvent);\n },\n () => {\n assembler.close();\n audio.close();\n images.close();\n video.close();\n files.close();\n }\n );\n }\n\n get subgraphs(): AsyncIterable<SubgraphHandle> {\n if (this.#subgraphsIterable) return this.#subgraphsIterable;\n const buffer = new MultiCursorBuffer<SubgraphHandle>();\n this.#subgraphsIterable = buffer;\n void (async () => {\n const rawHandle = await this.#session.subscribe({\n channels: [\"lifecycle\"],\n namespaces: [this.namespace],\n });\n const discovery = new SubgraphDiscoveryHandle(\n rawHandle,\n this.#session,\n this.namespace\n );\n for await (const sub of discovery) {\n buffer.push(sub);\n }\n buffer.close();\n })();\n return buffer;\n }\n\n /**\n * Create a raw channel subscription scoped to this subagent's namespace.\n */\n subscribe<TChannel extends Channel>(\n channel: TChannel,\n options?: SubscribeOptions\n ): Promise<\n SubscriptionHandle<EventForChannel<TChannel>, YieldForChannel<TChannel>>\n >;\n subscribe<const TChannels extends readonly Channel[]>(\n channels: TChannels,\n options?: SubscribeOptions\n ): Promise<\n SubscriptionHandle<EventForChannels<TChannels>, YieldForChannels<TChannels>>\n >;\n subscribe(params: SubscribeParams): Promise<SubscriptionHandle<Event>>;\n subscribe(\n paramsOrChannels: SubscribeParams | Channel | string | readonly Channel[],\n options: SubscribeOptions = {}\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<any> {\n if (\n typeof paramsOrChannels === \"object\" &&\n !Array.isArray(paramsOrChannels) &&\n \"channels\" in paramsOrChannels\n ) {\n return this.#session.subscribe({\n ...paramsOrChannels,\n namespaces: paramsOrChannels.namespaces ?? [this.namespace],\n });\n }\n\n return this.#session.subscribe(paramsOrChannels as Channel, {\n ...options,\n namespaces: options.namespaces ?? [this.namespace],\n });\n }\n\n async #startProjection(\n channels: Channel[],\n onEvent: (event: Event) => void,\n onDone: () => void\n ): Promise<void> {\n try {\n const rawHandle = await this.#session.subscribe({\n channels,\n namespaces: [this.namespace],\n });\n for await (const event of rawHandle) {\n onEvent(event);\n }\n } finally {\n onDone();\n }\n }\n}\n\n/**\n * Async iterable that yields {@link SubagentHandle} instances as task\n * tool calls are discovered from the `tools` channel.\n *\n * Mirrors the in-process `createSubagentTransformer` from DeepAgent:\n * watches for `tool_name === \"task\"` with `tool-started`, extracts\n * `subagent_type` and `description` from the input, and resolves\n * `output` on `tool-finished`.\n */\nexport class SubagentDiscoveryHandle implements AsyncIterable<SubagentHandle> {\n readonly #source: SubscriptionHandle<Event>;\n readonly #session: Subscribable;\n readonly #queue: SubagentHandle[] = [];\n readonly #waiters: Array<(value: IteratorResult<SubagentHandle>) => void> =\n [];\n readonly #pending = new Map<\n string,\n {\n resolveOutput: (v: unknown) => void;\n rejectOutput: (e: unknown) => void;\n }\n >();\n #sourcePump?: Promise<void>;\n #closed = false;\n\n constructor(source: SubscriptionHandle<Event>, session: Subscribable) {\n this.#source = source;\n this.#session = session;\n }\n\n #processEvent(event: Event): SubagentHandle | undefined {\n if (event.method !== \"tools\") return undefined;\n const tools = event as ToolsEvent;\n const data = tools.params.data;\n const toolCallId = (data as Record<string, unknown>).tool_call_id as string;\n const toolName = (data as Record<string, unknown>).tool_name as string;\n\n if (toolName === \"task\" && data.event === \"tool-started\") {\n const rawInput = (data as Record<string, unknown>).input;\n const input: { description?: string; subagent_type?: string } =\n typeof rawInput === \"string\"\n ? JSON.parse(rawInput)\n : ((rawInput as Record<string, unknown>) ?? {});\n\n const name = input.subagent_type ?? \"unknown\";\n const description = input.description ?? \"\";\n\n let resolveTaskInput!: (v: string) => void;\n let resolveOutput!: (v: unknown) => void;\n let rejectOutput!: (e: unknown) => void;\n\n const taskInput = new Promise<string>((r) => {\n resolveTaskInput = r;\n });\n const output = new Promise<unknown>((res, rej) => {\n resolveOutput = res;\n rejectOutput = rej;\n });\n\n resolveTaskInput(description);\n this.#pending.set(toolCallId, { resolveOutput, rejectOutput });\n\n const namespace = [...tools.params.namespace];\n\n return new SubagentHandle(\n name,\n toolCallId,\n namespace,\n taskInput,\n output,\n this.#session\n );\n }\n\n if (toolCallId) {\n const pending = this.#pending.get(toolCallId);\n if (pending) {\n if (data.event === \"tool-finished\") {\n pending.resolveOutput((data as Record<string, unknown>).output);\n this.#pending.delete(toolCallId);\n } else if (data.event === \"tool-error\") {\n const message =\n ((data as Record<string, unknown>).message as string) ??\n \"unknown error\";\n pending.rejectOutput(new Error(message));\n this.#pending.delete(toolCallId);\n }\n }\n }\n\n return undefined;\n }\n\n #start(): void {\n if (this.#sourcePump) return;\n this.#sourcePump = (async () => {\n for await (const event of this.#source) {\n const handle = this.#processEvent(event);\n if (!handle) continue;\n\n const waiter = this.#waiters.shift();\n if (waiter) {\n waiter({ done: false, value: handle });\n } else {\n this.#queue.push(handle);\n }\n }\n this.#closed = true;\n for (const pending of this.#pending.values()) {\n pending.resolveOutput(undefined);\n }\n this.#pending.clear();\n while (this.#waiters.length > 0) {\n this.#waiters.shift()?.({ done: true, value: undefined });\n }\n })();\n }\n\n async close(): Promise<void> {\n this.#closed = true;\n await this.#source.unsubscribe();\n }\n\n [Symbol.asyncIterator](): AsyncIterator<SubagentHandle> {\n this.#start();\n return {\n next: async () => {\n if (this.#queue.length > 0) {\n return { done: false, value: this.#queue.shift()! };\n }\n if (this.#closed) {\n return { done: true, value: undefined };\n }\n return await new Promise<IteratorResult<SubagentHandle>>((resolve) => {\n this.#waiters.push(resolve);\n });\n },\n return: async () => {\n await this.close();\n return { done: true, value: undefined };\n },\n };\n }\n}\n"],"mappings":"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"}