@librechat/agents 3.2.53 → 3.2.55

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.
@@ -321,7 +321,7 @@ function _formatContent(message) {
321
321
  if ((0, _langchain_core_messages.isAIMessage)(message) && (signature == null || signature === "")) return null;
322
322
  return {
323
323
  type: "thinking",
324
- thinking: thinkingPart.thinking,
324
+ thinking: thinkingPart.thinking ?? "",
325
325
  signature: thinkingPart.signature,
326
326
  ...cacheControl != null ? { cache_control: cacheControl } : {}
327
327
  };
@@ -1 +1 @@
1
- {"version":3,"file":"message_inputs.cjs","names":["HumanMessage","isAnthropicImageBlockParam","redactedPart","compactionPart"],"sources":["../../../../../src/llm/anthropic/utils/message_inputs.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable no-console */\n/**\n * This util file contains functions for converting LangChain messages to Anthropic messages.\n */\nimport { createHash } from 'node:crypto';\nimport { ToolCall } from '@langchain/core/messages/tool';\nimport {\n type BaseMessage,\n type SystemMessage,\n HumanMessage,\n type AIMessage,\n type ToolMessage,\n isAIMessage,\n type Data,\n type StandardContentBlockConverter,\n MessageContentComplex,\n isDataContentBlock,\n convertToProviderContentBlock,\n parseBase64DataUrl,\n} from '@langchain/core/messages';\nimport {\n AnthropicImageBlockParam,\n AnthropicMessageCreateParams,\n AnthropicTextBlockParam,\n AnthropicDocumentBlockParam,\n AnthropicThinkingBlockParam,\n AnthropicRedactedThinkingBlockParam,\n AnthropicServerToolUseBlockParam,\n AnthropicWebSearchToolResultBlockParam,\n isAnthropicImageBlockParam,\n AnthropicSearchResultBlockParam,\n AnthropicCompactionBlockParam,\n AnthropicToolResponse,\n} from '../types';\nimport { Constants } from '@/common';\n\ntype StandardTextBlock = Data.StandardTextBlock;\ntype StandardImageBlock = Data.StandardImageBlock;\ntype StandardFileBlock = Data.StandardFileBlock;\ntype ImageUrlContentBlock = MessageContentComplex & {\n image_url: string | { url: string };\n};\ntype GoogleFunctionCallBlock = MessageContentComplex & {\n functionCall: {\n name: string;\n args: Record<string, unknown>;\n };\n};\n\nconst ANTHROPIC_EMPTY_TEXT_PLACEHOLDER = '_';\nconst CLAUDE_4_RELEASE_DATE_MODEL_PATTERN =\n /claude-(?:opus|sonnet|haiku)-4-\\d{8}(?:[-.@]|$)/i;\nconst CLAUDE_4_MINOR_MODEL_PATTERN =\n /claude-(?:opus|sonnet|haiku)-4[-.](\\d+)(?:[-.@]|$)/i;\n\nfunction _formatImage(imageUrl: string) {\n const parsed = parseBase64DataUrl({ dataUrl: imageUrl });\n if (parsed) {\n return {\n type: 'base64',\n media_type: parsed.mime_type,\n data: parsed.data,\n };\n }\n let parsedUrl: URL;\n\n try {\n parsedUrl = new URL(imageUrl);\n } catch {\n throw new Error(\n [\n `Malformed image URL: ${JSON.stringify(\n imageUrl\n )}. Content blocks of type 'image_url' must be a valid http, https, or base64-encoded data URL.`,\n 'Example: data:image/png;base64,/9j/4AAQSk...',\n 'Example: https://example.com/image.jpg',\n ].join('\\n\\n')\n );\n }\n\n if (parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') {\n return {\n type: 'url',\n url: imageUrl,\n };\n }\n\n throw new Error(\n [\n `Invalid image URL protocol: ${JSON.stringify(\n parsedUrl.protocol\n )}. Anthropic only supports images as http, https, or base64-encoded data URLs on 'image_url' content blocks.`,\n 'Example: data:image/png;base64,/9j/4AAQSk...',\n 'Example: https://example.com/image.jpg',\n ].join('\\n\\n')\n );\n}\n\nconst ANTHROPIC_TOOL_USE_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;\nconst ANTHROPIC_TOOL_USE_ID_MAX_LENGTH = 64;\nconst ANTHROPIC_TOOL_USE_ID_HASH_LENGTH = 10;\n\n/**\n * Normalize a tool-call ID to satisfy Anthropic's `^[a-zA-Z0-9_-]+$` and 64-char\n * constraints. Pure and deterministic — same input always yields the same output,\n * so paired `tool_use.id` and `tool_result.tool_use_id` stay matched without\n * needing a session map. IDs that already comply pass through unchanged.\n *\n * For non-compliant inputs we sanitize then append a short SHA-256 prefix of\n * the original ID to preserve uniqueness when truncation would otherwise\n * collapse distinct IDs to the same value (e.g. two long Responses-style IDs\n * sharing a 64-char prefix). The hash is computed against the raw input so\n * inputs that differ only after the truncation cutoff still produce distinct\n * outputs.\n */\nexport function normalizeAnthropicToolCallId(id: string): string;\nexport function normalizeAnthropicToolCallId(\n id: string | undefined\n): string | undefined;\nexport function normalizeAnthropicToolCallId(\n id: string | undefined\n): string | undefined {\n if (id == null) {\n return id;\n }\n if (\n id.length <= ANTHROPIC_TOOL_USE_ID_MAX_LENGTH &&\n ANTHROPIC_TOOL_USE_ID_PATTERN.test(id)\n ) {\n return id;\n }\n const sanitized = id.replace(/[^a-zA-Z0-9_-]/g, '_');\n const hash = createHash('sha256')\n .update(id)\n .digest('hex')\n .slice(0, ANTHROPIC_TOOL_USE_ID_HASH_LENGTH);\n const prefixMaxLength =\n ANTHROPIC_TOOL_USE_ID_MAX_LENGTH - ANTHROPIC_TOOL_USE_ID_HASH_LENGTH - 1;\n return `${sanitized.slice(0, prefixMaxLength)}_${hash}`;\n}\n\n/**\n * Lift any `cache_control` off the inner blocks of a tool result onto the\n * `tool_result` block itself. Anthropic documents the top-level\n * `messages.content` block as the cacheable position and does not document\n * caching of sub-content blocks; the API currently honors a nested marker, but\n * anchoring on the documented position keeps the single tail breakpoint robust\n * (and mirrors the Bedrock cachePoint hoist). The first marker found wins; it is\n * stripped from every inner block so exactly one survives, on the outer block.\n */\nfunction hoistToolResultCacheControl(\n content: string | MessageContentComplex[]\n): { content: string | MessageContentComplex[]; cacheControl: unknown } {\n if (!Array.isArray(content)) {\n return { content, cacheControl: undefined };\n }\n let cacheControl: unknown;\n const stripped = content.map((block) => {\n if ('cache_control' in block) {\n cacheControl ??= (block as Record<string, unknown>).cache_control;\n const clone = { ...(block as Record<string, unknown>) };\n delete clone.cache_control;\n return clone as MessageContentComplex;\n }\n return block;\n });\n // `stripped` is element-equal to `content` when no marker was present.\n return { content: stripped, cacheControl };\n}\n\nfunction _ensureMessageContents(\n messages: BaseMessage[]\n): (SystemMessage | HumanMessage | AIMessage)[] {\n // Merge runs of human/tool messages into single human messages with content blocks.\n const updatedMsgs: BaseMessage[] = [];\n for (const message of messages) {\n if (message._getType() === 'tool') {\n if (typeof message.content === 'string') {\n const previousMessage = updatedMsgs[updatedMsgs.length - 1];\n if (\n previousMessage._getType() === 'human' &&\n Array.isArray(previousMessage.content) &&\n 'type' in previousMessage.content[0] &&\n previousMessage.content[0].type === 'tool_result'\n ) {\n // If the previous message was a tool result, we merge this tool message into it.\n (previousMessage.content as MessageContentComplex[]).push({\n type: 'tool_result',\n content: message.content,\n tool_use_id: normalizeAnthropicToolCallId(\n (message as ToolMessage).tool_call_id\n ),\n });\n } else {\n // If not, we create a new human message with the tool result.\n updatedMsgs.push(\n new HumanMessage({\n content: [\n {\n type: 'tool_result',\n content: message.content,\n tool_use_id: normalizeAnthropicToolCallId(\n (message as ToolMessage).tool_call_id\n ),\n },\n ],\n })\n );\n }\n } else {\n const toolMessageContent = (\n message as { content?: BaseMessage['content'] | null }\n ).content;\n // Hoist a tail cache_control off the inner content onto the\n // tool_result block itself (the documented cacheable position).\n const { content: hoistedContent, cacheControl } =\n toolMessageContent != null\n ? hoistToolResultCacheControl(_formatContent(message))\n : { content: undefined, cacheControl: undefined };\n updatedMsgs.push(\n new HumanMessage({\n content: [\n {\n type: 'tool_result',\n ...(hoistedContent != null ? { content: hoistedContent } : {}),\n ...(cacheControl != null\n ? { cache_control: cacheControl as { type: 'ephemeral' } }\n : {}),\n tool_use_id: normalizeAnthropicToolCallId(\n (message as ToolMessage).tool_call_id\n ),\n },\n ],\n })\n );\n }\n } else {\n updatedMsgs.push(message);\n }\n }\n return updatedMsgs as (SystemMessage | HumanMessage | AIMessage)[];\n}\n\nexport function _convertLangChainToolCallToAnthropic(\n toolCall: ToolCall\n): AnthropicToolResponse {\n if (toolCall.id === undefined) {\n throw new Error('Anthropic requires all tool calls to have an \"id\".');\n }\n const isServerTool = toolCall.id.startsWith(\n Constants.ANTHROPIC_SERVER_TOOL_PREFIX\n );\n return {\n type: isServerTool ? 'server_tool_use' : 'tool_use',\n id: isServerTool ? toolCall.id : normalizeAnthropicToolCallId(toolCall.id),\n name: toolCall.name,\n input: toolCall.args,\n };\n}\n\nconst standardContentBlockConverter: StandardContentBlockConverter<{\n text: AnthropicTextBlockParam;\n image: AnthropicImageBlockParam;\n file: AnthropicDocumentBlockParam;\n}> = {\n providerName: 'anthropic',\n\n fromStandardTextBlock(block: StandardTextBlock): AnthropicTextBlockParam {\n return {\n type: 'text',\n text: block.text,\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicTextBlockParam;\n },\n\n fromStandardImageBlock(block: StandardImageBlock): AnthropicImageBlockParam {\n if (block.source_type === 'url') {\n const data = parseBase64DataUrl({\n dataUrl: block.url,\n asTypedArray: false,\n });\n if (data) {\n return {\n type: 'image',\n source: {\n type: 'base64',\n data: data.data,\n media_type: data.mime_type,\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicImageBlockParam;\n } else {\n return {\n type: 'image',\n source: {\n type: 'url',\n url: block.url,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicImageBlockParam;\n }\n } else {\n if (block.source_type === 'base64') {\n return {\n type: 'image',\n source: {\n type: 'base64',\n data: block.data,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicImageBlockParam;\n } else {\n throw new Error(`Unsupported image source type: ${block.source_type}`);\n }\n }\n },\n\n fromStandardFileBlock(block: StandardFileBlock): AnthropicDocumentBlockParam {\n const mime_type = (block.mime_type ?? '').split(';')[0];\n\n if (block.source_type === 'url') {\n if (mime_type === 'application/pdf' || mime_type === '') {\n return {\n type: 'document',\n source: {\n type: 'url',\n url: block.url,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n }\n throw new Error(\n `Unsupported file mime type for file url source: ${block.mime_type}`\n );\n } else if (block.source_type === 'text') {\n if (mime_type === 'text/plain' || mime_type === '') {\n return {\n type: 'document',\n source: {\n type: 'text',\n data: block.text,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n } else {\n throw new Error(\n `Unsupported file mime type for file text source: ${block.mime_type}`\n );\n }\n } else if (block.source_type === 'base64') {\n if (mime_type === 'application/pdf' || mime_type === '') {\n return {\n type: 'document',\n source: {\n type: 'base64',\n data: block.data,\n media_type: 'application/pdf',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n } else if (\n ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].includes(\n mime_type\n )\n ) {\n return {\n type: 'document',\n source: {\n type: 'content',\n content: [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: block.data,\n media_type: mime_type as\n | 'image/jpeg'\n | 'image/png'\n | 'image/gif'\n | 'image/webp',\n },\n },\n ],\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n } else {\n throw new Error(\n `Unsupported file mime type for file base64 source: ${block.mime_type}`\n );\n }\n } else {\n throw new Error(`Unsupported file source type: ${block.source_type}`);\n }\n },\n};\n\nfunction _formatContent(message: BaseMessage) {\n const toolTypes = [\n 'tool_use',\n 'tool_result',\n 'input_json_delta',\n 'server_tool_use',\n 'web_search_tool_result',\n 'web_search_result',\n ];\n const textTypes = ['text', 'text_delta'];\n /**\n * Reasoning blocks emitted by other providers — Bedrock's `reasoning_content`,\n * Google's `reasoning`, and LibreChat's `think`. Their signatures are\n * provider-specific and cannot be validated by Anthropic, so on a\n * cross-provider handoff (e.g. Bedrock → Anthropic) we drop them rather than\n * forwarding an unusable block. The receiving model produces its own thinking.\n */\n const foreignReasoningTypes = ['reasoning_content', 'reasoning', 'think'];\n const { content } = message;\n\n if (typeof content === 'string') {\n return content;\n } else {\n const contentParts = content as MessageContentComplex[];\n const contentBlocks = contentParts.map((contentPart) => {\n /**\n * Normalize server_tool_use blocks into a clean shape the API accepts.\n * These blocks may arrive with the correct type (server_tool_use) or mislabeled\n * as text/tool_use after chunk concatenation or state serialization.\n * Regardless of current type, if the id starts with 'srvtoolu_' we rebuild\n * a clean block with only the properties the API expects.\n */\n if (\n 'id' in contentPart &&\n typeof (contentPart as Record<string, unknown>).id === 'string' &&\n ((contentPart as Record<string, unknown>).id as string).startsWith(\n Constants.ANTHROPIC_SERVER_TOOL_PREFIX\n ) &&\n 'name' in contentPart\n ) {\n const rawPart = contentPart as Record<string, unknown>;\n let input = rawPart.input;\n if (typeof input === 'string') {\n try {\n input = JSON.parse(input);\n } catch {\n input = {};\n }\n }\n const corrected: AnthropicServerToolUseBlockParam = {\n type: 'server_tool_use',\n id: rawPart.id as string,\n name: (rawPart.name ?? 'web_search') as 'web_search',\n input: (input ?? {}) as Record<string, unknown>,\n };\n return corrected;\n }\n\n /**\n * Normalize web_search_tool_result blocks into a clean shape.\n * Same rationale as above — the block may carry extra properties from\n * streaming (input, index, etc.) that the API rejects. Rebuild cleanly.\n */\n if (\n 'tool_use_id' in contentPart &&\n typeof (contentPart as Record<string, unknown>).tool_use_id ===\n 'string' &&\n (\n (contentPart as Record<string, unknown>).tool_use_id as string\n ).startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) &&\n 'content' in contentPart\n ) {\n const rawPart = contentPart as Record<string, unknown>;\n const content = rawPart.content;\n const isValidContent =\n Array.isArray(content) ||\n (content != null &&\n typeof content === 'object' &&\n 'type' in content &&\n (content as Record<string, unknown>).type ===\n 'web_search_tool_result_error');\n\n if (isValidContent) {\n const corrected: AnthropicWebSearchToolResultBlockParam = {\n type: 'web_search_tool_result',\n tool_use_id: rawPart.tool_use_id as string,\n content:\n content as AnthropicWebSearchToolResultBlockParam['content'],\n };\n return corrected;\n }\n return null;\n }\n\n /**\n * Skip non-server malformed blocks that have tool fields mixed with text type.\n */\n if (\n 'id' in contentPart &&\n 'name' in contentPart &&\n 'input' in contentPart &&\n contentPart.type === 'text'\n ) {\n return null;\n }\n if (\n 'tool_use_id' in contentPart &&\n 'content' in contentPart &&\n contentPart.type === 'text'\n ) {\n return null;\n }\n\n // Core's v1 streaming aggregation can leave a partial tool-input delta as a\n // standalone block typed `text` carrying `input` but no `text`. The assembled\n // input is restored on the tool_use block from `message.tool_calls`, so drop it.\n if (\n contentPart.type === 'text' &&\n 'input' in contentPart &&\n !('text' in contentPart)\n ) {\n return null;\n }\n\n if (isDataContentBlock(contentPart)) {\n return convertToProviderContentBlock(\n contentPart,\n standardContentBlockConverter\n );\n }\n\n const cacheControl =\n 'cache_control' in contentPart ? contentPart.cache_control : undefined;\n\n if (contentPart.type === 'image_url') {\n let source;\n const imageUrl = (contentPart as ImageUrlContentBlock).image_url;\n if (typeof imageUrl === 'string') {\n source = _formatImage(imageUrl);\n } else {\n source = _formatImage(imageUrl.url);\n }\n return {\n type: 'image' as const, // Explicitly setting the type as \"image\"\n source,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n } else if (isAnthropicImageBlockParam(contentPart)) {\n return contentPart;\n } else if (contentPart.type === 'document') {\n // PDF\n return {\n ...contentPart,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n } else if (contentPart.type === 'thinking') {\n const thinkingPart = contentPart as AnthropicThinkingBlockParam;\n // Google thinking-enabled output reuses `type: 'thinking'` but carries\n // no Anthropic signature. Anthropic rejects an unsigned thinking block,\n // so on an assistant turn treat it as foreign reasoning and drop it\n // rather than forward an unusable block. Signed (Anthropic-native)\n // thinking is forwarded as before.\n const signature = (thinkingPart as { signature?: string }).signature;\n if (isAIMessage(message) && (signature == null || signature === '')) {\n return null;\n }\n const block: AnthropicThinkingBlockParam = {\n type: 'thinking' as const, // Explicitly setting the type as \"thinking\"\n thinking: thinkingPart.thinking,\n signature: thinkingPart.signature,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n return block;\n } else if (contentPart.type === 'redacted_thinking') {\n const redactedPart = contentPart as AnthropicRedactedThinkingBlockParam;\n const block: AnthropicRedactedThinkingBlockParam = {\n type: 'redacted_thinking' as const, // Explicitly setting the type as \"redacted_thinking\"\n data: redactedPart.data,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n return block;\n } else if (contentPart.type === 'search_result') {\n const searchResultPart = contentPart as AnthropicSearchResultBlockParam;\n const block: AnthropicSearchResultBlockParam = {\n type: 'search_result' as const,\n title: searchResultPart.title,\n source: searchResultPart.source,\n ...('cache_control' in contentPart &&\n contentPart.cache_control != null\n ? { cache_control: contentPart.cache_control }\n : {}),\n ...('citations' in contentPart && contentPart.citations != null\n ? { citations: contentPart.citations }\n : {}),\n content: searchResultPart.content,\n };\n return block;\n } else if (contentPart.type === 'compaction') {\n const compactionPart = contentPart as AnthropicCompactionBlockParam;\n const block: AnthropicCompactionBlockParam = {\n type: 'compaction' as const,\n content: compactionPart.content,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n return block;\n } else if (\n textTypes.some((t) => t === contentPart.type) &&\n 'text' in contentPart\n ) {\n // Assuming contentPart is of type MessageContentText here\n return {\n type: 'text' as const, // Explicitly setting the type as \"text\"\n text: contentPart.text,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n ...('citations' in contentPart && contentPart.citations != null\n ? { citations: contentPart.citations }\n : {}),\n };\n } else if (toolTypes.some((t) => t === contentPart.type)) {\n const contentPartCopy = { ...contentPart };\n if ('index' in contentPartCopy) {\n // Anthropic does not support passing the index field here, so we remove it.\n delete contentPartCopy.index;\n }\n\n if (contentPartCopy.type === 'input_json_delta') {\n // Orphaned partial tool-input delta with no id of its own. The assembled\n // input is restored on the tool_use block from `message.tool_calls`; drop it.\n return null;\n }\n\n if (\n contentPartCopy.type === 'tool_use' &&\n 'id' in contentPartCopy &&\n typeof contentPartCopy.id === 'string' &&\n contentPartCopy.id.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX)\n ) {\n contentPartCopy.type = 'server_tool_use';\n }\n\n // Core's streaming aggregation can leave the inline tool_use input empty\n // (the assembled arguments live in `message.tool_calls` or, for persisted\n // messages, in sibling input_json_delta blocks). Restore it when missing.\n if (\n contentPartCopy.type === 'tool_use' &&\n typeof contentPartCopy.id === 'string' &&\n (contentPartCopy.input === '' || contentPartCopy.input == null)\n ) {\n const matchingToolCall = isAIMessage(message)\n ? message.tool_calls?.find(\n (toolCall) => toolCall.id === contentPartCopy.id\n )\n : undefined;\n if (matchingToolCall) {\n contentPartCopy.input = matchingToolCall.args;\n } else {\n const blockIndex = (contentPart as Record<string, unknown>).index;\n const merged = contentParts\n .filter((part) => {\n const p = part as Record<string, unknown>;\n return (\n p.type === 'input_json_delta' &&\n p.index === blockIndex &&\n typeof p.input === 'string'\n );\n })\n .reduce(\n (acc, part) => acc + (part as Record<string, unknown>).input,\n ''\n );\n if (merged !== '') {\n contentPartCopy.input = merged;\n }\n }\n }\n\n if ('input' in contentPartCopy) {\n // Anthropic tool use inputs should be valid objects, when applicable.\n if (typeof contentPartCopy.input === 'string') {\n try {\n contentPartCopy.input = JSON.parse(contentPartCopy.input);\n } catch {\n contentPartCopy.input = {};\n }\n }\n }\n\n /**\n * For multi-turn conversations with citations, we must preserve ALL blocks\n * including server_tool_use, web_search_tool_result, and web_search_result.\n * Citations reference search results by index, so filtering changes indices and breaks references.\n *\n * The ToolNode already handles skipping server tool invocations via the srvtoolu_ prefix check.\n */\n\n // TODO: Fix when SDK types are fixed\n return {\n ...contentPartCopy,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n } else if (\n 'functionCall' in contentPart &&\n contentPart.functionCall != null &&\n typeof contentPart.functionCall === 'object' &&\n isAIMessage(message)\n ) {\n const functionCallPart = contentPart as GoogleFunctionCallBlock;\n const correspondingToolCall = message.tool_calls?.find(\n (toolCall) => toolCall.name === functionCallPart.functionCall.name\n );\n if (!correspondingToolCall) {\n throw new Error(\n `Could not find tool call for function call ${functionCallPart.functionCall.name}`\n );\n }\n // Google GenAI models include a `functionCall` object inside content. We should ignore it as Anthropic will not support it.\n return {\n id: correspondingToolCall.id,\n type: 'tool_use',\n name: correspondingToolCall.name,\n input: functionCallPart.functionCall.args,\n };\n } else if (\n isAIMessage(message) &&\n foreignReasoningTypes.some((t) => t === contentPart.type)\n ) {\n // Foreign reasoning on an ASSISTANT turn (Bedrock `reasoning_content`,\n // Google `reasoning`, LibreChat `think`) carries provider-specific\n // signatures Anthropic cannot validate; drop it so a cross-provider\n // handoff doesn't crash. The same types on a user/tool turn are real\n // input and fall through to the throw below rather than being silently\n // dropped — as does any other unknown block (user media, Google\n // code-execution), which must be surfaced, not discarded.\n return null;\n } else {\n console.error(\n 'Unsupported content part:',\n JSON.stringify(contentPart, null, 2)\n );\n throw new Error('Unsupported message content format');\n }\n });\n const filteredContentBlocks = contentBlocks.filter(\n (block) =>\n block !== null &&\n !(\n block.type === 'text' &&\n 'text' in block &&\n typeof block.text === 'string' &&\n block.text.trim() === ''\n )\n );\n return filteredContentBlocks.length > 0\n ? filteredContentBlocks\n : [{ type: 'text' as const, text: ANTHROPIC_EMPTY_TEXT_PLACEHOLDER }];\n }\n}\n\n/**\n * Formats messages as a prompt for the model.\n * Used in LangSmith, export is important here.\n * @param messages The base messages to format as a prompt.\n * @returns The formatted prompt.\n */\nexport function _convertMessagesToAnthropicPayload(\n messages: BaseMessage[]\n): AnthropicMessageCreateParams {\n const mergedMessages = _ensureMessageContents(messages);\n let system;\n if (mergedMessages.length > 0 && mergedMessages[0]._getType() === 'system') {\n system = messages[0].content;\n }\n const conversationMessages =\n system !== undefined ? mergedMessages.slice(1) : mergedMessages;\n const formattedMessages = conversationMessages.map((message) => {\n let role;\n if (message._getType() === 'human') {\n role = 'user' as const;\n } else if (message._getType() === 'ai') {\n role = 'assistant' as const;\n } else if (message._getType() === 'tool') {\n role = 'user' as const;\n } else if (message._getType() === 'system') {\n throw new Error(\n 'System messages are only permitted as the first passed message.'\n );\n } else {\n throw new Error(`Message type \"${message._getType()}\" is not supported.`);\n }\n const isAI = isAIMessage(message);\n const toolCalls = isAI ? (message.tool_calls ?? []) : [];\n if (isAI && toolCalls.length > 0) {\n if (typeof message.content === 'string') {\n const clientToolCalls = toolCalls.filter(\n (tc) =>\n !(\n tc.id?.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) ?? false\n )\n );\n if (message.content === '') {\n return {\n role,\n content:\n clientToolCalls.length > 0\n ? clientToolCalls.map(_convertLangChainToolCallToAnthropic)\n : [\n {\n type: 'text' as const,\n text: ANTHROPIC_EMPTY_TEXT_PLACEHOLDER,\n },\n ],\n };\n } else {\n return {\n role,\n content: [\n { type: 'text' as const, text: message.content },\n ...clientToolCalls.map(_convertLangChainToolCallToAnthropic),\n ],\n };\n }\n } else {\n const formattedContent = _formatContent(message);\n const formattedBlocks = Array.isArray(formattedContent)\n ? formattedContent\n : [];\n // Tool calls already materialized as content blocks by `_formatContent`.\n // Derived from the FORMATTED output (not the raw content by type) so\n // that Google `functionCall` parts — which `_formatContent` converts\n // into `tool_use` — count as represented and are not appended twice.\n const representedToolIds = new Set(\n formattedBlocks\n .filter(\n (block) =>\n block != null &&\n (block.type === 'tool_use' || block.type === 'server_tool_use')\n )\n .map((block) => (block as { id?: string }).id)\n );\n // Client tool calls present in `tool_calls` but absent from the\n // formatted content — e.g. a Bedrock extended-thinking turn records the\n // tool only on `tool_calls` and leaves `content` as just the reasoning\n // block. Without materializing them, dropping that reasoning block\n // silently loses the (handoff) tool call instead of forwarding it.\n const unrepresentedToolCalls = toolCalls.filter(\n (toolCall) =>\n !(\n toolCall.id?.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) ??\n false\n ) && !representedToolIds.has(toolCall.id)\n );\n if (unrepresentedToolCalls.length === 0) {\n return { role, content: formattedContent };\n }\n const existingBlocks = formattedBlocks.filter(\n (block) =>\n !(\n block != null &&\n block.type === 'text' &&\n 'text' in block &&\n block.text === ANTHROPIC_EMPTY_TEXT_PLACEHOLDER\n )\n );\n return {\n role,\n content: [\n ...existingBlocks,\n ...unrepresentedToolCalls.map(_convertLangChainToolCallToAnthropic),\n ],\n };\n }\n } else {\n return {\n role,\n content: _formatContent(message),\n };\n }\n });\n return {\n messages: mergeMessages(formattedMessages),\n system,\n } as AnthropicMessageCreateParams;\n}\n\nexport function modelDisallowsAssistantPrefill(model?: string): boolean {\n const modelId = model ?? '';\n if (CLAUDE_4_RELEASE_DATE_MODEL_PATTERN.test(modelId)) {\n return false;\n }\n\n const match = CLAUDE_4_MINOR_MODEL_PATTERN.exec(modelId);\n if (!match) {\n return false;\n }\n return Number(match[1]) >= 6;\n}\n\nfunction messagesHaveCacheControl(\n messages: AnthropicMessageCreateParams['messages']\n): boolean {\n return messages.some(\n (message) =>\n Array.isArray(message.content) &&\n message.content.some((block) => 'cache_control' in block)\n );\n}\n\n/** Anthropic rejects cache_control on these reasoning blocks. */\nconst NON_CACHEABLE_PAYLOAD_BLOCK_TYPES = new Set([\n 'thinking',\n 'redacted_thinking',\n]);\n\n/**\n * Place one ephemeral `cache_control` on the last cacheable block of the final\n * message of an already-converted Anthropic payload. Used to re-anchor the tail\n * breakpoint after a trailing assistant prefill is stripped. Operates on the\n * post-conversion payload, where blocks the converter drops (foreign reasoning,\n * input_json_delta) are already gone — only native thinking blocks must be\n * skipped. Returns a new array only when it actually places a marker.\n */\nfunction reanchorTailCacheControl(\n messages: AnthropicMessageCreateParams['messages'],\n ttl?: '1h'\n): AnthropicMessageCreateParams['messages'] {\n if (messages.length === 0) {\n return messages;\n }\n const cacheControl =\n ttl === '1h'\n ? ({ type: 'ephemeral', ttl: '1h' } as const)\n : ({ type: 'ephemeral' } as const);\n const lastIndex = messages.length - 1;\n const tail = messages[lastIndex];\n const content = tail.content;\n\n if (typeof content === 'string') {\n if (content.trim() === '') {\n return messages;\n }\n const next = [...messages];\n next[lastIndex] = {\n ...tail,\n content: [{ type: 'text', text: content, cache_control: cacheControl }],\n } as (typeof messages)[number];\n return next;\n }\n\n if (!Array.isArray(content)) {\n return messages;\n }\n\n let anchor = -1;\n for (let i = 0; i < content.length; i++) {\n const type = (content[i] as { type?: string }).type;\n if (type == null || NON_CACHEABLE_PAYLOAD_BLOCK_TYPES.has(type)) {\n continue;\n }\n if (\n type === 'text' &&\n ((content[i] as { text?: string }).text ?? '').trim() === ''\n ) {\n continue;\n }\n anchor = i;\n }\n if (anchor < 0) {\n return messages;\n }\n\n const next = [...messages];\n next[lastIndex] = {\n ...tail,\n content: content.map((block, i) =>\n i === anchor ? { ...block, cache_control: cacheControl } : block\n ),\n } as (typeof messages)[number];\n return next;\n}\n\n/**\n * Find the extended-cache TTL (`'1h'`) carried by an existing `cache_control`\n * breakpoint, so {@link reanchorTailCacheControl} can re-apply the same TTL the\n * stripped prefill had. Returns `undefined` for the legacy 5-minute default\n * (no `ttl`), keeping that path byte-identical to before.\n */\nfunction findCacheControlTtl(\n messages: AnthropicMessageCreateParams['messages']\n): '1h' | undefined {\n for (const message of messages) {\n if (!Array.isArray(message.content)) {\n continue;\n }\n for (const block of message.content) {\n const cacheControl = (block as { cache_control?: { ttl?: unknown } })\n .cache_control;\n if (cacheControl?.ttl === '1h') {\n return '1h';\n }\n }\n }\n return undefined;\n}\n\nexport function stripUnsupportedAssistantPrefill<\n T extends Pick<AnthropicMessageCreateParams, 'messages'> & { model?: string },\n>(request: T): T {\n if (!modelDisallowsAssistantPrefill(request.model)) {\n return request;\n }\n\n const messages = request.messages;\n if (\n messages.length <= 1 ||\n messages[messages.length - 1]?.role !== 'assistant'\n ) {\n return request;\n }\n\n const nextMessages = [...messages];\n while (\n nextMessages.length > 1 &&\n nextMessages[nextMessages.length - 1]?.role === 'assistant'\n ) {\n nextMessages.pop();\n }\n\n /**\n * If a single tail prompt-cache breakpoint rode the stripped assistant\n * prefill, the survivors may now carry no `cache_control` at all, dropping\n * message caching for this request. Re-anchor the breakpoint on the new tail\n * (only when one was actually lost, so caching-off requests stay untouched).\n */\n const reanchored =\n messagesHaveCacheControl(messages) &&\n !messagesHaveCacheControl(nextMessages)\n ? reanchorTailCacheControl(nextMessages, findCacheControlTtl(messages))\n : nextMessages;\n\n return {\n ...request,\n messages: reanchored,\n };\n}\n\nfunction mergeMessages(messages: AnthropicMessageCreateParams['messages']) {\n if (messages.length <= 1) {\n return messages;\n }\n\n const result: AnthropicMessageCreateParams['messages'] = [];\n let currentMessage = messages[0];\n\n type ContentBlocks = Exclude<\n AnthropicMessageCreateParams['messages'][number]['content'],\n string\n >;\n const normalizeContent = (\n content: AnthropicMessageCreateParams['messages'][number]['content']\n ): ContentBlocks => {\n if (typeof content === 'string') {\n return [{ type: 'text', text: content }];\n }\n return content;\n };\n\n const isToolResultMessage = (msg: (typeof messages)[0]) => {\n if (msg.role !== 'user') return false;\n\n if (typeof msg.content === 'string') {\n return false;\n }\n\n return (\n Array.isArray(msg.content) &&\n msg.content.every((item) => item.type === 'tool_result')\n );\n };\n\n for (let i = 1; i < messages.length; i += 1) {\n const nextMessage = messages[i];\n\n if (\n isToolResultMessage(currentMessage) &&\n isToolResultMessage(nextMessage)\n ) {\n // Merge the messages by combining their content arrays\n currentMessage = {\n ...currentMessage,\n content: [\n ...normalizeContent(currentMessage.content),\n ...normalizeContent(nextMessage.content),\n ],\n };\n } else {\n result.push(currentMessage);\n currentMessage = nextMessage;\n }\n }\n\n result.push(currentMessage);\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"message_inputs.cjs","names":["HumanMessage","isAnthropicImageBlockParam","redactedPart","compactionPart"],"sources":["../../../../../src/llm/anthropic/utils/message_inputs.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/explicit-function-return-type */\n/* eslint-disable no-console */\n/**\n * This util file contains functions for converting LangChain messages to Anthropic messages.\n */\nimport { createHash } from 'node:crypto';\nimport { ToolCall } from '@langchain/core/messages/tool';\nimport {\n type BaseMessage,\n type SystemMessage,\n HumanMessage,\n type AIMessage,\n type ToolMessage,\n isAIMessage,\n type Data,\n type StandardContentBlockConverter,\n MessageContentComplex,\n isDataContentBlock,\n convertToProviderContentBlock,\n parseBase64DataUrl,\n} from '@langchain/core/messages';\nimport {\n AnthropicImageBlockParam,\n AnthropicMessageCreateParams,\n AnthropicTextBlockParam,\n AnthropicDocumentBlockParam,\n AnthropicThinkingBlockParam,\n AnthropicRedactedThinkingBlockParam,\n AnthropicServerToolUseBlockParam,\n AnthropicWebSearchToolResultBlockParam,\n isAnthropicImageBlockParam,\n AnthropicSearchResultBlockParam,\n AnthropicCompactionBlockParam,\n AnthropicToolResponse,\n} from '../types';\nimport { Constants } from '@/common';\n\ntype StandardTextBlock = Data.StandardTextBlock;\ntype StandardImageBlock = Data.StandardImageBlock;\ntype StandardFileBlock = Data.StandardFileBlock;\ntype ImageUrlContentBlock = MessageContentComplex & {\n image_url: string | { url: string };\n};\ntype GoogleFunctionCallBlock = MessageContentComplex & {\n functionCall: {\n name: string;\n args: Record<string, unknown>;\n };\n};\n\nconst ANTHROPIC_EMPTY_TEXT_PLACEHOLDER = '_';\nconst CLAUDE_4_RELEASE_DATE_MODEL_PATTERN =\n /claude-(?:opus|sonnet|haiku)-4-\\d{8}(?:[-.@]|$)/i;\nconst CLAUDE_4_MINOR_MODEL_PATTERN =\n /claude-(?:opus|sonnet|haiku)-4[-.](\\d+)(?:[-.@]|$)/i;\n\nfunction _formatImage(imageUrl: string) {\n const parsed = parseBase64DataUrl({ dataUrl: imageUrl });\n if (parsed) {\n return {\n type: 'base64',\n media_type: parsed.mime_type,\n data: parsed.data,\n };\n }\n let parsedUrl: URL;\n\n try {\n parsedUrl = new URL(imageUrl);\n } catch {\n throw new Error(\n [\n `Malformed image URL: ${JSON.stringify(\n imageUrl\n )}. Content blocks of type 'image_url' must be a valid http, https, or base64-encoded data URL.`,\n 'Example: data:image/png;base64,/9j/4AAQSk...',\n 'Example: https://example.com/image.jpg',\n ].join('\\n\\n')\n );\n }\n\n if (parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') {\n return {\n type: 'url',\n url: imageUrl,\n };\n }\n\n throw new Error(\n [\n `Invalid image URL protocol: ${JSON.stringify(\n parsedUrl.protocol\n )}. Anthropic only supports images as http, https, or base64-encoded data URLs on 'image_url' content blocks.`,\n 'Example: data:image/png;base64,/9j/4AAQSk...',\n 'Example: https://example.com/image.jpg',\n ].join('\\n\\n')\n );\n}\n\nconst ANTHROPIC_TOOL_USE_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;\nconst ANTHROPIC_TOOL_USE_ID_MAX_LENGTH = 64;\nconst ANTHROPIC_TOOL_USE_ID_HASH_LENGTH = 10;\n\n/**\n * Normalize a tool-call ID to satisfy Anthropic's `^[a-zA-Z0-9_-]+$` and 64-char\n * constraints. Pure and deterministic — same input always yields the same output,\n * so paired `tool_use.id` and `tool_result.tool_use_id` stay matched without\n * needing a session map. IDs that already comply pass through unchanged.\n *\n * For non-compliant inputs we sanitize then append a short SHA-256 prefix of\n * the original ID to preserve uniqueness when truncation would otherwise\n * collapse distinct IDs to the same value (e.g. two long Responses-style IDs\n * sharing a 64-char prefix). The hash is computed against the raw input so\n * inputs that differ only after the truncation cutoff still produce distinct\n * outputs.\n */\nexport function normalizeAnthropicToolCallId(id: string): string;\nexport function normalizeAnthropicToolCallId(\n id: string | undefined\n): string | undefined;\nexport function normalizeAnthropicToolCallId(\n id: string | undefined\n): string | undefined {\n if (id == null) {\n return id;\n }\n if (\n id.length <= ANTHROPIC_TOOL_USE_ID_MAX_LENGTH &&\n ANTHROPIC_TOOL_USE_ID_PATTERN.test(id)\n ) {\n return id;\n }\n const sanitized = id.replace(/[^a-zA-Z0-9_-]/g, '_');\n const hash = createHash('sha256')\n .update(id)\n .digest('hex')\n .slice(0, ANTHROPIC_TOOL_USE_ID_HASH_LENGTH);\n const prefixMaxLength =\n ANTHROPIC_TOOL_USE_ID_MAX_LENGTH - ANTHROPIC_TOOL_USE_ID_HASH_LENGTH - 1;\n return `${sanitized.slice(0, prefixMaxLength)}_${hash}`;\n}\n\n/**\n * Lift any `cache_control` off the inner blocks of a tool result onto the\n * `tool_result` block itself. Anthropic documents the top-level\n * `messages.content` block as the cacheable position and does not document\n * caching of sub-content blocks; the API currently honors a nested marker, but\n * anchoring on the documented position keeps the single tail breakpoint robust\n * (and mirrors the Bedrock cachePoint hoist). The first marker found wins; it is\n * stripped from every inner block so exactly one survives, on the outer block.\n */\nfunction hoistToolResultCacheControl(\n content: string | MessageContentComplex[]\n): { content: string | MessageContentComplex[]; cacheControl: unknown } {\n if (!Array.isArray(content)) {\n return { content, cacheControl: undefined };\n }\n let cacheControl: unknown;\n const stripped = content.map((block) => {\n if ('cache_control' in block) {\n cacheControl ??= (block as Record<string, unknown>).cache_control;\n const clone = { ...(block as Record<string, unknown>) };\n delete clone.cache_control;\n return clone as MessageContentComplex;\n }\n return block;\n });\n // `stripped` is element-equal to `content` when no marker was present.\n return { content: stripped, cacheControl };\n}\n\nfunction _ensureMessageContents(\n messages: BaseMessage[]\n): (SystemMessage | HumanMessage | AIMessage)[] {\n // Merge runs of human/tool messages into single human messages with content blocks.\n const updatedMsgs: BaseMessage[] = [];\n for (const message of messages) {\n if (message._getType() === 'tool') {\n if (typeof message.content === 'string') {\n const previousMessage = updatedMsgs[updatedMsgs.length - 1];\n if (\n previousMessage._getType() === 'human' &&\n Array.isArray(previousMessage.content) &&\n 'type' in previousMessage.content[0] &&\n previousMessage.content[0].type === 'tool_result'\n ) {\n // If the previous message was a tool result, we merge this tool message into it.\n (previousMessage.content as MessageContentComplex[]).push({\n type: 'tool_result',\n content: message.content,\n tool_use_id: normalizeAnthropicToolCallId(\n (message as ToolMessage).tool_call_id\n ),\n });\n } else {\n // If not, we create a new human message with the tool result.\n updatedMsgs.push(\n new HumanMessage({\n content: [\n {\n type: 'tool_result',\n content: message.content,\n tool_use_id: normalizeAnthropicToolCallId(\n (message as ToolMessage).tool_call_id\n ),\n },\n ],\n })\n );\n }\n } else {\n const toolMessageContent = (\n message as { content?: BaseMessage['content'] | null }\n ).content;\n // Hoist a tail cache_control off the inner content onto the\n // tool_result block itself (the documented cacheable position).\n const { content: hoistedContent, cacheControl } =\n toolMessageContent != null\n ? hoistToolResultCacheControl(_formatContent(message))\n : { content: undefined, cacheControl: undefined };\n updatedMsgs.push(\n new HumanMessage({\n content: [\n {\n type: 'tool_result',\n ...(hoistedContent != null ? { content: hoistedContent } : {}),\n ...(cacheControl != null\n ? { cache_control: cacheControl as { type: 'ephemeral' } }\n : {}),\n tool_use_id: normalizeAnthropicToolCallId(\n (message as ToolMessage).tool_call_id\n ),\n },\n ],\n })\n );\n }\n } else {\n updatedMsgs.push(message);\n }\n }\n return updatedMsgs as (SystemMessage | HumanMessage | AIMessage)[];\n}\n\nexport function _convertLangChainToolCallToAnthropic(\n toolCall: ToolCall\n): AnthropicToolResponse {\n if (toolCall.id === undefined) {\n throw new Error('Anthropic requires all tool calls to have an \"id\".');\n }\n const isServerTool = toolCall.id.startsWith(\n Constants.ANTHROPIC_SERVER_TOOL_PREFIX\n );\n return {\n type: isServerTool ? 'server_tool_use' : 'tool_use',\n id: isServerTool ? toolCall.id : normalizeAnthropicToolCallId(toolCall.id),\n name: toolCall.name,\n input: toolCall.args,\n };\n}\n\nconst standardContentBlockConverter: StandardContentBlockConverter<{\n text: AnthropicTextBlockParam;\n image: AnthropicImageBlockParam;\n file: AnthropicDocumentBlockParam;\n}> = {\n providerName: 'anthropic',\n\n fromStandardTextBlock(block: StandardTextBlock): AnthropicTextBlockParam {\n return {\n type: 'text',\n text: block.text,\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicTextBlockParam;\n },\n\n fromStandardImageBlock(block: StandardImageBlock): AnthropicImageBlockParam {\n if (block.source_type === 'url') {\n const data = parseBase64DataUrl({\n dataUrl: block.url,\n asTypedArray: false,\n });\n if (data) {\n return {\n type: 'image',\n source: {\n type: 'base64',\n data: data.data,\n media_type: data.mime_type,\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicImageBlockParam;\n } else {\n return {\n type: 'image',\n source: {\n type: 'url',\n url: block.url,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicImageBlockParam;\n }\n } else {\n if (block.source_type === 'base64') {\n return {\n type: 'image',\n source: {\n type: 'base64',\n data: block.data,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n } as AnthropicImageBlockParam;\n } else {\n throw new Error(`Unsupported image source type: ${block.source_type}`);\n }\n }\n },\n\n fromStandardFileBlock(block: StandardFileBlock): AnthropicDocumentBlockParam {\n const mime_type = (block.mime_type ?? '').split(';')[0];\n\n if (block.source_type === 'url') {\n if (mime_type === 'application/pdf' || mime_type === '') {\n return {\n type: 'document',\n source: {\n type: 'url',\n url: block.url,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n }\n throw new Error(\n `Unsupported file mime type for file url source: ${block.mime_type}`\n );\n } else if (block.source_type === 'text') {\n if (mime_type === 'text/plain' || mime_type === '') {\n return {\n type: 'document',\n source: {\n type: 'text',\n data: block.text,\n media_type: block.mime_type ?? '',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n } else {\n throw new Error(\n `Unsupported file mime type for file text source: ${block.mime_type}`\n );\n }\n } else if (block.source_type === 'base64') {\n if (mime_type === 'application/pdf' || mime_type === '') {\n return {\n type: 'document',\n source: {\n type: 'base64',\n data: block.data,\n media_type: 'application/pdf',\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n } else if (\n ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].includes(\n mime_type\n )\n ) {\n return {\n type: 'document',\n source: {\n type: 'content',\n content: [\n {\n type: 'image',\n source: {\n type: 'base64',\n data: block.data,\n media_type: mime_type as\n | 'image/jpeg'\n | 'image/png'\n | 'image/gif'\n | 'image/webp',\n },\n },\n ],\n },\n ...('cache_control' in (block.metadata ?? {})\n ? { cache_control: block.metadata!.cache_control }\n : {}),\n ...('citations' in (block.metadata ?? {})\n ? { citations: block.metadata!.citations }\n : {}),\n ...('context' in (block.metadata ?? {})\n ? { context: block.metadata!.context }\n : {}),\n ...('title' in (block.metadata ?? {})\n ? { title: block.metadata!.title }\n : {}),\n } as AnthropicDocumentBlockParam;\n } else {\n throw new Error(\n `Unsupported file mime type for file base64 source: ${block.mime_type}`\n );\n }\n } else {\n throw new Error(`Unsupported file source type: ${block.source_type}`);\n }\n },\n};\n\nfunction _formatContent(message: BaseMessage) {\n const toolTypes = [\n 'tool_use',\n 'tool_result',\n 'input_json_delta',\n 'server_tool_use',\n 'web_search_tool_result',\n 'web_search_result',\n ];\n const textTypes = ['text', 'text_delta'];\n /**\n * Reasoning blocks emitted by other providers — Bedrock's `reasoning_content`,\n * Google's `reasoning`, and LibreChat's `think`. Their signatures are\n * provider-specific and cannot be validated by Anthropic, so on a\n * cross-provider handoff (e.g. Bedrock → Anthropic) we drop them rather than\n * forwarding an unusable block. The receiving model produces its own thinking.\n */\n const foreignReasoningTypes = ['reasoning_content', 'reasoning', 'think'];\n const { content } = message;\n\n if (typeof content === 'string') {\n return content;\n } else {\n const contentParts = content as MessageContentComplex[];\n const contentBlocks = contentParts.map((contentPart) => {\n /**\n * Normalize server_tool_use blocks into a clean shape the API accepts.\n * These blocks may arrive with the correct type (server_tool_use) or mislabeled\n * as text/tool_use after chunk concatenation or state serialization.\n * Regardless of current type, if the id starts with 'srvtoolu_' we rebuild\n * a clean block with only the properties the API expects.\n */\n if (\n 'id' in contentPart &&\n typeof (contentPart as Record<string, unknown>).id === 'string' &&\n ((contentPart as Record<string, unknown>).id as string).startsWith(\n Constants.ANTHROPIC_SERVER_TOOL_PREFIX\n ) &&\n 'name' in contentPart\n ) {\n const rawPart = contentPart as Record<string, unknown>;\n let input = rawPart.input;\n if (typeof input === 'string') {\n try {\n input = JSON.parse(input);\n } catch {\n input = {};\n }\n }\n const corrected: AnthropicServerToolUseBlockParam = {\n type: 'server_tool_use',\n id: rawPart.id as string,\n name: (rawPart.name ?? 'web_search') as 'web_search',\n input: (input ?? {}) as Record<string, unknown>,\n };\n return corrected;\n }\n\n /**\n * Normalize web_search_tool_result blocks into a clean shape.\n * Same rationale as above — the block may carry extra properties from\n * streaming (input, index, etc.) that the API rejects. Rebuild cleanly.\n */\n if (\n 'tool_use_id' in contentPart &&\n typeof (contentPart as Record<string, unknown>).tool_use_id ===\n 'string' &&\n (\n (contentPart as Record<string, unknown>).tool_use_id as string\n ).startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) &&\n 'content' in contentPart\n ) {\n const rawPart = contentPart as Record<string, unknown>;\n const content = rawPart.content;\n const isValidContent =\n Array.isArray(content) ||\n (content != null &&\n typeof content === 'object' &&\n 'type' in content &&\n (content as Record<string, unknown>).type ===\n 'web_search_tool_result_error');\n\n if (isValidContent) {\n const corrected: AnthropicWebSearchToolResultBlockParam = {\n type: 'web_search_tool_result',\n tool_use_id: rawPart.tool_use_id as string,\n content:\n content as AnthropicWebSearchToolResultBlockParam['content'],\n };\n return corrected;\n }\n return null;\n }\n\n /**\n * Skip non-server malformed blocks that have tool fields mixed with text type.\n */\n if (\n 'id' in contentPart &&\n 'name' in contentPart &&\n 'input' in contentPart &&\n contentPart.type === 'text'\n ) {\n return null;\n }\n if (\n 'tool_use_id' in contentPart &&\n 'content' in contentPart &&\n contentPart.type === 'text'\n ) {\n return null;\n }\n\n // Core's v1 streaming aggregation can leave a partial tool-input delta as a\n // standalone block typed `text` carrying `input` but no `text`. The assembled\n // input is restored on the tool_use block from `message.tool_calls`, so drop it.\n if (\n contentPart.type === 'text' &&\n 'input' in contentPart &&\n !('text' in contentPart)\n ) {\n return null;\n }\n\n if (isDataContentBlock(contentPart)) {\n return convertToProviderContentBlock(\n contentPart,\n standardContentBlockConverter\n );\n }\n\n const cacheControl =\n 'cache_control' in contentPart ? contentPart.cache_control : undefined;\n\n if (contentPart.type === 'image_url') {\n let source;\n const imageUrl = (contentPart as ImageUrlContentBlock).image_url;\n if (typeof imageUrl === 'string') {\n source = _formatImage(imageUrl);\n } else {\n source = _formatImage(imageUrl.url);\n }\n return {\n type: 'image' as const, // Explicitly setting the type as \"image\"\n source,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n } else if (isAnthropicImageBlockParam(contentPart)) {\n return contentPart;\n } else if (contentPart.type === 'document') {\n // PDF\n return {\n ...contentPart,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n } else if (contentPart.type === 'thinking') {\n const thinkingPart = contentPart as AnthropicThinkingBlockParam;\n // Google thinking-enabled output reuses `type: 'thinking'` but carries\n // no Anthropic signature. Anthropic rejects an unsigned thinking block,\n // so on an assistant turn treat it as foreign reasoning and drop it\n // rather than forward an unusable block. Signed (Anthropic-native)\n // thinking is forwarded as before.\n const signature = (thinkingPart as { signature?: string }).signature;\n if (isAIMessage(message) && (signature == null || signature === '')) {\n return null;\n }\n // Opus 4.7+ omits thinking text by default (signed but text-less\n // block), so `thinking` may be absent on a signed block even though the\n // type declares it required. Coerce to '' — JSON.stringify drops an\n // undefined value, which would send a `thinking` block missing its\n // required `thinking` field and trip a 400\n // `messages.N.content.M.thinking.thinking: Field required`.\n const thinkingText =\n (thinkingPart as { thinking?: string }).thinking ?? '';\n const block: AnthropicThinkingBlockParam = {\n type: 'thinking' as const, // Explicitly setting the type as \"thinking\"\n thinking: thinkingText,\n signature: thinkingPart.signature,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n return block;\n } else if (contentPart.type === 'redacted_thinking') {\n const redactedPart = contentPart as AnthropicRedactedThinkingBlockParam;\n const block: AnthropicRedactedThinkingBlockParam = {\n type: 'redacted_thinking' as const, // Explicitly setting the type as \"redacted_thinking\"\n data: redactedPart.data,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n return block;\n } else if (contentPart.type === 'search_result') {\n const searchResultPart = contentPart as AnthropicSearchResultBlockParam;\n const block: AnthropicSearchResultBlockParam = {\n type: 'search_result' as const,\n title: searchResultPart.title,\n source: searchResultPart.source,\n ...('cache_control' in contentPart &&\n contentPart.cache_control != null\n ? { cache_control: contentPart.cache_control }\n : {}),\n ...('citations' in contentPart && contentPart.citations != null\n ? { citations: contentPart.citations }\n : {}),\n content: searchResultPart.content,\n };\n return block;\n } else if (contentPart.type === 'compaction') {\n const compactionPart = contentPart as AnthropicCompactionBlockParam;\n const block: AnthropicCompactionBlockParam = {\n type: 'compaction' as const,\n content: compactionPart.content,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n };\n return block;\n } else if (\n textTypes.some((t) => t === contentPart.type) &&\n 'text' in contentPart\n ) {\n // Assuming contentPart is of type MessageContentText here\n return {\n type: 'text' as const, // Explicitly setting the type as \"text\"\n text: contentPart.text,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n ...('citations' in contentPart && contentPart.citations != null\n ? { citations: contentPart.citations }\n : {}),\n };\n } else if (toolTypes.some((t) => t === contentPart.type)) {\n const contentPartCopy = { ...contentPart };\n if ('index' in contentPartCopy) {\n // Anthropic does not support passing the index field here, so we remove it.\n delete contentPartCopy.index;\n }\n\n if (contentPartCopy.type === 'input_json_delta') {\n // Orphaned partial tool-input delta with no id of its own. The assembled\n // input is restored on the tool_use block from `message.tool_calls`; drop it.\n return null;\n }\n\n if (\n contentPartCopy.type === 'tool_use' &&\n 'id' in contentPartCopy &&\n typeof contentPartCopy.id === 'string' &&\n contentPartCopy.id.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX)\n ) {\n contentPartCopy.type = 'server_tool_use';\n }\n\n // Core's streaming aggregation can leave the inline tool_use input empty\n // (the assembled arguments live in `message.tool_calls` or, for persisted\n // messages, in sibling input_json_delta blocks). Restore it when missing.\n if (\n contentPartCopy.type === 'tool_use' &&\n typeof contentPartCopy.id === 'string' &&\n (contentPartCopy.input === '' || contentPartCopy.input == null)\n ) {\n const matchingToolCall = isAIMessage(message)\n ? message.tool_calls?.find(\n (toolCall) => toolCall.id === contentPartCopy.id\n )\n : undefined;\n if (matchingToolCall) {\n contentPartCopy.input = matchingToolCall.args;\n } else {\n const blockIndex = (contentPart as Record<string, unknown>).index;\n const merged = contentParts\n .filter((part) => {\n const p = part as Record<string, unknown>;\n return (\n p.type === 'input_json_delta' &&\n p.index === blockIndex &&\n typeof p.input === 'string'\n );\n })\n .reduce(\n (acc, part) => acc + (part as Record<string, unknown>).input,\n ''\n );\n if (merged !== '') {\n contentPartCopy.input = merged;\n }\n }\n }\n\n if ('input' in contentPartCopy) {\n // Anthropic tool use inputs should be valid objects, when applicable.\n if (typeof contentPartCopy.input === 'string') {\n try {\n contentPartCopy.input = JSON.parse(contentPartCopy.input);\n } catch {\n contentPartCopy.input = {};\n }\n }\n }\n\n /**\n * For multi-turn conversations with citations, we must preserve ALL blocks\n * including server_tool_use, web_search_tool_result, and web_search_result.\n * Citations reference search results by index, so filtering changes indices and breaks references.\n *\n * The ToolNode already handles skipping server tool invocations via the srvtoolu_ prefix check.\n */\n\n // TODO: Fix when SDK types are fixed\n return {\n ...contentPartCopy,\n ...(cacheControl != null ? { cache_control: cacheControl } : {}),\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any;\n } else if (\n 'functionCall' in contentPart &&\n contentPart.functionCall != null &&\n typeof contentPart.functionCall === 'object' &&\n isAIMessage(message)\n ) {\n const functionCallPart = contentPart as GoogleFunctionCallBlock;\n const correspondingToolCall = message.tool_calls?.find(\n (toolCall) => toolCall.name === functionCallPart.functionCall.name\n );\n if (!correspondingToolCall) {\n throw new Error(\n `Could not find tool call for function call ${functionCallPart.functionCall.name}`\n );\n }\n // Google GenAI models include a `functionCall` object inside content. We should ignore it as Anthropic will not support it.\n return {\n id: correspondingToolCall.id,\n type: 'tool_use',\n name: correspondingToolCall.name,\n input: functionCallPart.functionCall.args,\n };\n } else if (\n isAIMessage(message) &&\n foreignReasoningTypes.some((t) => t === contentPart.type)\n ) {\n // Foreign reasoning on an ASSISTANT turn (Bedrock `reasoning_content`,\n // Google `reasoning`, LibreChat `think`) carries provider-specific\n // signatures Anthropic cannot validate; drop it so a cross-provider\n // handoff doesn't crash. The same types on a user/tool turn are real\n // input and fall through to the throw below rather than being silently\n // dropped — as does any other unknown block (user media, Google\n // code-execution), which must be surfaced, not discarded.\n return null;\n } else {\n console.error(\n 'Unsupported content part:',\n JSON.stringify(contentPart, null, 2)\n );\n throw new Error('Unsupported message content format');\n }\n });\n const filteredContentBlocks = contentBlocks.filter(\n (block) =>\n block !== null &&\n !(\n block.type === 'text' &&\n 'text' in block &&\n typeof block.text === 'string' &&\n block.text.trim() === ''\n )\n );\n return filteredContentBlocks.length > 0\n ? filteredContentBlocks\n : [{ type: 'text' as const, text: ANTHROPIC_EMPTY_TEXT_PLACEHOLDER }];\n }\n}\n\n/**\n * Formats messages as a prompt for the model.\n * Used in LangSmith, export is important here.\n * @param messages The base messages to format as a prompt.\n * @returns The formatted prompt.\n */\nexport function _convertMessagesToAnthropicPayload(\n messages: BaseMessage[]\n): AnthropicMessageCreateParams {\n const mergedMessages = _ensureMessageContents(messages);\n let system;\n if (mergedMessages.length > 0 && mergedMessages[0]._getType() === 'system') {\n system = messages[0].content;\n }\n const conversationMessages =\n system !== undefined ? mergedMessages.slice(1) : mergedMessages;\n const formattedMessages = conversationMessages.map((message) => {\n let role;\n if (message._getType() === 'human') {\n role = 'user' as const;\n } else if (message._getType() === 'ai') {\n role = 'assistant' as const;\n } else if (message._getType() === 'tool') {\n role = 'user' as const;\n } else if (message._getType() === 'system') {\n throw new Error(\n 'System messages are only permitted as the first passed message.'\n );\n } else {\n throw new Error(`Message type \"${message._getType()}\" is not supported.`);\n }\n const isAI = isAIMessage(message);\n const toolCalls = isAI ? (message.tool_calls ?? []) : [];\n if (isAI && toolCalls.length > 0) {\n if (typeof message.content === 'string') {\n const clientToolCalls = toolCalls.filter(\n (tc) =>\n !(\n tc.id?.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) ?? false\n )\n );\n if (message.content === '') {\n return {\n role,\n content:\n clientToolCalls.length > 0\n ? clientToolCalls.map(_convertLangChainToolCallToAnthropic)\n : [\n {\n type: 'text' as const,\n text: ANTHROPIC_EMPTY_TEXT_PLACEHOLDER,\n },\n ],\n };\n } else {\n return {\n role,\n content: [\n { type: 'text' as const, text: message.content },\n ...clientToolCalls.map(_convertLangChainToolCallToAnthropic),\n ],\n };\n }\n } else {\n const formattedContent = _formatContent(message);\n const formattedBlocks = Array.isArray(formattedContent)\n ? formattedContent\n : [];\n // Tool calls already materialized as content blocks by `_formatContent`.\n // Derived from the FORMATTED output (not the raw content by type) so\n // that Google `functionCall` parts — which `_formatContent` converts\n // into `tool_use` — count as represented and are not appended twice.\n const representedToolIds = new Set(\n formattedBlocks\n .filter(\n (block) =>\n block != null &&\n (block.type === 'tool_use' || block.type === 'server_tool_use')\n )\n .map((block) => (block as { id?: string }).id)\n );\n // Client tool calls present in `tool_calls` but absent from the\n // formatted content — e.g. a Bedrock extended-thinking turn records the\n // tool only on `tool_calls` and leaves `content` as just the reasoning\n // block. Without materializing them, dropping that reasoning block\n // silently loses the (handoff) tool call instead of forwarding it.\n const unrepresentedToolCalls = toolCalls.filter(\n (toolCall) =>\n !(\n toolCall.id?.startsWith(Constants.ANTHROPIC_SERVER_TOOL_PREFIX) ??\n false\n ) && !representedToolIds.has(toolCall.id)\n );\n if (unrepresentedToolCalls.length === 0) {\n return { role, content: formattedContent };\n }\n const existingBlocks = formattedBlocks.filter(\n (block) =>\n !(\n block != null &&\n block.type === 'text' &&\n 'text' in block &&\n block.text === ANTHROPIC_EMPTY_TEXT_PLACEHOLDER\n )\n );\n return {\n role,\n content: [\n ...existingBlocks,\n ...unrepresentedToolCalls.map(_convertLangChainToolCallToAnthropic),\n ],\n };\n }\n } else {\n return {\n role,\n content: _formatContent(message),\n };\n }\n });\n return {\n messages: mergeMessages(formattedMessages),\n system,\n } as AnthropicMessageCreateParams;\n}\n\nexport function modelDisallowsAssistantPrefill(model?: string): boolean {\n const modelId = model ?? '';\n if (CLAUDE_4_RELEASE_DATE_MODEL_PATTERN.test(modelId)) {\n return false;\n }\n\n const match = CLAUDE_4_MINOR_MODEL_PATTERN.exec(modelId);\n if (!match) {\n return false;\n }\n return Number(match[1]) >= 6;\n}\n\nfunction messagesHaveCacheControl(\n messages: AnthropicMessageCreateParams['messages']\n): boolean {\n return messages.some(\n (message) =>\n Array.isArray(message.content) &&\n message.content.some((block) => 'cache_control' in block)\n );\n}\n\n/** Anthropic rejects cache_control on these reasoning blocks. */\nconst NON_CACHEABLE_PAYLOAD_BLOCK_TYPES = new Set([\n 'thinking',\n 'redacted_thinking',\n]);\n\n/**\n * Place one ephemeral `cache_control` on the last cacheable block of the final\n * message of an already-converted Anthropic payload. Used to re-anchor the tail\n * breakpoint after a trailing assistant prefill is stripped. Operates on the\n * post-conversion payload, where blocks the converter drops (foreign reasoning,\n * input_json_delta) are already gone — only native thinking blocks must be\n * skipped. Returns a new array only when it actually places a marker.\n */\nfunction reanchorTailCacheControl(\n messages: AnthropicMessageCreateParams['messages'],\n ttl?: '1h'\n): AnthropicMessageCreateParams['messages'] {\n if (messages.length === 0) {\n return messages;\n }\n const cacheControl =\n ttl === '1h'\n ? ({ type: 'ephemeral', ttl: '1h' } as const)\n : ({ type: 'ephemeral' } as const);\n const lastIndex = messages.length - 1;\n const tail = messages[lastIndex];\n const content = tail.content;\n\n if (typeof content === 'string') {\n if (content.trim() === '') {\n return messages;\n }\n const next = [...messages];\n next[lastIndex] = {\n ...tail,\n content: [{ type: 'text', text: content, cache_control: cacheControl }],\n } as (typeof messages)[number];\n return next;\n }\n\n if (!Array.isArray(content)) {\n return messages;\n }\n\n let anchor = -1;\n for (let i = 0; i < content.length; i++) {\n const type = (content[i] as { type?: string }).type;\n if (type == null || NON_CACHEABLE_PAYLOAD_BLOCK_TYPES.has(type)) {\n continue;\n }\n if (\n type === 'text' &&\n ((content[i] as { text?: string }).text ?? '').trim() === ''\n ) {\n continue;\n }\n anchor = i;\n }\n if (anchor < 0) {\n return messages;\n }\n\n const next = [...messages];\n next[lastIndex] = {\n ...tail,\n content: content.map((block, i) =>\n i === anchor ? { ...block, cache_control: cacheControl } : block\n ),\n } as (typeof messages)[number];\n return next;\n}\n\n/**\n * Find the extended-cache TTL (`'1h'`) carried by an existing `cache_control`\n * breakpoint, so {@link reanchorTailCacheControl} can re-apply the same TTL the\n * stripped prefill had. Returns `undefined` for the legacy 5-minute default\n * (no `ttl`), keeping that path byte-identical to before.\n */\nfunction findCacheControlTtl(\n messages: AnthropicMessageCreateParams['messages']\n): '1h' | undefined {\n for (const message of messages) {\n if (!Array.isArray(message.content)) {\n continue;\n }\n for (const block of message.content) {\n const cacheControl = (block as { cache_control?: { ttl?: unknown } })\n .cache_control;\n if (cacheControl?.ttl === '1h') {\n return '1h';\n }\n }\n }\n return undefined;\n}\n\nexport function stripUnsupportedAssistantPrefill<\n T extends Pick<AnthropicMessageCreateParams, 'messages'> & { model?: string },\n>(request: T): T {\n if (!modelDisallowsAssistantPrefill(request.model)) {\n return request;\n }\n\n const messages = request.messages;\n if (\n messages.length <= 1 ||\n messages[messages.length - 1]?.role !== 'assistant'\n ) {\n return request;\n }\n\n const nextMessages = [...messages];\n while (\n nextMessages.length > 1 &&\n nextMessages[nextMessages.length - 1]?.role === 'assistant'\n ) {\n nextMessages.pop();\n }\n\n /**\n * If a single tail prompt-cache breakpoint rode the stripped assistant\n * prefill, the survivors may now carry no `cache_control` at all, dropping\n * message caching for this request. Re-anchor the breakpoint on the new tail\n * (only when one was actually lost, so caching-off requests stay untouched).\n */\n const reanchored =\n messagesHaveCacheControl(messages) &&\n !messagesHaveCacheControl(nextMessages)\n ? reanchorTailCacheControl(nextMessages, findCacheControlTtl(messages))\n : nextMessages;\n\n return {\n ...request,\n messages: reanchored,\n };\n}\n\nfunction mergeMessages(messages: AnthropicMessageCreateParams['messages']) {\n if (messages.length <= 1) {\n return messages;\n }\n\n const result: AnthropicMessageCreateParams['messages'] = [];\n let currentMessage = messages[0];\n\n type ContentBlocks = Exclude<\n AnthropicMessageCreateParams['messages'][number]['content'],\n string\n >;\n const normalizeContent = (\n content: AnthropicMessageCreateParams['messages'][number]['content']\n ): ContentBlocks => {\n if (typeof content === 'string') {\n return [{ type: 'text', text: content }];\n }\n return content;\n };\n\n const isToolResultMessage = (msg: (typeof messages)[0]) => {\n if (msg.role !== 'user') return false;\n\n if (typeof msg.content === 'string') {\n return false;\n }\n\n return (\n Array.isArray(msg.content) &&\n msg.content.every((item) => item.type === 'tool_result')\n );\n };\n\n for (let i = 1; i < messages.length; i += 1) {\n const nextMessage = messages[i];\n\n if (\n isToolResultMessage(currentMessage) &&\n isToolResultMessage(nextMessage)\n ) {\n // Merge the messages by combining their content arrays\n currentMessage = {\n ...currentMessage,\n content: [\n ...normalizeContent(currentMessage.content),\n ...normalizeContent(nextMessage.content),\n ],\n };\n } else {\n result.push(currentMessage);\n currentMessage = nextMessage;\n }\n }\n\n result.push(currentMessage);\n return result;\n}\n"],"mappings":"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"}
@@ -4,6 +4,7 @@ const require_core = require("../messages/core.cjs");
4
4
  require("../messages/index.cjs");
5
5
  const require_toolOutputReferences = require("../tools/toolOutputReferences.cjs");
6
6
  const require_stream = require("../stream.cjs");
7
+ const require_truncation = require("./truncation.cjs");
7
8
  const require_providers = require("./providers.cjs");
8
9
  const require_init = require("./init.cjs");
9
10
  let _langchain_core_messages = require("@langchain/core/messages");
@@ -137,10 +138,12 @@ async function attemptInvoke({ model, messages, provider, context, onChunk }, co
137
138
  }
138
139
  if (require_providers.manualToolStreamProviders.has(provider)) finalChunk = require_core.modifyDeltaProperties(provider, finalChunk);
139
140
  if ((finalChunk?.tool_calls?.length ?? 0) > 0) finalChunk.tool_calls = finalChunk.tool_calls?.filter((tool_call) => !!tool_call.name);
141
+ require_truncation.assertNotTruncatedToolCall(finalChunk, provider);
140
142
  return { messages: [finalChunk] };
141
143
  }
142
144
  const finalMessage = await model.invoke(messagesForProvider, config);
143
145
  if ((finalMessage.tool_calls?.length ?? 0) > 0) finalMessage.tool_calls = finalMessage.tool_calls?.filter((tool_call) => !!tool_call.name);
146
+ require_truncation.assertNotTruncatedToolCall(finalMessage, provider);
144
147
  return { messages: [finalMessage] };
145
148
  }
146
149
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"invoke.cjs","names":["ChatModelStreamHandler","AIMessageChunk","annotateMessagesForLLM","manualToolStreamProviders","modifyDeltaProperties","initializeModel"],"sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';\nimport type * as t from '@/types';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { Constants, GraphEvents, Providers } from '@/common';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { modifyDeltaProperties } from '@/messages';\nimport { ChatModelStreamHandler } from '@/stream';\nimport { initializeModel } from '@/llm/init';\n\n/**\n * Context passed to `attemptInvoke`. Matches the subset of Graph that\n * `ChatModelStreamHandler.handle` needs *plus* the explicit\n * `getOrCreateToolOutputRegistry()` accessor that `attemptInvoke`\n * itself calls to pull the run-scoped tool-output registry off the\n * graph and project each relevant ToolMessage into a transient\n * annotated copy before the provider call.\n *\n * The intersection is intentional: `Parameters<...>[3]` resolves\n * indirectly through the stream handler's signature (which returns\n * `StandardGraph` and already exposes the accessor since #117), but\n * stating it explicitly here surfaces the contract at the call site —\n * a developer reading `attemptInvoke` doesn't have to chase the\n * upstream handler's parameter list to discover that\n * `context?.getOrCreateToolOutputRegistry()` is a real thing. Single\n * optional chain only — the method itself is required on the\n * `StandardGraph` branch of the intersection, so the second `?.` is\n * unnecessary at the call site.\n *\n * `NonNullable<...>` strips `undefined` from the upstream parameter\n * type so the intersection doesn't collapse to `never` on the\n * undefined branch; callers express optionality via `context?:\n * InvokeContext` on the function signature instead.\n *\n * Callers without a registry (e.g. summarization) simply pass no\n * `context` and the transform safely no-ops.\n */\nexport type InvokeContext = NonNullable<\n Parameters<ChatModelStreamHandler['handle']>[3]\n> & {\n getOrCreateToolOutputRegistry?(): ToolOutputReferenceRegistry | undefined;\n};\n\n/**\n * Per-chunk callback for custom stream processing.\n * When provided, replaces the default `ChatModelStreamHandler`.\n */\nexport type OnChunk = (chunk: AIMessageChunk) => void | Promise<void>;\n\nfunction getRegisteredDefaultChatStreamHandler(\n context?: InvokeContext\n): ChatModelStreamHandler | undefined {\n const handler = context?.handlerRegistry?.getHandler(\n GraphEvents.CHAT_MODEL_STREAM\n );\n return handler instanceof ChatModelStreamHandler ? handler : undefined;\n}\n\nfunction hasReasoningDetails(chunk: AIMessageChunk): boolean {\n const reasoningDetails = chunk.additional_kwargs.reasoning_details;\n return Array.isArray(reasoningDetails) && reasoningDetails.length > 0;\n}\n\nfunction removeOpenRouterFinalReasoningReplayContent({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): AIMessageChunk {\n const content = getOpenRouterFinalReasoningContent({\n current,\n next,\n provider,\n });\n if (content == null || content === next.content) {\n return next;\n }\n\n return new AIMessageChunk(\n Object.assign({}, next, {\n content,\n })\n );\n}\n\nfunction getOpenRouterFinalReasoningContent({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): string | undefined {\n if (\n provider !== Providers.OPENROUTER ||\n current == null ||\n !hasReasoningDetails(next) ||\n typeof current.content !== 'string' ||\n current.content === '' ||\n typeof next.content !== 'string' ||\n next.content === ''\n ) {\n return undefined;\n }\n if (!next.content.startsWith(current.content)) {\n return next.content;\n }\n return next.content.slice(current.content.length);\n}\n\nfunction removeReasoningDetails(\n additionalKwargs: AIMessageChunk['additional_kwargs']\n): AIMessageChunk['additional_kwargs'] {\n return Object.fromEntries(\n Object.entries(additionalKwargs).filter(\n ([key]) => key !== 'reasoning_details'\n )\n );\n}\n\nfunction getStreamHandlingChunk({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): AIMessageChunk | undefined {\n const content = getOpenRouterFinalReasoningContent({\n current,\n next,\n provider,\n });\n if (content == null) {\n return next;\n }\n if (content === '') {\n return undefined;\n }\n return new AIMessageChunk(\n Object.assign({}, next, {\n content,\n additional_kwargs: removeReasoningDetails(next.additional_kwargs),\n })\n );\n}\n\nfunction appendStreamChunk({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): AIMessageChunk {\n if (current == null) {\n return next;\n }\n return concat(\n current,\n removeOpenRouterFinalReasoningReplayContent({ current, next, provider })\n );\n}\n\n/**\n * Invokes a chat model with the given messages, handling both streaming and\n * non-streaming paths.\n *\n * By default, stream chunks are processed through a `ChatModelStreamHandler`\n * that dispatches run steps (MESSAGE_CREATION, TOOL_CALLS) for the graph.\n * Pass an `onChunk` callback to override this with custom chunk processing\n * (e.g. summarization delta events).\n */\nexport async function attemptInvoke(\n {\n model,\n messages,\n provider,\n context,\n onChunk,\n }: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n context?: InvokeContext;\n onChunk?: OnChunk;\n },\n config?: RunnableConfig\n): Promise<Partial<t.BaseGraphState>> {\n /**\n * Pull the run-scoped tool output registry off the graph (when one\n * exists) and project ToolMessages carrying ref metadata into a\n * transient annotated copy. The original `messages` array stays\n * untouched so the graph state never sees `[ref: …]` / `_ref`\n * payload.\n */\n const registry = context?.getOrCreateToolOutputRegistry();\n const runId = config?.configurable?.run_id as string | undefined;\n const messagesForProvider = annotateMessagesForLLM(messages, registry, runId);\n\n /**\n * Stamp the provider that is ACTUALLY serving this invocation onto the\n * callback metadata. `attemptInvoke` is the single funnel for primary,\n * fallback, and summarization model calls, so consumers that need\n * provider attribution per call (the subagent usage-capture handler)\n * read this key instead of trusting static agent config — which is\n * wrong for fallback-served calls — or `ls_provider` — which derived\n * providers inherit from their base class.\n */\n config = {\n ...config,\n metadata: {\n ...(config?.metadata ?? {}),\n [Constants.INVOKED_PROVIDER]: provider,\n },\n };\n\n if (model.stream) {\n const stream = await model.stream(messagesForProvider, config);\n let finalChunk: AIMessageChunk | undefined;\n const registeredStreamHandler =\n getRegisteredDefaultChatStreamHandler(context);\n\n if (onChunk) {\n for await (const chunk of stream) {\n await onChunk(chunk);\n finalChunk = appendStreamChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n }\n } else if (registeredStreamHandler == null) {\n const metadata = config.metadata as Record<string, unknown> | undefined;\n const streamHandler = new ChatModelStreamHandler();\n for await (const chunk of stream) {\n const handlingChunk = getStreamHandlingChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n if (handlingChunk != null) {\n await streamHandler.handle(\n GraphEvents.CHAT_MODEL_STREAM,\n { chunk: handlingChunk },\n metadata,\n context\n );\n }\n finalChunk = appendStreamChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n }\n } else {\n const metadata = config.metadata as Record<string, unknown> | undefined;\n for await (const chunk of stream) {\n const handlingChunk = getStreamHandlingChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n if (handlingChunk != null && handlingChunk !== chunk) {\n await registeredStreamHandler.handle(\n GraphEvents.CHAT_MODEL_STREAM,\n { chunk: handlingChunk },\n metadata,\n context\n );\n }\n finalChunk = appendStreamChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n }\n }\n\n if (manualToolStreamProviders.has(provider)) {\n finalChunk = modifyDeltaProperties(provider, finalChunk);\n }\n\n if ((finalChunk?.tool_calls?.length ?? 0) > 0) {\n finalChunk!.tool_calls = finalChunk!.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n\n return { messages: [finalChunk as AIMessageChunk] };\n }\n\n const finalMessage = await model.invoke(messagesForProvider, config);\n if ((finalMessage.tool_calls?.length ?? 0) > 0) {\n finalMessage.tool_calls = finalMessage.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n return { messages: [finalMessage] };\n}\n\n/**\n * Best-effort read of the configured model name from client options.\n * Providers disagree on the key (`model` vs `modelName`).\n */\nfunction extractClientOptionsModel(\n clientOptions: t.ClientOptions | undefined\n): string | undefined {\n const options = clientOptions as\n | { model?: unknown; modelName?: unknown }\n | undefined;\n if (typeof options?.model === 'string' && options.model !== '') {\n return options.model;\n }\n if (typeof options?.modelName === 'string' && options.modelName !== '') {\n return options.modelName;\n }\n return undefined;\n}\n\n/**\n * Attempts each fallback provider in order until one succeeds.\n * Throws the last error if all fallbacks fail.\n */\nexport async function tryFallbackProviders({\n fallbacks,\n tools,\n messages,\n config,\n primaryError,\n context,\n onChunk,\n}: {\n fallbacks: Array<{ provider: Providers; clientOptions?: t.ClientOptions }>;\n tools?: t.GraphTools;\n messages: BaseMessage[];\n config?: RunnableConfig;\n primaryError: unknown;\n context?: InvokeContext;\n onChunk?: OnChunk;\n}): Promise<Partial<t.BaseGraphState> | undefined> {\n let lastError: unknown = primaryError;\n for (const fb of fallbacks) {\n try {\n const fbModel = initializeModel({\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n tools,\n });\n /**\n * Stamp the fallback's configured model onto callback metadata so\n * per-call attribution (subagent usage capture) doesn't fall back to\n * the PRIMARY config's model when the provider reports no\n * `ls_model_name`. The serving provider is stamped uniformly by\n * `attemptInvoke` (`INVOKED_PROVIDER`).\n */\n const fbModelName = extractClientOptionsModel(fb.clientOptions);\n const fbConfig: RunnableConfig | undefined =\n fbModelName == null\n ? config\n : {\n ...config,\n metadata: {\n ...(config?.metadata ?? {}),\n [Constants.INVOKED_MODEL]: fbModelName,\n },\n };\n const result = await attemptInvoke(\n {\n model: fbModel as t.ChatModel,\n messages,\n provider: fb.provider,\n context,\n onChunk,\n },\n fbConfig\n );\n return result;\n } catch (e) {\n lastError = e;\n continue;\n }\n }\n if (lastError !== undefined) {\n throw lastError;\n }\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"invoke.cjs","names":["ChatModelStreamHandler","AIMessageChunk","annotateMessagesForLLM","manualToolStreamProviders","modifyDeltaProperties","initializeModel"],"sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';\nimport type * as t from '@/types';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { assertNotTruncatedToolCall } from '@/llm/truncation';\nimport { Constants, GraphEvents, Providers } from '@/common';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { modifyDeltaProperties } from '@/messages';\nimport { ChatModelStreamHandler } from '@/stream';\nimport { initializeModel } from '@/llm/init';\n\n/**\n * Context passed to `attemptInvoke`. Matches the subset of Graph that\n * `ChatModelStreamHandler.handle` needs *plus* the explicit\n * `getOrCreateToolOutputRegistry()` accessor that `attemptInvoke`\n * itself calls to pull the run-scoped tool-output registry off the\n * graph and project each relevant ToolMessage into a transient\n * annotated copy before the provider call.\n *\n * The intersection is intentional: `Parameters<...>[3]` resolves\n * indirectly through the stream handler's signature (which returns\n * `StandardGraph` and already exposes the accessor since #117), but\n * stating it explicitly here surfaces the contract at the call site —\n * a developer reading `attemptInvoke` doesn't have to chase the\n * upstream handler's parameter list to discover that\n * `context?.getOrCreateToolOutputRegistry()` is a real thing. Single\n * optional chain only — the method itself is required on the\n * `StandardGraph` branch of the intersection, so the second `?.` is\n * unnecessary at the call site.\n *\n * `NonNullable<...>` strips `undefined` from the upstream parameter\n * type so the intersection doesn't collapse to `never` on the\n * undefined branch; callers express optionality via `context?:\n * InvokeContext` on the function signature instead.\n *\n * Callers without a registry (e.g. summarization) simply pass no\n * `context` and the transform safely no-ops.\n */\nexport type InvokeContext = NonNullable<\n Parameters<ChatModelStreamHandler['handle']>[3]\n> & {\n getOrCreateToolOutputRegistry?(): ToolOutputReferenceRegistry | undefined;\n};\n\n/**\n * Per-chunk callback for custom stream processing.\n * When provided, replaces the default `ChatModelStreamHandler`.\n */\nexport type OnChunk = (chunk: AIMessageChunk) => void | Promise<void>;\n\nfunction getRegisteredDefaultChatStreamHandler(\n context?: InvokeContext\n): ChatModelStreamHandler | undefined {\n const handler = context?.handlerRegistry?.getHandler(\n GraphEvents.CHAT_MODEL_STREAM\n );\n return handler instanceof ChatModelStreamHandler ? handler : undefined;\n}\n\nfunction hasReasoningDetails(chunk: AIMessageChunk): boolean {\n const reasoningDetails = chunk.additional_kwargs.reasoning_details;\n return Array.isArray(reasoningDetails) && reasoningDetails.length > 0;\n}\n\nfunction removeOpenRouterFinalReasoningReplayContent({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): AIMessageChunk {\n const content = getOpenRouterFinalReasoningContent({\n current,\n next,\n provider,\n });\n if (content == null || content === next.content) {\n return next;\n }\n\n return new AIMessageChunk(\n Object.assign({}, next, {\n content,\n })\n );\n}\n\nfunction getOpenRouterFinalReasoningContent({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): string | undefined {\n if (\n provider !== Providers.OPENROUTER ||\n current == null ||\n !hasReasoningDetails(next) ||\n typeof current.content !== 'string' ||\n current.content === '' ||\n typeof next.content !== 'string' ||\n next.content === ''\n ) {\n return undefined;\n }\n if (!next.content.startsWith(current.content)) {\n return next.content;\n }\n return next.content.slice(current.content.length);\n}\n\nfunction removeReasoningDetails(\n additionalKwargs: AIMessageChunk['additional_kwargs']\n): AIMessageChunk['additional_kwargs'] {\n return Object.fromEntries(\n Object.entries(additionalKwargs).filter(\n ([key]) => key !== 'reasoning_details'\n )\n );\n}\n\nfunction getStreamHandlingChunk({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): AIMessageChunk | undefined {\n const content = getOpenRouterFinalReasoningContent({\n current,\n next,\n provider,\n });\n if (content == null) {\n return next;\n }\n if (content === '') {\n return undefined;\n }\n return new AIMessageChunk(\n Object.assign({}, next, {\n content,\n additional_kwargs: removeReasoningDetails(next.additional_kwargs),\n })\n );\n}\n\nfunction appendStreamChunk({\n current,\n next,\n provider,\n}: {\n current?: AIMessageChunk;\n next: AIMessageChunk;\n provider: Providers;\n}): AIMessageChunk {\n if (current == null) {\n return next;\n }\n return concat(\n current,\n removeOpenRouterFinalReasoningReplayContent({ current, next, provider })\n );\n}\n\n/**\n * Invokes a chat model with the given messages, handling both streaming and\n * non-streaming paths.\n *\n * By default, stream chunks are processed through a `ChatModelStreamHandler`\n * that dispatches run steps (MESSAGE_CREATION, TOOL_CALLS) for the graph.\n * Pass an `onChunk` callback to override this with custom chunk processing\n * (e.g. summarization delta events).\n */\nexport async function attemptInvoke(\n {\n model,\n messages,\n provider,\n context,\n onChunk,\n }: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n context?: InvokeContext;\n onChunk?: OnChunk;\n },\n config?: RunnableConfig\n): Promise<Partial<t.BaseGraphState>> {\n /**\n * Pull the run-scoped tool output registry off the graph (when one\n * exists) and project ToolMessages carrying ref metadata into a\n * transient annotated copy. The original `messages` array stays\n * untouched so the graph state never sees `[ref: …]` / `_ref`\n * payload.\n */\n const registry = context?.getOrCreateToolOutputRegistry();\n const runId = config?.configurable?.run_id as string | undefined;\n const messagesForProvider = annotateMessagesForLLM(messages, registry, runId);\n\n /**\n * Stamp the provider that is ACTUALLY serving this invocation onto the\n * callback metadata. `attemptInvoke` is the single funnel for primary,\n * fallback, and summarization model calls, so consumers that need\n * provider attribution per call (the subagent usage-capture handler)\n * read this key instead of trusting static agent config — which is\n * wrong for fallback-served calls — or `ls_provider` — which derived\n * providers inherit from their base class.\n */\n config = {\n ...config,\n metadata: {\n ...(config?.metadata ?? {}),\n [Constants.INVOKED_PROVIDER]: provider,\n },\n };\n\n if (model.stream) {\n const stream = await model.stream(messagesForProvider, config);\n let finalChunk: AIMessageChunk | undefined;\n const registeredStreamHandler =\n getRegisteredDefaultChatStreamHandler(context);\n\n if (onChunk) {\n for await (const chunk of stream) {\n await onChunk(chunk);\n finalChunk = appendStreamChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n }\n } else if (registeredStreamHandler == null) {\n const metadata = config.metadata as Record<string, unknown> | undefined;\n const streamHandler = new ChatModelStreamHandler();\n for await (const chunk of stream) {\n const handlingChunk = getStreamHandlingChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n if (handlingChunk != null) {\n await streamHandler.handle(\n GraphEvents.CHAT_MODEL_STREAM,\n { chunk: handlingChunk },\n metadata,\n context\n );\n }\n finalChunk = appendStreamChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n }\n } else {\n const metadata = config.metadata as Record<string, unknown> | undefined;\n for await (const chunk of stream) {\n const handlingChunk = getStreamHandlingChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n if (handlingChunk != null && handlingChunk !== chunk) {\n await registeredStreamHandler.handle(\n GraphEvents.CHAT_MODEL_STREAM,\n { chunk: handlingChunk },\n metadata,\n context\n );\n }\n finalChunk = appendStreamChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n }\n }\n\n if (manualToolStreamProviders.has(provider)) {\n finalChunk = modifyDeltaProperties(provider, finalChunk);\n }\n\n if ((finalChunk?.tool_calls?.length ?? 0) > 0) {\n finalChunk!.tool_calls = finalChunk!.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n\n assertNotTruncatedToolCall(finalChunk, provider);\n return { messages: [finalChunk as AIMessageChunk] };\n }\n\n const finalMessage = await model.invoke(messagesForProvider, config);\n if ((finalMessage.tool_calls?.length ?? 0) > 0) {\n finalMessage.tool_calls = finalMessage.tool_calls?.filter(\n (tool_call: ToolCall) => !!tool_call.name\n );\n }\n assertNotTruncatedToolCall(finalMessage, provider);\n return { messages: [finalMessage] };\n}\n\n/**\n * Best-effort read of the configured model name from client options.\n * Providers disagree on the key (`model` vs `modelName`).\n */\nfunction extractClientOptionsModel(\n clientOptions: t.ClientOptions | undefined\n): string | undefined {\n const options = clientOptions as\n | { model?: unknown; modelName?: unknown }\n | undefined;\n if (typeof options?.model === 'string' && options.model !== '') {\n return options.model;\n }\n if (typeof options?.modelName === 'string' && options.modelName !== '') {\n return options.modelName;\n }\n return undefined;\n}\n\n/**\n * Attempts each fallback provider in order until one succeeds.\n * Throws the last error if all fallbacks fail.\n */\nexport async function tryFallbackProviders({\n fallbacks,\n tools,\n messages,\n config,\n primaryError,\n context,\n onChunk,\n}: {\n fallbacks: Array<{ provider: Providers; clientOptions?: t.ClientOptions }>;\n tools?: t.GraphTools;\n messages: BaseMessage[];\n config?: RunnableConfig;\n primaryError: unknown;\n context?: InvokeContext;\n onChunk?: OnChunk;\n}): Promise<Partial<t.BaseGraphState> | undefined> {\n let lastError: unknown = primaryError;\n for (const fb of fallbacks) {\n try {\n const fbModel = initializeModel({\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n tools,\n });\n /**\n * Stamp the fallback's configured model onto callback metadata so\n * per-call attribution (subagent usage capture) doesn't fall back to\n * the PRIMARY config's model when the provider reports no\n * `ls_model_name`. The serving provider is stamped uniformly by\n * `attemptInvoke` (`INVOKED_PROVIDER`).\n */\n const fbModelName = extractClientOptionsModel(fb.clientOptions);\n const fbConfig: RunnableConfig | undefined =\n fbModelName == null\n ? config\n : {\n ...config,\n metadata: {\n ...(config?.metadata ?? {}),\n [Constants.INVOKED_MODEL]: fbModelName,\n },\n };\n const result = await attemptInvoke(\n {\n model: fbModel as t.ChatModel,\n messages,\n provider: fb.provider,\n context,\n onChunk,\n },\n fbConfig\n );\n return result;\n } catch (e) {\n lastError = e;\n continue;\n }\n }\n if (lastError !== undefined) {\n throw lastError;\n }\n return undefined;\n}\n"],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ require("../common/enum.cjs");
2
+ require("../common/index.cjs");
3
+ //#region src/llm/truncation.ts
4
+ /**
5
+ * Providers whose adapters deliver tool calls as complete, atomic objects
6
+ * rather than streamed argument deltas. Google/Vertex (GenAI) emit each
7
+ * `functionCall` whole and seal it on arrival, so a `MAX_TOKENS` finish does
8
+ * NOT imply the arguments were cut off — the truncation guard must skip them.
9
+ */
10
+ const ATOMIC_TOOL_CALL_ARG_PROVIDERS = new Set(["google", "vertexai"]);
11
+ const MAX_TOKEN_VALUES = new Set([
12
+ "max_tokens",
13
+ "max_token",
14
+ "maxtokens",
15
+ "max_output_tokens"
16
+ ]);
17
+ const LENGTH_VALUES = new Set(["length"]);
18
+ function normalizeStopValue(value) {
19
+ if (typeof value !== "string") return null;
20
+ const normalized = value.trim().toLowerCase();
21
+ if (MAX_TOKEN_VALUES.has(normalized)) return "max_tokens";
22
+ if (LENGTH_VALUES.has(normalized)) return "length";
23
+ return null;
24
+ }
25
+ /**
26
+ * Reads the truncation stop reason off a message, covering every provider
27
+ * shape we stream:
28
+ * - `response_metadata.stopReason` (Bedrock Converse, non-streaming)
29
+ * - `response_metadata.messageStop.stopReason` (Bedrock Converse streaming)
30
+ * - `response_metadata.stop_reason` (Anthropic, non-streaming)
31
+ * - `additional_kwargs.stop_reason` (Anthropic streaming `message_delta`)
32
+ * - `response_metadata.finish_reason` (OpenAI chat-completions / compatible)
33
+ * - `response_metadata.incomplete_details.reason` (OpenAI Responses API)
34
+ * - `response_metadata.finishReason` (Google)
35
+ *
36
+ * Returns the normalized reason when the model stopped because it hit the
37
+ * output token ceiling, otherwise `null`.
38
+ */
39
+ function getTruncationStopReason(message) {
40
+ const meta = message?.response_metadata;
41
+ const additionalKwargs = message?.additional_kwargs;
42
+ if (meta == null && additionalKwargs == null) return null;
43
+ const messageStop = meta?.messageStop;
44
+ const incompleteDetails = meta?.incomplete_details;
45
+ const candidates = [
46
+ meta?.stopReason,
47
+ meta?.stop_reason,
48
+ meta?.finish_reason,
49
+ meta?.finishReason,
50
+ messageStop?.stopReason,
51
+ incompleteDetails?.reason,
52
+ additionalKwargs?.stop_reason
53
+ ];
54
+ for (const candidate of candidates) {
55
+ const normalized = normalizeStopValue(candidate);
56
+ if (normalized != null) return normalized;
57
+ }
58
+ return null;
59
+ }
60
+ function hasOpenToolCall(message) {
61
+ const m = message;
62
+ return (m.tool_calls?.length ?? 0) > 0 || (m.tool_call_chunks?.length ?? 0) > 0 || (m.invalid_tool_calls?.length ?? 0) > 0;
63
+ }
64
+ /**
65
+ * Error raised when a model turn is cut off by the output token limit while it
66
+ * was still emitting a tool call. The arguments are necessarily incomplete, so
67
+ * executing or re-prompting the call would loop on a malformed request. Failing
68
+ * fast with this surfaces an actionable message to the caller instead.
69
+ */
70
+ var OutputTruncationError = class extends Error {
71
+ stopReason;
72
+ toolCallNames;
73
+ constructor(stopReason, toolCallNames) {
74
+ const named = toolCallNames.length > 0 ? ` (tool call: ${toolCallNames.join(", ")})` : "";
75
+ super(`The model response was truncated at the maximum output token limit before the tool call arguments were complete${named}. Increase the model's max output tokens, or have the model produce smaller arguments — for large files, write a lean main file and move bulky content into separate files.`);
76
+ this.name = "OutputTruncationError";
77
+ this.stopReason = stopReason;
78
+ this.toolCallNames = toolCallNames;
79
+ }
80
+ };
81
+ function collectToolCallNames(message) {
82
+ const m = message;
83
+ const names = [
84
+ ...m.tool_calls ?? [],
85
+ ...m.tool_call_chunks ?? [],
86
+ ...m.invalid_tool_calls ?? []
87
+ ].map((tc) => tc.name).filter((name) => typeof name === "string" && name !== "");
88
+ return [...new Set(names)];
89
+ }
90
+ /**
91
+ * Throws {@link OutputTruncationError} when `message` was truncated by the
92
+ * output token limit AND still carries a tool call. For providers that stream
93
+ * tool arguments incrementally (Anthropic, Bedrock, OpenAI), a tool call
94
+ * emitted under truncation has incomplete arguments — the tool-use block is the
95
+ * last thing the model streams — so letting it through makes the agent loop on a
96
+ * malformed call. No-ops for normal completions, truncated plain-text turns, and
97
+ * providers that deliver complete tool calls atomically (Google/Vertex), where
98
+ * `MAX_TOKENS` does not imply the arguments were cut off.
99
+ */
100
+ function assertNotTruncatedToolCall(message, provider) {
101
+ if (message == null) return;
102
+ if (provider != null && ATOMIC_TOOL_CALL_ARG_PROVIDERS.has(provider)) return;
103
+ const stopReason = getTruncationStopReason(message);
104
+ if (stopReason == null || !hasOpenToolCall(message)) return;
105
+ throw new OutputTruncationError(stopReason, collectToolCallNames(message));
106
+ }
107
+ //#endregion
108
+ exports.assertNotTruncatedToolCall = assertNotTruncatedToolCall;
109
+
110
+ //# sourceMappingURL=truncation.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncation.cjs","names":[],"sources":["../../../src/llm/truncation.ts"],"sourcesContent":["import type { AIMessageChunk, BaseMessage } from '@langchain/core/messages';\nimport { Providers } from '@/common';\n\n/**\n * Providers whose adapters deliver tool calls as complete, atomic objects\n * rather than streamed argument deltas. Google/Vertex (GenAI) emit each\n * `functionCall` whole and seal it on arrival, so a `MAX_TOKENS` finish does\n * NOT imply the arguments were cut off — the truncation guard must skip them.\n */\nconst ATOMIC_TOOL_CALL_ARG_PROVIDERS = new Set<Providers>([\n Providers.GOOGLE,\n Providers.VERTEXAI,\n]);\n\n/**\n * Normalized truncation reasons across providers. Providers disagree on the\n * key and the value: Anthropic/Bedrock use `max_tokens`, OpenAI uses `length`,\n * Google uses `MAX_TOKENS`.\n */\nexport type TruncationStopReason = 'max_tokens' | 'length';\n\nconst MAX_TOKEN_VALUES = new Set([\n 'max_tokens',\n 'max_token',\n 'maxtokens',\n 'max_output_tokens',\n]);\nconst LENGTH_VALUES = new Set(['length']);\n\nfunction normalizeStopValue(value: unknown): TruncationStopReason | null {\n if (typeof value !== 'string') {\n return null;\n }\n const normalized = value.trim().toLowerCase();\n if (MAX_TOKEN_VALUES.has(normalized)) {\n return 'max_tokens';\n }\n if (LENGTH_VALUES.has(normalized)) {\n return 'length';\n }\n return null;\n}\n\n/**\n * Reads the truncation stop reason off a message, covering every provider\n * shape we stream:\n * - `response_metadata.stopReason` (Bedrock Converse, non-streaming)\n * - `response_metadata.messageStop.stopReason` (Bedrock Converse streaming)\n * - `response_metadata.stop_reason` (Anthropic, non-streaming)\n * - `additional_kwargs.stop_reason` (Anthropic streaming `message_delta`)\n * - `response_metadata.finish_reason` (OpenAI chat-completions / compatible)\n * - `response_metadata.incomplete_details.reason` (OpenAI Responses API)\n * - `response_metadata.finishReason` (Google)\n *\n * Returns the normalized reason when the model stopped because it hit the\n * output token ceiling, otherwise `null`.\n */\nexport function getTruncationStopReason(\n message:\n | Pick<BaseMessage, 'response_metadata' | 'additional_kwargs'>\n | undefined\n | null\n): TruncationStopReason | null {\n const meta = message?.response_metadata as\n | Record<string, unknown>\n | undefined;\n const additionalKwargs = message?.additional_kwargs as\n | Record<string, unknown>\n | undefined;\n if (meta == null && additionalKwargs == null) {\n return null;\n }\n\n const messageStop = meta?.messageStop as { stopReason?: unknown } | undefined;\n const incompleteDetails = meta?.incomplete_details as\n | { reason?: unknown }\n | undefined;\n const candidates: unknown[] = [\n meta?.stopReason,\n meta?.stop_reason,\n meta?.finish_reason,\n meta?.finishReason,\n messageStop?.stopReason,\n incompleteDetails?.reason,\n additionalKwargs?.stop_reason,\n ];\n\n for (const candidate of candidates) {\n const normalized = normalizeStopValue(candidate);\n if (normalized != null) {\n return normalized;\n }\n }\n return null;\n}\n\nfunction hasOpenToolCall(message: AIMessageChunk | BaseMessage): boolean {\n const m = message as AIMessageChunk & {\n tool_calls?: unknown[];\n tool_call_chunks?: unknown[];\n invalid_tool_calls?: unknown[];\n };\n return (\n (m.tool_calls?.length ?? 0) > 0 ||\n (m.tool_call_chunks?.length ?? 0) > 0 ||\n (m.invalid_tool_calls?.length ?? 0) > 0\n );\n}\n\n/**\n * Error raised when a model turn is cut off by the output token limit while it\n * was still emitting a tool call. The arguments are necessarily incomplete, so\n * executing or re-prompting the call would loop on a malformed request. Failing\n * fast with this surfaces an actionable message to the caller instead.\n */\nexport class OutputTruncationError extends Error {\n readonly stopReason: TruncationStopReason;\n readonly toolCallNames: string[];\n\n constructor(stopReason: TruncationStopReason, toolCallNames: string[]) {\n const named =\n toolCallNames.length > 0\n ? ` (tool call: ${toolCallNames.join(', ')})`\n : '';\n super(\n 'The model response was truncated at the maximum output token limit before ' +\n `the tool call arguments were complete${named}. Increase the model's max ` +\n 'output tokens, or have the model produce smaller arguments — for large ' +\n 'files, write a lean main file and move bulky content into separate files.'\n );\n this.name = 'OutputTruncationError';\n this.stopReason = stopReason;\n this.toolCallNames = toolCallNames;\n }\n}\n\nfunction collectToolCallNames(message: AIMessageChunk | BaseMessage): string[] {\n const m = message as AIMessageChunk & {\n tool_calls?: Array<{ name?: string }>;\n tool_call_chunks?: Array<{ name?: string }>;\n invalid_tool_calls?: Array<{ name?: string }>;\n };\n const names = [\n ...(m.tool_calls ?? []),\n ...(m.tool_call_chunks ?? []),\n ...(m.invalid_tool_calls ?? []),\n ]\n .map((tc) => tc.name)\n .filter((name): name is string => typeof name === 'string' && name !== '');\n return [...new Set(names)];\n}\n\n/**\n * Throws {@link OutputTruncationError} when `message` was truncated by the\n * output token limit AND still carries a tool call. For providers that stream\n * tool arguments incrementally (Anthropic, Bedrock, OpenAI), a tool call\n * emitted under truncation has incomplete arguments — the tool-use block is the\n * last thing the model streams — so letting it through makes the agent loop on a\n * malformed call. No-ops for normal completions, truncated plain-text turns, and\n * providers that deliver complete tool calls atomically (Google/Vertex), where\n * `MAX_TOKENS` does not imply the arguments were cut off.\n */\nexport function assertNotTruncatedToolCall(\n message: AIMessageChunk | BaseMessage | undefined | null,\n provider?: Providers\n): void {\n if (message == null) {\n return;\n }\n if (provider != null && ATOMIC_TOOL_CALL_ARG_PROVIDERS.has(provider)) {\n return;\n }\n const stopReason = getTruncationStopReason(message);\n if (stopReason == null || !hasOpenToolCall(message)) {\n return;\n }\n throw new OutputTruncationError(stopReason, collectToolCallNames(message));\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,iCAAiC,IAAI,IAAe,CAAA,UAAA,UAG1D,CAAC;AASD,MAAM,mBAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,gBAAgB,IAAI,IAAI,CAAC,QAAQ,CAAC;AAExC,SAAS,mBAAmB,OAA6C;CACvE,IAAI,OAAO,UAAU,UACnB,OAAO;CAET,MAAM,aAAa,MAAM,KAAK,CAAC,CAAC,YAAY;CAC5C,IAAI,iBAAiB,IAAI,UAAU,GACjC,OAAO;CAET,IAAI,cAAc,IAAI,UAAU,GAC9B,OAAO;CAET,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAgB,wBACd,SAI6B;CAC7B,MAAM,OAAO,SAAS;CAGtB,MAAM,mBAAmB,SAAS;CAGlC,IAAI,QAAQ,QAAQ,oBAAoB,MACtC,OAAO;CAGT,MAAM,cAAc,MAAM;CAC1B,MAAM,oBAAoB,MAAM;CAGhC,MAAM,aAAwB;EAC5B,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,aAAa;EACb,mBAAmB;EACnB,kBAAkB;CACpB;CAEA,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,aAAa,mBAAmB,SAAS;EAC/C,IAAI,cAAc,MAChB,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAgD;CACvE,MAAM,IAAI;CAKV,QACG,EAAE,YAAY,UAAU,KAAK,MAC7B,EAAE,kBAAkB,UAAU,KAAK,MACnC,EAAE,oBAAoB,UAAU,KAAK;AAE1C;;;;;;;AAQA,IAAa,wBAAb,cAA2C,MAAM;CAC/C;CACA;CAEA,YAAY,YAAkC,eAAyB;EACrE,MAAM,QACJ,cAAc,SAAS,IACnB,gBAAgB,cAAc,KAAK,IAAI,EAAE,KACzC;EACN,MACE,kHAC0C,MAAM,4KAGlD;EACA,KAAK,OAAO;EACZ,KAAK,aAAa;EAClB,KAAK,gBAAgB;CACvB;AACF;AAEA,SAAS,qBAAqB,SAAiD;CAC7E,MAAM,IAAI;CAKV,MAAM,QAAQ;EACZ,GAAI,EAAE,cAAc,CAAC;EACrB,GAAI,EAAE,oBAAoB,CAAC;EAC3B,GAAI,EAAE,sBAAsB,CAAC;CAC/B,CAAC,CACE,KAAK,OAAO,GAAG,IAAI,CAAC,CACpB,QAAQ,SAAyB,OAAO,SAAS,YAAY,SAAS,EAAE;CAC3E,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC3B;;;;;;;;;;;AAYA,SAAgB,2BACd,SACA,UACM;CACN,IAAI,WAAW,MACb;CAEF,IAAI,YAAY,QAAQ,+BAA+B,IAAI,QAAQ,GACjE;CAEF,MAAM,aAAa,wBAAwB,OAAO;CAClD,IAAI,cAAc,QAAQ,CAAC,gBAAgB,OAAO,GAChD;CAEF,MAAM,IAAI,sBAAsB,YAAY,qBAAqB,OAAO,CAAC;AAC3E"}
@@ -321,7 +321,7 @@ function _formatContent(message) {
321
321
  if (isAIMessage(message) && (signature == null || signature === "")) return null;
322
322
  return {
323
323
  type: "thinking",
324
- thinking: thinkingPart.thinking,
324
+ thinking: thinkingPart.thinking ?? "",
325
325
  signature: thinkingPart.signature,
326
326
  ...cacheControl != null ? { cache_control: cacheControl } : {}
327
327
  };