UNPKG

@langchain/langgraph

Version:
232 lines (231 loc) 8.16 kB
import "../constants.js"; import { BaseMessage, BaseMessageChunk, ToolMessage } from "@langchain/core/messages"; import { BaseCallbackHandler } from "@langchain/core/callbacks/base"; //#region src/pregel/messages-v2.ts function getResponseMetadata(message) { if ("response_metadata" in message && typeof message.response_metadata === "object" && message.response_metadata != null) return message.response_metadata; } function getUsageMetadata(message) { if ("usage_metadata" in message && typeof message.usage_metadata === "object" && message.usage_metadata != null) return message.usage_metadata; } function startBlockFor(block) { switch (block.type) { case "text": return { type: "text", text: "" }; case "reasoning": return { type: "reasoning", reasoning: "" }; case "tool_call": case "tool_call_chunk": return { type: "tool_call_chunk", ...block.id != null ? { id: block.id } : {}, ...block.name != null ? { name: block.name } : {}, args: "" }; default: return block; } } function deltaFor(block) { switch (block.type) { case "text": { const text = typeof block.text === "string" ? block.text : ""; return text.length > 0 ? { event: "content-block-delta", index: typeof block.index === "number" ? block.index : 0, delta: { type: "text-delta", text } } : void 0; } case "reasoning": { const reasoning = typeof block.reasoning === "string" ? block.reasoning : ""; return reasoning.length > 0 ? { event: "content-block-delta", index: typeof block.index === "number" ? block.index : 0, delta: { type: "reasoning-delta", reasoning } } : void 0; } case "tool_call_chunk": return { event: "content-block-delta", index: typeof block.index === "number" ? block.index : 0, delta: { type: "block-delta", fields: { ...block, type: "tool_call_chunk" } } }; default: return; } } /** * A callback handler that implements protocol-native stream_mode=messages. * * LangChain Core owns chat model content-block event construction. This handler * only captures LangGraph metadata, forwards Core events to the Pregel messages * channel, and emits a small non-streaming fallback for models that cannot * produce stream events. */ var StreamProtocolMessagesHandler = class extends BaseCallbackHandler { name = "StreamProtocolMessagesHandler"; streamFn; metadatas = {}; seen = {}; streamedRunIds = /* @__PURE__ */ new Set(); stableMessageIdMap = {}; lc_prefer_chat_model_stream_events = true; awaitHandlers = true; constructor(streamFn) { super(); this.streamFn = streamFn; } normalizeMessageId(message, runId) { let messageId = message.id; if (runId != null) if (ToolMessage.isInstance(message)) messageId ??= `run-${runId}-tool-${message.tool_call_id}`; else { if (messageId == null || messageId === `run-${runId}`) messageId = this.stableMessageIdMap[runId] ?? messageId ?? `run-${runId}`; this.stableMessageIdMap[runId] ??= messageId; } if (messageId !== message.id) { message.id = messageId; message.lc_kwargs.id = messageId; } if (message.id != null) this.seen[message.id] = message; return message.id; } emit(meta, data, runId) { const metadata = runId != null ? { ...meta[1], run_id: runId } : meta[1]; this.streamFn([ meta[0], "messages", [data, metadata] ]); } emitFinalMessage(meta, message, runId, dedupe = false) { const existingId = message.id ?? (runId != null ? this.stableMessageIdMap[runId] : void 0); if (dedupe && existingId != null && this.seen[existingId] !== void 0) return; const messageId = this.normalizeMessageId(message, runId); const role = message.type === "human" ? "human" : message.type === "system" ? "system" : message.type === "tool" ? "tool" : "ai"; const toolCallId = role === "tool" && ToolMessage.isInstance(message) ? message.tool_call_id : void 0; this.emit(meta, { event: "message-start", ...messageId != null ? { id: messageId } : {}, ...role !== "ai" ? { role } : {}, ...typeof toolCallId === "string" ? { tool_call_id: toolCallId } : {} }, runId); (Array.isArray(message.content) ? message.content : typeof message.content === "string" && message.content.length > 0 ? [{ type: "text", text: message.content }] : []).forEach((block, offset) => { const index = typeof block.index === "number" ? block.index : offset; this.emit(meta, { event: "content-block-start", index, content: startBlockFor(block) }, runId); const delta = deltaFor({ ...block, index }); if (delta != null) this.emit(meta, delta, runId); this.emit(meta, { event: "content-block-finish", index, content: block }, runId); }); this.emit(meta, { event: "message-finish", ...getUsageMetadata(message) != null ? { usage: getUsageMetadata(message) } : {}, ...getResponseMetadata(message) != null ? { responseMetadata: getResponseMetadata(message) } : {} }, runId); } handleChatModelStart(_llm, _messages, runId, _parentRunId, _extraParams, tags, metadata, name) { if (metadata && (!tags || !tags.includes("langsmith:nostream") && !tags.includes("nostream"))) this.metadatas[runId] = [metadata.langgraph_checkpoint_ns.split("|"), { tags, name, ...metadata }]; } handleLLMNewToken() {} handleChatModelStreamEvent(event, runId) { const meta = this.metadatas[runId]; if (meta === void 0) return; let forwarded = event; if (event.event === "message-start") { this.streamedRunIds.add(runId); const id = event.id ?? `run-${runId}`; this.seen[id] = true; this.stableMessageIdMap[runId] ??= id; if (event.id == null) forwarded = { ...event, id }; } this.emit(meta, forwarded, runId); } handleLLMEnd(output, runId) { const meta = this.metadatas[runId]; if (meta === void 0) return; const chatGeneration = output.generations?.[0]?.[0]; const message = BaseMessage.isInstance(chatGeneration?.message) ? chatGeneration.message : void 0; if (message != null) if (this.streamedRunIds.has(runId)) { const messageId = this.normalizeMessageId(message, runId); if (messageId != null) this.seen[messageId] = message; } else this.emitFinalMessage(meta, message, runId, true); this.streamedRunIds.delete(runId); delete this.metadatas[runId]; delete this.stableMessageIdMap[runId]; } handleLLMError(_err, runId) { this.streamedRunIds.delete(runId); delete this.metadatas[runId]; delete this.stableMessageIdMap[runId]; } handleChainStart(_chain, inputs, runId, _parentRunId, tags, metadata, _runType, name) { if (metadata !== void 0 && name === metadata.langgraph_node && (tags === void 0 || !tags.includes("langsmith:hidden"))) { this.metadatas[runId] = [metadata.langgraph_checkpoint_ns.split("|"), { tags, name, ...metadata }]; if (typeof inputs === "object") { for (const value of Object.values(inputs)) if ((BaseMessage.isInstance(value) || BaseMessageChunk.isInstance(value)) && value.id !== void 0) this.seen[value.id] = value; else if (Array.isArray(value)) { for (const item of value) if ((BaseMessage.isInstance(item) || BaseMessageChunk.isInstance(item)) && item.id !== void 0) this.seen[item.id] = item; } } } } handleChainEnd(outputs, runId) { const meta = this.metadatas[runId]; delete this.metadatas[runId]; if (meta === void 0) return; const emitMessage = (value) => { if (BaseMessage.isInstance(value) && !ToolMessage.isInstance(value)) this.emitFinalMessage(meta, value, runId, true); }; if (BaseMessage.isInstance(outputs)) emitMessage(outputs); else if (Array.isArray(outputs)) for (const value of outputs) emitMessage(value); else if (outputs != null && typeof outputs === "object") for (const value of Object.values(outputs)) if (Array.isArray(value)) for (const item of value) emitMessage(item); else emitMessage(value); delete this.stableMessageIdMap[runId]; } handleChainError(_err, runId) { delete this.metadatas[runId]; delete this.stableMessageIdMap[runId]; } }; //#endregion export { StreamProtocolMessagesHandler }; //# sourceMappingURL=messages-v2.js.map