@librechat/agents 3.3.10 → 3.3.12
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.
- package/dist/cjs/graphs/Graph.cjs +248 -27
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +228 -16
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +13 -2
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +160 -43
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +190 -13
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/streamLimits.cjs +723 -0
- package/dist/cjs/llm/streamLimits.cjs.map +1 -0
- package/dist/cjs/main.cjs +12 -3
- package/dist/cjs/messages/core.cjs +592 -27
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/run.cjs +7 -3
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +4 -1
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/stream.cjs +105 -14
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +157 -37
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -2
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +4 -3
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +5 -3
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +77 -4
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +3 -2
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/search/crw-scraper.cjs +7 -1
- package/dist/cjs/tools/search/crw-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/crw-search.cjs +3 -1
- package/dist/cjs/tools/search/crw-search.cjs.map +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs +7 -1
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs +7 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-search.cjs +3 -1
- package/dist/cjs/tools/search/keenable-search.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +26 -8
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +30 -10
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/serper-scraper.cjs +7 -1
- package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs +7 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-search.cjs +3 -1
- package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +17 -3
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +54 -3
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +2 -1
- package/dist/cjs/utils/misc.cjs +12 -0
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/cjs/utils/proxy.cjs +63 -0
- package/dist/cjs/utils/proxy.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +247 -26
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +228 -16
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +13 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +160 -43
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +192 -15
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/streamLimits.mjs +704 -0
- package/dist/esm/llm/streamLimits.mjs.map +1 -0
- package/dist/esm/main.mjs +8 -6
- package/dist/esm/messages/core.mjs +592 -28
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/run.mjs +7 -3
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +4 -1
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/stream.mjs +105 -14
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +157 -37
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -2
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +4 -3
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +5 -3
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +77 -4
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +3 -2
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/search/crw-scraper.mjs +7 -1
- package/dist/esm/tools/search/crw-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/crw-search.mjs +3 -1
- package/dist/esm/tools/search/crw-search.mjs.map +1 -1
- package/dist/esm/tools/search/firecrawl.mjs +7 -1
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-scraper.mjs +7 -1
- package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-search.mjs +3 -1
- package/dist/esm/tools/search/keenable-search.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs +26 -8
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +30 -10
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/serper-scraper.mjs +7 -1
- package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-scraper.mjs +7 -1
- package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-search.mjs +3 -1
- package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +17 -3
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +54 -3
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +2 -1
- package/dist/esm/utils/misc.mjs +12 -1
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/esm/utils/proxy.mjs +62 -0
- package/dist/esm/utils/proxy.mjs.map +1 -0
- package/dist/types/graphs/Graph.d.ts +53 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +1 -0
- package/dist/types/llm/invoke.d.ts +26 -5
- package/dist/types/llm/openai/index.d.ts +3 -0
- package/dist/types/llm/streamLimits.d.ts +314 -0
- package/dist/types/messages/core.d.ts +11 -6
- package/dist/types/run.d.ts +1 -0
- package/dist/types/summarization/node.d.ts +27 -2
- package/dist/types/tools/BashExecutor.d.ts +2 -2
- package/dist/types/tools/CodeExecutor.d.ts +3 -3
- package/dist/types/tools/ToolNode.d.ts +11 -1
- package/dist/types/tools/search/crw-scraper.d.ts +2 -0
- package/dist/types/tools/search/firecrawl.d.ts +2 -0
- package/dist/types/tools/search/keenable-scraper.d.ts +2 -0
- package/dist/types/tools/search/rerankers.d.ts +9 -5
- package/dist/types/tools/search/serper-scraper.d.ts +2 -0
- package/dist/types/tools/search/tavily-scraper.d.ts +2 -0
- package/dist/types/tools/search/types.d.ts +28 -33
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +44 -0
- package/dist/types/types/graph.d.ts +7 -1
- package/dist/types/types/run.d.ts +46 -1
- package/dist/types/types/tools.d.ts +21 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/misc.d.ts +7 -0
- package/dist/types/utils/proxy.d.ts +31 -0
- package/package.json +2 -1
- package/src/__tests__/stream.eagerArgsDivergence.test.ts +158 -0
- package/src/__tests__/stream.eagerEventExecution.test.ts +1 -0
- package/src/__tests__/stream.streamLimits.test.ts +1982 -0
- package/src/graphs/Graph.ts +302 -27
- package/src/graphs/__tests__/Graph.breakerLifecycle.test.ts +234 -0
- package/src/index.ts +11 -0
- package/src/langfuseToolOutputTracing.ts +410 -14
- package/src/llm/bedrock/index.ts +22 -2
- package/src/llm/custom-chat-models.smoke.test.ts +747 -0
- package/src/llm/invoke.streamLimits.test.ts +142 -0
- package/src/llm/invoke.test.ts +187 -1
- package/src/llm/invoke.ts +231 -43
- package/src/llm/openai/cacheWriteTokens.test.ts +112 -0
- package/src/llm/openai/index.ts +348 -31
- package/src/llm/openai/llm.spec.ts +107 -6
- package/src/llm/streamLimits.test.ts +450 -0
- package/src/llm/streamLimits.ts +1158 -0
- package/src/messages/core.ts +1290 -42
- package/src/messages/formatAgentMessages.test.ts +2623 -0
- package/src/run.ts +4 -0
- package/src/session/AgentSession.ts +5 -0
- package/src/specs/langfuse-tool-output-tracing.test.ts +887 -0
- package/src/specs/preemptSeal.test.ts +374 -5
- package/src/stream.ts +141 -6
- package/src/summarization/__tests__/node.test.ts +269 -0
- package/src/summarization/chunkHandler.test.ts +196 -0
- package/src/summarization/node.ts +203 -6
- package/src/tools/BashExecutor.ts +4 -3
- package/src/tools/CodeExecutor.ts +5 -4
- package/src/tools/ProgrammaticToolCalling.ts +7 -5
- package/src/tools/ToolNode.ts +109 -6
- package/src/tools/ToolSearch.ts +4 -3
- package/src/tools/__tests__/BashExecutor.test.ts +2 -2
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +2 -4
- package/src/tools/__tests__/SubagentExecutor.test.ts +126 -0
- package/src/tools/__tests__/ToolNode.breakerSignal.test.ts +389 -0
- package/src/tools/__tests__/ToolNode.streamLimits.test.ts +69 -0
- package/src/tools/search/crw-scraper.ts +6 -0
- package/src/tools/search/crw-search.ts +6 -1
- package/src/tools/search/firecrawl.ts +6 -0
- package/src/tools/search/http-agent.test.ts +133 -0
- package/src/tools/search/keenable-scraper.ts +6 -0
- package/src/tools/search/keenable-search.ts +6 -1
- package/src/tools/search/rerankers.ts +36 -10
- package/src/tools/search/search.ts +29 -11
- package/src/tools/search/serper-scraper.ts +6 -0
- package/src/tools/search/tavily-scraper.ts +6 -0
- package/src/tools/search/tavily-search.ts +2 -0
- package/src/tools/search/tool.ts +16 -0
- package/src/tools/search/types.ts +31 -33
- package/src/tools/subagent/SubagentExecutor.ts +96 -3
- package/src/types/graph.ts +7 -0
- package/src/types/run.ts +49 -1
- package/src/types/tools.ts +21 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/misc.ts +19 -0
- package/src/utils/proxy.test.ts +176 -0
- package/src/utils/proxy.ts +93 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.cjs","names":["isOpenAILike","projectToolStreamContentForProvider","projectOpenAIResponsesToolMessageContent","stripAnthropicCacheControl","stripBedrockCacheControl","projectComputerCallOutputsToText","projectOpenRouterToolMessageContent","projectOpenAIChatToolMessageContent","projectSingleTextToolOutputsToText","projectCacheControlledToolOutputsToText","projectStructuredToolOutputsToText","dispatchesChatModelStream","AIMessageChunk","CallbackManager","CallbackManagerForLLMRun","safeDispatchCustomEvent","annotateMessagesForLLM","isAnthropicLike","appendPredecessorHandoffCue","removePredecessorHandoffCue","strictAlternationProviders","coalesceAdjacentUserTurns","appendCallbacks","ChatModelStreamHandler","canSealPreempt","manualToolStreamProviders","modifyDeltaProperties","getContextOverflowInfo","initializeModel"],"sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport {\n CallbackManager,\n CallbackManagerForLLMRun,\n type Callbacks,\n} from '@langchain/core/callbacks/manager';\nimport { getCallbackManagerForConfig } from '@langchain/core/runnables';\nimport type { Serialized } from '@langchain/core/load/serializable';\nimport type { ChatGeneration } from '@langchain/core/outputs';\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 { ContextOverflowContext } from '@/utils/errors';\nimport type * as t from '@/types';\nimport {\n projectCacheControlledToolOutputsToText,\n projectComputerCallOutputsToText,\n projectOpenAIChatToolMessageContent,\n projectOpenAIResponsesToolMessageContent,\n projectOpenRouterToolMessageContent,\n projectSingleTextToolOutputsToText,\n projectStructuredToolOutputsToText,\n projectToolStreamContentForProvider,\n} from '@/messages/core';\nimport {\n stripAnthropicCacheControl,\n stripBedrockCacheControl,\n} from '@/messages/cache';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { assertNotTruncatedToolCall } from '@/llm/truncation';\nimport { Constants, ContentTypes, GraphEvents, Providers } from '@/common';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { appendCallbacks } from '@/utils/callbacks';\nimport { safeDispatchCustomEvent } from '@/utils/events';\nimport { getContextOverflowInfo } from '@/utils/errors';\nimport {\n modifyDeltaProperties,\n coalesceAdjacentUserTurns,\n strictAlternationProviders,\n appendPredecessorHandoffCue,\n removePredecessorHandoffCue,\n} from '@/messages';\nimport { canSealPreempt } from '@/llm/preempt';\nimport { ChatModelStreamHandler, dispatchesChatModelStream } from '@/stream';\nimport { initializeModel } from '@/llm/init';\nimport { isAnthropicLike, isOpenAILike } from '@/utils/llm';\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\nexport function usesNativeOpenAIResponses(\n model: t.ChatModel,\n provider: Providers,\n callOptions?: unknown\n): boolean {\n if (!isOpenAILike(provider)) {\n return false;\n }\n let candidate: unknown = model;\n let effectiveCallOptions = callOptions;\n const seen = new Set<object>();\n for (let depth = 0; depth < 20; depth++) {\n if (candidate == null || typeof candidate !== 'object') {\n return false;\n }\n if (seen.has(candidate)) {\n return false;\n }\n seen.add(candidate);\n const runnable = candidate as {\n _useResponsesApi?: (options?: unknown) => boolean;\n bound?: unknown;\n defaultOptions?: unknown;\n last?: unknown;\n constructor?: { name?: unknown };\n };\n try {\n if (\n runnable.defaultOptions != null &&\n typeof runnable.defaultOptions === 'object' &&\n !Array.isArray(runnable.defaultOptions) &&\n effectiveCallOptions != null &&\n typeof effectiveCallOptions === 'object' &&\n !Array.isArray(effectiveCallOptions)\n ) {\n effectiveCallOptions = {\n ...(runnable.defaultOptions as Record<string, unknown>),\n ...(effectiveCallOptions as Record<string, unknown>),\n };\n } else if (effectiveCallOptions == null) {\n effectiveCallOptions = runnable.defaultOptions;\n }\n if (\n runnable._useResponsesApi?.(effectiveCallOptions) === true ||\n runnable._useResponsesApi?.(undefined) === true\n ) {\n return true;\n }\n } catch {\n // Continue through RunnableSequence/RunnableBinding wrappers.\n }\n if (\n typeof runnable.constructor?.name === 'string' &&\n runnable.constructor.name.includes('Responses')\n ) {\n return true;\n }\n if (runnable.last != null && typeof runnable.last === 'object') {\n candidate = runnable.last;\n continue;\n }\n if (runnable.bound != null && typeof runnable.bound === 'object') {\n candidate = runnable.bound;\n continue;\n }\n return false;\n }\n return false;\n}\n\n/**\n * Produces the exact provider-facing message representation before a model\n * adapter serializes it. This is shared by invocation and Graph's final budget\n * guard so structured tool output cannot grow after the payload was measured.\n */\nexport function projectMessagesForProvider({\n model,\n messages,\n provider,\n maxToolResultChars,\n callOptions,\n}: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n maxToolResultChars?: number;\n callOptions?: unknown;\n}): BaseMessage[] {\n const providerInputMessages = projectToolStreamContentForProvider(messages);\n if (usesNativeOpenAIResponses(model, provider, callOptions)) {\n return projectOpenAIResponsesToolMessageContent(\n stripAnthropicCacheControl(\n stripBedrockCacheControl(providerInputMessages)\n ),\n maxToolResultChars\n );\n }\n if (provider === Providers.OPENROUTER) {\n return projectComputerCallOutputsToText(\n projectOpenRouterToolMessageContent(\n stripBedrockCacheControl(providerInputMessages),\n maxToolResultChars\n )\n );\n }\n if (isOpenAILike(provider)) {\n return projectComputerCallOutputsToText(\n projectOpenAIChatToolMessageContent(\n stripAnthropicCacheControl(\n stripBedrockCacheControl(providerInputMessages)\n ),\n maxToolResultChars\n )\n );\n }\n if (provider === Providers.ANTHROPIC) {\n return projectComputerCallOutputsToText(\n projectSingleTextToolOutputsToText(\n stripBedrockCacheControl(providerInputMessages),\n maxToolResultChars\n )\n );\n }\n if (provider === Providers.BEDROCK) {\n return stripAnthropicCacheControl(\n projectComputerCallOutputsToText(\n projectCacheControlledToolOutputsToText(\n providerInputMessages,\n maxToolResultChars\n )\n )\n );\n }\n return projectComputerCallOutputsToText(\n projectStructuredToolOutputsToText(\n projectSingleTextToolOutputsToText(\n stripAnthropicCacheControl(\n stripBedrockCacheControl(providerInputMessages)\n ),\n maxToolResultChars\n ),\n maxToolResultChars\n )\n );\n}\n\n/**\n * The registered handler that owns content-part dispatch, if any.\n *\n * Detected by brand rather than by `instanceof`: a host that registers\n * `new ChatModelStreamHandler()` to opt out of sealing gets wrapped by\n * `createRunHandlers` on every `AgentSession` run, and by\n * `composeEventHandlers` on a key collision. Both wrappers forward to the same\n * dispatcher while failing an identity check, so an identity test would\n * silently revoke the opt-out documented on `StreamPreemption`.\n */\nfunction getRegisteredDefaultChatStreamHandler(\n context?: InvokeContext\n): t.EventHandler | undefined {\n const handler = context?.handlerRegistry?.getHandler(\n GraphEvents.CHAT_MODEL_STREAM\n );\n return dispatchesChatModelStream(handler) ? 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\n/**\n * Best-effort output-token count for a sealed turn, used only when the\n * provider never got to send its usage chunk.\n */\nfunction countSealedTokens(\n context: InvokeContext | undefined,\n metadata: Record<string, unknown> | undefined,\n messages: BaseMessage[]\n): number | undefined {\n try {\n const counter = context?.getAgentContext(metadata).tokenCounter;\n if (counter == null) {\n return undefined;\n }\n let total = 0;\n for (const message of messages) {\n total += counter(message);\n }\n return total;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Instruction overhead the provider processed but that never appears in the\n * message array: `createCallModel` pipes the model through\n * `agentContext.systemRunnable` and binds tool schemas AFTER `messages` is\n * formed, so the system prompt, dynamic instructions, summary and tool\n * schemas are all billed yet invisible here.\n *\n * Read per-node via `getAgentContext(metadata)` rather than the graph-level\n * accessor, which is hardcoded to `defaultAgentId` and would report the wrong\n * agent's overhead in a `MultiAgentGraph`.\n */\nfunction sealedInstructionOverhead(\n context: InvokeContext | undefined,\n metadata: Record<string, unknown> | undefined\n): number {\n try {\n const agentContext = context?.getAgentContext(metadata);\n return (\n agentContext?.resolvedInstructionOverhead ??\n agentContext?.instructionTokens ??\n 0\n );\n } catch {\n return 0;\n }\n}\n\n/**\n * Best-effort usage for a turn the provider never got to bill us for.\n *\n * The prompt matters as much as the completion: the provider processed the\n * ENTIRE prompt — messages plus instruction overhead — before we sealed, and\n * every resume re-sends it, so under-counting input hides the expensive half\n * of a preempted run.\n *\n * ESTIMATE, NOT MEASUREMENT. Messages are counted with the host's tokenizer\n * rather than the provider's, and `toolSchemaTokens` applies a heuristic\n * multiplier. It is also an over-count on the fallback path, where\n * `tryFallbackProviders` builds a bare model with no `systemRunnable` pipe so\n * the system prompt genuinely is not sent. Accepted rather than threaded\n * through a flag: only the fallback-plus-seal combination is affected, and an\n * over-count is safer than the previous fabricated `input_tokens: 0`.\n *\n * Marked `estimated_usage` so calibration can refuse to learn from it — a\n * ratio derived from the same counter that produced the estimate is\n * self-consistent by construction and would drag a provider's real\n * calibration toward 1.0.\n */\nfunction synthesizeSealedUsage(\n context: InvokeContext | undefined,\n chunk: AIMessageChunk,\n prompt: BaseMessage[],\n metadata: Record<string, unknown> | undefined\n): void {\n if (chunk.usage_metadata != null) {\n return;\n }\n const outputTokens = countSealedTokens(context, metadata, [chunk]);\n if (outputTokens == null) {\n return;\n }\n const inputTokens =\n (countSealedTokens(context, metadata, prompt) ?? 0) +\n sealedInstructionOverhead(context, metadata);\n chunk.usage_metadata = {\n input_tokens: inputTokens,\n output_tokens: outputTokens,\n total_tokens: inputTokens + outputTokens,\n };\n chunk.response_metadata = {\n ...chunk.response_metadata,\n estimated_usage: true,\n };\n}\n\nfunction getMessageText(chunk: AIMessageChunk): string {\n if (typeof chunk.content === 'string') {\n return chunk.content;\n }\n let text = '';\n for (const block of chunk.content) {\n if (block.type === ContentTypes.TEXT) {\n const value = block[ContentTypes.TEXT];\n if (typeof value === 'string') {\n text += value;\n }\n }\n }\n return text;\n}\n\n/**\n * Ends the real model run for a turn that was sealed mid-stream.\n *\n * Mandatory, not cosmetic. `@langchain/core`'s `_streamIterator` calls\n * `handleLLMEnd` after its try/catch with no `finally`, so breaking out of the\n * consumer's `for await` produces a *return* completion that fires neither\n * `handleLLMError` nor `handleLLMEnd`. The run would stay open in every\n * callback handler: the host records no usage — and since each seal re-sends\n * the whole prompt, N preemptions cost N unrecorded prompts — while LangSmith\n * and Langfuse hold a span that never closes.\n *\n * `runId` cannot be dictated from here (the bound runnable consumes\n * `config.runId` for its own run and hands the chat model a fresh one), but it\n * can be OBSERVED: the capture handler installed at the `model.stream` call\n * records it from `handleChatModelStart`, which fires before the first chunk.\n * Rebuilding the manager against that id closes the real run, and the host's\n * `on_chat_model_end` then arrives through the ordinary `streamEvents` path.\n *\n * Falls back to a custom-event dispatch if the id was never observed, so the\n * host still records usage even when the native close is unavailable.\n */\n/**\n * Every callbacks source the real model run would compose beyond the per-call\n * config. `model` here is whatever `createCallModel` produced — with tools\n * that is `bindTools(...)`'s `RunnableBinding`, and a system runnable pipes a\n * `RunnableSequence` on top — while `clientOptions.callbacks` lives on the\n * chat model at the BOTTOM of that stack. Walks `bound` (bindings) and\n * `last`/`steps` (sequences), collecting each wrapper's own `callbacks` and\n * any binding-config callbacks along the way, since the binding merges its\n * config into the call before the chat model composes.\n */\nfunction collectModelCallbackSources(model: unknown): Callbacks[] {\n const sources: Callbacks[] = [];\n const seen = new Set<unknown>();\n let current: unknown = model;\n while (current != null && typeof current === 'object' && !seen.has(current)) {\n seen.add(current);\n const wrapper = current as {\n callbacks?: Callbacks;\n config?: { callbacks?: Callbacks };\n bound?: unknown;\n last?: unknown;\n steps?: unknown[];\n };\n if (wrapper.callbacks != null) {\n sources.push(wrapper.callbacks);\n }\n if (wrapper.config?.callbacks != null) {\n sources.push(wrapper.config.callbacks);\n }\n current =\n wrapper.bound ??\n wrapper.last ??\n (Array.isArray(wrapper.steps)\n ? wrapper.steps[wrapper.steps.length - 1]\n : undefined);\n }\n return sources;\n}\n\n/**\n * The serving model's id, read through the same wrapper stack\n * `collectModelCallbackSources` walks — `bindTools` returns a\n * `RunnableBinding` and a system runnable pipes a `RunnableSequence`, and\n * neither exposes the chat model's `model` at the top level.\n */\nexport function resolveServingModelId(model: unknown): string | undefined {\n const seen = new Set<unknown>();\n let current: unknown = model;\n while (current != null && typeof current === 'object' && !seen.has(current)) {\n seen.add(current);\n const wrapper = current as {\n model?: unknown;\n bound?: unknown;\n last?: unknown;\n steps?: unknown[];\n };\n if (typeof wrapper.model === 'string' && wrapper.model !== '') {\n return wrapper.model;\n }\n current =\n wrapper.bound ??\n wrapper.last ??\n (Array.isArray(wrapper.steps)\n ? wrapper.steps[wrapper.steps.length - 1]\n : undefined);\n }\n return undefined;\n}\n\nasync function endSealedModelRun(\n context: InvokeContext | undefined,\n chunk: AIMessageChunk,\n prompt: BaseMessage[],\n llmRunId: string | undefined,\n config?: RunnableConfig,\n model?: t.ChatModel\n): Promise<void> {\n const metadata = config?.metadata as Record<string, unknown> | undefined;\n synthesizeSealedUsage(context, chunk, prompt, metadata);\n if (llmRunId != null) {\n try {\n let callbackManager = await getCallbackManagerForConfig(config);\n /**\n * The real model run composes the per-call config's callbacks WITH the\n * model's own (`CallbackManager.configure(config.callbacks,\n * this.callbacks, …)` in `@langchain/core`'s base chat model), so a\n * handler supplied via `clientOptions.callbacks` received\n * `handleChatModelStart` for this run. Rebuilding from the config alone\n * would close the run for every handler EXCEPT those — leaving their\n * span open forever. Composed the same way the real run composes:\n * model callbacks appended non-inheritable, parent run id preserved by\n * `copy`, tracers deduped by `configure`.\n */\n for (const source of collectModelCallbackSources(model)) {\n callbackManager =\n CallbackManager.configure(callbackManager ?? undefined, source) ??\n callbackManager;\n }\n if (callbackManager != null) {\n const runManager = new CallbackManagerForLLMRun(\n llmRunId,\n callbackManager.handlers,\n callbackManager.inheritableHandlers,\n callbackManager.tags,\n callbackManager.inheritableTags,\n callbackManager.metadata,\n callbackManager.inheritableMetadata,\n callbackManager.getParentRunId()\n );\n const generation: ChatGeneration = {\n text: getMessageText(chunk),\n message: chunk,\n };\n await runManager.handleLLMEnd({\n generations: [[generation]],\n llmOutput: {},\n });\n return;\n }\n } catch (e) {\n /**\n * A sealed answer that reaches the user is worth more than a tidy\n * trace. Fall through to the custom event rather than failing the run.\n */\n // eslint-disable-next-line no-console\n console.warn(\n '[attemptInvoke] Native close of the sealed model run failed; falling back to a custom event:',\n e instanceof Error ? e.message : e\n );\n }\n }\n await safeDispatchCustomEvent(\n GraphEvents.CHAT_MODEL_END,\n { output: chunk },\n config\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 invocationMessages = projectMessagesForProvider({\n model,\n messages,\n provider,\n callOptions: config,\n });\n const registry = context?.getOrCreateToolOutputRegistry();\n const runId = config?.configurable?.run_id as string | undefined;\n const annotated = annotateMessagesForLLM(\n invocationMessages,\n registry,\n runId\n );\n /**\n * Keyed on the provider ACTUALLY serving this call, not the agent's primary.\n * `createCallModel` normalizes for the primary, but `tryFallbackProviders`\n * re-sends the same array — so an OpenAI primary that fails after a boundary\n * injected two human turns would hand a Bedrock or Mistral fallback the\n * consecutive user turns those APIs reject, and the recovery request would\n * fail for a reason unrelated to the original failure.\n *\n * `attemptInvoke` is the single funnel for primary, fallback and\n * summarization calls, so applying it here covers all three. Idempotent, so\n * the primary simply re-runs a no-op over already-coalesced messages.\n */\n /**\n * Serving-provider re-keying for the predecessor handoff cue (#345). The\n * PRIMARY's cue is baked in createCallModel's measured transform stage —\n * appending after measurement could push a just-fits prompt over budget —\n * so this funnel only corrects for fallbacks crossing provider families:\n * a tolerant primary falling back to a Claude surface gains the cue here,\n * and an Anthropic primary falling back to OpenAI/Mistral/Nova has the\n * Claude-only synthetic turn stripped. Both helpers are identity on their\n * no-op paths, so the primary's own pass re-runs for free.\n *\n * The serving model id is read through the wrapper stack (`bindTools`'\n * binding, a system runnable's sequence) — a wrapper's top-level `.model`\n * is undefined, and `isAnthropicLike` would otherwise default a wrapped\n * Bedrock-Nova model to Claude. The context cast is widened deliberately:\n * the type says every context is a full Graph, but summarization passes\n * none and long-standing tests pass partial stubs.\n */\n const isRunProduced = (\n context as\n | { isRunProducedMessage?: (message: BaseMessage) => boolean }\n | undefined\n )?.isRunProducedMessage;\n const cued = isAnthropicLike(provider, {\n model: resolveServingModelId(model),\n })\n ? appendPredecessorHandoffCue(\n annotated,\n isRunProduced == null\n ? undefined\n : (message): boolean => isRunProduced.call(context, message)\n )\n : removePredecessorHandoffCue(annotated);\n const messagesForProvider = strictAlternationProviders.has(provider)\n ? coalesceAdjacentUserTurns(cued)\n : cued;\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 /**\n * Observed, not dictated. `handleChatModelStart` fires with the chat\n * model's real run id before the first chunk, which is the only way to\n * name the run a seal has to close — pinning `config.runId` does not\n * survive the bound runnable. Installed only when preemption is\n * configured, so a run that cannot seal carries no extra handler.\n */\n let sealedRunId: string | undefined;\n const streamConfig =\n context?.preemption == null\n ? config\n : {\n ...config,\n callbacks: appendCallbacks(config.callbacks, [\n {\n handleChatModelStart: (\n _llm: Serialized,\n _messages: BaseMessage[][],\n runId: string\n ): void => {\n sealedRunId ??= runId;\n },\n },\n ]),\n };\n const stream = await model.stream(messagesForProvider, streamConfig);\n let finalChunk: AIMessageChunk | undefined;\n let preempted = false;\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 * Only this loop may seal. The registered-handler branch below\n * dispatches through `run.ts`'s decoupled `streamEvents` consumer,\n * which can lag the accumulated chunk — sealing there would let the\n * host index a content part the user has not been shown yet.\n */\n /**\n * Cheap poll first, shape check second, budget claim last. The claim\n * is what makes this safe under a parallel `MultiAgentGraph`: several\n * agents share one graph and can each see the poll as true, but only\n * one can take the slot, and a chunk that cannot seal never spends it.\n */\n if (\n context?.shouldPreemptStream() === true &&\n canSealPreempt(finalChunk) &&\n context.claimPreemptSeal()\n ) {\n preempted = true;\n break;\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 (preempted && finalChunk != null) {\n finalChunk.response_metadata = {\n ...finalChunk.response_metadata,\n preempted: true,\n };\n await endSealedModelRun(\n context,\n finalChunk,\n messagesForProvider,\n sealedRunId,\n config,\n model\n );\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 * Identifies which fallback produced an error, so a caller planning a\n * recovery can reason about the client that actually failed rather than the\n * primary's configuration — their context windows and output allowances\n * differ, which is the whole reason a fallback exists.\n */\nexport interface FallbackErrorContext {\n provider: Providers;\n clientOptions?: t.ClientOptions;\n maxContextTokens?: number;\n}\n\nexport interface FallbackOverflowCandidate {\n error: unknown;\n context: FallbackErrorContext;\n}\n\nconst fallbackErrorContexts = new WeakMap<object, FallbackErrorContext>();\nconst fallbackOverflowCandidates = new WeakMap<\n object,\n FallbackOverflowCandidate[]\n>();\n\nfunction attachFallbackErrorContext(\n error: unknown,\n fallbackContext: FallbackErrorContext\n): void {\n if (typeof error !== 'object' || error === null) {\n return;\n }\n fallbackErrorContexts.set(error, fallbackContext);\n}\n\n/** Reads back the fallback attribution attached by `tryFallbackProviders`. */\nexport function getFallbackErrorContext(\n error: unknown\n): FallbackErrorContext | undefined {\n if (typeof error !== 'object' || error === null) {\n return undefined;\n }\n return fallbackErrorContexts.get(error);\n}\n\n/** Returns every fallback overflow retained from an exhausted provider chain. */\nexport function getFallbackOverflowCandidates(\n error: unknown\n): FallbackOverflowCandidate[] {\n if (typeof error !== 'object' || error === null) {\n return [];\n }\n return [...(fallbackOverflowCandidates.get(error) ?? [])];\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 *\n * When every fallback fails, a context overflow among them is thrown in\n * preference to whichever failure happened to come last. An overflow is the\n * one failure the caller can act on — it compacts and retries — and losing it\n * behind a later unrelated error would surface a dead end instead. Ordinary\n * failures still throw last-error-wins.\n */\nexport async function tryFallbackProviders({\n fallbacks,\n tools,\n messages,\n config,\n primaryError,\n context,\n onChunk,\n overflowContext,\n prepareProviderMessages,\n}: {\n fallbacks: t.FallbackConfig[];\n tools?: t.GraphTools;\n messages: BaseMessage[];\n config?: RunnableConfig;\n primaryError: unknown;\n context?: InvokeContext;\n onChunk?: OnChunk;\n /**\n * Prompt-size corroboration for signatures that are not self-describing.\n * Vertex AI's overflow is a bare `400` with no reason, so without this a\n * fallback that overflows is indistinguishable from any other 400 and would\n * be dropped in favour of whichever failure came last.\n */\n overflowContext?: ContextOverflowContext;\n /**\n * Optional final payload guard used by Graph. It receives the initialized,\n * tool-bound fallback model so Responses-vs-Chat projection is exact before\n * the fallback request is measured and sent.\n */\n prepareProviderMessages?: (input: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n clientOptions?: t.ClientOptions;\n maxContextTokens?: number;\n config?: RunnableConfig;\n }) => BaseMessage[] | Promise<BaseMessage[]>;\n}): Promise<Partial<t.BaseGraphState> | undefined> {\n const isOverflow = (\n error: unknown,\n contextOverride = overflowContext\n ): boolean => getContextOverflowInfo(error, contextOverride) != null;\n let lastError: unknown = primaryError;\n /**\n * Tracked apart from the primary's overflow. A caller reaching this\n * function with an overflowing primary has already failed to recover from\n * it, so a fallback overflow — which may sit against a different window and\n * output allowance — is the more useful of the two to surface.\n */\n const overflowCandidates: FallbackOverflowCandidate[] = [];\n const primaryOverflowError: unknown = isOverflow(primaryError)\n ? primaryError\n : undefined;\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 fallbackMessages =\n (await prepareProviderMessages?.({\n model: fbModel as t.ChatModel,\n messages,\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n maxContextTokens: fb.maxContextTokens,\n config: fbConfig,\n })) ?? messages;\n const result = await attemptInvoke(\n {\n model: fbModel as t.ChatModel,\n messages: fallbackMessages,\n provider: fb.provider,\n context,\n onChunk,\n },\n fbConfig\n );\n return result;\n } catch (e) {\n lastError = e;\n const fallbackOverflowContext: ContextOverflowContext = {\n provider: fb.provider,\n maxContextTokens: fb.maxContextTokens,\n ...(overflowContext?.provider === fb.provider\n ? {\n estimatedPromptTokens: overflowContext.estimatedPromptTokens,\n }\n : {}),\n };\n if (isOverflow(e, fallbackOverflowContext)) {\n const errorContext: FallbackErrorContext = {\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n maxContextTokens: fb.maxContextTokens,\n };\n attachFallbackErrorContext(e, errorContext);\n overflowCandidates.push({ error: e, context: errorContext });\n }\n continue;\n }\n }\n /**\n * Preference order: a fallback overflow, then the primary's overflow, then\n * whichever failure came last. An overflow is the only one of the three a\n * caller can act on, and the fallback's carries the client attribution that\n * makes a correct retry budget possible.\n */\n const preferred =\n overflowCandidates[0]?.error ?? primaryOverflowError ?? lastError;\n if (\n overflowCandidates.length > 0 &&\n typeof preferred === 'object' &&\n preferred !== null\n ) {\n fallbackOverflowCandidates.set(preferred, overflowCandidates);\n }\n if (preferred !== undefined) {\n throw preferred;\n }\n return undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"invoke.cjs","names":["isOpenAILike","projectToolStreamContentForProvider","projectOpenAIResponsesToolMessageContent","stripAnthropicCacheControl","stripBedrockCacheControl","projectComputerCallOutputsToText","projectOpenRouterToolMessageContent","projectOpenAIChatToolMessageContent","projectSingleTextToolOutputsToText","projectCacheControlledToolOutputsToText","projectStructuredToolOutputsToText","dispatchesChatModelStream","AIMessageChunk","CallbackManager","CallbackManagerForLLMRun","safeDispatchCustomEvent","STREAM_LIMIT_ATTEMPT_KEY","streamLimitAccountingEnabled","resolveGenerationKey","annotateMessagesForLLM","isAnthropicLike","appendPredecessorHandoffCue","removePredecessorHandoffCue","strictAlternationProviders","coalesceAdjacentUserTurns","appendCallbacks","StreamLimitExceededError","ChatModelStreamHandler","canSealPreempt","STREAM_LIMIT_REDISPATCH_KEY","manualToolStreamProviders","modifyDeltaProperties","getContextOverflowInfo","initializeModel"],"sources":["../../../src/llm/invoke.ts"],"sourcesContent":["import { concat } from '@langchain/core/utils/stream';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport { getCallbackManagerForConfig } from '@langchain/core/runnables';\nimport {\n CallbackManager,\n CallbackManagerForLLMRun,\n type Callbacks,\n} from '@langchain/core/callbacks/manager';\nimport type { Serialized } from '@langchain/core/load/serializable';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type { ChatGeneration } from '@langchain/core/outputs';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';\nimport type { StreamLimitState } from '@/llm/streamLimits';\nimport type { ContextOverflowContext } from '@/utils/errors';\nimport type * as t from '@/types';\nimport {\n projectCacheControlledToolOutputsToText,\n projectComputerCallOutputsToText,\n projectOpenAIChatToolMessageContent,\n projectOpenAIResponsesToolMessageContent,\n projectOpenRouterToolMessageContent,\n projectSingleTextToolOutputsToText,\n projectStructuredToolOutputsToText,\n projectToolStreamContentForProvider,\n} from '@/messages/core';\nimport {\n modifyDeltaProperties,\n coalesceAdjacentUserTurns,\n strictAlternationProviders,\n appendPredecessorHandoffCue,\n removePredecessorHandoffCue,\n} from '@/messages';\nimport {\n stripAnthropicCacheControl,\n stripBedrockCacheControl,\n} from '@/messages/cache';\nimport { ChatModelStreamHandler, dispatchesChatModelStream } from '@/stream';\nimport { Constants, ContentTypes, GraphEvents, Providers } from '@/common';\nimport {\n enforceStreamLimitsForWireChunk,\n registerActiveStreamLimitGeneration,\n releaseStreamLimitGeneration,\n resolveGenerationKey,\n streamLimitAccountingEnabled,\n StreamLimitExceededError,\n STREAM_LIMIT_REDISPATCH_KEY,\n STREAM_LIMIT_ATTEMPT_KEY,\n} from '@/llm/streamLimits';\nimport { annotateMessagesForLLM } from '@/tools/toolOutputReferences';\nimport { assertNotTruncatedToolCall } from '@/llm/truncation';\nimport { manualToolStreamProviders } from '@/llm/providers';\nimport { isAnthropicLike, isOpenAILike } from '@/utils/llm';\nimport { safeDispatchCustomEvent } from '@/utils/events';\nimport { getContextOverflowInfo } from '@/utils/errors';\nimport { appendCallbacks } from '@/utils/callbacks';\nimport { canSealPreempt } from '@/llm/preempt';\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 *\n * `metadata` is the attempt's callback metadata (carrying the provider and\n * stream-limit attempt stamps), so consumers that count against the stream\n * limits key each model attempt separately.\n */\nexport type OnChunk = (\n chunk: AIMessageChunk,\n metadata?: Record<string, unknown>\n) => void | Promise<void>;\n\n/** Unique per-model-attempt sequence; see the stamp in `attemptInvoke`. */\nlet streamLimitAttemptSeq = 0;\n\nexport function usesNativeOpenAIResponses(\n model: t.ChatModel,\n provider: Providers,\n callOptions?: unknown\n): boolean {\n if (!isOpenAILike(provider)) {\n return false;\n }\n let candidate: unknown = model;\n let effectiveCallOptions = callOptions;\n const seen = new Set<object>();\n for (let depth = 0; depth < 20; depth++) {\n if (candidate == null || typeof candidate !== 'object') {\n return false;\n }\n if (seen.has(candidate)) {\n return false;\n }\n seen.add(candidate);\n const runnable = candidate as {\n _useResponsesApi?: (options?: unknown) => boolean;\n bound?: unknown;\n defaultOptions?: unknown;\n last?: unknown;\n constructor?: { name?: unknown };\n };\n try {\n if (\n runnable.defaultOptions != null &&\n typeof runnable.defaultOptions === 'object' &&\n !Array.isArray(runnable.defaultOptions) &&\n effectiveCallOptions != null &&\n typeof effectiveCallOptions === 'object' &&\n !Array.isArray(effectiveCallOptions)\n ) {\n effectiveCallOptions = {\n ...(runnable.defaultOptions as Record<string, unknown>),\n ...(effectiveCallOptions as Record<string, unknown>),\n };\n } else if (effectiveCallOptions == null) {\n effectiveCallOptions = runnable.defaultOptions;\n }\n if (\n runnable._useResponsesApi?.(effectiveCallOptions) === true ||\n runnable._useResponsesApi?.(undefined) === true\n ) {\n return true;\n }\n } catch {\n // Continue through RunnableSequence/RunnableBinding wrappers.\n }\n if (\n typeof runnable.constructor?.name === 'string' &&\n runnable.constructor.name.includes('Responses')\n ) {\n return true;\n }\n if (runnable.last != null && typeof runnable.last === 'object') {\n candidate = runnable.last;\n continue;\n }\n if (runnable.bound != null && typeof runnable.bound === 'object') {\n candidate = runnable.bound;\n continue;\n }\n return false;\n }\n return false;\n}\n\n/**\n * Produces the exact provider-facing message representation before a model\n * adapter serializes it. This is shared by invocation and Graph's final budget\n * guard so structured tool output cannot grow after the payload was measured.\n */\nexport function projectMessagesForProvider({\n model,\n messages,\n provider,\n maxToolResultChars,\n callOptions,\n}: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n maxToolResultChars?: number;\n callOptions?: unknown;\n}): BaseMessage[] {\n const nativeOpenAIResponses = usesNativeOpenAIResponses(\n model,\n provider,\n callOptions\n );\n const providerInputMessages = projectToolStreamContentForProvider(\n messages,\n nativeOpenAIResponses ? 'native' : 'fallback',\n maxToolResultChars\n );\n if (nativeOpenAIResponses) {\n return projectOpenAIResponsesToolMessageContent(\n stripAnthropicCacheControl(\n stripBedrockCacheControl(providerInputMessages)\n ),\n maxToolResultChars\n );\n }\n if (provider === Providers.OPENROUTER) {\n return projectComputerCallOutputsToText(\n projectOpenRouterToolMessageContent(\n stripBedrockCacheControl(providerInputMessages),\n maxToolResultChars\n )\n );\n }\n if (isOpenAILike(provider)) {\n return projectComputerCallOutputsToText(\n projectOpenAIChatToolMessageContent(\n stripAnthropicCacheControl(\n stripBedrockCacheControl(providerInputMessages)\n ),\n maxToolResultChars\n )\n );\n }\n if (provider === Providers.ANTHROPIC) {\n return projectComputerCallOutputsToText(\n projectSingleTextToolOutputsToText(\n stripBedrockCacheControl(providerInputMessages),\n maxToolResultChars\n )\n );\n }\n if (provider === Providers.BEDROCK) {\n return stripAnthropicCacheControl(\n projectComputerCallOutputsToText(\n projectCacheControlledToolOutputsToText(\n providerInputMessages,\n maxToolResultChars\n )\n )\n );\n }\n return projectComputerCallOutputsToText(\n projectStructuredToolOutputsToText(\n projectSingleTextToolOutputsToText(\n stripAnthropicCacheControl(\n stripBedrockCacheControl(providerInputMessages)\n ),\n maxToolResultChars\n ),\n maxToolResultChars\n )\n );\n}\n\n/**\n * The registered handler that owns content-part dispatch, if any.\n *\n * Detected by brand rather than by `instanceof`: a host that registers\n * `new ChatModelStreamHandler()` to opt out of sealing gets wrapped by\n * `createRunHandlers` on every `AgentSession` run, and by\n * `composeEventHandlers` on a key collision. Both wrappers forward to the same\n * dispatcher while failing an identity check, so an identity test would\n * silently revoke the opt-out documented on `StreamPreemption`.\n */\nfunction getRegisteredDefaultChatStreamHandler(\n context?: InvokeContext\n): t.EventHandler | undefined {\n const handler = context?.handlerRegistry?.getHandler(\n GraphEvents.CHAT_MODEL_STREAM\n );\n return dispatchesChatModelStream(handler) ? 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\n/**\n * Best-effort output-token count for a sealed turn, used only when the\n * provider never got to send its usage chunk.\n */\nfunction countSealedTokens(\n context: InvokeContext | undefined,\n metadata: Record<string, unknown> | undefined,\n messages: BaseMessage[]\n): number | undefined {\n try {\n const counter = context?.getAgentContext(metadata).tokenCounter;\n if (counter == null) {\n return undefined;\n }\n let total = 0;\n for (const message of messages) {\n total += counter(message);\n }\n return total;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Instruction overhead the provider processed but that never appears in the\n * message array: `createCallModel` pipes the model through\n * `agentContext.systemRunnable` and binds tool schemas AFTER `messages` is\n * formed, so the system prompt, dynamic instructions, summary and tool\n * schemas are all billed yet invisible here.\n *\n * Read per-node via `getAgentContext(metadata)` rather than the graph-level\n * accessor, which is hardcoded to `defaultAgentId` and would report the wrong\n * agent's overhead in a `MultiAgentGraph`.\n */\nfunction sealedInstructionOverhead(\n context: InvokeContext | undefined,\n metadata: Record<string, unknown> | undefined\n): number {\n try {\n const agentContext = context?.getAgentContext(metadata);\n return (\n agentContext?.resolvedInstructionOverhead ??\n agentContext?.instructionTokens ??\n 0\n );\n } catch {\n return 0;\n }\n}\n\n/**\n * Best-effort usage for a turn the provider never got to bill us for.\n *\n * The prompt matters as much as the completion: the provider processed the\n * ENTIRE prompt — messages plus instruction overhead — before we sealed, and\n * every resume re-sends it, so under-counting input hides the expensive half\n * of a preempted run.\n *\n * ESTIMATE, NOT MEASUREMENT. Messages are counted with the host's tokenizer\n * rather than the provider's, and `toolSchemaTokens` applies a heuristic\n * multiplier. It is also an over-count on the fallback path, where\n * `tryFallbackProviders` builds a bare model with no `systemRunnable` pipe so\n * the system prompt genuinely is not sent. Accepted rather than threaded\n * through a flag: only the fallback-plus-seal combination is affected, and an\n * over-count is safer than the previous fabricated `input_tokens: 0`.\n *\n * Marked `estimated_usage` so calibration can refuse to learn from it — a\n * ratio derived from the same counter that produced the estimate is\n * self-consistent by construction and would drag a provider's real\n * calibration toward 1.0.\n */\nfunction synthesizeSealedUsage(\n context: InvokeContext | undefined,\n chunk: AIMessageChunk,\n prompt: BaseMessage[],\n metadata: Record<string, unknown> | undefined\n): void {\n if (chunk.usage_metadata != null) {\n return;\n }\n const outputTokens = countSealedTokens(context, metadata, [chunk]);\n if (outputTokens == null) {\n return;\n }\n const inputTokens =\n (countSealedTokens(context, metadata, prompt) ?? 0) +\n sealedInstructionOverhead(context, metadata);\n const usageMetadata = {\n input_tokens: inputTokens,\n output_tokens: outputTokens,\n total_tokens: inputTokens + outputTokens,\n };\n chunk.usage_metadata = usageMetadata;\n chunk.lc_kwargs.usage_metadata = usageMetadata;\n const responseMetadata = {\n ...chunk.response_metadata,\n estimated_usage: true,\n };\n chunk.response_metadata = responseMetadata;\n chunk.lc_kwargs.response_metadata = responseMetadata;\n}\n\nfunction getMessageText(chunk: AIMessageChunk): string {\n if (typeof chunk.content === 'string') {\n return chunk.content;\n }\n let text = '';\n for (const block of chunk.content) {\n if (block.type === ContentTypes.TEXT) {\n const value = block[ContentTypes.TEXT];\n if (typeof value === 'string') {\n text += value;\n }\n }\n }\n return text;\n}\n\n/**\n * Ends the real model run for a turn that was sealed mid-stream.\n *\n * Mandatory, not cosmetic. `@langchain/core`'s `_streamIterator` calls\n * `handleLLMEnd` after its try/catch with no `finally`, so breaking out of the\n * consumer's `for await` produces a *return* completion that fires neither\n * `handleLLMError` nor `handleLLMEnd`. The run would stay open in every\n * callback handler: the host records no usage — and since each seal re-sends\n * the whole prompt, N preemptions cost N unrecorded prompts — while LangSmith\n * and Langfuse hold a span that never closes.\n *\n * `runId` cannot be dictated from here (the bound runnable consumes\n * `config.runId` for its own run and hands the chat model a fresh one), but it\n * can be OBSERVED: the capture handler installed at the `model.stream` call\n * records it from `handleChatModelStart`, which fires before the first chunk.\n * Rebuilding the manager against that id closes the real run, and the host's\n * `on_chat_model_end` then arrives through the ordinary `streamEvents` path.\n *\n * Falls back to a custom-event dispatch if the id was never observed, so the\n * host still records usage even when the native close is unavailable.\n */\n/**\n * Every callbacks source the real model run would compose beyond the per-call\n * config. `model` here is whatever `createCallModel` produced — with tools\n * that is `bindTools(...)`'s `RunnableBinding`, and a system runnable pipes a\n * `RunnableSequence` on top — while `clientOptions.callbacks` lives on the\n * chat model at the BOTTOM of that stack. Walks `bound` (bindings) and\n * `last`/`steps` (sequences), collecting each wrapper's own `callbacks` and\n * any binding-config callbacks along the way, since the binding merges its\n * config into the call before the chat model composes.\n */\nfunction collectModelCallbackSources(model: unknown): Callbacks[] {\n const sources: Callbacks[] = [];\n const seen = new Set<unknown>();\n let current: unknown = model;\n while (current != null && typeof current === 'object' && !seen.has(current)) {\n seen.add(current);\n const wrapper = current as {\n callbacks?: Callbacks;\n config?: { callbacks?: Callbacks };\n bound?: unknown;\n last?: unknown;\n steps?: unknown[];\n };\n if (wrapper.callbacks != null) {\n sources.push(wrapper.callbacks);\n }\n if (wrapper.config?.callbacks != null) {\n sources.push(wrapper.config.callbacks);\n }\n current =\n wrapper.bound ??\n wrapper.last ??\n (Array.isArray(wrapper.steps)\n ? wrapper.steps[wrapper.steps.length - 1]\n : undefined);\n }\n return sources;\n}\n\n/**\n * The serving model's id, read through the same wrapper stack\n * `collectModelCallbackSources` walks — `bindTools` returns a\n * `RunnableBinding` and a system runnable pipes a `RunnableSequence`, and\n * neither exposes the chat model's `model` at the top level.\n */\nexport function resolveServingModelId(model: unknown): string | undefined {\n const seen = new Set<unknown>();\n let current: unknown = model;\n while (current != null && typeof current === 'object' && !seen.has(current)) {\n seen.add(current);\n const wrapper = current as {\n model?: unknown;\n bound?: unknown;\n last?: unknown;\n steps?: unknown[];\n };\n if (typeof wrapper.model === 'string' && wrapper.model !== '') {\n return wrapper.model;\n }\n current =\n wrapper.bound ??\n wrapper.last ??\n (Array.isArray(wrapper.steps)\n ? wrapper.steps[wrapper.steps.length - 1]\n : undefined);\n }\n return undefined;\n}\n\nasync function endSealedModelRun(\n context: InvokeContext | undefined,\n chunk: AIMessageChunk,\n prompt: BaseMessage[],\n llmRunId: string | undefined,\n config?: RunnableConfig,\n model?: t.ChatModel\n): Promise<void> {\n const metadata = config?.metadata as Record<string, unknown> | undefined;\n synthesizeSealedUsage(context, chunk, prompt, metadata);\n if (llmRunId != null) {\n try {\n let callbackManager = await getCallbackManagerForConfig(config);\n /**\n * The real model run composes the per-call config's callbacks WITH the\n * model's own (`CallbackManager.configure(config.callbacks,\n * this.callbacks, …)` in `@langchain/core`'s base chat model), so a\n * handler supplied via `clientOptions.callbacks` received\n * `handleChatModelStart` for this run. Rebuilding from the config alone\n * would close the run for every handler EXCEPT those — leaving their\n * span open forever. Composed the same way the real run composes:\n * model callbacks appended non-inheritable, parent run id preserved by\n * `copy`, tracers deduped by `configure`.\n */\n for (const source of collectModelCallbackSources(model)) {\n callbackManager =\n CallbackManager.configure(callbackManager ?? undefined, source) ??\n callbackManager;\n }\n if (callbackManager != null) {\n const runManager = new CallbackManagerForLLMRun(\n llmRunId,\n callbackManager.handlers,\n callbackManager.inheritableHandlers,\n callbackManager.tags,\n callbackManager.inheritableTags,\n callbackManager.metadata,\n callbackManager.inheritableMetadata,\n callbackManager.getParentRunId()\n );\n const generation: ChatGeneration = {\n text: getMessageText(chunk),\n message: chunk,\n };\n await runManager.handleLLMEnd({\n generations: [[generation]],\n llmOutput: {},\n });\n return;\n }\n } catch (e) {\n /**\n * A sealed answer that reaches the user is worth more than a tidy\n * trace. Fall through to the custom event rather than failing the run.\n */\n // eslint-disable-next-line no-console\n console.warn(\n '[attemptInvoke] Native close of the sealed model run failed; falling back to a custom event:',\n e instanceof Error ? e.message : e\n );\n }\n }\n await safeDispatchCustomEvent(\n GraphEvents.CHAT_MODEL_END,\n { output: chunk },\n config\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 */\ninterface AttemptInvokeParams {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n context?: InvokeContext;\n onChunk?: OnChunk;\n /** Accounting owner for callers that deliberately pass no `context`\n * (summarization) — used ONLY for the attempt's accounting lease, never\n * for charge claims. */\n streamLimitState?: StreamLimitState;\n}\n\n/**\n * One model attempt. Stamps the attempt identity into callback metadata\n * (see the generation-key notes in `streamLimits.ts`), leases the attempt's\n * accounting for its LIFETIME, and releases both from `finally`: retention\n * must follow the attempt — a cancellation-ignoring straggler keeps its\n * original budget no matter how many runs start and reset while it drains.\n */\nexport async function attemptInvoke(\n params: AttemptInvokeParams,\n config?: RunnableConfig\n): Promise<Partial<t.BaseGraphState>> {\n const stampedConfig: RunnableConfig = {\n ...config,\n metadata: {\n ...(config?.metadata ?? {}),\n [Constants.INVOKED_PROVIDER]: params.provider,\n /**\n * One `attemptInvoke` call is one model attempt; primary, fallback,\n * and retry attempts within a node otherwise share the same langgraph\n * metadata, so without a unique attempt stamp a fallback re-streaming\n * a tool call from scratch would be charged the failed primary's\n * partial bytes (or a same-named sibling fallback's) and could\n * falsely trip the stream limits. The stamp rides the same metadata\n * rebuild that already attributes the serving provider.\n */\n [STREAM_LIMIT_ATTEMPT_KEY]: ++streamLimitAttemptSeq,\n },\n };\n const rawLeaseTarget = params.context ?? params.streamLimitState;\n /** No lease when no guard can fire: the lease only protects accounting\n * entries, and fully disabled guards must allocate no bookkeeping at\n * all — per-attempt included. */\n const leaseTarget =\n rawLeaseTarget != null && streamLimitAccountingEnabled(rawLeaseTarget)\n ? rawLeaseTarget\n : undefined;\n const generationKey =\n leaseTarget != null\n ? resolveGenerationKey(\n stampedConfig.metadata as Record<string, unknown>\n )\n : undefined;\n if (leaseTarget != null && generationKey != null) {\n registerActiveStreamLimitGeneration(leaseTarget, generationKey);\n }\n try {\n return await attemptInvokeBody(params, stampedConfig);\n } finally {\n if (leaseTarget != null && generationKey != null) {\n releaseStreamLimitGeneration(leaseTarget, generationKey);\n }\n }\n}\n\nasync function attemptInvokeBody(\n {\n model,\n messages,\n provider,\n context,\n onChunk,\n }: AttemptInvokeParams,\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 invocationMessages = projectMessagesForProvider({\n model,\n messages,\n provider,\n callOptions: config,\n });\n const registry = context?.getOrCreateToolOutputRegistry();\n const runId = config.configurable?.run_id as string | undefined;\n const annotated = annotateMessagesForLLM(invocationMessages, registry, runId);\n /**\n * Keyed on the provider ACTUALLY serving this call, not the agent's primary.\n * `createCallModel` normalizes for the primary, but `tryFallbackProviders`\n * re-sends the same array — so an OpenAI primary that fails after a boundary\n * injected two human turns would hand a Bedrock or Mistral fallback the\n * consecutive user turns those APIs reject, and the recovery request would\n * fail for a reason unrelated to the original failure.\n *\n * `attemptInvoke` is the single funnel for primary, fallback and\n * summarization calls, so applying it here covers all three. Idempotent, so\n * the primary simply re-runs a no-op over already-coalesced messages.\n */\n /**\n * Serving-provider re-keying for the predecessor handoff cue (#345). The\n * PRIMARY's cue is baked in createCallModel's measured transform stage —\n * appending after measurement could push a just-fits prompt over budget —\n * so this funnel only corrects for fallbacks crossing provider families:\n * a tolerant primary falling back to a Claude surface gains the cue here,\n * and an Anthropic primary falling back to OpenAI/Mistral/Nova has the\n * Claude-only synthetic turn stripped. Both helpers are identity on their\n * no-op paths, so the primary's own pass re-runs for free.\n *\n * The serving model id is read through the wrapper stack (`bindTools`'\n * binding, a system runnable's sequence) — a wrapper's top-level `.model`\n * is undefined, and `isAnthropicLike` would otherwise default a wrapped\n * Bedrock-Nova model to Claude. The context cast is widened deliberately:\n * the type says every context is a full Graph, but summarization passes\n * none and long-standing tests pass partial stubs.\n */\n const isRunProduced = (\n context as\n | { isRunProducedMessage?: (message: BaseMessage) => boolean }\n | undefined\n )?.isRunProducedMessage;\n const cued = isAnthropicLike(provider, {\n model: resolveServingModelId(model),\n })\n ? appendPredecessorHandoffCue(\n annotated,\n isRunProduced == null\n ? undefined\n : (message): boolean => isRunProduced.call(context, message)\n )\n : removePredecessorHandoffCue(annotated);\n const messagesForProvider = strictAlternationProviders.has(provider)\n ? coalesceAdjacentUserTurns(cued)\n : cued;\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 if (model.stream) {\n /**\n * Observed, not dictated. `handleChatModelStart` fires with the chat\n * model's real run id before the first chunk, which is the only way to\n * name the run a seal has to close — pinning `config.runId` does not\n * survive the bound runnable. Installed only when preemption is\n * configured, so a run that cannot seal carries no extra handler.\n */\n let sealedRunId: string | undefined;\n const streamConfig =\n context?.preemption == null\n ? config\n : {\n ...config,\n callbacks: appendCallbacks(config.callbacks, [\n {\n handleChatModelStart: (\n _llm: Serialized,\n _messages: BaseMessage[][],\n runId: string\n ): void => {\n sealedRunId ??= runId;\n },\n },\n ]),\n };\n const stream = await model.stream(messagesForProvider, streamConfig);\n let finalChunk: AIMessageChunk | undefined;\n let preempted = false;\n const registeredStreamHandler =\n getRegisteredDefaultChatStreamHandler(context);\n /** A sibling's trip aborts the composed signal, but an adapter that\n * ignores cancellation keeps yielding — and text-only chunks with the\n * event cap off never throw in enforcement, so nothing else would stop\n * the drain. Checked on every yielded chunk in all three loops;\n * throwing closes the iterator and tears down the provider stream. */\n const throwIfBreakerTripped = (): void => {\n const signal = config.signal;\n if (\n signal?.aborted === true &&\n signal.reason instanceof StreamLimitExceededError\n ) {\n throw signal.reason;\n }\n };\n\n if (onChunk) {\n const attemptMetadata = config.metadata as\n | Record<string, unknown>\n | undefined;\n for await (const chunk of stream) {\n throwIfBreakerTripped();\n /** An onChunk consumer replaces the stream handler entirely, so\n * stream limits are enforced here for every such caller — public\n * package consumers get no other accounting. The internal\n * summarization onChunk charges producer-side itself and passes no\n * context, precisely so this claim and its own never stack. */\n if (context != null) {\n enforceStreamLimitsForWireChunk({\n graph: context,\n metadata: attemptMetadata,\n chunk,\n });\n }\n await onChunk(chunk, attemptMetadata);\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 throwIfBreakerTripped();\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 } else if (context != null) {\n /**\n * A replay-skipped chunk yields no handling chunk, and in this\n * local branch no `streamEvents` consumer judges the wire event\n * either — yet a cumulative OpenRouter replay can still carry\n * `tool_call_chunks` or complete `tool_calls` that are appended\n * below. Charge the full limits (event budget and argument bytes)\n * directly so neither cap can be bypassed. Consumer side: the\n * local handler.handle call above claims as consumer, and one\n * reused chunk object can alternate between these two arms.\n */\n enforceStreamLimitsForWireChunk({\n graph: context,\n metadata,\n chunk,\n side: 'consumer',\n });\n }\n finalChunk = appendStreamChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n /**\n * Only this loop may seal. The registered-handler branch below\n * dispatches through `run.ts`'s decoupled `streamEvents` consumer,\n * which can lag the accumulated chunk — sealing there would let the\n * host index a content part the user has not been shown yet.\n */\n /**\n * Cheap poll first, shape check second, budget claim last. The claim\n * is what makes this safe under a parallel `MultiAgentGraph`: several\n * agents share one graph and can each see the poll as true, but only\n * one can take the slot, and a chunk that cannot seal never spends it.\n */\n if (\n context?.shouldPreemptStream() === true &&\n canSealPreempt(finalChunk) &&\n context.claimPreemptSeal()\n ) {\n preempted = true;\n break;\n }\n }\n } else {\n const metadata = config.metadata as Record<string, unknown> | undefined;\n /**\n * The original wire chunk still reaches the registered handler through\n * `streamEvents` (where the late-reasoning skip discards it AFTER the\n * event guard counts it), so this inline re-dispatch of the transformed\n * chunk is marked to not consume a second event-budget slot. Allocated\n * once per attempt, only when a transformation occurs.\n */\n let redispatchMetadata: Record<string, unknown> | undefined;\n for await (const chunk of stream) {\n throwIfBreakerTripped();\n /**\n * Charged synchronously, ahead of the decoupled `streamEvents`\n * reader that will echo this same chunk to the registered handler:\n * a lagging reader would otherwise let an oversized complete call\n * return to LangGraph and reach ToolNode before the queued handler\n * throws. The chunk is marked so the echo skips accounting.\n */\n if (context != null) {\n enforceStreamLimitsForWireChunk({ graph: context, metadata, chunk });\n }\n const handlingChunk = getStreamHandlingChunk({\n current: finalChunk,\n next: chunk,\n provider,\n });\n if (handlingChunk != null && handlingChunk !== chunk) {\n redispatchMetadata ??= {\n ...(metadata ?? {}),\n [STREAM_LIMIT_REDISPATCH_KEY]: true,\n };\n await registeredStreamHandler.handle(\n GraphEvents.CHAT_MODEL_STREAM,\n { chunk: handlingChunk },\n redispatchMetadata,\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 (preempted && finalChunk != null) {\n const responseMetadata = {\n ...finalChunk.response_metadata,\n preempted: true,\n };\n finalChunk.response_metadata = responseMetadata;\n finalChunk.lc_kwargs.response_metadata = responseMetadata;\n await endSealedModelRun(\n context,\n finalChunk,\n messagesForProvider,\n sealedRunId,\n config,\n model\n );\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 * Identifies which fallback produced an error, so a caller planning a\n * recovery can reason about the client that actually failed rather than the\n * primary's configuration — their context windows and output allowances\n * differ, which is the whole reason a fallback exists.\n */\nexport interface FallbackErrorContext {\n provider: Providers;\n clientOptions?: t.ClientOptions;\n maxContextTokens?: number;\n}\n\nexport interface FallbackOverflowCandidate {\n error: unknown;\n context: FallbackErrorContext;\n}\n\nconst fallbackErrorContexts = new WeakMap<object, FallbackErrorContext>();\nconst fallbackOverflowCandidates = new WeakMap<\n object,\n FallbackOverflowCandidate[]\n>();\n\nfunction attachFallbackErrorContext(\n error: unknown,\n fallbackContext: FallbackErrorContext\n): void {\n if (typeof error !== 'object' || error === null) {\n return;\n }\n fallbackErrorContexts.set(error, fallbackContext);\n}\n\n/** Reads back the fallback attribution attached by `tryFallbackProviders`. */\nexport function getFallbackErrorContext(\n error: unknown\n): FallbackErrorContext | undefined {\n if (typeof error !== 'object' || error === null) {\n return undefined;\n }\n return fallbackErrorContexts.get(error);\n}\n\n/** Returns every fallback overflow retained from an exhausted provider chain. */\nexport function getFallbackOverflowCandidates(\n error: unknown\n): FallbackOverflowCandidate[] {\n if (typeof error !== 'object' || error === null) {\n return [];\n }\n return [...(fallbackOverflowCandidates.get(error) ?? [])];\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 *\n * When every fallback fails, a context overflow among them is thrown in\n * preference to whichever failure happened to come last. An overflow is the\n * one failure the caller can act on — it compacts and retries — and losing it\n * behind a later unrelated error would surface a dead end instead. Ordinary\n * failures still throw last-error-wins.\n */\nexport async function tryFallbackProviders({\n fallbacks,\n tools,\n messages,\n config,\n primaryError,\n context,\n onChunk,\n streamLimitState,\n overflowContext,\n prepareProviderMessages,\n}: {\n fallbacks: t.FallbackConfig[];\n tools?: t.GraphTools;\n messages: BaseMessage[];\n config?: RunnableConfig;\n primaryError: unknown;\n context?: InvokeContext;\n onChunk?: OnChunk;\n /** Accounting-lease owner forwarded to each fallback attempt (see\n * `AttemptInvokeParams.streamLimitState`). */\n streamLimitState?: StreamLimitState;\n /**\n * Prompt-size corroboration for signatures that are not self-describing.\n * Vertex AI's overflow is a bare `400` with no reason, so without this a\n * fallback that overflows is indistinguishable from any other 400 and would\n * be dropped in favour of whichever failure came last.\n */\n overflowContext?: ContextOverflowContext;\n /**\n * Optional final payload guard used by Graph. It receives the initialized,\n * tool-bound fallback model so Responses-vs-Chat projection is exact before\n * the fallback request is measured and sent.\n */\n prepareProviderMessages?: (input: {\n model: t.ChatModel;\n messages: BaseMessage[];\n provider: Providers;\n clientOptions?: t.ClientOptions;\n maxContextTokens?: number;\n config?: RunnableConfig;\n }) => BaseMessage[] | Promise<BaseMessage[]>;\n}): Promise<Partial<t.BaseGraphState> | undefined> {\n const isOverflow = (\n error: unknown,\n contextOverride = overflowContext\n ): boolean => getContextOverflowInfo(error, contextOverride) != null;\n let lastError: unknown = primaryError;\n /**\n * Tracked apart from the primary's overflow. A caller reaching this\n * function with an overflowing primary has already failed to recover from\n * it, so a fallback overflow — which may sit against a different window and\n * output allowance — is the more useful of the two to surface.\n */\n const overflowCandidates: FallbackOverflowCandidate[] = [];\n const primaryOverflowError: unknown = isOverflow(primaryError)\n ? primaryError\n : undefined;\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 fallbackMessages =\n (await prepareProviderMessages?.({\n model: fbModel as t.ChatModel,\n messages,\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n maxContextTokens: fb.maxContextTokens,\n config: fbConfig,\n })) ?? messages;\n /** A sibling can trip the breaker while the preparation above is\n * awaited — and the catch below only sees attempts that THROW, so a\n * provider that ignores an aborted signal and succeeds would resolve\n * a run that must reject. Check before every fallback invocation. */\n if (\n config?.signal?.aborted === true &&\n config.signal.reason instanceof StreamLimitExceededError\n ) {\n throw config.signal.reason;\n }\n const result = await attemptInvoke(\n {\n model: fbModel as t.ChatModel,\n messages: fallbackMessages,\n provider: fb.provider,\n context,\n onChunk,\n streamLimitState,\n },\n fbConfig\n );\n return result;\n } catch (e) {\n /**\n * A tripped stream circuit breaker is a deliberate abort, not a\n * provider failure. Continuing would try the remaining fallbacks and a\n * succeeding one would resolve a run that must reject.\n */\n if (e instanceof StreamLimitExceededError) {\n throw e;\n }\n /** A parallel sibling's trip aborts this branch's composed signal, and\n * a provider can surface that as a generic abort error; advancing to\n * the next fallback would start new provider work after the safety\n * abort. Rethrow the breaker's own reason instead. */\n if (\n config?.signal?.aborted === true &&\n config.signal.reason instanceof StreamLimitExceededError\n ) {\n throw config.signal.reason;\n }\n lastError = e;\n const fallbackOverflowContext: ContextOverflowContext = {\n provider: fb.provider,\n maxContextTokens: fb.maxContextTokens,\n ...(overflowContext?.provider === fb.provider\n ? {\n estimatedPromptTokens: overflowContext.estimatedPromptTokens,\n }\n : {}),\n };\n if (isOverflow(e, fallbackOverflowContext)) {\n const errorContext: FallbackErrorContext = {\n provider: fb.provider,\n clientOptions: fb.clientOptions,\n maxContextTokens: fb.maxContextTokens,\n };\n attachFallbackErrorContext(e, errorContext);\n overflowCandidates.push({ error: e, context: errorContext });\n }\n continue;\n }\n }\n /**\n * Preference order: a fallback overflow, then the primary's overflow, then\n * whichever failure came last. An overflow is the only one of the three a\n * caller can act on, and the fallback's carries the client attribution that\n * makes a correct retry budget possible.\n */\n const preferred =\n overflowCandidates[0]?.error ?? primaryOverflowError ?? lastError;\n if (\n overflowCandidates.length > 0 &&\n typeof preferred === 'object' &&\n preferred !== null\n ) {\n fallbackOverflowCandidates.set(preferred, overflowCandidates);\n }\n if (preferred !== undefined) {\n throw preferred;\n }\n return undefined;\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_cache = require("../../messages/cache.cjs");
|
|
2
2
|
const require_core = require("../../messages/core.cjs");
|
|
3
|
-
const require_intentArg = require("../../tools/intentArg.cjs");
|
|
4
3
|
const require_streamedToolCallSeals = require("../../tools/streamedToolCallSeals.cjs");
|
|
4
|
+
const require_intentArg = require("../../tools/intentArg.cjs");
|
|
5
5
|
const require_index = require("./utils/index.cjs");
|
|
6
6
|
const require_streamMetadata = require("./streamMetadata.cjs");
|
|
7
7
|
let _langchain_openai = require("@langchain/openai");
|
|
@@ -123,6 +123,9 @@ function addChatCacheBreakpoints(messages) {
|
|
|
123
123
|
function isResponseMessage(item) {
|
|
124
124
|
return item.type === "message";
|
|
125
125
|
}
|
|
126
|
+
function isResponseInputRole(role) {
|
|
127
|
+
return role === "system" || role === "developer" || role === "user";
|
|
128
|
+
}
|
|
126
129
|
/** Only `input_text`/`input_image`/`input_file` accept a Responses breakpoint;
|
|
127
130
|
* `output_text`/`refusal` (replayed assistant blocks) are rejected with a 400. */
|
|
128
131
|
function isCacheableResponsePart(part) {
|
|
@@ -165,7 +168,7 @@ function addResponseCacheBreakpoints(input) {
|
|
|
165
168
|
/** Only input roles take a Responses breakpoint. Assistant/tool turns
|
|
166
169
|
* carry output content (string or output_text) that the API rejects
|
|
167
170
|
* under an input marker, so they're never eligible. */
|
|
168
|
-
if (item.role
|
|
171
|
+
if (!isResponseInputRole(item.role)) return false;
|
|
169
172
|
const content = item.content;
|
|
170
173
|
return typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.some((part) => isCacheableResponsePart(part));
|
|
171
174
|
})));
|
|
@@ -178,7 +181,7 @@ function shouldIncludeEncryptedReasoning(model, params) {
|
|
|
178
181
|
}
|
|
179
182
|
function getCacheWriteTokens(message) {
|
|
180
183
|
const responseMetadata = message.response_metadata;
|
|
181
|
-
const reported = responseMetadata.usage?.input_tokens_details
|
|
184
|
+
const reported = responseMetadata.usage?.input_tokens_details?.cache_write_tokens;
|
|
182
185
|
if (reported != null) return reported;
|
|
183
186
|
const serialized = responseMetadata.metadata?.[CACHE_WRITE_METADATA_KEY];
|
|
184
187
|
if (serialized == null) return;
|
|
@@ -202,7 +205,7 @@ function attachCacheWriteUsage(message) {
|
|
|
202
205
|
};
|
|
203
206
|
}
|
|
204
207
|
function attachCacheWriteMetadata(response) {
|
|
205
|
-
const cacheWriteTokens = response.usage?.input_tokens_details
|
|
208
|
+
const cacheWriteTokens = response.usage?.input_tokens_details?.cache_write_tokens;
|
|
206
209
|
if (cacheWriteTokens == null) return response;
|
|
207
210
|
return {
|
|
208
211
|
...response,
|
|
@@ -215,6 +218,165 @@ function attachCacheWriteMetadata(response) {
|
|
|
215
218
|
function isResponsesStream(result) {
|
|
216
219
|
return Symbol.asyncIterator in result;
|
|
217
220
|
}
|
|
221
|
+
const RESPONSES_REPLAY_OUTPUT_ITEM_TYPES = new Set([
|
|
222
|
+
"local_shell_call_output",
|
|
223
|
+
"shell_call_output",
|
|
224
|
+
"apply_patch_call_output",
|
|
225
|
+
"program_output"
|
|
226
|
+
]);
|
|
227
|
+
function isResponsesReplayOutputItem(item) {
|
|
228
|
+
return typeof item === "object" && item != null && "type" in item && typeof item.type === "string" && RESPONSES_REPLAY_OUTPUT_ITEM_TYPES.has(item.type);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* LangChain's Responses converter places the authoritative terminal output in
|
|
232
|
+
* response_metadata.output. Its chunk merge has no way to delete provisional
|
|
233
|
+
* tool_outputs or replay-position sidecars, so remove those preemption-only
|
|
234
|
+
* captures once that terminal output arrives. An interrupted stream has no
|
|
235
|
+
* terminal chunk and keeps the captures for replay.
|
|
236
|
+
*/
|
|
237
|
+
var ResponsesReplayAIMessageChunk = class extends _langchain_core_messages.AIMessageChunk {
|
|
238
|
+
get lc_id() {
|
|
239
|
+
return [...this.lc_namespace, _langchain_core_messages.AIMessageChunk.lc_name()];
|
|
240
|
+
}
|
|
241
|
+
concat(chunk) {
|
|
242
|
+
const combined = super.concat(chunk);
|
|
243
|
+
if (!Array.isArray(chunk.response_metadata.output)) return combined;
|
|
244
|
+
delete combined.additional_kwargs[require_core.OPENAI_RESPONSES_REPLAY_POSITIONS_KEY];
|
|
245
|
+
const toolOutputs = combined.additional_kwargs.tool_outputs;
|
|
246
|
+
if (!Array.isArray(toolOutputs)) return combined;
|
|
247
|
+
const retainedToolOutputs = toolOutputs.filter((item) => !isResponsesReplayOutputItem(item));
|
|
248
|
+
if (retainedToolOutputs.length === toolOutputs.length) return combined;
|
|
249
|
+
if (retainedToolOutputs.length > 0) combined.additional_kwargs.tool_outputs = retainedToolOutputs;
|
|
250
|
+
else delete combined.additional_kwargs.tool_outputs;
|
|
251
|
+
return combined;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
function makeResponsesReplayAggregationSafe(chunk) {
|
|
255
|
+
if (!_langchain_core_messages.AIMessageChunk.isInstance(chunk.message)) return chunk;
|
|
256
|
+
const message = chunk.message;
|
|
257
|
+
chunk.message = new ResponsesReplayAIMessageChunk({
|
|
258
|
+
id: message.id,
|
|
259
|
+
name: message.name,
|
|
260
|
+
content: message.content,
|
|
261
|
+
additional_kwargs: message.additional_kwargs,
|
|
262
|
+
response_metadata: message.response_metadata,
|
|
263
|
+
tool_calls: message.tool_calls,
|
|
264
|
+
invalid_tool_calls: message.invalid_tool_calls,
|
|
265
|
+
tool_call_chunks: message.tool_call_chunks,
|
|
266
|
+
usage_metadata: message.usage_metadata
|
|
267
|
+
});
|
|
268
|
+
return chunk;
|
|
269
|
+
}
|
|
270
|
+
function remapResponsesTextBlockIndex(chunk, event, textBlockIndices) {
|
|
271
|
+
const position = iife(() => {
|
|
272
|
+
if (event.type === "response.output_text.delta" || event.type === "response.output_text.annotation.added") return {
|
|
273
|
+
contentIndex: event.content_index,
|
|
274
|
+
outputIndex: event.output_index
|
|
275
|
+
};
|
|
276
|
+
if (event.type === "response.output_item.added" && event.item.type === "message") return {
|
|
277
|
+
contentIndex: 0,
|
|
278
|
+
outputIndex: event.output_index
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
if (position == null || !Array.isArray(chunk.message.content)) return;
|
|
282
|
+
const key = `${position.outputIndex}:${position.contentIndex}`;
|
|
283
|
+
let blockIndex = textBlockIndices.get(key);
|
|
284
|
+
if (blockIndex == null) {
|
|
285
|
+
blockIndex = textBlockIndices.size;
|
|
286
|
+
textBlockIndices.set(key, blockIndex);
|
|
287
|
+
}
|
|
288
|
+
const content = chunk.message.content.map((block) => typeof block === "object" && block.type === "text" ? {
|
|
289
|
+
...block,
|
|
290
|
+
index: blockIndex
|
|
291
|
+
} : block);
|
|
292
|
+
chunk.message.content = content;
|
|
293
|
+
chunk.message.lc_kwargs.content = content;
|
|
294
|
+
}
|
|
295
|
+
function convertDroppedResponsesReplayOutput(event) {
|
|
296
|
+
if (event.type !== "response.output_item.done") return null;
|
|
297
|
+
if (event.item.type === "reasoning") return new _langchain_core_outputs.ChatGenerationChunk({
|
|
298
|
+
text: "",
|
|
299
|
+
message: new _langchain_core_messages.AIMessageChunk({
|
|
300
|
+
content: [],
|
|
301
|
+
additional_kwargs: { reasoning: {
|
|
302
|
+
status: event.item.status,
|
|
303
|
+
...typeof event.item.encrypted_content === "string" ? { encrypted_content: event.item.encrypted_content } : {}
|
|
304
|
+
} },
|
|
305
|
+
response_metadata: { model_provider: "openai" }
|
|
306
|
+
})
|
|
307
|
+
});
|
|
308
|
+
if (!RESPONSES_REPLAY_OUTPUT_ITEM_TYPES.has(event.item.type)) return null;
|
|
309
|
+
return new _langchain_core_outputs.ChatGenerationChunk({
|
|
310
|
+
text: "",
|
|
311
|
+
message: new _langchain_core_messages.AIMessageChunk({
|
|
312
|
+
content: [],
|
|
313
|
+
additional_kwargs: { tool_outputs: [event.item] },
|
|
314
|
+
response_metadata: { model_provider: "openai" }
|
|
315
|
+
})
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function attachResponsesReplayPosition(chunk, event, seenPositions) {
|
|
319
|
+
let position;
|
|
320
|
+
if (event.type === "response.output_text.delta" && event.delta.length > 0) position = {
|
|
321
|
+
contentIndex: event.content_index,
|
|
322
|
+
itemId: event.item_id,
|
|
323
|
+
kind: "text",
|
|
324
|
+
outputIndex: event.output_index
|
|
325
|
+
};
|
|
326
|
+
else if (event.type === "response.output_item.added" && event.item.type === "message" && typeof event.item.id === "string" && event.item.id.length > 0) position = {
|
|
327
|
+
itemId: event.item.id,
|
|
328
|
+
kind: "message",
|
|
329
|
+
outputIndex: event.output_index
|
|
330
|
+
};
|
|
331
|
+
else if (event.type === "response.output_item.added" && event.item.type === "reasoning" && typeof event.item.id === "string" && event.item.id.length > 0) position = {
|
|
332
|
+
itemId: event.item.id,
|
|
333
|
+
kind: "reasoning",
|
|
334
|
+
outputIndex: event.output_index
|
|
335
|
+
};
|
|
336
|
+
else if (event.type === "response.output_item.done" && (RESPONSES_REPLAY_OUTPUT_ITEM_TYPES.has(event.item.type) || Array.isArray(chunk.message.additional_kwargs.tool_outputs))) {
|
|
337
|
+
let itemId;
|
|
338
|
+
if (typeof event.item.id === "string" && event.item.id.length > 0) itemId = event.item.id;
|
|
339
|
+
else if ("call_id" in event.item && typeof event.item.call_id === "string" && event.item.call_id.length > 0) itemId = event.item.call_id;
|
|
340
|
+
if (itemId != null) position = {
|
|
341
|
+
itemId,
|
|
342
|
+
kind: "output",
|
|
343
|
+
outputIndex: event.output_index
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
if (position == null) return;
|
|
347
|
+
const positionKey = `${position.kind}:${position.itemId}:${position.outputIndex}:${position.contentIndex ?? ""}`;
|
|
348
|
+
if (seenPositions.has(positionKey)) return;
|
|
349
|
+
seenPositions.add(positionKey);
|
|
350
|
+
const existing = chunk.message.additional_kwargs[require_core.OPENAI_RESPONSES_REPLAY_POSITIONS_KEY];
|
|
351
|
+
const additionalKwargs = {
|
|
352
|
+
...chunk.message.additional_kwargs,
|
|
353
|
+
[require_core.OPENAI_RESPONSES_REPLAY_POSITIONS_KEY]: [...Array.isArray(existing) ? existing : [], position]
|
|
354
|
+
};
|
|
355
|
+
chunk.message.additional_kwargs = additionalKwargs;
|
|
356
|
+
chunk.message.lc_kwargs.additional_kwargs = additionalKwargs;
|
|
357
|
+
}
|
|
358
|
+
async function* convertLibreChatResponsesStream(stream, options, runManager) {
|
|
359
|
+
const seenReplayPositions = /* @__PURE__ */ new Set();
|
|
360
|
+
const responsesTextBlockIndices = /* @__PURE__ */ new Map();
|
|
361
|
+
try {
|
|
362
|
+
for await (const event of stream) {
|
|
363
|
+
options.signal?.throwIfAborted();
|
|
364
|
+
const convertedChunk = (0, _langchain_openai.convertResponsesDeltaToChatGenerationChunk)(event) ?? convertDroppedResponsesReplayOutput(event);
|
|
365
|
+
if (convertedChunk == null) continue;
|
|
366
|
+
const chunk = makeResponsesReplayAggregationSafe(convertedChunk);
|
|
367
|
+
remapResponsesTextBlockIndex(chunk, event, responsesTextBlockIndices);
|
|
368
|
+
attachResponsesReplayPosition(chunk, event, seenReplayPositions);
|
|
369
|
+
attachCacheWriteUsage(chunk.message);
|
|
370
|
+
await runManager?.handleLLMNewToken(chunk.text || "", {
|
|
371
|
+
prompt: options.promptIndex ?? 0,
|
|
372
|
+
completion: 0
|
|
373
|
+
}, void 0, void 0, void 0, { chunk });
|
|
374
|
+
yield chunk;
|
|
375
|
+
}
|
|
376
|
+
} catch (e) {
|
|
377
|
+
throw (0, _langchain_openai.wrapOpenAIClientError)(e);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
218
380
|
function createUsageMetadata(usage) {
|
|
219
381
|
const usageMetadata = {
|
|
220
382
|
input_tokens: usage?.prompt_tokens ?? 0,
|
|
@@ -817,10 +979,16 @@ var LibreChatOpenAIResponses = class extends _langchain_openai.ChatOpenAIRespons
|
|
|
817
979
|
return result;
|
|
818
980
|
}
|
|
819
981
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
982
|
+
const projectedMessages = projectOpenAIResponsesProviderMessages(messages);
|
|
983
|
+
yield* convertLibreChatResponsesStream(await this.completionWithRetry({
|
|
984
|
+
...this.invocationParams(options),
|
|
985
|
+
input: (0, _langchain_openai.convertMessagesToResponsesInput)({
|
|
986
|
+
messages: projectedMessages,
|
|
987
|
+
zdrEnabled: this.zdrEnabled ?? false,
|
|
988
|
+
model: this.model
|
|
989
|
+
}),
|
|
990
|
+
stream: true
|
|
991
|
+
}, options), options, runManager);
|
|
824
992
|
}
|
|
825
993
|
async *_streamChatModelEvents(messages, options, runManager) {
|
|
826
994
|
yield* super._streamChatModelEvents(projectOpenAIResponsesProviderMessages(messages), options, runManager);
|
|
@@ -935,15 +1103,24 @@ var LibreChatAzureOpenAIResponses = class extends _langchain_openai.AzureChatOpe
|
|
|
935
1103
|
return isResponsesStream(result) ? result : attachCacheWriteMetadata(result);
|
|
936
1104
|
}
|
|
937
1105
|
async _generate(messages, options, runManager) {
|
|
938
|
-
const result = await super._generate(messages, options, runManager);
|
|
1106
|
+
const result = await super._generate(projectOpenAIResponsesProviderMessages(messages), options, runManager);
|
|
939
1107
|
for (const generation of result.generations) attachCacheWriteUsage(generation.message);
|
|
940
1108
|
return result;
|
|
941
1109
|
}
|
|
942
1110
|
async *_streamResponseChunks(messages, options, runManager) {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1111
|
+
const projectedMessages = projectOpenAIResponsesProviderMessages(messages);
|
|
1112
|
+
yield* convertLibreChatResponsesStream(await this.completionWithRetry({
|
|
1113
|
+
...this.invocationParams(options),
|
|
1114
|
+
input: (0, _langchain_openai.convertMessagesToResponsesInput)({
|
|
1115
|
+
messages: projectedMessages,
|
|
1116
|
+
zdrEnabled: this.zdrEnabled ?? false,
|
|
1117
|
+
model: this.model
|
|
1118
|
+
}),
|
|
1119
|
+
stream: true
|
|
1120
|
+
}, options), options, runManager);
|
|
1121
|
+
}
|
|
1122
|
+
async *_streamChatModelEvents(messages, options, runManager) {
|
|
1123
|
+
yield* super._streamChatModelEvents(projectOpenAIResponsesProviderMessages(messages), options, runManager);
|
|
947
1124
|
}
|
|
948
1125
|
_getReasoningParams(options) {
|
|
949
1126
|
return getGatedReasoningParams(this.model, this.reasoning, options);
|