@langchain/langgraph-sdk
Version:
Client library for interacting with the LangGraph API
1 lines • 8.13 kB
Source Map (JSON)
{"version":3,"file":"message-coercion.cjs","names":["HumanMessage","AIMessage","SystemMessage","ToolMessage","RemoveMessage"],"sources":["../../src/stream/message-coercion.ts"],"sourcesContent":["import type { BaseMessage, BaseMessageChunk } from \"@langchain/core/messages\";\nimport {\n AIMessage,\n HumanMessage,\n RemoveMessage,\n SystemMessage,\n ToolMessage,\n coerceMessageLikeToMessage,\n} from \"@langchain/core/messages\";\nimport type { Message } from \"../types.messages.js\";\n\ntype MessageLike = Omit<Message, \"type\"> & { type: string };\n\ntype ToolCallLike = {\n id?: string;\n name?: string;\n args?: unknown;\n input?: unknown;\n};\n\ntype SerializedMessageWithContentBlocks = MessageLike & {\n content?: unknown;\n contentBlocks?: unknown;\n content_blocks?: unknown;\n};\n\n/**\n * Stream-local message coercion for serialized messages returned by\n * `getState()`, `getHistory()`, and `values` events.\n *\n * LangGraph API payloads may carry v1 content blocks as snake_case\n * `content_blocks`, while `@langchain/core` message constructors only\n * understand camelCase `contentBlocks` (or `content`). Normalize that\n * boundary here so stream consumers always see `BaseMessage.text`.\n */\nexport function tryCoerceMessageLikeToMessage(\n message: MessageLike\n): BaseMessage | BaseMessageChunk {\n const normalized = normalizeAIMessageToolCalls(message);\n\n if (normalized.type === \"human\" || normalized.type === \"user\") {\n return new HumanMessage(normalized);\n }\n\n if (normalized.type === \"ai\" || normalized.type === \"assistant\") {\n return new AIMessage(normalized);\n }\n\n if (normalized.type === \"system\") {\n return new SystemMessage(normalized);\n }\n\n if (normalized.type === \"tool\" && \"tool_call_id\" in normalized) {\n return new ToolMessage({\n ...normalized,\n tool_call_id: normalized.tool_call_id as string,\n });\n }\n\n if (normalized.type === \"remove\" && normalized.id != null) {\n return new RemoveMessage({ ...normalized, id: normalized.id });\n }\n\n return coerceMessageLikeToMessage(normalized);\n}\n\n/**\n * Ensures all messages in an array are BaseMessage class instances.\n * Messages that are already class instances pass through unchanged.\n */\nexport function ensureMessageInstances(\n messages: (Message | BaseMessage)[]\n): (BaseMessage | BaseMessageChunk)[] {\n return messages.map((msg) => {\n if (typeof (msg as BaseMessage).getType === \"function\") {\n return msg as BaseMessage;\n }\n return tryCoerceMessageLikeToMessage(\n msg as Omit<Message, \"type\"> & { type: string }\n );\n });\n}\n\nfunction normalizeSerializedContentBlocks<T extends MessageLike>(\n message: T\n): T {\n const record = message as SerializedMessageWithContentBlocks;\n const contentBlocks = record.contentBlocks ?? record.content_blocks;\n if (!Array.isArray(contentBlocks) || contentBlocks.length === 0) {\n return message;\n }\n\n const shouldPreferContentBlocks =\n isEmptyContent(record.content) ||\n (!hasTextContent(record.content) && hasTextContent(contentBlocks));\n if (!shouldPreferContentBlocks && record.contentBlocks === contentBlocks) {\n return message;\n }\n\n return {\n ...message,\n content: shouldPreferContentBlocks ? contentBlocks : record.content,\n contentBlocks,\n } as T;\n}\n\nexport function normalizeAIMessageToolCalls<T extends MessageLike>(\n message: T\n): T {\n const normalized = normalizeSerializedContentBlocks(message);\n const record = normalized as T & {\n content?: unknown;\n tool_calls?: unknown;\n };\n if (Array.isArray(record.tool_calls) && record.tool_calls.length > 0) {\n return normalized;\n }\n\n const toolCalls = extractToolCallsFromContent(record.content);\n if (toolCalls.length === 0) return normalized;\n return {\n ...normalized,\n tool_calls: toolCalls,\n };\n}\n\nfunction extractToolCallsFromContent(content: unknown) {\n if (!Array.isArray(content)) return [];\n return content.flatMap(\n (\n block\n ): Array<{\n id: string;\n name: string;\n args: Record<string, unknown>;\n type: \"tool_call\";\n }> => {\n if (block == null || typeof block !== \"object\") return [];\n const record = block as ToolCallLike & { type?: unknown };\n if (record.type !== \"tool_call\" && record.type !== \"tool_use\") return [];\n return [\n {\n id: record.id ?? \"\",\n name: record.name ?? \"\",\n args: normalizeToolCallArgs(record.args ?? record.input),\n type: \"tool_call\",\n },\n ];\n }\n );\n}\n\nfunction normalizeToolCallArgs(value: unknown): Record<string, unknown> {\n if (value != null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n if (typeof value === \"string\" && value.length > 0) {\n try {\n const parsed = JSON.parse(value);\n if (\n parsed != null &&\n typeof parsed === \"object\" &&\n !Array.isArray(parsed)\n ) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // Streaming input fragments are expected to be invalid until finalized.\n }\n }\n return {};\n}\n\nfunction isEmptyContent(content: unknown): boolean {\n return (\n content == null ||\n content === \"\" ||\n (Array.isArray(content) && content.length === 0)\n );\n}\n\nfunction hasTextContent(content: unknown): boolean {\n if (typeof content === \"string\") return content.length > 0;\n if (!Array.isArray(content)) return false;\n return content.some((block) => {\n if (typeof block === \"string\") return block.length > 0;\n if (block == null || typeof block !== \"object\") return false;\n const record = block as { type?: unknown; text?: unknown };\n return (\n record.type === \"text\" &&\n typeof record.text === \"string\" &&\n record.text.length > 0\n );\n });\n}\n"],"mappings":"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"}