@librechat/agents 3.1.71-dev.0 → 3.1.71
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 +7 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +13 -2
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -1
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +84 -55
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/toolOutputReferences.cjs +195 -0
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +7 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +13 -2
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -1
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +85 -56
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/toolOutputReferences.mjs +195 -1
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/types/graphs/Graph.d.ts +9 -2
- package/dist/types/llm/invoke.d.ts +29 -3
- package/dist/types/tools/ToolNode.d.ts +11 -13
- package/dist/types/tools/toolOutputReferences.d.ts +31 -0
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/messages.d.ts +26 -0
- package/package.json +1 -1
- package/src/graphs/Graph.ts +8 -1
- package/src/llm/invoke.test.ts +446 -0
- package/src/llm/invoke.ts +45 -5
- package/src/tools/BashExecutor.ts +3 -1
- package/src/tools/ToolNode.ts +94 -81
- package/src/tools/__tests__/BashExecutor.test.ts +13 -0
- package/src/tools/__tests__/ToolNode.outputReferences.test.ts +98 -55
- package/src/tools/__tests__/annotateMessagesForLLM.test.ts +479 -0
- package/src/tools/toolOutputReferences.ts +235 -0
- package/src/types/index.ts +1 -0
- package/src/types/messages.ts +27 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BashExecutor.cjs","sources":["../../../src/tools/BashExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\nexport const BashExecutionToolSchema = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: `The bash command or script to execute.\n- The environment is stateless; variables and state don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- Use \\`echo\\`, \\`printf\\`, or \\`cat\\` for all outputs.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',\n },\n },\n required: ['command'],\n} as const;\n\nexport const BashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Supplemental prompt documenting the tool-output reference feature.\n *\n * Hosts should append this (separated by a blank line) to the base\n * {@link BashExecutionToolDescription} only when\n * `RunConfig.toolOutputReferences.enabled` is `true`. When the feature\n * is disabled, including this text would tell the LLM to emit\n * `{{tool0turn0}}` placeholders that pass through unsubstituted and\n * leak into the shell.\n */\nexport const BashToolOutputReferencesGuide = `\nReferencing previous tool outputs:\n- Every successful tool result is tagged with a reference key of the form \\`tool<idx>turn<turn>\\` (e.g., \\`tool0turn0\\`). The key appears either as a \\`[ref: tool0turn0]\\` prefix line or, when the output is a JSON object, as a \\`_ref\\` field on the object.\n- To pipe a previous tool output into this tool, embed the placeholder \\`{{tool<idx>turn<turn>}}\\` literally anywhere in the \\`command\\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.\n- The substituted value is the original output string (no \\`[ref: …]\\` prefix, no \\`_ref\\` key), so it is safe to pipe directly into \\`jq\\`, \\`grep\\`, \\`awk\\`, etc.\n- Example: \\`echo '{{tool0turn0}}' | jq '.foo'\\` takes the full output of the first tool from the first turn and pipes it into jq.\n- Unknown reference keys are left in place and surfaced as \\`[unresolved refs: …]\\` after the output.\n`.trim();\n\n/**\n * Composes the bash tool description, optionally appending the\n * tool-output references guide. Hosts that enable\n * `RunConfig.toolOutputReferences` should pass `enableToolOutputReferences: true`\n * when registering the tool so the LLM learns the `{{…}}` syntax it\n * will actually be able to use.\n */\nexport function buildBashExecutionToolDescription(options?: {\n enableToolOutputReferences?: boolean;\n}): string {\n if (options?.enableToolOutputReferences === true) {\n return `${BashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return BashExecutionToolDescription;\n}\n\nexport const BashExecutionToolName = Constants.BASH_TOOL;\n\n/**\n * Default bash tool definition using the base description.\n *\n * When `RunConfig.toolOutputReferences.enabled` is `true`, build a\n * reference-aware description with\n * {@link buildBashExecutionToolDescription}\n * (`{ enableToolOutputReferences: true }`) and construct a custom\n * definition using it — using this constant as-is leaves the LLM\n * unaware of the `{{tool<i>turn<n>}}` syntax.\n */\nexport const BashExecutionToolDefinition = {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n} as const;\n\nfunction createBashExecutionTool(\n params: t.BashExecutionToolParams = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n const { command, ...rest } = rawInput as {\n command: string;\n args?: string[];\n };\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...params,\n };\n\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error:\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"names":["config","getCodeBaseURL","Constants","tool","HttpsProxyAgent","imageExtRegex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BashExecutor.cjs","sources":["../../../src/tools/BashExecutor.ts"],"sourcesContent":["import { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { Constants } from '@/common';\n\nconfig();\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files from previous executions are automatically available and can be modified.';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\nconst baseEndpoint = getCodeBaseURL();\nconst EXEC_ENDPOINT = `${baseEndpoint}/exec`;\n\nexport const BashExecutionToolSchema = {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n description: `The bash command or script to execute.\n- The environment is stateless; variables and state don't persist between executions.\n- Generated files from previous executions are automatically available in \"/mnt/data/\".\n- Files from previous executions are automatically available and can be modified in place.\n- Input code **IS ALREADY** displayed to the user, so **DO NOT** repeat it in your response unless asked.\n- Output code **IS NOT** displayed to the user, so **DO** write all desired output explicitly.\n- IMPORTANT: You MUST explicitly print/output ALL results you want the user to see.\n- Use \\`echo\\`, \\`printf\\`, or \\`cat\\` for all outputs.`,\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Additional arguments to execute the command with. This should only be used if the input command requires additional arguments to run.',\n },\n },\n required: ['command'],\n} as const;\n\nexport const BashExecutionToolDescription = `\nRuns bash commands and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.\n\nUsage:\n- No network access available.\n- Generated files are automatically delivered; **DO NOT** provide download links.\n- NEVER use this tool to execute malicious commands.\n`.trim();\n\n/**\n * Supplemental prompt documenting the tool-output reference feature.\n *\n * Hosts should append this (separated by a blank line) to the base\n * {@link BashExecutionToolDescription} only when\n * `RunConfig.toolOutputReferences.enabled` is `true`. When the feature\n * is disabled, including this text would tell the LLM to emit\n * `{{tool0turn0}}` placeholders that pass through unsubstituted and\n * leak into the shell.\n */\nexport const BashToolOutputReferencesGuide = `\nReferencing previous tool outputs:\n- Every successful tool result is tagged with a reference key of the form \\`tool<idx>turn<turn>\\` (e.g., \\`tool0turn0\\`). The key appears either as a \\`[ref: tool0turn0]\\` prefix line or, when the output is a JSON object, as a \\`_ref\\` field on the object.\n- To pipe a previous tool output into this tool, embed the placeholder \\`{{tool<idx>turn<turn>}}\\` literally anywhere in the \\`command\\` string (or any string arg). It will be substituted with the stored output verbatim before the command runs.\n- The substituted value is the original output string (no \\`[ref: …]\\` prefix, no \\`_ref\\` key), so it is safe to pipe directly into \\`jq\\`, \\`grep\\`, \\`awk\\`, etc.\n- Example (simple ASCII output): \\`echo '{{tool0turn0}}' | jq '.foo'\\` takes the full output of the first tool from the first turn and pipes it into jq.\n- For payloads that may contain quotes, parentheses, backticks, or arbitrary bytes (random/binary data, JSON with embedded quotes, multi-line strings), prefer a quoted-delimiter heredoc over \\`echo '…'\\`. The heredoc body is not interpreted by the shell, so substituted payloads pass through unchanged.\n- Heredoc example: \\`wc -c << 'EOF'\\\\n{{tool0turn0}}\\\\nEOF\\` (the quotes around \\`'EOF'\\` disable interpolation inside the body).\n- Unknown reference keys are left in place and surfaced as \\`[unresolved refs: …]\\` after the output.\n`.trim();\n\n/**\n * Composes the bash tool description, optionally appending the\n * tool-output references guide. Hosts that enable\n * `RunConfig.toolOutputReferences` should pass `enableToolOutputReferences: true`\n * when registering the tool so the LLM learns the `{{…}}` syntax it\n * will actually be able to use.\n */\nexport function buildBashExecutionToolDescription(options?: {\n enableToolOutputReferences?: boolean;\n}): string {\n if (options?.enableToolOutputReferences === true) {\n return `${BashExecutionToolDescription}\\n\\n${BashToolOutputReferencesGuide}`;\n }\n return BashExecutionToolDescription;\n}\n\nexport const BashExecutionToolName = Constants.BASH_TOOL;\n\n/**\n * Default bash tool definition using the base description.\n *\n * When `RunConfig.toolOutputReferences.enabled` is `true`, build a\n * reference-aware description with\n * {@link buildBashExecutionToolDescription}\n * (`{ enableToolOutputReferences: true }`) and construct a custom\n * definition using it — using this constant as-is leaves the LLM\n * unaware of the `{{tool<i>turn<n>}}` syntax.\n */\nexport const BashExecutionToolDefinition = {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n} as const;\n\nfunction createBashExecutionTool(\n params: t.BashExecutionToolParams = {}\n): DynamicStructuredTool {\n return tool(\n async (rawInput, config) => {\n const { command, ...rest } = rawInput as {\n command: string;\n args?: string[];\n };\n const { session_id, _injected_files } = (config.toolCall ?? {}) as {\n session_id?: string;\n _injected_files?: t.CodeEnvFile[];\n };\n\n const postData: Record<string, unknown> = {\n lang: 'bash',\n code: command,\n ...rest,\n ...params,\n };\n\n if (_injected_files && _injected_files.length > 0) {\n postData.files = _injected_files;\n } else if (session_id != null && session_id.length > 0) {\n try {\n const filesEndpoint = `${baseEndpoint}/files/${session_id}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n },\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch files for session: ${response.status}`\n );\n }\n\n const files = await response.json();\n if (Array.isArray(files) && files.length > 0) {\n const fileReferences: t.CodeEnvFile[] = files.map((file) => {\n const nameParts = file.name.split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id,\n id,\n name: file.metadata['original-filename'],\n };\n });\n\n postData.files = fileReferences;\n }\n } catch {\n // eslint-disable-next-line no-console\n console.warn(`Failed to fetch files for session: ${session_id}`);\n }\n }\n\n try {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n },\n body: JSON.stringify(postData),\n };\n\n if (process.env.PROXY != null && process.env.PROXY !== '') {\n fetchOptions.agent = new HttpsProxyAgent(process.env.PROXY);\n }\n const response = await fetch(EXEC_ENDPOINT, fetchOptions);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const result: t.ExecuteResult = await response.json();\n let formattedOutput = '';\n if (result.stdout) {\n formattedOutput += `stdout:\\n${result.stdout}\\n`;\n } else {\n formattedOutput += emptyOutputMessage;\n }\n if (result.stderr) formattedOutput += `stderr:\\n${result.stderr}\\n`;\n if (result.files && result.files.length > 0) {\n formattedOutput += 'Generated files:\\n';\n\n const fileCount = result.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = result.files[i];\n const isImage = imageExtRegex.test(file.name);\n formattedOutput += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formattedOutput += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formattedOutput += `\\n\\n${accessMessage}`;\n return [\n formattedOutput.trim(),\n {\n session_id: result.session_id,\n files: result.files,\n },\n ];\n }\n\n return [formattedOutput.trim(), { session_id: result.session_id }];\n } catch (error) {\n throw new Error(\n `Execution error:\\n\\n${(error as Error | undefined)?.message}`\n );\n }\n },\n {\n name: BashExecutionToolName,\n description: BashExecutionToolDescription,\n schema: BashExecutionToolSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\nexport { createBashExecutionTool };\n"],"names":["config","getCodeBaseURL","Constants","tool","HttpsProxyAgent","imageExtRegex"],"mappings":"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"}
|
|
@@ -302,13 +302,17 @@ class ToolNode extends run.RunnableCallable {
|
|
|
302
302
|
const isError = toolMsg.status === 'error';
|
|
303
303
|
if (isError) {
|
|
304
304
|
/**
|
|
305
|
-
* Error ToolMessages bypass registration
|
|
306
|
-
*
|
|
307
|
-
*
|
|
305
|
+
* Error ToolMessages bypass registration but still stamp the
|
|
306
|
+
* unresolved-refs hint into `additional_kwargs` so the lazy
|
|
307
|
+
* annotation transform surfaces it to the LLM, letting the
|
|
308
|
+
* model self-correct when its reference key caused the
|
|
309
|
+
* failure. Persisted `content` stays clean.
|
|
308
310
|
*/
|
|
309
|
-
if (unresolvedRefs.length > 0
|
|
310
|
-
|
|
311
|
-
|
|
311
|
+
if (unresolvedRefs.length > 0) {
|
|
312
|
+
toolMsg.additional_kwargs = {
|
|
313
|
+
...toolMsg.additional_kwargs,
|
|
314
|
+
_unresolvedRefs: unresolvedRefs,
|
|
315
|
+
};
|
|
312
316
|
}
|
|
313
317
|
return toolMsg;
|
|
314
318
|
}
|
|
@@ -316,7 +320,14 @@ class ToolNode extends run.RunnableCallable {
|
|
|
316
320
|
if (typeof toolMsg.content === 'string') {
|
|
317
321
|
const rawContent = toolMsg.content;
|
|
318
322
|
const llmContent = truncation.truncateToolResultContent(rawContent, this.maxToolResultChars);
|
|
319
|
-
toolMsg.content =
|
|
323
|
+
toolMsg.content = llmContent;
|
|
324
|
+
const refMeta = this.recordOutputReference(runId, rawContent, refKey, unresolvedRefs);
|
|
325
|
+
if (refMeta != null) {
|
|
326
|
+
toolMsg.additional_kwargs = {
|
|
327
|
+
...toolMsg.additional_kwargs,
|
|
328
|
+
...refMeta,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
320
331
|
}
|
|
321
332
|
else {
|
|
322
333
|
/**
|
|
@@ -324,22 +335,16 @@ class ToolNode extends run.RunnableCallable {
|
|
|
324
335
|
* image). Known limitation: we cannot register under a
|
|
325
336
|
* reference key because there's no canonical serialized
|
|
326
337
|
* form. Warn once per tool per run when the caller
|
|
327
|
-
* intended to register.
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
* paths already emit. Prepended as a leading text block
|
|
332
|
-
* to keep the original content ordering intact.
|
|
338
|
+
* intended to register. The unresolved-refs hint is still
|
|
339
|
+
* stamped as metadata; the lazy transform prepends a text
|
|
340
|
+
* block at request time so the LLM gets the self-correction
|
|
341
|
+
* signal.
|
|
333
342
|
*/
|
|
334
|
-
if (unresolvedRefs.length > 0
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
343
|
+
if (unresolvedRefs.length > 0) {
|
|
344
|
+
toolMsg.additional_kwargs = {
|
|
345
|
+
...toolMsg.additional_kwargs,
|
|
346
|
+
_unresolvedRefs: unresolvedRefs,
|
|
338
347
|
};
|
|
339
|
-
toolMsg.content = [
|
|
340
|
-
warningBlock,
|
|
341
|
-
...toolMsg.content,
|
|
342
|
-
];
|
|
343
348
|
}
|
|
344
349
|
if (refKey != null &&
|
|
345
350
|
this.toolOutputRegistry.claimWarnOnce(runId, call.name)) {
|
|
@@ -353,12 +358,15 @@ class ToolNode extends run.RunnableCallable {
|
|
|
353
358
|
}
|
|
354
359
|
const rawContent = typeof output === 'string' ? output : JSON.stringify(output);
|
|
355
360
|
const truncated = truncation.truncateToolResultContent(rawContent, this.maxToolResultChars);
|
|
356
|
-
const
|
|
361
|
+
const refMeta = this.recordOutputReference(runId, rawContent, refKey, unresolvedRefs);
|
|
357
362
|
return new messages.ToolMessage({
|
|
358
363
|
status: 'success',
|
|
359
364
|
name: tool.name,
|
|
360
|
-
content,
|
|
365
|
+
content: truncated,
|
|
361
366
|
tool_call_id: call.id,
|
|
367
|
+
...(refMeta != null && {
|
|
368
|
+
additional_kwargs: refMeta,
|
|
369
|
+
}),
|
|
362
370
|
});
|
|
363
371
|
}
|
|
364
372
|
catch (_e) {
|
|
@@ -402,51 +410,64 @@ class ToolNode extends run.RunnableCallable {
|
|
|
402
410
|
});
|
|
403
411
|
}
|
|
404
412
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
413
|
+
const errorContent = `Error: ${e.message}\n Please fix your mistakes.`;
|
|
414
|
+
const refMeta = unresolvedRefs.length > 0
|
|
415
|
+
? this.recordOutputReference(runId, errorContent, undefined, unresolvedRefs)
|
|
416
|
+
: undefined;
|
|
409
417
|
return new messages.ToolMessage({
|
|
410
418
|
status: 'error',
|
|
411
419
|
content: errorContent,
|
|
412
420
|
name: call.name,
|
|
413
421
|
tool_call_id: call.id ?? '',
|
|
422
|
+
...(refMeta != null && {
|
|
423
|
+
additional_kwargs: refMeta,
|
|
424
|
+
}),
|
|
414
425
|
});
|
|
415
426
|
}
|
|
416
427
|
}
|
|
417
428
|
/**
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
429
|
+
* Registers the full, raw output under `refKey` (when provided) and
|
|
430
|
+
* builds the per-message ref metadata stamped onto the resulting
|
|
431
|
+
* `ToolMessage.additional_kwargs`. The metadata is read at LLM-
|
|
432
|
+
* request time by `annotateMessagesForLLM` to produce a transient
|
|
433
|
+
* annotated copy of the message — the persisted `content` itself
|
|
434
|
+
* stays clean.
|
|
421
435
|
*
|
|
422
|
-
* @param llmContent The content string the LLM will see. This is
|
|
423
|
-
* the already-truncated, post-hook view; the annotation is
|
|
424
|
-
* applied on top of it.
|
|
425
436
|
* @param registryContent The full, untruncated output to store in
|
|
426
437
|
* the registry so `{{tool<i>turn<n>}}` substitutions deliver the
|
|
427
438
|
* complete payload. Ignored when `refKey` is undefined.
|
|
428
439
|
* @param refKey Precomputed `tool<i>turn<n>` key, or undefined when
|
|
429
440
|
* the output is not to be registered (errors, disabled feature,
|
|
430
441
|
* unavailable batch/turn).
|
|
431
|
-
* @param unresolved Placeholder keys that did not resolve;
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* so parallel `invoke()` calls on the same ToolNode cannot race on
|
|
436
|
-
* the shared turn field.
|
|
442
|
+
* @param unresolved Placeholder keys that did not resolve; surfaced
|
|
443
|
+
* to the LLM lazily so it can self-correct.
|
|
444
|
+
* @returns A `ToolMessageRefMetadata` object when there is anything
|
|
445
|
+
* to stamp, otherwise `undefined`.
|
|
437
446
|
*/
|
|
438
|
-
|
|
447
|
+
recordOutputReference(runId, registryContent, refKey, unresolved) {
|
|
439
448
|
if (this.toolOutputRegistry != null && refKey != null) {
|
|
440
449
|
this.toolOutputRegistry.set(runId, refKey, registryContent);
|
|
441
450
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
451
|
+
if (refKey == null && unresolved.length === 0)
|
|
452
|
+
return undefined;
|
|
453
|
+
const meta = {};
|
|
454
|
+
if (refKey != null) {
|
|
455
|
+
meta._refKey = refKey;
|
|
456
|
+
/**
|
|
457
|
+
* Stamp the registry scope alongside the key so the lazy
|
|
458
|
+
* annotation transform can look up the right bucket. Anonymous
|
|
459
|
+
* invocations get a synthetic per-batch scope (`\0anon-<n>`)
|
|
460
|
+
* that `attemptInvoke` cannot derive from
|
|
461
|
+
* `config.configurable.run_id` — without this, anonymous-run
|
|
462
|
+
* refs would silently fail registry lookup and the LLM would
|
|
463
|
+
* never see `[ref: …]` markers for outputs that were registered.
|
|
464
|
+
*/
|
|
465
|
+
if (runId != null)
|
|
466
|
+
meta._refScope = runId;
|
|
467
|
+
}
|
|
468
|
+
if (unresolved.length > 0)
|
|
469
|
+
meta._unresolvedRefs = unresolved;
|
|
470
|
+
return meta;
|
|
450
471
|
}
|
|
451
472
|
/**
|
|
452
473
|
* Builds code session context for injection into event-driven tool calls.
|
|
@@ -781,19 +802,24 @@ class ToolNode extends run.RunnableCallable {
|
|
|
781
802
|
if (result.status === 'error') {
|
|
782
803
|
contentString = `Error: ${result.errorMessage ?? 'Unknown error'}\n Please fix your mistakes.`;
|
|
783
804
|
/**
|
|
784
|
-
* Error results bypass registration
|
|
785
|
-
*
|
|
786
|
-
*
|
|
805
|
+
* Error results bypass registration but stamp the
|
|
806
|
+
* unresolved-refs hint into `additional_kwargs` so the lazy
|
|
807
|
+
* annotation transform surfaces it to the LLM at request
|
|
808
|
+
* time, letting the model self-correct when its reference
|
|
809
|
+
* key caused the failure. Persisted `content` stays clean.
|
|
787
810
|
*/
|
|
788
811
|
const unresolved = unresolvedByCallId.get(result.toolCallId) ?? [];
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
812
|
+
const errorRefMeta = unresolved.length > 0
|
|
813
|
+
? this.recordOutputReference(registryRunId, contentString, undefined, unresolved)
|
|
814
|
+
: undefined;
|
|
792
815
|
toolMessage = new messages.ToolMessage({
|
|
793
816
|
status: 'error',
|
|
794
817
|
content: contentString,
|
|
795
818
|
name: toolName,
|
|
796
819
|
tool_call_id: result.toolCallId,
|
|
820
|
+
...(errorRefMeta != null && {
|
|
821
|
+
additional_kwargs: errorRefMeta,
|
|
822
|
+
}),
|
|
797
823
|
});
|
|
798
824
|
if (hasFailureHook) {
|
|
799
825
|
await executeHooks.executeHooks({
|
|
@@ -855,13 +881,16 @@ class ToolNode extends run.RunnableCallable {
|
|
|
855
881
|
turn != null
|
|
856
882
|
? toolOutputReferences.buildReferenceKey(batchIndex, turn)
|
|
857
883
|
: undefined;
|
|
858
|
-
|
|
884
|
+
const successRefMeta = this.recordOutputReference(registryRunId, registryRaw, refKey, unresolved);
|
|
859
885
|
toolMessage = new messages.ToolMessage({
|
|
860
886
|
status: 'success',
|
|
861
887
|
name: toolName,
|
|
862
888
|
content: contentString,
|
|
863
889
|
artifact: result.artifact,
|
|
864
890
|
tool_call_id: result.toolCallId,
|
|
891
|
+
...(successRefMeta != null && {
|
|
892
|
+
additional_kwargs: successRefMeta,
|
|
893
|
+
}),
|
|
865
894
|
});
|
|
866
895
|
}
|
|
867
896
|
this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn);
|