@librechat/agents 3.1.81 → 3.1.82
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 +102 -35
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +13 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +50 -13
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +17 -7
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/toolCache.cjs +55 -0
- package/dist/cjs/llm/openrouter/toolCache.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +70 -12
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +101 -34
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +13 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +50 -14
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +17 -7
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/toolCache.mjs +53 -0
- package/dist/esm/llm/openrouter/toolCache.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +70 -12
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +6 -1
- package/dist/types/llm/openrouter/index.d.ts +1 -0
- package/dist/types/llm/openrouter/toolCache.d.ts +2 -0
- package/dist/types/tools/ToolNode.d.ts +5 -0
- package/dist/types/types/run.d.ts +2 -0
- package/package.json +2 -1
- package/src/agents/AgentContext.ts +146 -38
- package/src/agents/__tests__/AgentContext.test.ts +198 -0
- package/src/graphs/Graph.ts +24 -0
- package/src/llm/custom-chat-models.smoke.test.ts +76 -0
- package/src/llm/openai/deepseek.test.ts +14 -1
- package/src/llm/openai/index.ts +38 -12
- package/src/llm/openrouter/index.ts +22 -7
- package/src/llm/openrouter/reasoning.test.ts +33 -0
- package/src/llm/openrouter/toolCache.test.ts +83 -0
- package/src/llm/openrouter/toolCache.ts +89 -0
- package/src/messages/cache.test.ts +127 -0
- package/src/scripts/openrouter_prompt_cache_live.ts +310 -0
- package/src/specs/agent-handoffs.live.test.ts +140 -0
- package/src/specs/agent-handoffs.test.ts +266 -2
- package/src/specs/openrouter.simple.test.ts +15 -8
- package/src/tools/ToolNode.ts +92 -13
- package/src/types/run.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../src/llm/openrouter/index.ts"],"sourcesContent":["import { ChatOpenAI } from '@/llm/openai';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type {\n ChatOpenAICallOptions,\n OpenAIChatInput,\n OpenAIClient,\n} from '@langchain/openai';\n\nexport type OpenRouterReasoningEffort =\n | 'xhigh'\n | 'high'\n | 'medium'\n | 'low'\n | 'minimal'\n | 'none';\n\nexport interface OpenRouterReasoning {\n effort?: OpenRouterReasoningEffort;\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n}\n\nexport interface ChatOpenRouterCallOptions\n extends Omit<ChatOpenAICallOptions, 'reasoning'> {\n /** @deprecated Use `reasoning` object instead */\n include_reasoning?: boolean;\n reasoning?: OpenRouterReasoning;\n modelKwargs?: OpenAIChatInput['modelKwargs'];\n}\n\nexport type ChatOpenRouterInput = Partial<\n ChatOpenRouterCallOptions & OpenAIChatInput\n>;\n\n/** invocationParams return type extended with OpenRouter reasoning */\nexport type OpenRouterInvocationParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n> & {\n reasoning?: OpenRouterReasoning;\n};\n\ntype InvocationParamsExtra = {\n streaming?: boolean;\n};\n\ninterface OpenRouterReasoningTextDetail {\n type: 'reasoning.text';\n text?: string;\n format?: string;\n index?: number;\n}\n\ninterface OpenRouterReasoningEncryptedDetail {\n type: 'reasoning.encrypted';\n id?: string;\n data?: string;\n format?: string;\n index?: number;\n}\n\ntype OpenRouterReasoningDetail =\n | OpenRouterReasoningTextDetail\n | OpenRouterReasoningEncryptedDetail;\n\nfunction isReasoningTextDetail(\n value: unknown\n): value is OpenRouterReasoningTextDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.text'\n );\n}\n\nfunction isReasoningEncryptedDetail(\n value: unknown\n): value is OpenRouterReasoningEncryptedDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.encrypted'\n );\n}\n\nfunction getReasoningDetails(value: unknown): OpenRouterReasoningDetail[] {\n if (!Array.isArray(value)) {\n return [];\n }\n return value.filter(\n (detail): detail is OpenRouterReasoningDetail =>\n isReasoningTextDetail(detail) || isReasoningEncryptedDetail(detail)\n );\n}\n\nexport class ChatOpenRouter extends ChatOpenAI {\n private openRouterReasoning?: OpenRouterReasoning;\n /** @deprecated Use `reasoning` object instead */\n private includeReasoning?: boolean;\n\n constructor(_fields: ChatOpenRouterInput) {\n const {\n include_reasoning,\n reasoning: openRouterReasoning,\n modelKwargs = {},\n ...fields\n } = _fields;\n\n // Extract reasoning from modelKwargs if provided there (e.g., from LLMConfig)\n const { reasoning: mkReasoning, ...restModelKwargs } = modelKwargs as {\n reasoning?: OpenRouterReasoning;\n } & Record<string, unknown>;\n\n super({\n ...fields,\n modelKwargs: restModelKwargs,\n includeReasoningDetails: true,\n convertReasoningDetailsToContent: true,\n });\n\n // Merge reasoning config: modelKwargs.reasoning < constructor reasoning\n if (mkReasoning != null || openRouterReasoning != null) {\n this.openRouterReasoning = {\n ...mkReasoning,\n ...openRouterReasoning,\n };\n }\n\n this.includeReasoning = include_reasoning;\n }\n static lc_name(): 'LibreChatOpenRouter' {\n return 'LibreChatOpenRouter';\n }\n\n // @ts-expect-error - OpenRouter reasoning extends OpenAI Reasoning with additional\n // effort levels ('xhigh' | 'none' | 'minimal') not in ReasoningEffort.\n // The parent's generic conditional return type cannot be widened in an override.\n override invocationParams(\n options?: this['ParsedCallOptions'],\n extra?: InvocationParamsExtra\n ): OpenRouterInvocationParams {\n type MutableParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n > & { reasoning_effort?: string; reasoning?: OpenRouterReasoning };\n\n const optionsWithDefaults = this._combineCallOptions(options);\n const params = (\n this._useResponsesApi(options)\n ? this.responses.invocationParams(optionsWithDefaults)\n : this.completions.invocationParams(optionsWithDefaults, extra)\n ) as MutableParams;\n\n // Remove the OpenAI-native reasoning_effort that the parent sets;\n // OpenRouter uses a `reasoning` object instead\n delete params.reasoning_effort;\n\n // Build the OpenRouter reasoning config\n const reasoning = this.buildOpenRouterReasoning(optionsWithDefaults);\n if (reasoning != null) {\n params.reasoning = reasoning;\n } else {\n delete params.reasoning;\n }\n\n return params;\n }\n\n private buildOpenRouterReasoning(\n options?: this['ParsedCallOptions']\n ): OpenRouterReasoning | undefined {\n let reasoning: OpenRouterReasoning | undefined;\n\n // 1. Instance-level reasoning config (from constructor)\n if (this.openRouterReasoning != null) {\n reasoning = { ...this.openRouterReasoning };\n }\n\n // 2. LangChain-style reasoning params (from parent's `this.reasoning`)\n const lcReasoning = this.getReasoningParams(options);\n if (lcReasoning?.effort != null) {\n reasoning = {\n ...reasoning,\n effort: lcReasoning.effort as OpenRouterReasoningEffort,\n };\n }\n\n // 3. Call-level reasoning override\n const callReasoning = (options as ChatOpenRouterCallOptions | undefined)\n ?.reasoning;\n if (callReasoning != null) {\n reasoning = { ...reasoning, ...callReasoning };\n }\n\n // 4. Legacy include_reasoning backward compatibility\n if (reasoning == null && this.includeReasoning === true) {\n reasoning = { enabled: true };\n }\n\n return reasoning;\n }\n\n override async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const reasoningTextByIndex = new Map<\n number,\n OpenRouterReasoningTextDetail\n >();\n const reasoningEncryptedById = new Map<\n string,\n OpenRouterReasoningEncryptedDetail\n >();\n\n for await (const generationChunk of super._streamResponseChunks(\n messages,\n options,\n runManager\n )) {\n let currentReasoningText = '';\n const reasoningDetails = getReasoningDetails(\n generationChunk.message.additional_kwargs.reasoning_details\n );\n\n for (const detail of reasoningDetails) {\n if (detail.type === 'reasoning.text') {\n currentReasoningText += detail.text ?? '';\n const index = detail.index ?? 0;\n const existing = reasoningTextByIndex.get(index);\n if (existing != null) {\n existing.text = `${existing.text ?? ''}${detail.text ?? ''}`;\n continue;\n }\n reasoningTextByIndex.set(index, {\n ...detail,\n text: detail.text ?? '',\n });\n continue;\n }\n if (detail.id != null) {\n reasoningEncryptedById.set(detail.id, { ...detail });\n }\n }\n\n if (\n currentReasoningText.length > 0 &&\n generationChunk.message.additional_kwargs.reasoning == null\n ) {\n generationChunk.message.additional_kwargs.reasoning =\n currentReasoningText;\n }\n\n if (generationChunk.generationInfo?.finish_reason != null) {\n const finalReasoningDetails = [\n ...reasoningTextByIndex.values(),\n ...reasoningEncryptedById.values(),\n ];\n if (finalReasoningDetails.length > 0) {\n generationChunk.message.additional_kwargs.reasoning_details =\n finalReasoningDetails;\n } else {\n delete generationChunk.message.additional_kwargs.reasoning_details;\n }\n yield generationChunk;\n continue;\n }\n\n delete generationChunk.message.additional_kwargs.reasoning_details;\n yield generationChunk;\n }\n }\n}\n"],"names":["ChatOpenAI"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/llm/openrouter/index.ts"],"sourcesContent":["import { ChatOpenAI } from '@/llm/openai';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type {\n ChatOpenAICallOptions,\n OpenAIChatInput,\n OpenAIClient,\n} from '@langchain/openai';\n\nexport type OpenRouterReasoningEffort =\n | 'xhigh'\n | 'high'\n | 'medium'\n | 'low'\n | 'minimal'\n | 'none';\n\nexport interface OpenRouterReasoning {\n effort?: OpenRouterReasoningEffort;\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n}\n\nexport interface ChatOpenRouterCallOptions\n extends Omit<ChatOpenAICallOptions, 'reasoning'> {\n /** @deprecated Use `reasoning` object instead */\n include_reasoning?: boolean;\n reasoning?: OpenRouterReasoning;\n modelKwargs?: OpenAIChatInput['modelKwargs'];\n promptCache?: boolean;\n}\n\nexport type ChatOpenRouterInput = Partial<\n ChatOpenRouterCallOptions & OpenAIChatInput\n>;\n\n/** invocationParams return type extended with OpenRouter reasoning */\nexport type OpenRouterInvocationParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n> & {\n reasoning?: OpenRouterReasoning;\n};\n\ntype InvocationParamsExtra = {\n streaming?: boolean;\n};\n\ninterface OpenRouterReasoningTextDetail {\n type: 'reasoning.text';\n text?: string;\n format?: string;\n index?: number;\n}\n\ninterface OpenRouterReasoningEncryptedDetail {\n type: 'reasoning.encrypted';\n id?: string;\n data?: string;\n format?: string;\n index?: number;\n}\n\ntype OpenRouterReasoningDetail =\n | OpenRouterReasoningTextDetail\n | OpenRouterReasoningEncryptedDetail;\n\nfunction isReasoningTextDetail(\n value: unknown\n): value is OpenRouterReasoningTextDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.text'\n );\n}\n\nfunction isReasoningEncryptedDetail(\n value: unknown\n): value is OpenRouterReasoningEncryptedDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.encrypted'\n );\n}\n\nfunction getReasoningDetails(value: unknown): OpenRouterReasoningDetail[] {\n if (!Array.isArray(value)) {\n return [];\n }\n return value.filter(\n (detail): detail is OpenRouterReasoningDetail =>\n isReasoningTextDetail(detail) || isReasoningEncryptedDetail(detail)\n );\n}\n\nexport class ChatOpenRouter extends ChatOpenAI {\n private openRouterReasoning?: OpenRouterReasoning;\n /** @deprecated Use `reasoning` object instead */\n private includeReasoning?: boolean;\n\n constructor(_fields: ChatOpenRouterInput) {\n const fieldsWithoutPromptCache: ChatOpenRouterInput = { ..._fields };\n delete fieldsWithoutPromptCache.promptCache;\n\n const {\n include_reasoning,\n reasoning: openRouterReasoning,\n modelKwargs = {},\n ...fields\n } = fieldsWithoutPromptCache;\n\n // Extract reasoning from modelKwargs if provided there (e.g., from LLMConfig)\n const { reasoning: mkReasoning, ...restModelKwargs } = modelKwargs as {\n reasoning?: OpenRouterReasoning;\n } & Record<string, unknown>;\n const mergedReasoning =\n mkReasoning != null || openRouterReasoning != null\n ? {\n ...mkReasoning,\n ...openRouterReasoning,\n }\n : undefined;\n const runtimeReasoning =\n mergedReasoning ??\n (include_reasoning === true ? { enabled: true } : undefined);\n const parentModelKwargs =\n runtimeReasoning == null\n ? restModelKwargs\n : { ...restModelKwargs, reasoning: runtimeReasoning };\n\n super({\n ...fields,\n modelKwargs: parentModelKwargs,\n includeReasoningDetails: true,\n convertReasoningDetailsToContent: true,\n });\n\n // Merge reasoning config: modelKwargs.reasoning < constructor reasoning\n if (mergedReasoning != null) {\n this.openRouterReasoning = mergedReasoning;\n }\n\n this.includeReasoning = include_reasoning;\n }\n static lc_name(): 'LibreChatOpenRouter' {\n return 'LibreChatOpenRouter';\n }\n\n // @ts-expect-error - OpenRouter reasoning extends OpenAI Reasoning with additional\n // effort levels ('xhigh' | 'none' | 'minimal') not in ReasoningEffort.\n // The parent's generic conditional return type cannot be widened in an override.\n override invocationParams(\n options?: this['ParsedCallOptions'],\n extra?: InvocationParamsExtra\n ): OpenRouterInvocationParams {\n type MutableParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n > & { reasoning_effort?: string; reasoning?: OpenRouterReasoning };\n\n const optionsWithDefaults = this._combineCallOptions(options);\n const params = (\n this._useResponsesApi(options)\n ? this.responses.invocationParams(optionsWithDefaults)\n : this.completions.invocationParams(optionsWithDefaults, extra)\n ) as MutableParams;\n\n // Remove the OpenAI-native reasoning_effort that the parent sets;\n // OpenRouter uses a `reasoning` object instead\n delete params.reasoning_effort;\n\n // Build the OpenRouter reasoning config\n const reasoning = this.buildOpenRouterReasoning(optionsWithDefaults);\n if (reasoning != null) {\n params.reasoning = reasoning;\n } else {\n delete params.reasoning;\n }\n\n return params;\n }\n\n private buildOpenRouterReasoning(\n options?: this['ParsedCallOptions']\n ): OpenRouterReasoning | undefined {\n let reasoning: OpenRouterReasoning | undefined;\n\n // 1. Instance-level reasoning config (from constructor)\n if (this.openRouterReasoning != null) {\n reasoning = { ...this.openRouterReasoning };\n }\n\n // 2. LangChain-style reasoning params (from parent's `this.reasoning`)\n const lcReasoning = this.getReasoningParams(options);\n if (lcReasoning?.effort != null) {\n reasoning = {\n ...reasoning,\n effort: lcReasoning.effort as OpenRouterReasoningEffort,\n };\n }\n\n // 3. Call-level reasoning override\n const callReasoning = (options as ChatOpenRouterCallOptions | undefined)\n ?.reasoning;\n if (callReasoning != null) {\n reasoning = { ...reasoning, ...callReasoning };\n }\n\n // 4. Legacy include_reasoning backward compatibility\n if (reasoning == null && this.includeReasoning === true) {\n reasoning = { enabled: true };\n }\n\n return reasoning;\n }\n\n override async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const reasoningTextByIndex = new Map<\n number,\n OpenRouterReasoningTextDetail\n >();\n const reasoningEncryptedById = new Map<\n string,\n OpenRouterReasoningEncryptedDetail\n >();\n\n for await (const generationChunk of super._streamResponseChunks(\n messages,\n options,\n runManager\n )) {\n let currentReasoningText = '';\n const reasoningDetails = getReasoningDetails(\n generationChunk.message.additional_kwargs.reasoning_details\n );\n\n for (const detail of reasoningDetails) {\n if (detail.type === 'reasoning.text') {\n currentReasoningText += detail.text ?? '';\n const index = detail.index ?? 0;\n const existing = reasoningTextByIndex.get(index);\n if (existing != null) {\n existing.text = `${existing.text ?? ''}${detail.text ?? ''}`;\n continue;\n }\n reasoningTextByIndex.set(index, {\n ...detail,\n text: detail.text ?? '',\n });\n continue;\n }\n if (detail.id != null) {\n reasoningEncryptedById.set(detail.id, { ...detail });\n }\n }\n\n if (\n currentReasoningText.length > 0 &&\n generationChunk.message.additional_kwargs.reasoning == null\n ) {\n generationChunk.message.additional_kwargs.reasoning =\n currentReasoningText;\n }\n\n if (generationChunk.generationInfo?.finish_reason != null) {\n const finalReasoningDetails = [\n ...reasoningTextByIndex.values(),\n ...reasoningEncryptedById.values(),\n ];\n if (finalReasoningDetails.length > 0) {\n generationChunk.message.additional_kwargs.reasoning_details =\n finalReasoningDetails;\n } else {\n delete generationChunk.message.additional_kwargs.reasoning_details;\n }\n yield generationChunk;\n continue;\n }\n\n delete generationChunk.message.additional_kwargs.reasoning_details;\n yield generationChunk;\n }\n }\n}\n"],"names":["ChatOpenAI"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var index = require('../openai/index.cjs');
|
|
4
|
+
|
|
5
|
+
const CACHE_CONTROL = { type: 'ephemeral' };
|
|
6
|
+
function getToolName(tool) {
|
|
7
|
+
const candidate = tool;
|
|
8
|
+
if (typeof candidate.name === 'string') {
|
|
9
|
+
return candidate.name;
|
|
10
|
+
}
|
|
11
|
+
if (typeof candidate.function?.name === 'string') {
|
|
12
|
+
return candidate.function.name;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
function hasDeferredMarker(tool) {
|
|
17
|
+
return tool.defer_loading === true;
|
|
18
|
+
}
|
|
19
|
+
function toOpenRouterTool(tool) {
|
|
20
|
+
const converted = index._convertToOpenAITool(tool);
|
|
21
|
+
if (hasDeferredMarker(tool)) {
|
|
22
|
+
return { ...converted, defer_loading: true };
|
|
23
|
+
}
|
|
24
|
+
return converted;
|
|
25
|
+
}
|
|
26
|
+
function markCacheControl(tool) {
|
|
27
|
+
return {
|
|
28
|
+
...tool,
|
|
29
|
+
cache_control: CACHE_CONTROL,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function partitionAndMarkOpenRouterToolCache(tools, isDeferred) {
|
|
33
|
+
if (tools == null || tools.length === 0) {
|
|
34
|
+
return tools;
|
|
35
|
+
}
|
|
36
|
+
const staticTools = [];
|
|
37
|
+
const deferredTools = [];
|
|
38
|
+
for (const tool of tools) {
|
|
39
|
+
const converted = toOpenRouterTool(tool);
|
|
40
|
+
const name = getToolName(converted) ?? getToolName(tool);
|
|
41
|
+
if (name != null && isDeferred(name)) {
|
|
42
|
+
deferredTools.push(converted);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
staticTools.push(converted);
|
|
46
|
+
}
|
|
47
|
+
if (staticTools.length === 0) {
|
|
48
|
+
return [...deferredTools];
|
|
49
|
+
}
|
|
50
|
+
staticTools[staticTools.length - 1] = markCacheControl(staticTools[staticTools.length - 1]);
|
|
51
|
+
return [...staticTools, ...deferredTools];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
exports.partitionAndMarkOpenRouterToolCache = partitionAndMarkOpenRouterToolCache;
|
|
55
|
+
//# sourceMappingURL=toolCache.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolCache.cjs","sources":["../../../../src/llm/openrouter/toolCache.ts"],"sourcesContent":["import type { BindToolsInput } from '@langchain/core/language_models/chat_models';\nimport type { OpenAIClient } from '@langchain/openai';\nimport type { GraphTools } from '@/types';\nimport { _convertToOpenAITool } from '@/llm/openai';\n\nconst CACHE_CONTROL = { type: 'ephemeral' as const };\n\ntype OpenRouterToolWithCacheControl = OpenAIClient.ChatCompletionTool & {\n cache_control?: typeof CACHE_CONTROL;\n defer_loading?: boolean;\n};\n\ntype ToolNameCandidate = {\n name?: unknown;\n function?: {\n name?: unknown;\n };\n defer_loading?: unknown;\n};\n\nfunction getToolName(tool: unknown): string | undefined {\n const candidate = tool as ToolNameCandidate;\n if (typeof candidate.name === 'string') {\n return candidate.name;\n }\n if (typeof candidate.function?.name === 'string') {\n return candidate.function.name;\n }\n return undefined;\n}\n\nfunction hasDeferredMarker(tool: unknown): boolean {\n return (tool as ToolNameCandidate).defer_loading === true;\n}\n\nfunction toOpenRouterTool(tool: unknown): OpenRouterToolWithCacheControl {\n const converted = _convertToOpenAITool(\n tool as BindToolsInput\n ) as OpenRouterToolWithCacheControl;\n\n if (hasDeferredMarker(tool)) {\n return { ...converted, defer_loading: true };\n }\n\n return converted;\n}\n\nfunction markCacheControl(\n tool: OpenRouterToolWithCacheControl\n): OpenRouterToolWithCacheControl {\n return {\n ...tool,\n cache_control: CACHE_CONTROL,\n };\n}\n\nexport function partitionAndMarkOpenRouterToolCache(\n tools: GraphTools | undefined,\n isDeferred: (toolName: string) => boolean\n): GraphTools | undefined {\n if (tools == null || tools.length === 0) {\n return tools;\n }\n\n const staticTools: OpenRouterToolWithCacheControl[] = [];\n const deferredTools: OpenRouterToolWithCacheControl[] = [];\n\n for (const tool of tools as readonly unknown[]) {\n const converted = toOpenRouterTool(tool);\n const name = getToolName(converted) ?? getToolName(tool);\n\n if (name != null && isDeferred(name)) {\n deferredTools.push(converted);\n continue;\n }\n\n staticTools.push(converted);\n }\n\n if (staticTools.length === 0) {\n return [...deferredTools] as GraphTools;\n }\n\n staticTools[staticTools.length - 1] = markCacheControl(\n staticTools[staticTools.length - 1]\n );\n\n return [...staticTools, ...deferredTools] as GraphTools;\n}\n"],"names":["_convertToOpenAITool"],"mappings":";;;;AAKA,MAAM,aAAa,GAAG,EAAE,IAAI,EAAE,WAAoB,EAAE;AAepD,SAAS,WAAW,CAAC,IAAa,EAAA;IAChC,MAAM,SAAS,GAAG,IAAyB;AAC3C,IAAA,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE;QACtC,OAAO,SAAS,CAAC,IAAI;IACvB;IACA,IAAI,OAAO,SAAS,CAAC,QAAQ,EAAE,IAAI,KAAK,QAAQ,EAAE;AAChD,QAAA,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI;IAChC;AACA,IAAA,OAAO,SAAS;AAClB;AAEA,SAAS,iBAAiB,CAAC,IAAa,EAAA;AACtC,IAAA,OAAQ,IAA0B,CAAC,aAAa,KAAK,IAAI;AAC3D;AAEA,SAAS,gBAAgB,CAAC,IAAa,EAAA;AACrC,IAAA,MAAM,SAAS,GAAGA,0BAAoB,CACpC,IAAsB,CACW;AAEnC,IAAA,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE;QAC3B,OAAO,EAAE,GAAG,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE;IAC9C;AAEA,IAAA,OAAO,SAAS;AAClB;AAEA,SAAS,gBAAgB,CACvB,IAAoC,EAAA;IAEpC,OAAO;AACL,QAAA,GAAG,IAAI;AACP,QAAA,aAAa,EAAE,aAAa;KAC7B;AACH;AAEM,SAAU,mCAAmC,CACjD,KAA6B,EAC7B,UAAyC,EAAA;IAEzC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AACvC,QAAA,OAAO,KAAK;IACd;IAEA,MAAM,WAAW,GAAqC,EAAE;IACxD,MAAM,aAAa,GAAqC,EAAE;AAE1D,IAAA,KAAK,MAAM,IAAI,IAAI,KAA2B,EAAE;AAC9C,QAAA,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC;QACxC,MAAM,IAAI,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC;QAExD,IAAI,IAAI,IAAI,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE;AACpC,YAAA,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC;YAC7B;QACF;AAEA,QAAA,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC;IAC7B;AAEA,IAAA,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;AAC5B,QAAA,OAAO,CAAC,GAAG,aAAa,CAAe;IACzC;AAEA,IAAA,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,gBAAgB,CACpD,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CACpC;AAED,IAAA,OAAO,CAAC,GAAG,WAAW,EAAE,GAAG,aAAa,CAAe;AACzD;;;;"}
|
|
@@ -34,6 +34,9 @@ require('./local/attachments.cjs');
|
|
|
34
34
|
function isSend(value) {
|
|
35
35
|
return value instanceof langgraph.Send;
|
|
36
36
|
}
|
|
37
|
+
function isHandoffToolName(name) {
|
|
38
|
+
return name.startsWith(_enum.Constants.LC_TRANSFER_TO_);
|
|
39
|
+
}
|
|
37
40
|
/**
|
|
38
41
|
* Format a fail-closed diagnostic for malformed approval-decision
|
|
39
42
|
* fields. Hosts deserialize resume payloads from untyped JSON, so
|
|
@@ -396,6 +399,58 @@ class ToolNode extends run.RunnableCallable {
|
|
|
396
399
|
getFileCheckpointer() {
|
|
397
400
|
return this.fileCheckpointer;
|
|
398
401
|
}
|
|
402
|
+
*getRegisteredHandoffNames() {
|
|
403
|
+
if (this.directToolNames != null) {
|
|
404
|
+
for (const toolName of this.directToolNames) {
|
|
405
|
+
yield toolName;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
for (const toolName of this.toolMap.keys()) {
|
|
409
|
+
if (this.directToolNames?.has(toolName) === true) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
yield toolName;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
hasRegisteredHandoffTool() {
|
|
416
|
+
for (const toolName of this.getRegisteredHandoffNames()) {
|
|
417
|
+
if (isHandoffToolName(toolName)) {
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
getHandoffToolNameSuggestion(callName) {
|
|
424
|
+
if (!isHandoffToolName(callName)) {
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
let suggestion;
|
|
428
|
+
for (const toolName of this.getRegisteredHandoffNames()) {
|
|
429
|
+
if (!isHandoffToolName(toolName) ||
|
|
430
|
+
toolName.length >= callName.length ||
|
|
431
|
+
!callName.startsWith(toolName)) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (suggestion == null || toolName.length > suggestion.length) {
|
|
435
|
+
suggestion = toolName;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return suggestion;
|
|
439
|
+
}
|
|
440
|
+
shouldHandleUnknownHandoffLocally(callName, hasRegisteredHandoffTool) {
|
|
441
|
+
if (!isHandoffToolName(callName) || this.toolMap.has(callName)) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
return hasRegisteredHandoffTool ?? this.hasRegisteredHandoffTool();
|
|
445
|
+
}
|
|
446
|
+
getUnknownToolErrorMessage(callName) {
|
|
447
|
+
const suggestion = this.getHandoffToolNameSuggestion(callName);
|
|
448
|
+
if (suggestion == null) {
|
|
449
|
+
return `Tool "${callName}" not found.`;
|
|
450
|
+
}
|
|
451
|
+
return (`Tool "${callName}" not found. Did you mean "${suggestion}"? ` +
|
|
452
|
+
'Handoff tool names must match exactly.');
|
|
453
|
+
}
|
|
399
454
|
/**
|
|
400
455
|
* Flush the per-Run direct-path turn cache. Called by the Graph at
|
|
401
456
|
* end-of-Run via `clearHeavyState`. The map intentionally survives
|
|
@@ -481,7 +536,7 @@ class ToolNode extends run.RunnableCallable {
|
|
|
481
536
|
const runId = batchScopeId ?? config.configurable?.run_id;
|
|
482
537
|
try {
|
|
483
538
|
if (tool === undefined) {
|
|
484
|
-
throw new Error(
|
|
539
|
+
throw new Error(this.getUnknownToolErrorMessage(call.name));
|
|
485
540
|
}
|
|
486
541
|
/**
|
|
487
542
|
* `usageCount` is the per-tool-name invocation index that
|
|
@@ -2123,8 +2178,9 @@ class ToolNode extends run.RunnableCallable {
|
|
|
2123
2178
|
const turn = this.toolOutputRegistry?.nextTurn(batchScopeId) ?? 0;
|
|
2124
2179
|
let outputs;
|
|
2125
2180
|
if (this.isSendInput(input)) {
|
|
2126
|
-
const
|
|
2127
|
-
|
|
2181
|
+
const isLocalTool = this.directToolNames?.has(input.lg_tool_call.name) === true ||
|
|
2182
|
+
this.shouldHandleUnknownHandoffLocally(input.lg_tool_call.name);
|
|
2183
|
+
if (this.eventDrivenMode && !isLocalTool) {
|
|
2128
2184
|
return this.executeViaEvent([input.lg_tool_call], config, input, {
|
|
2129
2185
|
batchIndices: [0],
|
|
2130
2186
|
turn,
|
|
@@ -2207,26 +2263,28 @@ class ToolNode extends run.RunnableCallable {
|
|
|
2207
2263
|
false));
|
|
2208
2264
|
}) ?? [];
|
|
2209
2265
|
if (this.eventDrivenMode && filteredCalls.length > 0) {
|
|
2210
|
-
const
|
|
2211
|
-
|
|
2212
|
-
return this.executeViaEvent(filteredCalls, config, input, {
|
|
2213
|
-
batchIndices: filteredIndices,
|
|
2214
|
-
turn,
|
|
2215
|
-
batchScopeId,
|
|
2216
|
-
});
|
|
2217
|
-
}
|
|
2266
|
+
const directToolNames = this.directToolNames;
|
|
2267
|
+
const hasRegisteredHandoffTool = this.hasRegisteredHandoffTool();
|
|
2218
2268
|
const directEntries = [];
|
|
2219
2269
|
const eventEntries = [];
|
|
2220
2270
|
for (let i = 0; i < filteredCalls.length; i++) {
|
|
2221
2271
|
const call = filteredCalls[i];
|
|
2222
2272
|
const entry = { call, batchIndex: i };
|
|
2223
|
-
if (
|
|
2273
|
+
if (directToolNames?.has(call.name) === true ||
|
|
2274
|
+
this.shouldHandleUnknownHandoffLocally(call.name, hasRegisteredHandoffTool)) {
|
|
2224
2275
|
directEntries.push(entry);
|
|
2225
2276
|
}
|
|
2226
2277
|
else {
|
|
2227
2278
|
eventEntries.push(entry);
|
|
2228
2279
|
}
|
|
2229
2280
|
}
|
|
2281
|
+
if (directEntries.length === 0) {
|
|
2282
|
+
return this.executeViaEvent(filteredCalls, config, input, {
|
|
2283
|
+
batchIndices: eventEntries.map((entry) => entry.batchIndex),
|
|
2284
|
+
turn,
|
|
2285
|
+
batchScopeId,
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2230
2288
|
const directCalls = directEntries.map((e) => e.call);
|
|
2231
2289
|
const directIndices = directEntries.map((e) => e.batchIndex);
|
|
2232
2290
|
const eventCalls = eventEntries.map((e) => e.call);
|