@librechat/agents 3.2.21 → 3.2.32
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/agents/AgentContext.cjs +3 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +200 -54
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +13 -7
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -1
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +2 -2
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +17 -1
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +2 -2
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +1 -1
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +38 -3
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +6 -2
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +2 -2
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/toolCache.cjs +8 -5
- package/dist/cjs/llm/bedrock/toolCache.cjs.map +1 -1
- package/dist/cjs/llm/fake.cjs +16 -14
- package/dist/cjs/llm/fake.cjs.map +1 -1
- package/dist/cjs/llm/google/index.cjs +22 -0
- package/dist/cjs/llm/google/index.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +88 -27
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +2 -2
- package/dist/cjs/llm/invoke.cjs +108 -11
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +1 -1
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +1 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +29 -8
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/content.cjs.map +1 -1
- package/dist/cjs/messages/contextPruning.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +129 -17
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/reducer.cjs +1 -1
- package/dist/cjs/messages/reducer.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +1 -1
- package/dist/cjs/messages/tools.cjs.map +1 -1
- package/dist/cjs/openai/index.cjs.map +1 -1
- package/dist/cjs/responses/index.cjs.map +1 -1
- package/dist/cjs/run.cjs +41 -20
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +4 -4
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +2 -2
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
- package/dist/cjs/session/handlers.cjs +2 -2
- package/dist/cjs/session/handlers.cjs.map +1 -1
- package/dist/cjs/stream.cjs +248 -25
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/Calculator.cjs +1 -1
- package/dist/cjs/tools/Calculator.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +37 -18
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +1 -1
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs +7 -4
- package/dist/cjs/tools/cloudflare/CloudflareSandboxExecutionEngine.cjs.map +1 -1
- package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs +4 -4
- package/dist/cjs/tools/cloudflare/CloudflareSandboxTools.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +2 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
- package/dist/cjs/tools/local/FileCheckpointer.cjs +2 -1
- package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +45 -19
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +3 -3
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalExecutionTools.cjs +2 -2
- package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +4 -3
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/local/attachments.cjs +0 -5
- package/dist/cjs/tools/local/attachments.cjs.map +1 -1
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +4 -4
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +7 -3
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-search.cjs +1 -1
- package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs +76 -8
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/handlers.cjs +1 -1
- package/dist/cjs/utils/handlers.cjs.map +1 -1
- package/dist/cjs/utils/run.cjs +1 -1
- package/dist/cjs/utils/run.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +3 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +200 -54
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +13 -7
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -1
- package/dist/esm/hooks/executeHooks.mjs.map +1 -1
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +2 -2
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +17 -2
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +2 -2
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +1 -1
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +38 -3
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +6 -2
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +2 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/bedrock/toolCache.mjs +8 -5
- package/dist/esm/llm/bedrock/toolCache.mjs.map +1 -1
- package/dist/esm/llm/fake.mjs +16 -14
- package/dist/esm/llm/fake.mjs.map +1 -1
- package/dist/esm/llm/google/index.mjs +23 -1
- package/dist/esm/llm/google/index.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +88 -27
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +2 -2
- package/dist/esm/llm/invoke.mjs +104 -7
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +1 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/vertexai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/cache.mjs +29 -8
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/content.mjs.map +1 -1
- package/dist/esm/messages/contextPruning.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +129 -18
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/reducer.mjs +1 -1
- package/dist/esm/messages/reducer.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs +1 -1
- package/dist/esm/messages/tools.mjs.map +1 -1
- package/dist/esm/openai/index.mjs.map +1 -1
- package/dist/esm/responses/index.mjs.map +1 -1
- package/dist/esm/run.mjs +41 -20
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +4 -4
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +2 -2
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
- package/dist/esm/session/handlers.mjs +2 -2
- package/dist/esm/session/handlers.mjs.map +1 -1
- package/dist/esm/stream.mjs +248 -25
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/Calculator.mjs +1 -1
- package/dist/esm/tools/Calculator.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +1 -1
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/SubagentTool.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +37 -18
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +1 -1
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs +7 -4
- package/dist/esm/tools/cloudflare/CloudflareSandboxExecutionEngine.mjs.map +1 -1
- package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs +4 -4
- package/dist/esm/tools/cloudflare/CloudflareSandboxTools.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +2 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
- package/dist/esm/tools/local/FileCheckpointer.mjs +2 -1
- package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +45 -19
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +3 -3
- package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
- package/dist/esm/tools/local/LocalExecutionTools.mjs +2 -2
- package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -1
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +4 -3
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/local/attachments.mjs +0 -5
- package/dist/esm/tools/local/attachments.mjs.map +1 -1
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +4 -4
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
- package/dist/esm/tools/search/firecrawl.mjs +1 -1
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs +8 -4
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-search.mjs +1 -1
- package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs +76 -9
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/handlers.mjs +1 -1
- package/dist/esm/utils/handlers.mjs.map +1 -1
- package/dist/esm/utils/run.mjs +1 -1
- package/dist/esm/utils/run.mjs.map +1 -1
- package/dist/types/agents/__tests__/promptCacheLiveHelpers.d.ts +1 -1
- package/dist/types/events.d.ts +1 -1
- package/dist/types/graphs/Graph.d.ts +7 -1
- package/dist/types/hooks/executeHooks.d.ts +1 -1
- package/dist/types/hooks/types.d.ts +5 -0
- package/dist/types/langfuse.d.ts +4 -0
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
- package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -1
- package/dist/types/llm/anthropic/utils/output_parsers.d.ts +2 -2
- package/dist/types/llm/bedrock/index.d.ts +2 -2
- package/dist/types/llm/fake.d.ts +3 -3
- package/dist/types/llm/google/index.d.ts +1 -0
- package/dist/types/llm/google/types.d.ts +1 -1
- package/dist/types/llm/google/utils/common.d.ts +2 -2
- package/dist/types/llm/google/utils/tools.d.ts +1 -1
- package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +1 -1
- package/dist/types/llm/openai/index.d.ts +2 -2
- package/dist/types/llm/openai/utils/index.d.ts +1 -1
- package/dist/types/llm/openrouter/index.d.ts +4 -4
- package/dist/types/messages/contextPruning.d.ts +1 -1
- package/dist/types/messages/format.d.ts +14 -4
- package/dist/types/messages/prune.d.ts +1 -1
- package/dist/types/session/JsonlSessionStore.d.ts +1 -1
- package/dist/types/session/handlers.d.ts +1 -1
- package/dist/types/session/types.d.ts +1 -1
- package/dist/types/summarization/node.d.ts +1 -1
- package/dist/types/tools/SubagentTool.d.ts +2 -2
- package/dist/types/tools/ToolNode.d.ts +9 -2
- package/dist/types/tools/cloudflare/CloudflareSandboxExecutionEngine.d.ts +1 -1
- package/dist/types/tools/search/types.d.ts +1 -1
- package/dist/types/tools/search/utils.d.ts +11 -0
- package/dist/types/types/graph.d.ts +4 -4
- package/dist/types/types/llm.d.ts +4 -3
- package/dist/types/types/messages.d.ts +1 -1
- package/dist/types/types/run.d.ts +6 -6
- package/dist/types/types/stream.d.ts +2 -2
- package/dist/types/types/tools.d.ts +5 -1
- package/dist/types/utils/handlers.d.ts +2 -2
- package/dist/types/utils/run.d.ts +1 -1
- package/package.json +13 -10
- package/src/__tests__/stream.eagerEventExecution.test.ts +543 -6
- package/src/agents/AgentContext.ts +2 -2
- package/src/agents/__tests__/AgentContext.test.ts +3 -3
- package/src/agents/__tests__/promptCacheLiveHelpers.ts +1 -1
- package/src/events.ts +1 -1
- package/src/graphs/Graph.ts +329 -72
- package/src/graphs/MultiAgentGraph.ts +1 -1
- package/src/graphs/__tests__/Graph.reasoning.test.ts +919 -6
- package/src/graphs/__tests__/MultiAgentGraph.test.ts +1 -1
- package/src/graphs/__tests__/composition.smoke.test.ts +1 -1
- package/src/hooks/__tests__/HookRegistry.test.ts +1 -1
- package/src/hooks/__tests__/compactHooks.test.ts +8 -8
- package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +34 -22
- package/src/hooks/__tests__/executeHooks.test.ts +3 -3
- package/src/hooks/__tests__/integration.test.ts +3 -3
- package/src/hooks/__tests__/toolHooks.test.ts +10 -10
- package/src/hooks/createWorkspacePolicyHook.ts +17 -14
- package/src/hooks/executeHooks.ts +1 -1
- package/src/hooks/types.ts +5 -0
- package/src/instrumentation.ts +11 -11
- package/src/langfuse.ts +35 -1
- package/src/langfuseToolOutputTracing.ts +2 -2
- package/src/llm/anthropic/index.ts +1 -1
- package/src/llm/anthropic/llm.spec.ts +36 -0
- package/src/llm/anthropic/utils/message_inputs.ts +46 -4
- package/src/llm/anthropic/utils/message_outputs.ts +9 -7
- package/src/llm/anthropic/utils/output_parsers.ts +5 -5
- package/src/llm/anthropic/utils/streaming-tool-input.test.ts +186 -0
- package/src/llm/bedrock/index.ts +4 -4
- package/src/llm/bedrock/toolCache.test.ts +48 -9
- package/src/llm/bedrock/toolCache.ts +11 -6
- package/src/llm/fake.ts +30 -25
- package/src/llm/google/index.ts +43 -1
- package/src/llm/google/llm.spec.ts +173 -1
- package/src/llm/google/types.ts +1 -1
- package/src/llm/google/utils/common.ts +154 -45
- package/src/llm/google/utils/tools.ts +8 -8
- package/src/llm/google/utils/zod_to_genai_parameters.ts +4 -4
- package/src/llm/invoke.test.ts +3 -3
- package/src/llm/invoke.ts +170 -10
- package/src/llm/openai/index.ts +4 -4
- package/src/llm/openai/utils/index.ts +14 -14
- package/src/llm/openrouter/index.ts +4 -4
- package/src/llm/openrouter/reasoning.test.ts +2 -2
- package/src/llm/vertexai/fixThoughtSignatures.test.ts +1 -1
- package/src/llm/vertexai/index.ts +1 -1
- package/src/messages/cache.test.ts +144 -0
- package/src/messages/cache.ts +50 -13
- package/src/messages/content.ts +1 -1
- package/src/messages/contextPruning.ts +1 -1
- package/src/messages/format.ts +236 -43
- package/src/messages/formatAgentMessages.skills.test.ts +205 -26
- package/src/messages/formatAgentMessages.test.ts +841 -10
- package/src/messages/labelContentByAgent.test.ts +2 -2
- package/src/messages/prune.ts +1 -1
- package/src/messages/reducer.ts +1 -1
- package/src/messages/tools.ts +1 -1
- package/src/openai/__tests__/openai.test.ts +2 -2
- package/src/openai/index.ts +1 -1
- package/src/responses/__tests__/responses.test.ts +2 -2
- package/src/responses/index.ts +1 -1
- package/src/run.ts +68 -41
- package/src/session/AgentSession.ts +6 -6
- package/src/session/JsonlSessionStore.ts +3 -3
- package/src/session/__tests__/JsonlSessionStore.test.ts +5 -5
- package/src/session/__tests__/handlers.test.ts +2 -2
- package/src/session/handlers.ts +5 -5
- package/src/session/types.ts +1 -1
- package/src/specs/agent-handoffs.test.ts +1 -1
- package/src/specs/langfuse-callbacks.test.ts +2 -2
- package/src/specs/langfuse-metadata.test.ts +39 -0
- package/src/specs/langfuse-tool-output-tracing.test.ts +1 -1
- package/src/specs/multi-agent-summarization.test.ts +4 -4
- package/src/specs/subagent.test.ts +3 -3
- package/src/specs/summarization-unit.test.ts +1 -1
- package/src/specs/thinking-handoff.test.ts +1 -1
- package/src/splitStream.test.ts +48 -0
- package/src/stream.test.ts +53 -3
- package/src/stream.ts +450 -39
- package/src/summarization/__tests__/aggregator.test.ts +2 -2
- package/src/summarization/__tests__/node.test.ts +2 -2
- package/src/summarization/node.ts +1 -1
- package/src/tools/BashProgrammaticToolCalling.ts +5 -5
- package/src/tools/Calculator.ts +1 -1
- package/src/tools/CodeExecutor.ts +2 -4
- package/src/tools/SubagentTool.ts +2 -2
- package/src/tools/ToolNode.ts +37 -16
- package/src/tools/ToolSearch.ts +1 -1
- package/src/tools/__tests__/CloudflareSandboxExecution.test.ts +4 -4
- package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +12 -12
- package/src/tools/__tests__/LocalExecutionTools.test.ts +125 -93
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +29 -5
- package/src/tools/__tests__/ReadFile.test.ts +1 -1
- package/src/tools/__tests__/SkillTool.test.ts +4 -4
- package/src/tools/__tests__/SubagentExecutor.test.ts +17 -13
- package/src/tools/__tests__/SubagentTool.test.ts +2 -2
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +1 -1
- package/src/tools/__tests__/ToolNode.outputReferences.test.ts +2 -5
- package/src/tools/__tests__/ToolNode.session.test.ts +1 -1
- package/src/tools/__tests__/ToolSearch.test.ts +1 -1
- package/src/tools/__tests__/annotateMessagesForLLM.test.ts +1 -1
- package/src/tools/__tests__/directToolHITLResumeScope.test.ts +35 -32
- package/src/tools/__tests__/directToolHooks.test.ts +41 -0
- package/src/tools/__tests__/handlers.test.ts +2 -2
- package/src/tools/__tests__/hitl.test.ts +11 -11
- package/src/tools/__tests__/localToolNames.test.ts +8 -6
- package/src/tools/__tests__/skillCatalog.test.ts +1 -1
- package/src/tools/__tests__/subagentHooks.test.ts +20 -10
- package/src/tools/__tests__/workspaceSeam.test.ts +20 -7
- package/src/tools/cloudflare/CloudflareSandboxExecutionEngine.ts +9 -6
- package/src/tools/cloudflare/CloudflareSandboxTools.ts +19 -19
- package/src/tools/handlers.ts +5 -5
- package/src/tools/local/CompileCheckTool.ts +4 -7
- package/src/tools/local/FileCheckpointer.ts +6 -5
- package/src/tools/local/LocalCodingTools.ts +100 -45
- package/src/tools/local/LocalExecutionEngine.ts +5 -5
- package/src/tools/local/LocalExecutionTools.ts +9 -9
- package/src/tools/local/LocalProgrammaticToolCalling.ts +5 -4
- package/src/tools/local/attachments.ts +0 -6
- package/src/tools/local/resolveLocalExecutionTools.ts +15 -15
- package/src/tools/search/firecrawl.ts +1 -1
- package/src/tools/search/jina-reranker.test.ts +148 -37
- package/src/tools/search/rerankers.ts +14 -4
- package/src/tools/search/tavily-search.ts +2 -2
- package/src/tools/search/types.ts +1 -1
- package/src/tools/search/utils.ts +99 -9
- package/src/tools/subagent/SubagentExecutor.ts +12 -6
- package/src/types/graph.ts +12 -12
- package/src/types/llm.ts +7 -6
- package/src/types/messages.ts +1 -1
- package/src/types/run.ts +7 -7
- package/src/types/stream.ts +2 -2
- package/src/types/tools.ts +5 -1
- package/src/utils/handlers.ts +2 -2
- package/src/utils/llmConfig.ts +1 -1
- package/src/utils/logging.ts +20 -10
- package/src/utils/run.ts +2 -2
package/src/llm/invoke.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { ToolCall } from '@langchain/core/messages/tool';
|
|
|
5
5
|
import type { BaseMessage } from '@langchain/core/messages';
|
|
6
6
|
import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
|
|
7
7
|
import type * as t from '@/types';
|
|
8
|
-
import { manualToolStreamProviders } from '@/llm/providers';
|
|
9
8
|
import { annotateMessagesForLLM } from '@/tools/toolOutputReferences';
|
|
9
|
+
import { manualToolStreamProviders } from '@/llm/providers';
|
|
10
10
|
import { modifyDeltaProperties } from '@/messages';
|
|
11
11
|
import { ChatModelStreamHandler } from '@/stream';
|
|
12
12
|
import { GraphEvents, Providers } from '@/common';
|
|
@@ -51,6 +51,127 @@ export type InvokeContext = NonNullable<
|
|
|
51
51
|
*/
|
|
52
52
|
export type OnChunk = (chunk: AIMessageChunk) => void | Promise<void>;
|
|
53
53
|
|
|
54
|
+
function getRegisteredDefaultChatStreamHandler(
|
|
55
|
+
context?: InvokeContext
|
|
56
|
+
): ChatModelStreamHandler | undefined {
|
|
57
|
+
const handler = context?.handlerRegistry?.getHandler(
|
|
58
|
+
GraphEvents.CHAT_MODEL_STREAM
|
|
59
|
+
);
|
|
60
|
+
return handler instanceof ChatModelStreamHandler ? handler : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function hasReasoningDetails(chunk: AIMessageChunk): boolean {
|
|
64
|
+
const reasoningDetails = chunk.additional_kwargs.reasoning_details;
|
|
65
|
+
return Array.isArray(reasoningDetails) && reasoningDetails.length > 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function removeOpenRouterFinalReasoningReplayContent({
|
|
69
|
+
current,
|
|
70
|
+
next,
|
|
71
|
+
provider,
|
|
72
|
+
}: {
|
|
73
|
+
current?: AIMessageChunk;
|
|
74
|
+
next: AIMessageChunk;
|
|
75
|
+
provider: Providers;
|
|
76
|
+
}): AIMessageChunk {
|
|
77
|
+
const content = getOpenRouterFinalReasoningContent({
|
|
78
|
+
current,
|
|
79
|
+
next,
|
|
80
|
+
provider,
|
|
81
|
+
});
|
|
82
|
+
if (content == null || content === next.content) {
|
|
83
|
+
return next;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return new AIMessageChunk(
|
|
87
|
+
Object.assign({}, next, {
|
|
88
|
+
content,
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getOpenRouterFinalReasoningContent({
|
|
94
|
+
current,
|
|
95
|
+
next,
|
|
96
|
+
provider,
|
|
97
|
+
}: {
|
|
98
|
+
current?: AIMessageChunk;
|
|
99
|
+
next: AIMessageChunk;
|
|
100
|
+
provider: Providers;
|
|
101
|
+
}): string | undefined {
|
|
102
|
+
if (
|
|
103
|
+
provider !== Providers.OPENROUTER ||
|
|
104
|
+
current == null ||
|
|
105
|
+
!hasReasoningDetails(next) ||
|
|
106
|
+
typeof current.content !== 'string' ||
|
|
107
|
+
current.content === '' ||
|
|
108
|
+
typeof next.content !== 'string' ||
|
|
109
|
+
next.content === ''
|
|
110
|
+
) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
if (!next.content.startsWith(current.content)) {
|
|
114
|
+
return next.content;
|
|
115
|
+
}
|
|
116
|
+
return next.content.slice(current.content.length);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function removeReasoningDetails(
|
|
120
|
+
additionalKwargs: AIMessageChunk['additional_kwargs']
|
|
121
|
+
): AIMessageChunk['additional_kwargs'] {
|
|
122
|
+
return Object.fromEntries(
|
|
123
|
+
Object.entries(additionalKwargs).filter(
|
|
124
|
+
([key]) => key !== 'reasoning_details'
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getStreamHandlingChunk({
|
|
130
|
+
current,
|
|
131
|
+
next,
|
|
132
|
+
provider,
|
|
133
|
+
}: {
|
|
134
|
+
current?: AIMessageChunk;
|
|
135
|
+
next: AIMessageChunk;
|
|
136
|
+
provider: Providers;
|
|
137
|
+
}): AIMessageChunk | undefined {
|
|
138
|
+
const content = getOpenRouterFinalReasoningContent({
|
|
139
|
+
current,
|
|
140
|
+
next,
|
|
141
|
+
provider,
|
|
142
|
+
});
|
|
143
|
+
if (content == null) {
|
|
144
|
+
return next;
|
|
145
|
+
}
|
|
146
|
+
if (content === '') {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
return new AIMessageChunk(
|
|
150
|
+
Object.assign({}, next, {
|
|
151
|
+
content,
|
|
152
|
+
additional_kwargs: removeReasoningDetails(next.additional_kwargs),
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function appendStreamChunk({
|
|
158
|
+
current,
|
|
159
|
+
next,
|
|
160
|
+
provider,
|
|
161
|
+
}: {
|
|
162
|
+
current?: AIMessageChunk;
|
|
163
|
+
next: AIMessageChunk;
|
|
164
|
+
provider: Providers;
|
|
165
|
+
}): AIMessageChunk {
|
|
166
|
+
if (current == null) {
|
|
167
|
+
return next;
|
|
168
|
+
}
|
|
169
|
+
return concat(
|
|
170
|
+
current,
|
|
171
|
+
removeOpenRouterFinalReasoningReplayContent({ current, next, provider })
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
54
175
|
/**
|
|
55
176
|
* Invokes a chat model with the given messages, handling both streaming and
|
|
56
177
|
* non-streaming paths.
|
|
@@ -90,23 +211,62 @@ export async function attemptInvoke(
|
|
|
90
211
|
if (model.stream) {
|
|
91
212
|
const stream = await model.stream(messagesForProvider, config);
|
|
92
213
|
let finalChunk: AIMessageChunk | undefined;
|
|
214
|
+
const registeredStreamHandler =
|
|
215
|
+
getRegisteredDefaultChatStreamHandler(context);
|
|
93
216
|
|
|
94
217
|
if (onChunk) {
|
|
95
218
|
for await (const chunk of stream) {
|
|
96
219
|
await onChunk(chunk);
|
|
97
|
-
finalChunk =
|
|
220
|
+
finalChunk = appendStreamChunk({
|
|
221
|
+
current: finalChunk,
|
|
222
|
+
next: chunk,
|
|
223
|
+
provider,
|
|
224
|
+
});
|
|
98
225
|
}
|
|
99
|
-
} else {
|
|
226
|
+
} else if (registeredStreamHandler == null) {
|
|
100
227
|
const metadata = config?.metadata as Record<string, unknown> | undefined;
|
|
101
228
|
const streamHandler = new ChatModelStreamHandler();
|
|
102
229
|
for await (const chunk of stream) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
109
|
-
|
|
230
|
+
const handlingChunk = getStreamHandlingChunk({
|
|
231
|
+
current: finalChunk,
|
|
232
|
+
next: chunk,
|
|
233
|
+
provider,
|
|
234
|
+
});
|
|
235
|
+
if (handlingChunk != null) {
|
|
236
|
+
await streamHandler.handle(
|
|
237
|
+
GraphEvents.CHAT_MODEL_STREAM,
|
|
238
|
+
{ chunk: handlingChunk },
|
|
239
|
+
metadata,
|
|
240
|
+
context
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
finalChunk = appendStreamChunk({
|
|
244
|
+
current: finalChunk,
|
|
245
|
+
next: chunk,
|
|
246
|
+
provider,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
const metadata = config?.metadata as Record<string, unknown> | undefined;
|
|
251
|
+
for await (const chunk of stream) {
|
|
252
|
+
const handlingChunk = getStreamHandlingChunk({
|
|
253
|
+
current: finalChunk,
|
|
254
|
+
next: chunk,
|
|
255
|
+
provider,
|
|
256
|
+
});
|
|
257
|
+
if (handlingChunk != null && handlingChunk !== chunk) {
|
|
258
|
+
await registeredStreamHandler.handle(
|
|
259
|
+
GraphEvents.CHAT_MODEL_STREAM,
|
|
260
|
+
{ chunk: handlingChunk },
|
|
261
|
+
metadata,
|
|
262
|
+
context
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
finalChunk = appendStreamChunk({
|
|
266
|
+
current: finalChunk,
|
|
267
|
+
next: chunk,
|
|
268
|
+
provider,
|
|
269
|
+
});
|
|
110
270
|
}
|
|
111
271
|
}
|
|
112
272
|
|
package/src/llm/openai/index.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { AzureOpenAI as AzureOpenAIClient } from 'openai';
|
|
2
2
|
import { ChatXAI as OriginalChatXAI } from '@langchain/xai';
|
|
3
3
|
import { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
4
|
+
import { ToolDefinition } from '@langchain/core/language_models/base';
|
|
5
|
+
import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
|
|
6
|
+
import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
4
7
|
import {
|
|
5
8
|
AIMessage,
|
|
6
9
|
AIMessageChunk,
|
|
7
10
|
isAIMessage,
|
|
8
11
|
} from '@langchain/core/messages';
|
|
9
|
-
import { ToolDefinition } from '@langchain/core/language_models/base';
|
|
10
12
|
import {
|
|
11
13
|
convertToOpenAITool,
|
|
12
14
|
isLangChainTool,
|
|
13
15
|
} from '@langchain/core/utils/function_calling';
|
|
14
|
-
import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
|
|
15
|
-
import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
16
16
|
import {
|
|
17
17
|
getEndpoint,
|
|
18
18
|
OpenAIClient,
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
AzureChatOpenAIResponses as OriginalAzureChatOpenAIResponses,
|
|
25
25
|
AzureChatOpenAICompletions as OriginalAzureChatOpenAICompletions,
|
|
26
26
|
} from '@langchain/openai';
|
|
27
|
-
import type { HeaderValue, HeadersLike } from './types';
|
|
28
27
|
import type {
|
|
29
28
|
BaseMessage,
|
|
30
29
|
BaseMessageChunk,
|
|
@@ -34,6 +33,7 @@ import type { BindToolsInput } from '@langchain/core/language_models/chat_models
|
|
|
34
33
|
import type { ChatGeneration, ChatResult } from '@langchain/core/outputs';
|
|
35
34
|
import type { ChatXAIInput } from '@langchain/xai';
|
|
36
35
|
import type * as t from '@langchain/openai';
|
|
36
|
+
import type { HeaderValue, HeadersLike } from './types';
|
|
37
37
|
import { isReasoningModel, _convertMessagesToOpenAIParams } from './utils';
|
|
38
38
|
|
|
39
39
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
2
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
3
3
|
import { type OpenAI as OpenAIClient } from 'openai';
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from '
|
|
4
|
+
import { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
5
|
+
import {
|
|
6
|
+
convertLangChainToolCallToOpenAI,
|
|
7
|
+
makeInvalidToolCall,
|
|
8
|
+
parseToolCall,
|
|
9
|
+
} from '@langchain/core/output_parsers/openai_tools';
|
|
10
10
|
import {
|
|
11
11
|
AIMessage,
|
|
12
12
|
AIMessageChunk,
|
|
@@ -25,24 +25,24 @@ import {
|
|
|
25
25
|
convertToProviderContentBlock,
|
|
26
26
|
isDataContentBlock,
|
|
27
27
|
} from '@langchain/core/messages';
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} from '
|
|
34
|
-
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
28
|
+
import type {
|
|
29
|
+
ChatCompletionContentPartText,
|
|
30
|
+
ChatCompletionContentPartImage,
|
|
31
|
+
ChatCompletionContentPartInputAudio,
|
|
32
|
+
ChatCompletionContentPart,
|
|
33
|
+
} from 'openai/resources/chat/completions';
|
|
35
34
|
import type {
|
|
36
35
|
OpenAICallOptions,
|
|
37
36
|
OpenAIChatInput,
|
|
38
37
|
ChatOpenAIReasoningSummary,
|
|
39
38
|
} from '@langchain/openai';
|
|
40
|
-
import {
|
|
39
|
+
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
41
40
|
import {
|
|
42
41
|
STREAMED_TOOL_CALL_SEAL_METADATA_KEY,
|
|
43
42
|
STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
|
|
44
43
|
OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER,
|
|
45
44
|
} from '@/tools/streamedToolCallSeals';
|
|
45
|
+
import { toLangChainContent } from '@/messages/langchain';
|
|
46
46
|
|
|
47
47
|
export type { OpenAICallOptions, OpenAIChatInput };
|
|
48
48
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ChatOpenAI, emitStreamChunkCallback } from '@/llm/openai';
|
|
2
|
-
import type { BaseMessage } from '@langchain/core/messages';
|
|
3
|
-
import type { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
4
|
-
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
5
1
|
import type {
|
|
6
2
|
ChatOpenAICallOptions,
|
|
7
3
|
OpenAIChatInput,
|
|
8
4
|
OpenAIClient,
|
|
9
5
|
} from '@langchain/openai';
|
|
6
|
+
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
7
|
+
import type { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
8
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
9
|
+
import { ChatOpenAI, emitStreamChunkCallback } from '@/llm/openai';
|
|
10
10
|
|
|
11
11
|
export type OpenRouterReasoningEffort =
|
|
12
12
|
| 'xhigh'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ChatOpenRouter } from './index';
|
|
2
|
-
import type { OpenRouterReasoning, ChatOpenRouterCallOptions } from './index';
|
|
3
1
|
import type { OpenAIChatInput } from '@langchain/openai';
|
|
2
|
+
import type { OpenRouterReasoning, ChatOpenRouterCallOptions } from './index';
|
|
3
|
+
import { ChatOpenRouter } from './index';
|
|
4
4
|
|
|
5
5
|
type CreateRouterOptions = Partial<
|
|
6
6
|
ChatOpenRouterCallOptions &
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expect, test, describe } from '@jest/globals';
|
|
2
|
-
import type { GeminiContent } from '@langchain/google-common';
|
|
3
2
|
import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
|
|
3
|
+
import type { GeminiContent } from '@langchain/google-common';
|
|
4
4
|
import { fixThoughtSignatures } from './index';
|
|
5
5
|
|
|
6
6
|
const SIG_A = 'AY89a1/sigA==';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ChatGoogle } from '@langchain/google-gauth';
|
|
2
2
|
import { ChatConnection } from '@langchain/google-common';
|
|
3
|
+
import { AIMessageChunk, isAIMessage } from '@langchain/core/messages';
|
|
3
4
|
import type {
|
|
4
5
|
GeminiContent,
|
|
5
6
|
GeminiRequest,
|
|
@@ -8,7 +9,6 @@ import type {
|
|
|
8
9
|
} from '@langchain/google-common';
|
|
9
10
|
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
10
11
|
import type { BaseMessage, UsageMetadata } from '@langchain/core/messages';
|
|
11
|
-
import { AIMessageChunk, isAIMessage } from '@langchain/core/messages';
|
|
12
12
|
import type { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
13
13
|
import type { GoogleThinkingConfig, VertexAIClientOptions } from '@/types';
|
|
14
14
|
|
|
@@ -13,9 +13,11 @@ import {
|
|
|
13
13
|
stripBedrockCacheControl,
|
|
14
14
|
addBedrockCacheControl,
|
|
15
15
|
addCacheControl,
|
|
16
|
+
addCacheControlToStablePrefixMessages,
|
|
16
17
|
} from './cache';
|
|
17
18
|
import { _convertMessagesToOpenAIParams } from '@/llm/openai/utils';
|
|
18
19
|
import { toLangChainContent } from './langchain';
|
|
20
|
+
import { formatAgentMessages } from './format';
|
|
19
21
|
import { ContentTypes } from '@/common/enum';
|
|
20
22
|
|
|
21
23
|
type CacheControlBlock = MessageContentComplex & {
|
|
@@ -766,6 +768,127 @@ describe('addBedrockCacheControl (Bedrock cache checkpoints)', () => {
|
|
|
766
768
|
});
|
|
767
769
|
});
|
|
768
770
|
|
|
771
|
+
describe('synthetic skill/meta messages are not cache-anchored', () => {
|
|
772
|
+
const hasAnthropicMarker = (m: BaseMessage): boolean =>
|
|
773
|
+
Array.isArray(m.content) &&
|
|
774
|
+
m.content.some((block) => 'cache_control' in block);
|
|
775
|
+
|
|
776
|
+
const hasBedrockCachePoint = (m: BaseMessage): boolean =>
|
|
777
|
+
Array.isArray(m.content) &&
|
|
778
|
+
m.content.some((block) => 'cachePoint' in block);
|
|
779
|
+
|
|
780
|
+
const skillBody = (skillName: string, content = 'SKILL BODY'): HumanMessage =>
|
|
781
|
+
new HumanMessage({
|
|
782
|
+
content,
|
|
783
|
+
additional_kwargs: { isMeta: true, source: 'skill', skillName },
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it('Anthropic: skips a trailing synthetic skill message; markers land on the real user messages', () => {
|
|
787
|
+
const messages: BaseMessage[] = [
|
|
788
|
+
new HumanMessage('First real question'),
|
|
789
|
+
new AIMessage('Answer'),
|
|
790
|
+
new HumanMessage('Second real question'),
|
|
791
|
+
skillBody('pdf-analyzer'),
|
|
792
|
+
];
|
|
793
|
+
|
|
794
|
+
const result = addCacheControl<BaseMessage>(messages);
|
|
795
|
+
|
|
796
|
+
expect(hasAnthropicMarker(result[3])).toBe(false);
|
|
797
|
+
expect(hasAnthropicMarker(result[2])).toBe(true);
|
|
798
|
+
expect(hasAnthropicMarker(result[0])).toBe(true);
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it('Anthropic: strips a stale marker from a synthetic skill message without re-adding one', () => {
|
|
802
|
+
const stale = new HumanMessage({
|
|
803
|
+
content: toLangChainContent([
|
|
804
|
+
{
|
|
805
|
+
type: 'text',
|
|
806
|
+
text: 'SKILL BODY',
|
|
807
|
+
cache_control: { type: 'ephemeral' },
|
|
808
|
+
} as MessageContentComplex,
|
|
809
|
+
]),
|
|
810
|
+
additional_kwargs: {
|
|
811
|
+
isMeta: true,
|
|
812
|
+
source: 'skill',
|
|
813
|
+
skillName: 'pdf-analyzer',
|
|
814
|
+
},
|
|
815
|
+
});
|
|
816
|
+
const messages: BaseMessage[] = [
|
|
817
|
+
new HumanMessage('Real question'),
|
|
818
|
+
new AIMessage('Answer'),
|
|
819
|
+
stale,
|
|
820
|
+
];
|
|
821
|
+
|
|
822
|
+
const result = addCacheControl<BaseMessage>(messages);
|
|
823
|
+
|
|
824
|
+
expect(hasAnthropicMarker(result[2])).toBe(false);
|
|
825
|
+
expect(hasAnthropicMarker(result[0])).toBe(true);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
it('Anthropic: detects skill messages by additional_kwargs.source even without isMeta', () => {
|
|
829
|
+
const messages: BaseMessage[] = [
|
|
830
|
+
new HumanMessage('Real question'),
|
|
831
|
+
new AIMessage('Answer'),
|
|
832
|
+
new HumanMessage({
|
|
833
|
+
content: 'SKILL BODY',
|
|
834
|
+
additional_kwargs: { source: 'skill', skillName: 'pdf-analyzer' },
|
|
835
|
+
}),
|
|
836
|
+
];
|
|
837
|
+
|
|
838
|
+
const result = addCacheControl<BaseMessage>(messages);
|
|
839
|
+
|
|
840
|
+
expect(hasAnthropicMarker(result[2])).toBe(false);
|
|
841
|
+
expect(hasAnthropicMarker(result[0])).toBe(true);
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('Bedrock: skips a trailing synthetic skill message; cachePoints land on the real user messages', () => {
|
|
845
|
+
const messages: BaseMessage[] = [
|
|
846
|
+
new HumanMessage('First real question'),
|
|
847
|
+
new AIMessage('Answer'),
|
|
848
|
+
new HumanMessage('Second real question'),
|
|
849
|
+
skillBody('pdf-analyzer'),
|
|
850
|
+
];
|
|
851
|
+
|
|
852
|
+
const result = addBedrockCacheControl<BaseMessage>(messages);
|
|
853
|
+
|
|
854
|
+
expect(hasBedrockCachePoint(result[3])).toBe(false);
|
|
855
|
+
expect(hasBedrockCachePoint(result[2])).toBe(true);
|
|
856
|
+
expect(hasBedrockCachePoint(result[0])).toBe(true);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
it('stable-prefix fallback: anchors the real user message, not a synthetic skill message', () => {
|
|
860
|
+
// Mirrors AgentContext's dynamic-tail path: the only assistant message is a
|
|
861
|
+
// skill-only tool call (no text), so the assistant-only pass adds no marker
|
|
862
|
+
// and the cacheable fallback runs. It must skip the reconstructed skill
|
|
863
|
+
// HumanMessage and anchor the real user message instead.
|
|
864
|
+
const messages: BaseMessage[] = [
|
|
865
|
+
new HumanMessage('Real stable question'),
|
|
866
|
+
new AIMessage({
|
|
867
|
+
content: toLangChainContent([
|
|
868
|
+
{
|
|
869
|
+
type: 'tool_use',
|
|
870
|
+
id: 'call_1',
|
|
871
|
+
name: 'skill',
|
|
872
|
+
input: { skillName: 'pdf-analyzer' },
|
|
873
|
+
} as MessageContentComplex,
|
|
874
|
+
]),
|
|
875
|
+
tool_calls: [
|
|
876
|
+
{ id: 'call_1', name: 'skill', args: { skillName: 'pdf-analyzer' } },
|
|
877
|
+
],
|
|
878
|
+
}),
|
|
879
|
+
skillBody('pdf-analyzer'),
|
|
880
|
+
];
|
|
881
|
+
|
|
882
|
+
const result = addCacheControlToStablePrefixMessages<BaseMessage>(
|
|
883
|
+
messages,
|
|
884
|
+
2
|
|
885
|
+
);
|
|
886
|
+
|
|
887
|
+
expect(hasAnthropicMarker(result[2])).toBe(false);
|
|
888
|
+
expect(hasAnthropicMarker(result[0])).toBe(true);
|
|
889
|
+
});
|
|
890
|
+
});
|
|
891
|
+
|
|
769
892
|
describe('stripAnthropicCacheControl', () => {
|
|
770
893
|
it('removes cache_control fields from content blocks', () => {
|
|
771
894
|
const messages: TestMsg[] = [
|
|
@@ -1474,6 +1597,27 @@ describe('Multi-turn cache cleanup', () => {
|
|
|
1474
1597
|
});
|
|
1475
1598
|
|
|
1476
1599
|
describe('LangChain message type preservation', () => {
|
|
1600
|
+
it('preserves direct roles for formatted LangChain messages after addCacheControl', () => {
|
|
1601
|
+
const { messages } = formatAgentMessages([
|
|
1602
|
+
{ role: 'user', content: 'Hello' },
|
|
1603
|
+
{ role: 'assistant', content: 'Hi there' },
|
|
1604
|
+
{ role: 'user', content: 'Thanks' },
|
|
1605
|
+
]);
|
|
1606
|
+
|
|
1607
|
+
const result = addCacheControl(messages);
|
|
1608
|
+
|
|
1609
|
+
expect(result.map((message) => message.role)).toEqual([
|
|
1610
|
+
'user',
|
|
1611
|
+
'assistant',
|
|
1612
|
+
'user',
|
|
1613
|
+
]);
|
|
1614
|
+
expect(result[0]).toBeInstanceOf(HumanMessage);
|
|
1615
|
+
expect(result[0]).not.toBe(messages[0]);
|
|
1616
|
+
expect(Object.keys(result[0])).not.toContain('role');
|
|
1617
|
+
expect(result[1]).toBe(messages[1]);
|
|
1618
|
+
expect(result[2]).not.toBe(messages[2]);
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1477
1621
|
it('should preserve instanceof for LangChain messages after addCacheControl', () => {
|
|
1478
1622
|
const messages: BaseMessage[] = [
|
|
1479
1623
|
new HumanMessage({ content: [{ type: 'text', text: 'Hello' }] }),
|
package/src/messages/cache.ts
CHANGED
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
SystemMessage,
|
|
7
7
|
MessageContentComplex,
|
|
8
8
|
} from '@langchain/core/messages';
|
|
9
|
-
import type { AnthropicMessage } from '@/types/messages';
|
|
10
9
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
11
|
-
import {
|
|
10
|
+
import type { AnthropicMessage } from '@/types/messages';
|
|
12
11
|
import { toLangChainContent } from './langchain';
|
|
12
|
+
import { ContentTypes } from '@/common/enum';
|
|
13
|
+
import { withMessageRole } from './format';
|
|
13
14
|
|
|
14
15
|
type MessageWithContent = {
|
|
15
16
|
content?: string | MessageContentComplex[];
|
|
@@ -56,19 +57,31 @@ function cloneMessage<T extends MessageWithContent>(
|
|
|
56
57
|
const msgType = message.getType();
|
|
57
58
|
switch (msgType) {
|
|
58
59
|
case 'ai':
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
return withMessageRole(
|
|
61
|
+
new AIMessage({
|
|
62
|
+
...baseParams,
|
|
63
|
+
tool_calls: (message as unknown as AIMessage).tool_calls,
|
|
64
|
+
}),
|
|
65
|
+
'assistant'
|
|
66
|
+
) as unknown as T;
|
|
63
67
|
case 'human':
|
|
64
|
-
return
|
|
68
|
+
return withMessageRole(
|
|
69
|
+
new HumanMessage(baseParams),
|
|
70
|
+
'user'
|
|
71
|
+
) as unknown as T;
|
|
65
72
|
case 'system':
|
|
66
|
-
return
|
|
73
|
+
return withMessageRole(
|
|
74
|
+
new SystemMessage(baseParams),
|
|
75
|
+
'system'
|
|
76
|
+
) as unknown as T;
|
|
67
77
|
case 'tool':
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
return withMessageRole(
|
|
79
|
+
new ToolMessage({
|
|
80
|
+
...baseParams,
|
|
81
|
+
tool_call_id: (message as unknown as ToolMessage).tool_call_id,
|
|
82
|
+
}),
|
|
83
|
+
'tool'
|
|
84
|
+
) as unknown as T;
|
|
72
85
|
default:
|
|
73
86
|
break;
|
|
74
87
|
}
|
|
@@ -169,6 +182,7 @@ export function addCacheControl<T extends AnthropicMessage | BaseMessage>(
|
|
|
169
182
|
const needsCacheAdd =
|
|
170
183
|
userMessagesModified < 2 &&
|
|
171
184
|
isUserMessage &&
|
|
185
|
+
!isSyntheticMetaMessage(originalMessage) &&
|
|
172
186
|
(typeof content === 'string' || hasArrayContent);
|
|
173
187
|
|
|
174
188
|
// Skip messages that don't need any work
|
|
@@ -250,6 +264,26 @@ function getMessageRole(message: MessageWithContent): string | undefined {
|
|
|
250
264
|
return undefined;
|
|
251
265
|
}
|
|
252
266
|
|
|
267
|
+
const SKILL_MESSAGE_SOURCE = 'skill';
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Synthetic skill/meta messages (reconstructed skill bodies, primed SKILL.md
|
|
271
|
+
* instructions) are re-injected every turn and are not stable conversation
|
|
272
|
+
* turns. They must not anchor a fresh prompt-cache marker — doing so pins the
|
|
273
|
+
* cache to a volatile/duplicated prefix. Stale markers are still stripped from
|
|
274
|
+
* them; only the *adding* of new markers is suppressed. Detected via
|
|
275
|
+
* `additional_kwargs.isMeta === true` or `additional_kwargs.source === 'skill'`.
|
|
276
|
+
*/
|
|
277
|
+
function isSyntheticMetaMessage(message: MessageWithContent): boolean {
|
|
278
|
+
const { additional_kwargs: kwargs } = message as {
|
|
279
|
+
additional_kwargs?: { isMeta?: unknown; source?: unknown };
|
|
280
|
+
};
|
|
281
|
+
if (kwargs == null) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
return kwargs.isMeta === true || kwargs.source === SKILL_MESSAGE_SOURCE;
|
|
285
|
+
}
|
|
286
|
+
|
|
253
287
|
function isCacheableConversationMessage(message: MessageWithContent): boolean {
|
|
254
288
|
const role = getMessageRole(message);
|
|
255
289
|
return (
|
|
@@ -292,7 +326,9 @@ function addCacheControlToRecentMessages<
|
|
|
292
326
|
const content = originalMessage.content;
|
|
293
327
|
const hasArrayContent = Array.isArray(content);
|
|
294
328
|
const canAddCache =
|
|
295
|
-
cachePointsAdded < maxCachePoints &&
|
|
329
|
+
cachePointsAdded < maxCachePoints &&
|
|
330
|
+
canUseMessage(originalMessage) &&
|
|
331
|
+
!isSyntheticMetaMessage(originalMessage);
|
|
296
332
|
|
|
297
333
|
if (!canAddCache && !hasArrayContent) {
|
|
298
334
|
continue;
|
|
@@ -523,6 +559,7 @@ export function addBedrockCacheControl<
|
|
|
523
559
|
isUserMessage &&
|
|
524
560
|
!isToolMessage &&
|
|
525
561
|
!isEmptyString &&
|
|
562
|
+
!isSyntheticMetaMessage(originalMessage) &&
|
|
526
563
|
(typeof content === 'string' || hasArrayContent);
|
|
527
564
|
|
|
528
565
|
if (!needsCacheAdd && !hasArrayContent && !hasSerializationProps) {
|
package/src/messages/content.ts
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { ToolMessage, type BaseMessage } from '@langchain/core/messages';
|
|
16
|
+
import type { ContextPruningSettings } from './contextPruningSettings';
|
|
16
17
|
import type { ContextPruningConfig } from '@/types/graph';
|
|
17
18
|
import type { TokenCounter } from '@/types/run';
|
|
18
|
-
import type { ContextPruningSettings } from './contextPruningSettings';
|
|
19
19
|
import { resolveContextPruningSettings } from './contextPruningSettings';
|
|
20
20
|
|
|
21
21
|
/**
|