UNPKG

@langchain/core

Version:
101 lines (100 loc) 3.78 kB
import { _isString } from "./utils.js"; //#region src/messages/block_translators/openrouter.ts /** * Converts an OpenRouter AI message to an array of v1 standard content blocks. * * OpenRouter returns reasoning output through two places on the Chat * Completions response: * * 1. `message.reasoning` / `delta.reasoning` — a flat string that summarizes * the model's chain of thought. The `@langchain/openrouter` converter * normalizes this into `additional_kwargs.reasoning_content` so it matches * the DeepSeek convention already used elsewhere in LangChain. * 2. `message.reasoning_details` / `delta.reasoning_details` — a structured * array of provider-specific reasoning artifacts (see the * `reasoning.summary` / `reasoning.encrypted` / `reasoning.text` union in * the OpenRouter API types). The converter preserves these verbatim under * `additional_kwargs.reasoning_details` for round-tripping back to the * provider on subsequent turns (e.g. Anthropic extended thinking requires * the original `signature` to be echoed back). * * When `reasoning_details` is present, visible blocks are emitted from * `reasoning.summary` / `reasoning.text` entries. If the array contains only * opaque artifacts (e.g. `reasoning.encrypted`), the flat `reasoning_content` * string is used as a fallback when available. * * @param message - The AI message containing OpenRouter-formatted content * @returns Array of content blocks in v1 standard format * * @example * ```typescript * const message = new AIMessage({ * content: "The answer is 42", * additional_kwargs: { reasoning_content: "Let me think about this..." }, * response_metadata: { model_provider: "openrouter" }, * }); * message.contentBlocks; * // [ * // { type: "reasoning", reasoning: "Let me think about this..." }, * // { type: "text", text: "The answer is 42" } * // ] * ``` */ function convertToV1FromOpenRouterMessage(message) { const blocks = []; const reasoningDetails = message.additional_kwargs?.reasoning_details; let hasVisibleReasoningFromDetails = false; if (Array.isArray(reasoningDetails) && reasoningDetails.length > 0) for (const detail of reasoningDetails) { if (detail == null || typeof detail !== "object") continue; const type = detail.type; if (type === "reasoning.summary") { const summary = detail.summary; if (_isString(summary) && summary.length > 0) { blocks.push({ type: "reasoning", reasoning: summary }); hasVisibleReasoningFromDetails = true; } } else if (type === "reasoning.text") { const text = detail.text; if (_isString(text) && text.length > 0) { blocks.push({ type: "reasoning", reasoning: text }); hasVisibleReasoningFromDetails = true; } } } if (!hasVisibleReasoningFromDetails) { const reasoningContent = message.additional_kwargs?.reasoning_content; if (_isString(reasoningContent) && reasoningContent.length > 0) blocks.push({ type: "reasoning", reasoning: reasoningContent }); } if (typeof message.content === "string") { if (message.content.length > 0) blocks.push({ type: "text", text: message.content }); } else for (const block of message.content) if (typeof block === "object" && "type" in block && block.type === "text" && "text" in block && _isString(block.text)) blocks.push({ type: "text", text: block.text }); for (const toolCall of message.tool_calls ?? []) blocks.push({ type: "tool_call", id: toolCall.id, name: toolCall.name, args: toolCall.args }); return blocks; } const ChatOpenRouterTranslator = { translateContent: convertToV1FromOpenRouterMessage, translateContentChunk: convertToV1FromOpenRouterMessage }; //#endregion export { ChatOpenRouterTranslator }; //# sourceMappingURL=openrouter.js.map