@librechat/agents 3.0.44 → 3.0.46
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/main.cjs +2 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +143 -16
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +142 -17
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +19 -0
- package/dist/types/types/tools.d.ts +3 -1
- package/package.json +1 -1
- package/src/tools/ProgrammaticToolCalling.ts +169 -16
- package/src/types/tools.ts +3 -1
package/dist/cjs/main.cjs
CHANGED
|
@@ -81,10 +81,12 @@ exports.imageExtRegex = CodeExecutor.imageExtRegex;
|
|
|
81
81
|
exports.createProgrammaticToolCallingTool = ProgrammaticToolCalling.createProgrammaticToolCallingTool;
|
|
82
82
|
exports.executeTools = ProgrammaticToolCalling.executeTools;
|
|
83
83
|
exports.extractUsedToolNames = ProgrammaticToolCalling.extractUsedToolNames;
|
|
84
|
+
exports.fetchSessionFiles = ProgrammaticToolCalling.fetchSessionFiles;
|
|
84
85
|
exports.filterToolsByUsage = ProgrammaticToolCalling.filterToolsByUsage;
|
|
85
86
|
exports.formatCompletedResponse = ProgrammaticToolCalling.formatCompletedResponse;
|
|
86
87
|
exports.makeRequest = ProgrammaticToolCalling.makeRequest;
|
|
87
88
|
exports.normalizeToPythonIdentifier = ProgrammaticToolCalling.normalizeToPythonIdentifier;
|
|
89
|
+
exports.unwrapToolResponse = ProgrammaticToolCalling.unwrapToolResponse;
|
|
88
90
|
exports.countNestedGroups = ToolSearchRegex.countNestedGroups;
|
|
89
91
|
exports.createToolSearchRegexTool = ToolSearchRegex.createToolSearchRegexTool;
|
|
90
92
|
exports.escapeRegexSpecialChars = ToolSearchRegex.escapeRegexSpecialChars;
|
package/dist/cjs/main.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -63,6 +63,7 @@ Requirements:
|
|
|
63
63
|
- Tools are pre-defined as async functions - DO NOT write function definitions
|
|
64
64
|
- Use await for all tool calls
|
|
65
65
|
- Use asyncio.gather() for parallel execution of independent calls
|
|
66
|
+
- DO NOT call asyncio.run() - an event loop is already running
|
|
66
67
|
- Only print() output flows back to the context window
|
|
67
68
|
- Tool results from programmatic calls do NOT consume context tokens`),
|
|
68
69
|
session_id: zod.z
|
|
@@ -190,6 +191,53 @@ function filterToolsByUsage(toolDefs, code, debug = false) {
|
|
|
190
191
|
}
|
|
191
192
|
return toolDefs.filter((tool) => usedToolNames.has(tool.name));
|
|
192
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Fetches files from a previous session to make them available for the current execution.
|
|
196
|
+
* Files are returned as CodeEnvFile references to be included in the request.
|
|
197
|
+
* @param baseUrl - The base URL for the Code API
|
|
198
|
+
* @param apiKey - The API key for authentication
|
|
199
|
+
* @param sessionId - The session ID to fetch files from
|
|
200
|
+
* @param proxy - Optional HTTP proxy URL
|
|
201
|
+
* @returns Array of CodeEnvFile references, or empty array if fetch fails
|
|
202
|
+
*/
|
|
203
|
+
async function fetchSessionFiles(baseUrl, apiKey, sessionId, proxy) {
|
|
204
|
+
try {
|
|
205
|
+
const filesEndpoint = `${baseUrl}/files/${sessionId}?detail=full`;
|
|
206
|
+
const fetchOptions = {
|
|
207
|
+
method: 'GET',
|
|
208
|
+
headers: {
|
|
209
|
+
'User-Agent': 'LibreChat/1.0',
|
|
210
|
+
'X-API-Key': apiKey,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
if (proxy != null && proxy !== '') {
|
|
214
|
+
fetchOptions.agent = new httpsProxyAgent.HttpsProxyAgent(proxy);
|
|
215
|
+
}
|
|
216
|
+
const response = await fetch(filesEndpoint, fetchOptions);
|
|
217
|
+
if (!response.ok) {
|
|
218
|
+
throw new Error(`Failed to fetch files for session: ${response.status}`);
|
|
219
|
+
}
|
|
220
|
+
const files = await response.json();
|
|
221
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
return files.map((file) => {
|
|
225
|
+
// Extract the ID from the file name (part after session ID prefix and before extension)
|
|
226
|
+
const nameParts = file.name.split('/');
|
|
227
|
+
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
228
|
+
return {
|
|
229
|
+
session_id: sessionId,
|
|
230
|
+
id,
|
|
231
|
+
name: file.metadata['original-filename'],
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
// eslint-disable-next-line no-console
|
|
237
|
+
console.warn(`Failed to fetch files for session: ${sessionId}, ${error.message}`);
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
193
241
|
/**
|
|
194
242
|
* Makes an HTTP request to the Code API.
|
|
195
243
|
* @param endpoint - The API endpoint URL
|
|
@@ -218,9 +266,82 @@ async function makeRequest(endpoint, apiKey, body, proxy) {
|
|
|
218
266
|
}
|
|
219
267
|
return (await response.json());
|
|
220
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Unwraps tool responses that may be formatted as tuples.
|
|
271
|
+
* MCP tools return [content, artifacts], we need to extract the raw data.
|
|
272
|
+
* @param result - The raw result from tool.invoke()
|
|
273
|
+
* @param isMCPTool - Whether this is an MCP tool (has mcp property)
|
|
274
|
+
* @returns Unwrapped raw data (string, object, or parsed JSON)
|
|
275
|
+
*/
|
|
276
|
+
function unwrapToolResponse(result, isMCPTool) {
|
|
277
|
+
// Only unwrap if this is an MCP tool and result is a tuple
|
|
278
|
+
if (!isMCPTool) {
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
// Check if result is a tuple/array with [content, artifacts]
|
|
282
|
+
if (Array.isArray(result) && result.length >= 1) {
|
|
283
|
+
const [content] = result;
|
|
284
|
+
// If first element is a string, return it
|
|
285
|
+
if (typeof content === 'string') {
|
|
286
|
+
// Try to parse as JSON if it looks like JSON
|
|
287
|
+
if (typeof content === 'string' && content.trim().startsWith('{')) {
|
|
288
|
+
try {
|
|
289
|
+
return JSON.parse(content);
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return content;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return content;
|
|
296
|
+
}
|
|
297
|
+
// If first element is an array (content blocks), extract text/data
|
|
298
|
+
if (Array.isArray(content)) {
|
|
299
|
+
// If it's an array of content blocks (like [{ type: 'text', text: '...' }])
|
|
300
|
+
if (content.length > 0 &&
|
|
301
|
+
typeof content[0] === 'object' &&
|
|
302
|
+
'type' in content[0]) {
|
|
303
|
+
// Extract text from content blocks
|
|
304
|
+
const texts = content
|
|
305
|
+
.filter((block) => {
|
|
306
|
+
if (typeof block !== 'object' || block === null)
|
|
307
|
+
return false;
|
|
308
|
+
const b = block;
|
|
309
|
+
return b.type === 'text' && typeof b.text === 'string';
|
|
310
|
+
})
|
|
311
|
+
.map((block) => {
|
|
312
|
+
const b = block;
|
|
313
|
+
return b.text;
|
|
314
|
+
});
|
|
315
|
+
if (texts.length > 0) {
|
|
316
|
+
const combined = texts.join('\n');
|
|
317
|
+
// Try to parse as JSON if it looks like JSON (objects or arrays)
|
|
318
|
+
if (combined.trim().startsWith('{') ||
|
|
319
|
+
combined.trim().startsWith('[')) {
|
|
320
|
+
try {
|
|
321
|
+
return JSON.parse(combined);
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
return combined;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return combined;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Otherwise return the content array as-is
|
|
331
|
+
return content;
|
|
332
|
+
}
|
|
333
|
+
// If first element is an object, return it
|
|
334
|
+
if (typeof content === 'object' && content !== null) {
|
|
335
|
+
return content;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Not a formatted response, return as-is
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
221
341
|
/**
|
|
222
342
|
* Executes tools in parallel when requested by the API.
|
|
223
343
|
* Uses Promise.all for parallel execution, catching individual errors.
|
|
344
|
+
* Unwraps formatted responses (e.g., MCP tool tuples) to raw data.
|
|
224
345
|
* @param toolCalls - Array of tool calls from the API
|
|
225
346
|
* @param toolMap - Map of tool names to executable tools
|
|
226
347
|
* @returns Array of tool results
|
|
@@ -240,9 +361,11 @@ async function executeTools(toolCalls, toolMap) {
|
|
|
240
361
|
const result = await tool.invoke(call.input, {
|
|
241
362
|
metadata: { [_enum.Constants.PROGRAMMATIC_TOOL_CALLING]: true },
|
|
242
363
|
});
|
|
364
|
+
const isMCPTool = tool.mcp === true;
|
|
365
|
+
const unwrappedResult = unwrapToolResponse(result, isMCPTool);
|
|
243
366
|
return {
|
|
244
367
|
call_id: call.id,
|
|
245
|
-
result,
|
|
368
|
+
result: unwrappedResult,
|
|
246
369
|
is_error: false,
|
|
247
370
|
};
|
|
248
371
|
}
|
|
@@ -334,25 +457,21 @@ function createProgrammaticToolCallingTool(initParams = {}) {
|
|
|
334
457
|
const debug = initParams.debug ?? process.env.PTC_DEBUG === 'true';
|
|
335
458
|
const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;
|
|
336
459
|
const description = `
|
|
337
|
-
Run tools
|
|
460
|
+
Run tools via Python code. Tools are injected as async functions—call with \`await\`.
|
|
338
461
|
|
|
339
|
-
|
|
462
|
+
Rules:
|
|
463
|
+
- Tools are pre-defined—DO NOT define them yourself
|
|
464
|
+
- Use \`await\` for calls, \`asyncio.gather()\` for parallel; NEVER call \`asyncio.run()\`
|
|
465
|
+
- Only \`print()\` output returns to the model; tool results are raw dicts/lists/strings
|
|
466
|
+
- Stateless: variables/imports don't persist; use \`session_id\` param for file access
|
|
467
|
+
- Files mount at \`/mnt/data/\` (READ-ONLY); write changes to NEW filenames
|
|
468
|
+
- Tool names normalized: hyphens→underscores, keywords get \`_tool\` suffix
|
|
340
469
|
|
|
341
|
-
|
|
342
|
-
- Tools are pre-defined as async functions - call them with await
|
|
343
|
-
- Use asyncio.gather() to run multiple tools in parallel
|
|
344
|
-
- Only print() output is returned - tool results stay in Python
|
|
470
|
+
When to use (vs. direct tool calls): loops, conditionals, parallel execution, aggregation.
|
|
345
471
|
|
|
346
472
|
Examples:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
- Parallel: sf, ny = await asyncio.gather(get_weather(city="SF"), get_weather(city="NY"))
|
|
350
|
-
|
|
351
|
-
When to use this instead of calling tools directly:
|
|
352
|
-
- You need to call tools in a loop (process many items)
|
|
353
|
-
- You want parallel execution (asyncio.gather)
|
|
354
|
-
- You need conditionals based on tool results
|
|
355
|
-
- You want to aggregate/filter data before returning
|
|
473
|
+
result = await get_weather(city="NYC"); print(result)
|
|
474
|
+
sf, ny = await asyncio.gather(get_weather(city="SF"), get_weather(city="NY"))
|
|
356
475
|
`.trim();
|
|
357
476
|
return tools.tool(async (params, config) => {
|
|
358
477
|
const { code, session_id, timeout = DEFAULT_TIMEOUT } = params;
|
|
@@ -377,11 +496,17 @@ When to use this instead of calling tools directly:
|
|
|
377
496
|
console.log(`[PTC Debug] Sending ${effectiveTools.length} tools to API ` +
|
|
378
497
|
`(filtered from ${toolDefs.length})`);
|
|
379
498
|
}
|
|
499
|
+
// Fetch files from previous session if session_id is provided
|
|
500
|
+
let files;
|
|
501
|
+
if (session_id != null && session_id.length > 0) {
|
|
502
|
+
files = await fetchSessionFiles(baseUrl, apiKey, session_id, proxy);
|
|
503
|
+
}
|
|
380
504
|
let response = await makeRequest(EXEC_ENDPOINT, apiKey, {
|
|
381
505
|
code,
|
|
382
506
|
tools: effectiveTools,
|
|
383
507
|
session_id,
|
|
384
508
|
timeout,
|
|
509
|
+
...(files && files.length > 0 ? { files } : {}),
|
|
385
510
|
}, proxy);
|
|
386
511
|
// ====================================================================
|
|
387
512
|
// Phase 2: Handle response loop
|
|
@@ -431,8 +556,10 @@ When to use this instead of calling tools directly:
|
|
|
431
556
|
exports.createProgrammaticToolCallingTool = createProgrammaticToolCallingTool;
|
|
432
557
|
exports.executeTools = executeTools;
|
|
433
558
|
exports.extractUsedToolNames = extractUsedToolNames;
|
|
559
|
+
exports.fetchSessionFiles = fetchSessionFiles;
|
|
434
560
|
exports.filterToolsByUsage = filterToolsByUsage;
|
|
435
561
|
exports.formatCompletedResponse = formatCompletedResponse;
|
|
436
562
|
exports.makeRequest = makeRequest;
|
|
437
563
|
exports.normalizeToPythonIdentifier = normalizeToPythonIdentifier;
|
|
564
|
+
exports.unwrapToolResponse = unwrapToolResponse;
|
|
438
565
|
//# sourceMappingURL=ProgrammaticToolCalling.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProgrammaticToolCalling.cjs","sources":["../../../src/tools/ProgrammaticToolCalling.ts"],"sourcesContent":["// src/tools/ProgrammaticToolCalling.ts\nimport { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files are READ-ONLY. Save changes to NEW filenames. To access these files in future executions, provide the `session_id` as a parameter (not in your code).';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\n/** Default max round-trips to prevent infinite loops */\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\n\n/** Default execution timeout in milliseconds */\nconst DEFAULT_TIMEOUT = 60000;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst ProgrammaticToolCallingSchema = z.object({\n code: z\n .string()\n .min(1)\n .describe(\n `Python code that calls tools programmatically. Tools are automatically available as async Python functions - DO NOT define them yourself.\n\nThe Code API generates async function stubs from the tool definitions. Just call them directly:\n\nExample (Simple call):\n result = await get_weather(city=\"San Francisco\")\n print(result)\n\nExample (Parallel - Fastest):\n results = await asyncio.gather(\n get_weather(city=\"SF\"),\n get_weather(city=\"NYC\"),\n get_weather(city=\"London\")\n )\n for city, weather in zip([\"SF\", \"NYC\", \"London\"], results):\n print(f\"{city}: {weather['temperature']}°F\")\n\nExample (Loop with processing):\n team = await get_team_members()\n for member in team:\n expenses = await get_expenses(user_id=member['id'])\n total = sum(e['amount'] for e in expenses)\n print(f\"{member['name']}: \\${total:.2f}\")\n\nExample (Conditional logic):\n data = await fetch_data(source=\"primary\")\n if not data:\n data = await fetch_data(source=\"backup\")\n print(f\"Got {len(data)} records\")\n\nRequirements:\n- Tools are pre-defined as async functions - DO NOT write function definitions\n- Use await for all tool calls\n- Use asyncio.gather() for parallel execution of independent calls\n- Only print() output flows back to the context window\n- Tool results from programmatic calls do NOT consume context tokens`\n ),\n session_id: z\n .string()\n .optional()\n .describe(\n 'Session ID for file access (same as regular code execution). Files load into /mnt/data/ and are READ-ONLY.'\n ),\n timeout: z\n .number()\n .int()\n .min(1000)\n .max(300000)\n .optional()\n .default(DEFAULT_TIMEOUT)\n .describe(\n 'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.'\n ),\n});\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/** Python reserved keywords that get `_tool` suffix in Code API */\nconst PYTHON_KEYWORDS = new Set([\n 'False',\n 'None',\n 'True',\n 'and',\n 'as',\n 'assert',\n 'async',\n 'await',\n 'break',\n 'class',\n 'continue',\n 'def',\n 'del',\n 'elif',\n 'else',\n 'except',\n 'finally',\n 'for',\n 'from',\n 'global',\n 'if',\n 'import',\n 'in',\n 'is',\n 'lambda',\n 'nonlocal',\n 'not',\n 'or',\n 'pass',\n 'raise',\n 'return',\n 'try',\n 'while',\n 'with',\n 'yield',\n]);\n\n/**\n * Normalizes a tool name to Python identifier format.\n * Must match the Code API's `normalizePythonFunctionName` exactly:\n * 1. Replace hyphens and spaces with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a Python keyword\n * @param name - The tool name to normalize\n * @returns Normalized Python-safe identifier\n */\nexport function normalizeToPythonIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s]/g, '_');\n\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (PYTHON_KEYWORDS.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the Python code.\n * Handles hyphen/underscore conversion since Python identifiers use underscores.\n * @param code - The Python code to analyze\n * @param toolNameMap - Map from normalized Python name to original tool name\n * @returns Set of original tool names found in the code\n */\nexport function extractUsedToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [pythonName, originalName] of toolNameMap) {\n const escapedName = pythonName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\s*\\\\(`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the code.\n * Handles the hyphen-to-underscore conversion for Python compatibility.\n * @param toolDefs - All available tool definitions\n * @param code - The Python code to analyze\n * @param debug - Enable debug logging\n * @returns Filtered array of tool definitions\n */\nexport function filterToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const tool of toolDefs) {\n const pythonName = normalizeToPythonIdentifier(tool.name);\n toolNameMap.set(pythonName, tool.name);\n }\n\n const usedToolNames = extractUsedToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[PTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((tool) => usedToolNames.has(tool.name));\n}\n\n/**\n * Makes an HTTP request to the Code API.\n * @param endpoint - The API endpoint URL\n * @param apiKey - The API key for authentication\n * @param body - The request body\n * @param proxy - Optional HTTP proxy URL\n * @returns The parsed API response\n */\nexport async function makeRequest(\n endpoint: string,\n apiKey: string,\n body: Record<string, unknown>,\n proxy?: string\n): Promise<t.ProgrammaticExecutionResponse> {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(body),\n };\n\n if (proxy != null && proxy !== '') {\n fetchOptions.agent = new HttpsProxyAgent(proxy);\n }\n\n const response = await fetch(endpoint, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `HTTP error! status: ${response.status}, body: ${errorText}`\n );\n }\n\n return (await response.json()) as t.ProgrammaticExecutionResponse;\n}\n\n/**\n * Executes tools in parallel when requested by the API.\n * Uses Promise.all for parallel execution, catching individual errors.\n * @param toolCalls - Array of tool calls from the API\n * @param toolMap - Map of tool names to executable tools\n * @returns Array of tool results\n */\nexport async function executeTools(\n toolCalls: t.PTCToolCall[],\n toolMap: t.ToolMap\n): Promise<t.PTCToolResult[]> {\n const executions = toolCalls.map(async (call): Promise<t.PTCToolResult> => {\n const tool = toolMap.get(call.name);\n\n if (!tool) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: `Tool '${call.name}' not found. Available tools: ${Array.from(toolMap.keys()).join(', ')}`,\n };\n }\n\n try {\n const result = await tool.invoke(call.input, {\n metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },\n });\n return {\n call_id: call.id,\n result,\n is_error: false,\n };\n } catch (error) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: (error as Error).message || 'Tool execution failed',\n };\n }\n });\n\n return await Promise.all(executions);\n}\n\n/**\n * Formats the completed response for the agent.\n * @param response - The completed API response\n * @returns Tuple of [formatted string, artifact]\n */\nexport function formatCompletedResponse(\n response: t.ProgrammaticExecutionResponse\n): [string, t.ProgrammaticExecutionArtifact] {\n let formatted = '';\n\n if (response.stdout != null && response.stdout !== '') {\n formatted += `stdout:\\n${response.stdout}\\n`;\n } else {\n formatted += emptyOutputMessage;\n }\n\n if (response.stderr != null && response.stderr !== '') {\n formatted += `stderr:\\n${response.stderr}\\n`;\n }\n\n if (response.files && response.files.length > 0) {\n formatted += 'Generated files:\\n';\n\n const fileCount = response.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = response.files[i];\n const isImage = imageExtRegex.test(file.name);\n formatted += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formatted += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formatted += `\\nsession_id: ${response.session_id}\\n\\n${accessMessage}`;\n }\n\n return [\n formatted.trim(),\n {\n session_id: response.session_id,\n files: response.files,\n },\n ];\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Programmatic Tool Calling tool for complex multi-tool workflows.\n *\n * This tool enables AI agents to write Python code that orchestrates multiple\n * tool calls programmatically, reducing LLM round-trips and token usage.\n *\n * The tool map must be provided at runtime via config.configurable.toolMap.\n *\n * @param params - Configuration parameters (apiKey, baseUrl, maxRoundTrips, proxy)\n * @returns A LangChain DynamicStructuredTool for programmatic tool calling\n *\n * @example\n * const ptcTool = createProgrammaticToolCallingTool({\n * apiKey: process.env.CODE_API_KEY,\n * maxRoundTrips: 20\n * });\n *\n * const [output, artifact] = await ptcTool.invoke(\n * { code, tools },\n * { configurable: { toolMap } }\n * );\n */\nexport function createProgrammaticToolCallingTool(\n initParams: t.ProgrammaticToolCallingParams = {}\n): DynamicStructuredTool<typeof ProgrammaticToolCallingSchema> {\n const apiKey =\n (initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n\n if (!apiKey) {\n throw new Error(\n 'No API key provided for programmatic tool calling. ' +\n 'Set CODE_API_KEY environment variable or pass apiKey in initParams.'\n );\n }\n\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n const description = `\nRun tools by writing Python code. Tools are available as async functions - just call them with await.\n\nThis is different from execute_code: here you can call your tools (like get_weather, get_expenses, etc.) directly in Python code.\n\nUsage:\n- Tools are pre-defined as async functions - call them with await\n- Use asyncio.gather() to run multiple tools in parallel\n- Only print() output is returned - tool results stay in Python\n\nExamples:\n- Simple: result = await get_weather(city=\"NYC\")\n- Loop: for user in users: data = await get_expenses(user_id=user['id'])\n- Parallel: sf, ny = await asyncio.gather(get_weather(city=\"SF\"), get_weather(city=\"NY\"))\n\nWhen to use this instead of calling tools directly:\n- You need to call tools in a loop (process many items)\n- You want parallel execution (asyncio.gather)\n- You need conditionals based on tool results\n- You want to aggregate/filter data before returning\n`.trim();\n\n return tool<typeof ProgrammaticToolCallingSchema>(\n async (params, config) => {\n const { code, session_id, timeout = DEFAULT_TIMEOUT } = params;\n\n // Extra params injected by ToolNode (follows web_search pattern)\n const { toolMap, toolDefs } = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache>;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n },\n proxy\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = await executeTools(\n response.tool_calls ?? [],\n toolMap\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n throw new Error(\n `Programmatic execution failed: ${(error as Error).message}`\n );\n }\n },\n {\n name: Constants.PROGRAMMATIC_TOOL_CALLING,\n description,\n schema: ProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":["config","z","HttpsProxyAgent","Constants","imageExtRegex","EnvVar","getEnvironmentVariable","getCodeBaseURL","tool"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProgrammaticToolCalling.cjs","sources":["../../../src/tools/ProgrammaticToolCalling.ts"],"sourcesContent":["// src/tools/ProgrammaticToolCalling.ts\nimport { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files are READ-ONLY. Save changes to NEW filenames. To access these files in future executions, provide the `session_id` as a parameter (not in your code).';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\n/** Default max round-trips to prevent infinite loops */\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\n\n/** Default execution timeout in milliseconds */\nconst DEFAULT_TIMEOUT = 60000;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst ProgrammaticToolCallingSchema = z.object({\n code: z\n .string()\n .min(1)\n .describe(\n `Python code that calls tools programmatically. Tools are automatically available as async Python functions - DO NOT define them yourself.\n\nThe Code API generates async function stubs from the tool definitions. Just call them directly:\n\nExample (Simple call):\n result = await get_weather(city=\"San Francisco\")\n print(result)\n\nExample (Parallel - Fastest):\n results = await asyncio.gather(\n get_weather(city=\"SF\"),\n get_weather(city=\"NYC\"),\n get_weather(city=\"London\")\n )\n for city, weather in zip([\"SF\", \"NYC\", \"London\"], results):\n print(f\"{city}: {weather['temperature']}°F\")\n\nExample (Loop with processing):\n team = await get_team_members()\n for member in team:\n expenses = await get_expenses(user_id=member['id'])\n total = sum(e['amount'] for e in expenses)\n print(f\"{member['name']}: \\${total:.2f}\")\n\nExample (Conditional logic):\n data = await fetch_data(source=\"primary\")\n if not data:\n data = await fetch_data(source=\"backup\")\n print(f\"Got {len(data)} records\")\n\nRequirements:\n- Tools are pre-defined as async functions - DO NOT write function definitions\n- Use await for all tool calls\n- Use asyncio.gather() for parallel execution of independent calls\n- DO NOT call asyncio.run() - an event loop is already running\n- Only print() output flows back to the context window\n- Tool results from programmatic calls do NOT consume context tokens`\n ),\n session_id: z\n .string()\n .optional()\n .describe(\n 'Session ID for file access (same as regular code execution). Files load into /mnt/data/ and are READ-ONLY.'\n ),\n timeout: z\n .number()\n .int()\n .min(1000)\n .max(300000)\n .optional()\n .default(DEFAULT_TIMEOUT)\n .describe(\n 'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.'\n ),\n});\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/** Python reserved keywords that get `_tool` suffix in Code API */\nconst PYTHON_KEYWORDS = new Set([\n 'False',\n 'None',\n 'True',\n 'and',\n 'as',\n 'assert',\n 'async',\n 'await',\n 'break',\n 'class',\n 'continue',\n 'def',\n 'del',\n 'elif',\n 'else',\n 'except',\n 'finally',\n 'for',\n 'from',\n 'global',\n 'if',\n 'import',\n 'in',\n 'is',\n 'lambda',\n 'nonlocal',\n 'not',\n 'or',\n 'pass',\n 'raise',\n 'return',\n 'try',\n 'while',\n 'with',\n 'yield',\n]);\n\n/**\n * Normalizes a tool name to Python identifier format.\n * Must match the Code API's `normalizePythonFunctionName` exactly:\n * 1. Replace hyphens and spaces with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a Python keyword\n * @param name - The tool name to normalize\n * @returns Normalized Python-safe identifier\n */\nexport function normalizeToPythonIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s]/g, '_');\n\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (PYTHON_KEYWORDS.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the Python code.\n * Handles hyphen/underscore conversion since Python identifiers use underscores.\n * @param code - The Python code to analyze\n * @param toolNameMap - Map from normalized Python name to original tool name\n * @returns Set of original tool names found in the code\n */\nexport function extractUsedToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [pythonName, originalName] of toolNameMap) {\n const escapedName = pythonName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\s*\\\\(`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the code.\n * Handles the hyphen-to-underscore conversion for Python compatibility.\n * @param toolDefs - All available tool definitions\n * @param code - The Python code to analyze\n * @param debug - Enable debug logging\n * @returns Filtered array of tool definitions\n */\nexport function filterToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const tool of toolDefs) {\n const pythonName = normalizeToPythonIdentifier(tool.name);\n toolNameMap.set(pythonName, tool.name);\n }\n\n const usedToolNames = extractUsedToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[PTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((tool) => usedToolNames.has(tool.name));\n}\n\n/**\n * Fetches files from a previous session to make them available for the current execution.\n * Files are returned as CodeEnvFile references to be included in the request.\n * @param baseUrl - The base URL for the Code API\n * @param apiKey - The API key for authentication\n * @param sessionId - The session ID to fetch files from\n * @param proxy - Optional HTTP proxy URL\n * @returns Array of CodeEnvFile references, or empty array if fetch fails\n */\nexport async function fetchSessionFiles(\n baseUrl: string,\n apiKey: string,\n sessionId: string,\n proxy?: string\n): Promise<t.CodeEnvFile[]> {\n try {\n const filesEndpoint = `${baseUrl}/files/${sessionId}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n };\n\n if (proxy != null && proxy !== '') {\n fetchOptions.agent = new HttpsProxyAgent(proxy);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(`Failed to fetch files for session: ${response.status}`);\n }\n\n const files = await response.json();\n if (!Array.isArray(files) || files.length === 0) {\n return [];\n }\n\n return files.map((file: Record<string, unknown>) => {\n // Extract the ID from the file name (part after session ID prefix and before extension)\n const nameParts = (file.name as string).split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id: sessionId,\n id,\n name: (file.metadata as Record<string, unknown>)[\n 'original-filename'\n ] as string,\n };\n });\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn(\n `Failed to fetch files for session: ${sessionId}, ${(error as Error).message}`\n );\n return [];\n }\n}\n\n/**\n * Makes an HTTP request to the Code API.\n * @param endpoint - The API endpoint URL\n * @param apiKey - The API key for authentication\n * @param body - The request body\n * @param proxy - Optional HTTP proxy URL\n * @returns The parsed API response\n */\nexport async function makeRequest(\n endpoint: string,\n apiKey: string,\n body: Record<string, unknown>,\n proxy?: string\n): Promise<t.ProgrammaticExecutionResponse> {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(body),\n };\n\n if (proxy != null && proxy !== '') {\n fetchOptions.agent = new HttpsProxyAgent(proxy);\n }\n\n const response = await fetch(endpoint, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `HTTP error! status: ${response.status}, body: ${errorText}`\n );\n }\n\n return (await response.json()) as t.ProgrammaticExecutionResponse;\n}\n\n/**\n * Unwraps tool responses that may be formatted as tuples.\n * MCP tools return [content, artifacts], we need to extract the raw data.\n * @param result - The raw result from tool.invoke()\n * @param isMCPTool - Whether this is an MCP tool (has mcp property)\n * @returns Unwrapped raw data (string, object, or parsed JSON)\n */\nexport function unwrapToolResponse(\n result: unknown,\n isMCPTool: boolean\n): unknown {\n // Only unwrap if this is an MCP tool and result is a tuple\n if (!isMCPTool) {\n return result;\n }\n\n // Check if result is a tuple/array with [content, artifacts]\n if (Array.isArray(result) && result.length >= 1) {\n const [content] = result;\n\n // If first element is a string, return it\n if (typeof content === 'string') {\n // Try to parse as JSON if it looks like JSON\n if (typeof content === 'string' && content.trim().startsWith('{')) {\n try {\n return JSON.parse(content);\n } catch {\n return content;\n }\n }\n return content;\n }\n\n // If first element is an array (content blocks), extract text/data\n if (Array.isArray(content)) {\n // If it's an array of content blocks (like [{ type: 'text', text: '...' }])\n if (\n content.length > 0 &&\n typeof content[0] === 'object' &&\n 'type' in content[0]\n ) {\n // Extract text from content blocks\n const texts = content\n .filter((block: unknown) => {\n if (typeof block !== 'object' || block === null) return false;\n const b = block as Record<string, unknown>;\n return b.type === 'text' && typeof b.text === 'string';\n })\n .map((block: unknown) => {\n const b = block as Record<string, unknown>;\n return b.text as string;\n });\n\n if (texts.length > 0) {\n const combined = texts.join('\\n');\n // Try to parse as JSON if it looks like JSON (objects or arrays)\n if (\n combined.trim().startsWith('{') ||\n combined.trim().startsWith('[')\n ) {\n try {\n return JSON.parse(combined);\n } catch {\n return combined;\n }\n }\n return combined;\n }\n }\n // Otherwise return the content array as-is\n return content;\n }\n\n // If first element is an object, return it\n if (typeof content === 'object' && content !== null) {\n return content;\n }\n }\n\n // Not a formatted response, return as-is\n return result;\n}\n\n/**\n * Executes tools in parallel when requested by the API.\n * Uses Promise.all for parallel execution, catching individual errors.\n * Unwraps formatted responses (e.g., MCP tool tuples) to raw data.\n * @param toolCalls - Array of tool calls from the API\n * @param toolMap - Map of tool names to executable tools\n * @returns Array of tool results\n */\nexport async function executeTools(\n toolCalls: t.PTCToolCall[],\n toolMap: t.ToolMap\n): Promise<t.PTCToolResult[]> {\n const executions = toolCalls.map(async (call): Promise<t.PTCToolResult> => {\n const tool = toolMap.get(call.name);\n\n if (!tool) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: `Tool '${call.name}' not found. Available tools: ${Array.from(toolMap.keys()).join(', ')}`,\n };\n }\n\n try {\n const result = await tool.invoke(call.input, {\n metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },\n });\n\n const isMCPTool = tool.mcp === true;\n const unwrappedResult = unwrapToolResponse(result, isMCPTool);\n\n return {\n call_id: call.id,\n result: unwrappedResult,\n is_error: false,\n };\n } catch (error) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: (error as Error).message || 'Tool execution failed',\n };\n }\n });\n\n return await Promise.all(executions);\n}\n\n/**\n * Formats the completed response for the agent.\n * @param response - The completed API response\n * @returns Tuple of [formatted string, artifact]\n */\nexport function formatCompletedResponse(\n response: t.ProgrammaticExecutionResponse\n): [string, t.ProgrammaticExecutionArtifact] {\n let formatted = '';\n\n if (response.stdout != null && response.stdout !== '') {\n formatted += `stdout:\\n${response.stdout}\\n`;\n } else {\n formatted += emptyOutputMessage;\n }\n\n if (response.stderr != null && response.stderr !== '') {\n formatted += `stderr:\\n${response.stderr}\\n`;\n }\n\n if (response.files && response.files.length > 0) {\n formatted += 'Generated files:\\n';\n\n const fileCount = response.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = response.files[i];\n const isImage = imageExtRegex.test(file.name);\n formatted += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formatted += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formatted += `\\nsession_id: ${response.session_id}\\n\\n${accessMessage}`;\n }\n\n return [\n formatted.trim(),\n {\n session_id: response.session_id,\n files: response.files,\n },\n ];\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Programmatic Tool Calling tool for complex multi-tool workflows.\n *\n * This tool enables AI agents to write Python code that orchestrates multiple\n * tool calls programmatically, reducing LLM round-trips and token usage.\n *\n * The tool map must be provided at runtime via config.configurable.toolMap.\n *\n * @param params - Configuration parameters (apiKey, baseUrl, maxRoundTrips, proxy)\n * @returns A LangChain DynamicStructuredTool for programmatic tool calling\n *\n * @example\n * const ptcTool = createProgrammaticToolCallingTool({\n * apiKey: process.env.CODE_API_KEY,\n * maxRoundTrips: 20\n * });\n *\n * const [output, artifact] = await ptcTool.invoke(\n * { code, tools },\n * { configurable: { toolMap } }\n * );\n */\nexport function createProgrammaticToolCallingTool(\n initParams: t.ProgrammaticToolCallingParams = {}\n): DynamicStructuredTool<typeof ProgrammaticToolCallingSchema> {\n const apiKey =\n (initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n\n if (!apiKey) {\n throw new Error(\n 'No API key provided for programmatic tool calling. ' +\n 'Set CODE_API_KEY environment variable or pass apiKey in initParams.'\n );\n }\n\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n const description = `\nRun tools via Python code. Tools are injected as async functions—call with \\`await\\`.\n\nRules:\n- Tools are pre-defined—DO NOT define them yourself\n- Use \\`await\\` for calls, \\`asyncio.gather()\\` for parallel; NEVER call \\`asyncio.run()\\`\n- Only \\`print()\\` output returns to the model; tool results are raw dicts/lists/strings\n- Stateless: variables/imports don't persist; use \\`session_id\\` param for file access\n- Files mount at \\`/mnt/data/\\` (READ-ONLY); write changes to NEW filenames\n- Tool names normalized: hyphens→underscores, keywords get \\`_tool\\` suffix\n\nWhen to use (vs. direct tool calls): loops, conditionals, parallel execution, aggregation.\n\nExamples:\n result = await get_weather(city=\"NYC\"); print(result)\n sf, ny = await asyncio.gather(get_weather(city=\"SF\"), get_weather(city=\"NY\"))\n`.trim();\n\n return tool<typeof ProgrammaticToolCallingSchema>(\n async (params, config) => {\n const { code, session_id, timeout = DEFAULT_TIMEOUT } = params;\n\n // Extra params injected by ToolNode (follows web_search pattern)\n const { toolMap, toolDefs } = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache>;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n // Fetch files from previous session if session_id is provided\n let files: t.CodeEnvFile[] | undefined;\n if (session_id != null && session_id.length > 0) {\n files = await fetchSessionFiles(baseUrl, apiKey, session_id, proxy);\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n ...(files && files.length > 0 ? { files } : {}),\n },\n proxy\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = await executeTools(\n response.tool_calls ?? [],\n toolMap\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n throw new Error(\n `Programmatic execution failed: ${(error as Error).message}`\n );\n }\n },\n {\n name: Constants.PROGRAMMATIC_TOOL_CALLING,\n description,\n schema: ProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":["config","z","HttpsProxyAgent","Constants","imageExtRegex","EnvVar","getEnvironmentVariable","getCodeBaseURL","tool"],"mappings":"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"}
|
package/dist/esm/main.mjs
CHANGED
|
@@ -13,7 +13,7 @@ export { Graph, StandardGraph } from './graphs/Graph.mjs';
|
|
|
13
13
|
export { MultiAgentGraph } from './graphs/MultiAgentGraph.mjs';
|
|
14
14
|
export { Calculator } from './tools/Calculator.mjs';
|
|
15
15
|
export { createCodeExecutionTool, getCodeBaseURL, imageExtRegex } from './tools/CodeExecutor.mjs';
|
|
16
|
-
export { createProgrammaticToolCallingTool, executeTools, extractUsedToolNames, filterToolsByUsage, formatCompletedResponse, makeRequest, normalizeToPythonIdentifier } from './tools/ProgrammaticToolCalling.mjs';
|
|
16
|
+
export { createProgrammaticToolCallingTool, executeTools, extractUsedToolNames, fetchSessionFiles, filterToolsByUsage, formatCompletedResponse, makeRequest, normalizeToPythonIdentifier, unwrapToolResponse } from './tools/ProgrammaticToolCalling.mjs';
|
|
17
17
|
export { countNestedGroups, createToolSearchRegexTool, escapeRegexSpecialChars, hasNestedQuantifiers, isDangerousPattern, sanitizeRegex } from './tools/ToolSearchRegex.mjs';
|
|
18
18
|
export { handleServerToolResult, handleToolCallChunks, handleToolCalls, toolResultTypes } from './tools/handlers.mjs';
|
|
19
19
|
export { createSearchTool } from './tools/search/tool.mjs';
|
|
@@ -61,6 +61,7 @@ Requirements:
|
|
|
61
61
|
- Tools are pre-defined as async functions - DO NOT write function definitions
|
|
62
62
|
- Use await for all tool calls
|
|
63
63
|
- Use asyncio.gather() for parallel execution of independent calls
|
|
64
|
+
- DO NOT call asyncio.run() - an event loop is already running
|
|
64
65
|
- Only print() output flows back to the context window
|
|
65
66
|
- Tool results from programmatic calls do NOT consume context tokens`),
|
|
66
67
|
session_id: z
|
|
@@ -188,6 +189,53 @@ function filterToolsByUsage(toolDefs, code, debug = false) {
|
|
|
188
189
|
}
|
|
189
190
|
return toolDefs.filter((tool) => usedToolNames.has(tool.name));
|
|
190
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Fetches files from a previous session to make them available for the current execution.
|
|
194
|
+
* Files are returned as CodeEnvFile references to be included in the request.
|
|
195
|
+
* @param baseUrl - The base URL for the Code API
|
|
196
|
+
* @param apiKey - The API key for authentication
|
|
197
|
+
* @param sessionId - The session ID to fetch files from
|
|
198
|
+
* @param proxy - Optional HTTP proxy URL
|
|
199
|
+
* @returns Array of CodeEnvFile references, or empty array if fetch fails
|
|
200
|
+
*/
|
|
201
|
+
async function fetchSessionFiles(baseUrl, apiKey, sessionId, proxy) {
|
|
202
|
+
try {
|
|
203
|
+
const filesEndpoint = `${baseUrl}/files/${sessionId}?detail=full`;
|
|
204
|
+
const fetchOptions = {
|
|
205
|
+
method: 'GET',
|
|
206
|
+
headers: {
|
|
207
|
+
'User-Agent': 'LibreChat/1.0',
|
|
208
|
+
'X-API-Key': apiKey,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
if (proxy != null && proxy !== '') {
|
|
212
|
+
fetchOptions.agent = new HttpsProxyAgent(proxy);
|
|
213
|
+
}
|
|
214
|
+
const response = await fetch(filesEndpoint, fetchOptions);
|
|
215
|
+
if (!response.ok) {
|
|
216
|
+
throw new Error(`Failed to fetch files for session: ${response.status}`);
|
|
217
|
+
}
|
|
218
|
+
const files = await response.json();
|
|
219
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
return files.map((file) => {
|
|
223
|
+
// Extract the ID from the file name (part after session ID prefix and before extension)
|
|
224
|
+
const nameParts = file.name.split('/');
|
|
225
|
+
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
226
|
+
return {
|
|
227
|
+
session_id: sessionId,
|
|
228
|
+
id,
|
|
229
|
+
name: file.metadata['original-filename'],
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
// eslint-disable-next-line no-console
|
|
235
|
+
console.warn(`Failed to fetch files for session: ${sessionId}, ${error.message}`);
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
191
239
|
/**
|
|
192
240
|
* Makes an HTTP request to the Code API.
|
|
193
241
|
* @param endpoint - The API endpoint URL
|
|
@@ -216,9 +264,82 @@ async function makeRequest(endpoint, apiKey, body, proxy) {
|
|
|
216
264
|
}
|
|
217
265
|
return (await response.json());
|
|
218
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Unwraps tool responses that may be formatted as tuples.
|
|
269
|
+
* MCP tools return [content, artifacts], we need to extract the raw data.
|
|
270
|
+
* @param result - The raw result from tool.invoke()
|
|
271
|
+
* @param isMCPTool - Whether this is an MCP tool (has mcp property)
|
|
272
|
+
* @returns Unwrapped raw data (string, object, or parsed JSON)
|
|
273
|
+
*/
|
|
274
|
+
function unwrapToolResponse(result, isMCPTool) {
|
|
275
|
+
// Only unwrap if this is an MCP tool and result is a tuple
|
|
276
|
+
if (!isMCPTool) {
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
// Check if result is a tuple/array with [content, artifacts]
|
|
280
|
+
if (Array.isArray(result) && result.length >= 1) {
|
|
281
|
+
const [content] = result;
|
|
282
|
+
// If first element is a string, return it
|
|
283
|
+
if (typeof content === 'string') {
|
|
284
|
+
// Try to parse as JSON if it looks like JSON
|
|
285
|
+
if (typeof content === 'string' && content.trim().startsWith('{')) {
|
|
286
|
+
try {
|
|
287
|
+
return JSON.parse(content);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return content;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return content;
|
|
294
|
+
}
|
|
295
|
+
// If first element is an array (content blocks), extract text/data
|
|
296
|
+
if (Array.isArray(content)) {
|
|
297
|
+
// If it's an array of content blocks (like [{ type: 'text', text: '...' }])
|
|
298
|
+
if (content.length > 0 &&
|
|
299
|
+
typeof content[0] === 'object' &&
|
|
300
|
+
'type' in content[0]) {
|
|
301
|
+
// Extract text from content blocks
|
|
302
|
+
const texts = content
|
|
303
|
+
.filter((block) => {
|
|
304
|
+
if (typeof block !== 'object' || block === null)
|
|
305
|
+
return false;
|
|
306
|
+
const b = block;
|
|
307
|
+
return b.type === 'text' && typeof b.text === 'string';
|
|
308
|
+
})
|
|
309
|
+
.map((block) => {
|
|
310
|
+
const b = block;
|
|
311
|
+
return b.text;
|
|
312
|
+
});
|
|
313
|
+
if (texts.length > 0) {
|
|
314
|
+
const combined = texts.join('\n');
|
|
315
|
+
// Try to parse as JSON if it looks like JSON (objects or arrays)
|
|
316
|
+
if (combined.trim().startsWith('{') ||
|
|
317
|
+
combined.trim().startsWith('[')) {
|
|
318
|
+
try {
|
|
319
|
+
return JSON.parse(combined);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return combined;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return combined;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Otherwise return the content array as-is
|
|
329
|
+
return content;
|
|
330
|
+
}
|
|
331
|
+
// If first element is an object, return it
|
|
332
|
+
if (typeof content === 'object' && content !== null) {
|
|
333
|
+
return content;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Not a formatted response, return as-is
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
219
339
|
/**
|
|
220
340
|
* Executes tools in parallel when requested by the API.
|
|
221
341
|
* Uses Promise.all for parallel execution, catching individual errors.
|
|
342
|
+
* Unwraps formatted responses (e.g., MCP tool tuples) to raw data.
|
|
222
343
|
* @param toolCalls - Array of tool calls from the API
|
|
223
344
|
* @param toolMap - Map of tool names to executable tools
|
|
224
345
|
* @returns Array of tool results
|
|
@@ -238,9 +359,11 @@ async function executeTools(toolCalls, toolMap) {
|
|
|
238
359
|
const result = await tool.invoke(call.input, {
|
|
239
360
|
metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },
|
|
240
361
|
});
|
|
362
|
+
const isMCPTool = tool.mcp === true;
|
|
363
|
+
const unwrappedResult = unwrapToolResponse(result, isMCPTool);
|
|
241
364
|
return {
|
|
242
365
|
call_id: call.id,
|
|
243
|
-
result,
|
|
366
|
+
result: unwrappedResult,
|
|
244
367
|
is_error: false,
|
|
245
368
|
};
|
|
246
369
|
}
|
|
@@ -332,25 +455,21 @@ function createProgrammaticToolCallingTool(initParams = {}) {
|
|
|
332
455
|
const debug = initParams.debug ?? process.env.PTC_DEBUG === 'true';
|
|
333
456
|
const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;
|
|
334
457
|
const description = `
|
|
335
|
-
Run tools
|
|
458
|
+
Run tools via Python code. Tools are injected as async functions—call with \`await\`.
|
|
336
459
|
|
|
337
|
-
|
|
460
|
+
Rules:
|
|
461
|
+
- Tools are pre-defined—DO NOT define them yourself
|
|
462
|
+
- Use \`await\` for calls, \`asyncio.gather()\` for parallel; NEVER call \`asyncio.run()\`
|
|
463
|
+
- Only \`print()\` output returns to the model; tool results are raw dicts/lists/strings
|
|
464
|
+
- Stateless: variables/imports don't persist; use \`session_id\` param for file access
|
|
465
|
+
- Files mount at \`/mnt/data/\` (READ-ONLY); write changes to NEW filenames
|
|
466
|
+
- Tool names normalized: hyphens→underscores, keywords get \`_tool\` suffix
|
|
338
467
|
|
|
339
|
-
|
|
340
|
-
- Tools are pre-defined as async functions - call them with await
|
|
341
|
-
- Use asyncio.gather() to run multiple tools in parallel
|
|
342
|
-
- Only print() output is returned - tool results stay in Python
|
|
468
|
+
When to use (vs. direct tool calls): loops, conditionals, parallel execution, aggregation.
|
|
343
469
|
|
|
344
470
|
Examples:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
- Parallel: sf, ny = await asyncio.gather(get_weather(city="SF"), get_weather(city="NY"))
|
|
348
|
-
|
|
349
|
-
When to use this instead of calling tools directly:
|
|
350
|
-
- You need to call tools in a loop (process many items)
|
|
351
|
-
- You want parallel execution (asyncio.gather)
|
|
352
|
-
- You need conditionals based on tool results
|
|
353
|
-
- You want to aggregate/filter data before returning
|
|
471
|
+
result = await get_weather(city="NYC"); print(result)
|
|
472
|
+
sf, ny = await asyncio.gather(get_weather(city="SF"), get_weather(city="NY"))
|
|
354
473
|
`.trim();
|
|
355
474
|
return tool(async (params, config) => {
|
|
356
475
|
const { code, session_id, timeout = DEFAULT_TIMEOUT } = params;
|
|
@@ -375,11 +494,17 @@ When to use this instead of calling tools directly:
|
|
|
375
494
|
console.log(`[PTC Debug] Sending ${effectiveTools.length} tools to API ` +
|
|
376
495
|
`(filtered from ${toolDefs.length})`);
|
|
377
496
|
}
|
|
497
|
+
// Fetch files from previous session if session_id is provided
|
|
498
|
+
let files;
|
|
499
|
+
if (session_id != null && session_id.length > 0) {
|
|
500
|
+
files = await fetchSessionFiles(baseUrl, apiKey, session_id, proxy);
|
|
501
|
+
}
|
|
378
502
|
let response = await makeRequest(EXEC_ENDPOINT, apiKey, {
|
|
379
503
|
code,
|
|
380
504
|
tools: effectiveTools,
|
|
381
505
|
session_id,
|
|
382
506
|
timeout,
|
|
507
|
+
...(files && files.length > 0 ? { files } : {}),
|
|
383
508
|
}, proxy);
|
|
384
509
|
// ====================================================================
|
|
385
510
|
// Phase 2: Handle response loop
|
|
@@ -426,5 +551,5 @@ When to use this instead of calling tools directly:
|
|
|
426
551
|
});
|
|
427
552
|
}
|
|
428
553
|
|
|
429
|
-
export { createProgrammaticToolCallingTool, executeTools, extractUsedToolNames, filterToolsByUsage, formatCompletedResponse, makeRequest, normalizeToPythonIdentifier };
|
|
554
|
+
export { createProgrammaticToolCallingTool, executeTools, extractUsedToolNames, fetchSessionFiles, filterToolsByUsage, formatCompletedResponse, makeRequest, normalizeToPythonIdentifier, unwrapToolResponse };
|
|
430
555
|
//# sourceMappingURL=ProgrammaticToolCalling.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProgrammaticToolCalling.mjs","sources":["../../../src/tools/ProgrammaticToolCalling.ts"],"sourcesContent":["// src/tools/ProgrammaticToolCalling.ts\nimport { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files are READ-ONLY. Save changes to NEW filenames. To access these files in future executions, provide the `session_id` as a parameter (not in your code).';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\n/** Default max round-trips to prevent infinite loops */\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\n\n/** Default execution timeout in milliseconds */\nconst DEFAULT_TIMEOUT = 60000;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst ProgrammaticToolCallingSchema = z.object({\n code: z\n .string()\n .min(1)\n .describe(\n `Python code that calls tools programmatically. Tools are automatically available as async Python functions - DO NOT define them yourself.\n\nThe Code API generates async function stubs from the tool definitions. Just call them directly:\n\nExample (Simple call):\n result = await get_weather(city=\"San Francisco\")\n print(result)\n\nExample (Parallel - Fastest):\n results = await asyncio.gather(\n get_weather(city=\"SF\"),\n get_weather(city=\"NYC\"),\n get_weather(city=\"London\")\n )\n for city, weather in zip([\"SF\", \"NYC\", \"London\"], results):\n print(f\"{city}: {weather['temperature']}°F\")\n\nExample (Loop with processing):\n team = await get_team_members()\n for member in team:\n expenses = await get_expenses(user_id=member['id'])\n total = sum(e['amount'] for e in expenses)\n print(f\"{member['name']}: \\${total:.2f}\")\n\nExample (Conditional logic):\n data = await fetch_data(source=\"primary\")\n if not data:\n data = await fetch_data(source=\"backup\")\n print(f\"Got {len(data)} records\")\n\nRequirements:\n- Tools are pre-defined as async functions - DO NOT write function definitions\n- Use await for all tool calls\n- Use asyncio.gather() for parallel execution of independent calls\n- Only print() output flows back to the context window\n- Tool results from programmatic calls do NOT consume context tokens`\n ),\n session_id: z\n .string()\n .optional()\n .describe(\n 'Session ID for file access (same as regular code execution). Files load into /mnt/data/ and are READ-ONLY.'\n ),\n timeout: z\n .number()\n .int()\n .min(1000)\n .max(300000)\n .optional()\n .default(DEFAULT_TIMEOUT)\n .describe(\n 'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.'\n ),\n});\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/** Python reserved keywords that get `_tool` suffix in Code API */\nconst PYTHON_KEYWORDS = new Set([\n 'False',\n 'None',\n 'True',\n 'and',\n 'as',\n 'assert',\n 'async',\n 'await',\n 'break',\n 'class',\n 'continue',\n 'def',\n 'del',\n 'elif',\n 'else',\n 'except',\n 'finally',\n 'for',\n 'from',\n 'global',\n 'if',\n 'import',\n 'in',\n 'is',\n 'lambda',\n 'nonlocal',\n 'not',\n 'or',\n 'pass',\n 'raise',\n 'return',\n 'try',\n 'while',\n 'with',\n 'yield',\n]);\n\n/**\n * Normalizes a tool name to Python identifier format.\n * Must match the Code API's `normalizePythonFunctionName` exactly:\n * 1. Replace hyphens and spaces with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a Python keyword\n * @param name - The tool name to normalize\n * @returns Normalized Python-safe identifier\n */\nexport function normalizeToPythonIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s]/g, '_');\n\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (PYTHON_KEYWORDS.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the Python code.\n * Handles hyphen/underscore conversion since Python identifiers use underscores.\n * @param code - The Python code to analyze\n * @param toolNameMap - Map from normalized Python name to original tool name\n * @returns Set of original tool names found in the code\n */\nexport function extractUsedToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [pythonName, originalName] of toolNameMap) {\n const escapedName = pythonName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\s*\\\\(`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the code.\n * Handles the hyphen-to-underscore conversion for Python compatibility.\n * @param toolDefs - All available tool definitions\n * @param code - The Python code to analyze\n * @param debug - Enable debug logging\n * @returns Filtered array of tool definitions\n */\nexport function filterToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const tool of toolDefs) {\n const pythonName = normalizeToPythonIdentifier(tool.name);\n toolNameMap.set(pythonName, tool.name);\n }\n\n const usedToolNames = extractUsedToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[PTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((tool) => usedToolNames.has(tool.name));\n}\n\n/**\n * Makes an HTTP request to the Code API.\n * @param endpoint - The API endpoint URL\n * @param apiKey - The API key for authentication\n * @param body - The request body\n * @param proxy - Optional HTTP proxy URL\n * @returns The parsed API response\n */\nexport async function makeRequest(\n endpoint: string,\n apiKey: string,\n body: Record<string, unknown>,\n proxy?: string\n): Promise<t.ProgrammaticExecutionResponse> {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(body),\n };\n\n if (proxy != null && proxy !== '') {\n fetchOptions.agent = new HttpsProxyAgent(proxy);\n }\n\n const response = await fetch(endpoint, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `HTTP error! status: ${response.status}, body: ${errorText}`\n );\n }\n\n return (await response.json()) as t.ProgrammaticExecutionResponse;\n}\n\n/**\n * Executes tools in parallel when requested by the API.\n * Uses Promise.all for parallel execution, catching individual errors.\n * @param toolCalls - Array of tool calls from the API\n * @param toolMap - Map of tool names to executable tools\n * @returns Array of tool results\n */\nexport async function executeTools(\n toolCalls: t.PTCToolCall[],\n toolMap: t.ToolMap\n): Promise<t.PTCToolResult[]> {\n const executions = toolCalls.map(async (call): Promise<t.PTCToolResult> => {\n const tool = toolMap.get(call.name);\n\n if (!tool) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: `Tool '${call.name}' not found. Available tools: ${Array.from(toolMap.keys()).join(', ')}`,\n };\n }\n\n try {\n const result = await tool.invoke(call.input, {\n metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },\n });\n return {\n call_id: call.id,\n result,\n is_error: false,\n };\n } catch (error) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: (error as Error).message || 'Tool execution failed',\n };\n }\n });\n\n return await Promise.all(executions);\n}\n\n/**\n * Formats the completed response for the agent.\n * @param response - The completed API response\n * @returns Tuple of [formatted string, artifact]\n */\nexport function formatCompletedResponse(\n response: t.ProgrammaticExecutionResponse\n): [string, t.ProgrammaticExecutionArtifact] {\n let formatted = '';\n\n if (response.stdout != null && response.stdout !== '') {\n formatted += `stdout:\\n${response.stdout}\\n`;\n } else {\n formatted += emptyOutputMessage;\n }\n\n if (response.stderr != null && response.stderr !== '') {\n formatted += `stderr:\\n${response.stderr}\\n`;\n }\n\n if (response.files && response.files.length > 0) {\n formatted += 'Generated files:\\n';\n\n const fileCount = response.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = response.files[i];\n const isImage = imageExtRegex.test(file.name);\n formatted += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formatted += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formatted += `\\nsession_id: ${response.session_id}\\n\\n${accessMessage}`;\n }\n\n return [\n formatted.trim(),\n {\n session_id: response.session_id,\n files: response.files,\n },\n ];\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Programmatic Tool Calling tool for complex multi-tool workflows.\n *\n * This tool enables AI agents to write Python code that orchestrates multiple\n * tool calls programmatically, reducing LLM round-trips and token usage.\n *\n * The tool map must be provided at runtime via config.configurable.toolMap.\n *\n * @param params - Configuration parameters (apiKey, baseUrl, maxRoundTrips, proxy)\n * @returns A LangChain DynamicStructuredTool for programmatic tool calling\n *\n * @example\n * const ptcTool = createProgrammaticToolCallingTool({\n * apiKey: process.env.CODE_API_KEY,\n * maxRoundTrips: 20\n * });\n *\n * const [output, artifact] = await ptcTool.invoke(\n * { code, tools },\n * { configurable: { toolMap } }\n * );\n */\nexport function createProgrammaticToolCallingTool(\n initParams: t.ProgrammaticToolCallingParams = {}\n): DynamicStructuredTool<typeof ProgrammaticToolCallingSchema> {\n const apiKey =\n (initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n\n if (!apiKey) {\n throw new Error(\n 'No API key provided for programmatic tool calling. ' +\n 'Set CODE_API_KEY environment variable or pass apiKey in initParams.'\n );\n }\n\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n const description = `\nRun tools by writing Python code. Tools are available as async functions - just call them with await.\n\nThis is different from execute_code: here you can call your tools (like get_weather, get_expenses, etc.) directly in Python code.\n\nUsage:\n- Tools are pre-defined as async functions - call them with await\n- Use asyncio.gather() to run multiple tools in parallel\n- Only print() output is returned - tool results stay in Python\n\nExamples:\n- Simple: result = await get_weather(city=\"NYC\")\n- Loop: for user in users: data = await get_expenses(user_id=user['id'])\n- Parallel: sf, ny = await asyncio.gather(get_weather(city=\"SF\"), get_weather(city=\"NY\"))\n\nWhen to use this instead of calling tools directly:\n- You need to call tools in a loop (process many items)\n- You want parallel execution (asyncio.gather)\n- You need conditionals based on tool results\n- You want to aggregate/filter data before returning\n`.trim();\n\n return tool<typeof ProgrammaticToolCallingSchema>(\n async (params, config) => {\n const { code, session_id, timeout = DEFAULT_TIMEOUT } = params;\n\n // Extra params injected by ToolNode (follows web_search pattern)\n const { toolMap, toolDefs } = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache>;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n },\n proxy\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = await executeTools(\n response.tool_calls ?? [],\n toolMap\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n throw new Error(\n `Programmatic execution failed: ${(error as Error).message}`\n );\n }\n },\n {\n name: Constants.PROGRAMMATIC_TOOL_CALLING,\n description,\n schema: ProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProgrammaticToolCalling.mjs","sources":["../../../src/tools/ProgrammaticToolCalling.ts"],"sourcesContent":["// src/tools/ProgrammaticToolCalling.ts\nimport { z } from 'zod';\nimport { config } from 'dotenv';\nimport fetch, { RequestInit } from 'node-fetch';\nimport { HttpsProxyAgent } from 'https-proxy-agent';\nimport { getEnvironmentVariable } from '@langchain/core/utils/env';\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { ToolCall } from '@langchain/core/messages/tool';\nimport type * as t from '@/types';\nimport { imageExtRegex, getCodeBaseURL } from './CodeExecutor';\nimport { EnvVar, Constants } from '@/common';\n\nconfig();\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst imageMessage = 'Image is already displayed to the user';\nconst otherMessage = 'File is already downloaded by the user';\nconst accessMessage =\n 'Note: Files are READ-ONLY. Save changes to NEW filenames. To access these files in future executions, provide the `session_id` as a parameter (not in your code).';\nconst emptyOutputMessage =\n 'stdout: Empty. Ensure you\\'re writing output explicitly.\\n';\n\n/** Default max round-trips to prevent infinite loops */\nconst DEFAULT_MAX_ROUND_TRIPS = 20;\n\n/** Default execution timeout in milliseconds */\nconst DEFAULT_TIMEOUT = 60000;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst ProgrammaticToolCallingSchema = z.object({\n code: z\n .string()\n .min(1)\n .describe(\n `Python code that calls tools programmatically. Tools are automatically available as async Python functions - DO NOT define them yourself.\n\nThe Code API generates async function stubs from the tool definitions. Just call them directly:\n\nExample (Simple call):\n result = await get_weather(city=\"San Francisco\")\n print(result)\n\nExample (Parallel - Fastest):\n results = await asyncio.gather(\n get_weather(city=\"SF\"),\n get_weather(city=\"NYC\"),\n get_weather(city=\"London\")\n )\n for city, weather in zip([\"SF\", \"NYC\", \"London\"], results):\n print(f\"{city}: {weather['temperature']}°F\")\n\nExample (Loop with processing):\n team = await get_team_members()\n for member in team:\n expenses = await get_expenses(user_id=member['id'])\n total = sum(e['amount'] for e in expenses)\n print(f\"{member['name']}: \\${total:.2f}\")\n\nExample (Conditional logic):\n data = await fetch_data(source=\"primary\")\n if not data:\n data = await fetch_data(source=\"backup\")\n print(f\"Got {len(data)} records\")\n\nRequirements:\n- Tools are pre-defined as async functions - DO NOT write function definitions\n- Use await for all tool calls\n- Use asyncio.gather() for parallel execution of independent calls\n- DO NOT call asyncio.run() - an event loop is already running\n- Only print() output flows back to the context window\n- Tool results from programmatic calls do NOT consume context tokens`\n ),\n session_id: z\n .string()\n .optional()\n .describe(\n 'Session ID for file access (same as regular code execution). Files load into /mnt/data/ and are READ-ONLY.'\n ),\n timeout: z\n .number()\n .int()\n .min(1000)\n .max(300000)\n .optional()\n .default(DEFAULT_TIMEOUT)\n .describe(\n 'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.'\n ),\n});\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/** Python reserved keywords that get `_tool` suffix in Code API */\nconst PYTHON_KEYWORDS = new Set([\n 'False',\n 'None',\n 'True',\n 'and',\n 'as',\n 'assert',\n 'async',\n 'await',\n 'break',\n 'class',\n 'continue',\n 'def',\n 'del',\n 'elif',\n 'else',\n 'except',\n 'finally',\n 'for',\n 'from',\n 'global',\n 'if',\n 'import',\n 'in',\n 'is',\n 'lambda',\n 'nonlocal',\n 'not',\n 'or',\n 'pass',\n 'raise',\n 'return',\n 'try',\n 'while',\n 'with',\n 'yield',\n]);\n\n/**\n * Normalizes a tool name to Python identifier format.\n * Must match the Code API's `normalizePythonFunctionName` exactly:\n * 1. Replace hyphens and spaces with underscores\n * 2. Remove any other invalid characters\n * 3. Prefix with underscore if starts with number\n * 4. Append `_tool` if it's a Python keyword\n * @param name - The tool name to normalize\n * @returns Normalized Python-safe identifier\n */\nexport function normalizeToPythonIdentifier(name: string): string {\n let normalized = name.replace(/[-\\s]/g, '_');\n\n normalized = normalized.replace(/[^a-zA-Z0-9_]/g, '');\n\n if (/^[0-9]/.test(normalized)) {\n normalized = '_' + normalized;\n }\n\n if (PYTHON_KEYWORDS.has(normalized)) {\n normalized = normalized + '_tool';\n }\n\n return normalized;\n}\n\n/**\n * Extracts tool names that are actually called in the Python code.\n * Handles hyphen/underscore conversion since Python identifiers use underscores.\n * @param code - The Python code to analyze\n * @param toolNameMap - Map from normalized Python name to original tool name\n * @returns Set of original tool names found in the code\n */\nexport function extractUsedToolNames(\n code: string,\n toolNameMap: Map<string, string>\n): Set<string> {\n const usedTools = new Set<string>();\n\n for (const [pythonName, originalName] of toolNameMap) {\n const escapedName = pythonName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const pattern = new RegExp(`\\\\b${escapedName}\\\\s*\\\\(`, 'g');\n\n if (pattern.test(code)) {\n usedTools.add(originalName);\n }\n }\n\n return usedTools;\n}\n\n/**\n * Filters tool definitions to only include tools actually used in the code.\n * Handles the hyphen-to-underscore conversion for Python compatibility.\n * @param toolDefs - All available tool definitions\n * @param code - The Python code to analyze\n * @param debug - Enable debug logging\n * @returns Filtered array of tool definitions\n */\nexport function filterToolsByUsage(\n toolDefs: t.LCTool[],\n code: string,\n debug = false\n): t.LCTool[] {\n const toolNameMap = new Map<string, string>();\n for (const tool of toolDefs) {\n const pythonName = normalizeToPythonIdentifier(tool.name);\n toolNameMap.set(pythonName, tool.name);\n }\n\n const usedToolNames = extractUsedToolNames(code, toolNameMap);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Tool filtering: found ${usedToolNames.size}/${toolDefs.length} tools in code`\n );\n if (usedToolNames.size > 0) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Matched tools: ${Array.from(usedToolNames).join(', ')}`\n );\n }\n }\n\n if (usedToolNames.size === 0) {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n '[PTC Debug] No tools detected in code - sending all tools as fallback'\n );\n }\n return toolDefs;\n }\n\n return toolDefs.filter((tool) => usedToolNames.has(tool.name));\n}\n\n/**\n * Fetches files from a previous session to make them available for the current execution.\n * Files are returned as CodeEnvFile references to be included in the request.\n * @param baseUrl - The base URL for the Code API\n * @param apiKey - The API key for authentication\n * @param sessionId - The session ID to fetch files from\n * @param proxy - Optional HTTP proxy URL\n * @returns Array of CodeEnvFile references, or empty array if fetch fails\n */\nexport async function fetchSessionFiles(\n baseUrl: string,\n apiKey: string,\n sessionId: string,\n proxy?: string\n): Promise<t.CodeEnvFile[]> {\n try {\n const filesEndpoint = `${baseUrl}/files/${sessionId}?detail=full`;\n const fetchOptions: RequestInit = {\n method: 'GET',\n headers: {\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n };\n\n if (proxy != null && proxy !== '') {\n fetchOptions.agent = new HttpsProxyAgent(proxy);\n }\n\n const response = await fetch(filesEndpoint, fetchOptions);\n if (!response.ok) {\n throw new Error(`Failed to fetch files for session: ${response.status}`);\n }\n\n const files = await response.json();\n if (!Array.isArray(files) || files.length === 0) {\n return [];\n }\n\n return files.map((file: Record<string, unknown>) => {\n // Extract the ID from the file name (part after session ID prefix and before extension)\n const nameParts = (file.name as string).split('/');\n const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';\n\n return {\n session_id: sessionId,\n id,\n name: (file.metadata as Record<string, unknown>)[\n 'original-filename'\n ] as string,\n };\n });\n } catch (error) {\n // eslint-disable-next-line no-console\n console.warn(\n `Failed to fetch files for session: ${sessionId}, ${(error as Error).message}`\n );\n return [];\n }\n}\n\n/**\n * Makes an HTTP request to the Code API.\n * @param endpoint - The API endpoint URL\n * @param apiKey - The API key for authentication\n * @param body - The request body\n * @param proxy - Optional HTTP proxy URL\n * @returns The parsed API response\n */\nexport async function makeRequest(\n endpoint: string,\n apiKey: string,\n body: Record<string, unknown>,\n proxy?: string\n): Promise<t.ProgrammaticExecutionResponse> {\n const fetchOptions: RequestInit = {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'LibreChat/1.0',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify(body),\n };\n\n if (proxy != null && proxy !== '') {\n fetchOptions.agent = new HttpsProxyAgent(proxy);\n }\n\n const response = await fetch(endpoint, fetchOptions);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `HTTP error! status: ${response.status}, body: ${errorText}`\n );\n }\n\n return (await response.json()) as t.ProgrammaticExecutionResponse;\n}\n\n/**\n * Unwraps tool responses that may be formatted as tuples.\n * MCP tools return [content, artifacts], we need to extract the raw data.\n * @param result - The raw result from tool.invoke()\n * @param isMCPTool - Whether this is an MCP tool (has mcp property)\n * @returns Unwrapped raw data (string, object, or parsed JSON)\n */\nexport function unwrapToolResponse(\n result: unknown,\n isMCPTool: boolean\n): unknown {\n // Only unwrap if this is an MCP tool and result is a tuple\n if (!isMCPTool) {\n return result;\n }\n\n // Check if result is a tuple/array with [content, artifacts]\n if (Array.isArray(result) && result.length >= 1) {\n const [content] = result;\n\n // If first element is a string, return it\n if (typeof content === 'string') {\n // Try to parse as JSON if it looks like JSON\n if (typeof content === 'string' && content.trim().startsWith('{')) {\n try {\n return JSON.parse(content);\n } catch {\n return content;\n }\n }\n return content;\n }\n\n // If first element is an array (content blocks), extract text/data\n if (Array.isArray(content)) {\n // If it's an array of content blocks (like [{ type: 'text', text: '...' }])\n if (\n content.length > 0 &&\n typeof content[0] === 'object' &&\n 'type' in content[0]\n ) {\n // Extract text from content blocks\n const texts = content\n .filter((block: unknown) => {\n if (typeof block !== 'object' || block === null) return false;\n const b = block as Record<string, unknown>;\n return b.type === 'text' && typeof b.text === 'string';\n })\n .map((block: unknown) => {\n const b = block as Record<string, unknown>;\n return b.text as string;\n });\n\n if (texts.length > 0) {\n const combined = texts.join('\\n');\n // Try to parse as JSON if it looks like JSON (objects or arrays)\n if (\n combined.trim().startsWith('{') ||\n combined.trim().startsWith('[')\n ) {\n try {\n return JSON.parse(combined);\n } catch {\n return combined;\n }\n }\n return combined;\n }\n }\n // Otherwise return the content array as-is\n return content;\n }\n\n // If first element is an object, return it\n if (typeof content === 'object' && content !== null) {\n return content;\n }\n }\n\n // Not a formatted response, return as-is\n return result;\n}\n\n/**\n * Executes tools in parallel when requested by the API.\n * Uses Promise.all for parallel execution, catching individual errors.\n * Unwraps formatted responses (e.g., MCP tool tuples) to raw data.\n * @param toolCalls - Array of tool calls from the API\n * @param toolMap - Map of tool names to executable tools\n * @returns Array of tool results\n */\nexport async function executeTools(\n toolCalls: t.PTCToolCall[],\n toolMap: t.ToolMap\n): Promise<t.PTCToolResult[]> {\n const executions = toolCalls.map(async (call): Promise<t.PTCToolResult> => {\n const tool = toolMap.get(call.name);\n\n if (!tool) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: `Tool '${call.name}' not found. Available tools: ${Array.from(toolMap.keys()).join(', ')}`,\n };\n }\n\n try {\n const result = await tool.invoke(call.input, {\n metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },\n });\n\n const isMCPTool = tool.mcp === true;\n const unwrappedResult = unwrapToolResponse(result, isMCPTool);\n\n return {\n call_id: call.id,\n result: unwrappedResult,\n is_error: false,\n };\n } catch (error) {\n return {\n call_id: call.id,\n result: null,\n is_error: true,\n error_message: (error as Error).message || 'Tool execution failed',\n };\n }\n });\n\n return await Promise.all(executions);\n}\n\n/**\n * Formats the completed response for the agent.\n * @param response - The completed API response\n * @returns Tuple of [formatted string, artifact]\n */\nexport function formatCompletedResponse(\n response: t.ProgrammaticExecutionResponse\n): [string, t.ProgrammaticExecutionArtifact] {\n let formatted = '';\n\n if (response.stdout != null && response.stdout !== '') {\n formatted += `stdout:\\n${response.stdout}\\n`;\n } else {\n formatted += emptyOutputMessage;\n }\n\n if (response.stderr != null && response.stderr !== '') {\n formatted += `stderr:\\n${response.stderr}\\n`;\n }\n\n if (response.files && response.files.length > 0) {\n formatted += 'Generated files:\\n';\n\n const fileCount = response.files.length;\n for (let i = 0; i < fileCount; i++) {\n const file = response.files[i];\n const isImage = imageExtRegex.test(file.name);\n formatted += `- /mnt/data/${file.name} | ${isImage ? imageMessage : otherMessage}`;\n\n if (i < fileCount - 1) {\n formatted += fileCount <= 3 ? ', ' : ',\\n';\n }\n }\n\n formatted += `\\nsession_id: ${response.session_id}\\n\\n${accessMessage}`;\n }\n\n return [\n formatted.trim(),\n {\n session_id: response.session_id,\n files: response.files,\n },\n ];\n}\n\n// ============================================================================\n// Tool Factory\n// ============================================================================\n\n/**\n * Creates a Programmatic Tool Calling tool for complex multi-tool workflows.\n *\n * This tool enables AI agents to write Python code that orchestrates multiple\n * tool calls programmatically, reducing LLM round-trips and token usage.\n *\n * The tool map must be provided at runtime via config.configurable.toolMap.\n *\n * @param params - Configuration parameters (apiKey, baseUrl, maxRoundTrips, proxy)\n * @returns A LangChain DynamicStructuredTool for programmatic tool calling\n *\n * @example\n * const ptcTool = createProgrammaticToolCallingTool({\n * apiKey: process.env.CODE_API_KEY,\n * maxRoundTrips: 20\n * });\n *\n * const [output, artifact] = await ptcTool.invoke(\n * { code, tools },\n * { configurable: { toolMap } }\n * );\n */\nexport function createProgrammaticToolCallingTool(\n initParams: t.ProgrammaticToolCallingParams = {}\n): DynamicStructuredTool<typeof ProgrammaticToolCallingSchema> {\n const apiKey =\n (initParams[EnvVar.CODE_API_KEY] as string | undefined) ??\n initParams.apiKey ??\n getEnvironmentVariable(EnvVar.CODE_API_KEY) ??\n '';\n\n if (!apiKey) {\n throw new Error(\n 'No API key provided for programmatic tool calling. ' +\n 'Set CODE_API_KEY environment variable or pass apiKey in initParams.'\n );\n }\n\n const baseUrl = initParams.baseUrl ?? getCodeBaseURL();\n const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;\n const proxy = initParams.proxy ?? process.env.PROXY;\n const debug = initParams.debug ?? process.env.PTC_DEBUG === 'true';\n const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;\n\n const description = `\nRun tools via Python code. Tools are injected as async functions—call with \\`await\\`.\n\nRules:\n- Tools are pre-defined—DO NOT define them yourself\n- Use \\`await\\` for calls, \\`asyncio.gather()\\` for parallel; NEVER call \\`asyncio.run()\\`\n- Only \\`print()\\` output returns to the model; tool results are raw dicts/lists/strings\n- Stateless: variables/imports don't persist; use \\`session_id\\` param for file access\n- Files mount at \\`/mnt/data/\\` (READ-ONLY); write changes to NEW filenames\n- Tool names normalized: hyphens→underscores, keywords get \\`_tool\\` suffix\n\nWhen to use (vs. direct tool calls): loops, conditionals, parallel execution, aggregation.\n\nExamples:\n result = await get_weather(city=\"NYC\"); print(result)\n sf, ny = await asyncio.gather(get_weather(city=\"SF\"), get_weather(city=\"NY\"))\n`.trim();\n\n return tool<typeof ProgrammaticToolCallingSchema>(\n async (params, config) => {\n const { code, session_id, timeout = DEFAULT_TIMEOUT } = params;\n\n // Extra params injected by ToolNode (follows web_search pattern)\n const { toolMap, toolDefs } = (config.toolCall ?? {}) as ToolCall &\n Partial<t.ProgrammaticCache>;\n\n if (toolMap == null || toolMap.size === 0) {\n throw new Error(\n 'No toolMap provided. ' +\n 'ToolNode should inject this from AgentContext when invoked through the graph.'\n );\n }\n\n if (toolDefs == null || toolDefs.length === 0) {\n throw new Error(\n 'No tool definitions provided. ' +\n 'Either pass tools in the input or ensure ToolNode injects toolDefs.'\n );\n }\n\n let roundTrip = 0;\n\n try {\n // ====================================================================\n // Phase 1: Filter tools and make initial request\n // ====================================================================\n\n const effectiveTools = filterToolsByUsage(toolDefs, code, debug);\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Sending ${effectiveTools.length} tools to API ` +\n `(filtered from ${toolDefs.length})`\n );\n }\n\n // Fetch files from previous session if session_id is provided\n let files: t.CodeEnvFile[] | undefined;\n if (session_id != null && session_id.length > 0) {\n files = await fetchSessionFiles(baseUrl, apiKey, session_id, proxy);\n }\n\n let response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n code,\n tools: effectiveTools,\n session_id,\n timeout,\n ...(files && files.length > 0 ? { files } : {}),\n },\n proxy\n );\n\n // ====================================================================\n // Phase 2: Handle response loop\n // ====================================================================\n\n while (response.status === 'tool_call_required') {\n roundTrip++;\n\n if (roundTrip > maxRoundTrips) {\n throw new Error(\n `Exceeded maximum round trips (${maxRoundTrips}). ` +\n 'This may indicate an infinite loop, excessive tool calls, ' +\n 'or a logic error in your code.'\n );\n }\n\n if (debug) {\n // eslint-disable-next-line no-console\n console.log(\n `[PTC Debug] Round trip ${roundTrip}: ${response.tool_calls?.length ?? 0} tool(s) to execute`\n );\n }\n\n const toolResults = await executeTools(\n response.tool_calls ?? [],\n toolMap\n );\n\n response = await makeRequest(\n EXEC_ENDPOINT,\n apiKey,\n {\n continuation_token: response.continuation_token,\n tool_results: toolResults,\n },\n proxy\n );\n }\n\n // ====================================================================\n // Phase 3: Handle final state\n // ====================================================================\n\n if (response.status === 'completed') {\n return formatCompletedResponse(response);\n }\n\n if (response.status === 'error') {\n throw new Error(\n `Execution error: ${response.error}` +\n (response.stderr != null && response.stderr !== ''\n ? `\\n\\nStderr:\\n${response.stderr}`\n : '')\n );\n }\n\n throw new Error(`Unexpected response status: ${response.status}`);\n } catch (error) {\n throw new Error(\n `Programmatic execution failed: ${(error as Error).message}`\n );\n }\n },\n {\n name: Constants.PROGRAMMATIC_TOOL_CALLING,\n description,\n schema: ProgrammaticToolCallingSchema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -42,6 +42,16 @@ export declare function extractUsedToolNames(code: string, toolNameMap: Map<stri
|
|
|
42
42
|
* @returns Filtered array of tool definitions
|
|
43
43
|
*/
|
|
44
44
|
export declare function filterToolsByUsage(toolDefs: t.LCTool[], code: string, debug?: boolean): t.LCTool[];
|
|
45
|
+
/**
|
|
46
|
+
* Fetches files from a previous session to make them available for the current execution.
|
|
47
|
+
* Files are returned as CodeEnvFile references to be included in the request.
|
|
48
|
+
* @param baseUrl - The base URL for the Code API
|
|
49
|
+
* @param apiKey - The API key for authentication
|
|
50
|
+
* @param sessionId - The session ID to fetch files from
|
|
51
|
+
* @param proxy - Optional HTTP proxy URL
|
|
52
|
+
* @returns Array of CodeEnvFile references, or empty array if fetch fails
|
|
53
|
+
*/
|
|
54
|
+
export declare function fetchSessionFiles(baseUrl: string, apiKey: string, sessionId: string, proxy?: string): Promise<t.CodeEnvFile[]>;
|
|
45
55
|
/**
|
|
46
56
|
* Makes an HTTP request to the Code API.
|
|
47
57
|
* @param endpoint - The API endpoint URL
|
|
@@ -51,9 +61,18 @@ export declare function filterToolsByUsage(toolDefs: t.LCTool[], code: string, d
|
|
|
51
61
|
* @returns The parsed API response
|
|
52
62
|
*/
|
|
53
63
|
export declare function makeRequest(endpoint: string, apiKey: string, body: Record<string, unknown>, proxy?: string): Promise<t.ProgrammaticExecutionResponse>;
|
|
64
|
+
/**
|
|
65
|
+
* Unwraps tool responses that may be formatted as tuples.
|
|
66
|
+
* MCP tools return [content, artifacts], we need to extract the raw data.
|
|
67
|
+
* @param result - The raw result from tool.invoke()
|
|
68
|
+
* @param isMCPTool - Whether this is an MCP tool (has mcp property)
|
|
69
|
+
* @returns Unwrapped raw data (string, object, or parsed JSON)
|
|
70
|
+
*/
|
|
71
|
+
export declare function unwrapToolResponse(result: unknown, isMCPTool: boolean): unknown;
|
|
54
72
|
/**
|
|
55
73
|
* Executes tools in parallel when requested by the API.
|
|
56
74
|
* Uses Promise.all for parallel execution, catching individual errors.
|
|
75
|
+
* Unwraps formatted responses (e.g., MCP tool tuples) to raw data.
|
|
57
76
|
* @param toolCalls - Array of tool calls from the API
|
|
58
77
|
* @param toolMap - Map of tool names to executable tools
|
|
59
78
|
* @returns Array of tool results
|
|
@@ -11,7 +11,9 @@ export type CustomToolCall = {
|
|
|
11
11
|
type?: 'tool_call';
|
|
12
12
|
output?: string;
|
|
13
13
|
};
|
|
14
|
-
export type GenericTool = StructuredToolInterface | RunnableToolLike
|
|
14
|
+
export type GenericTool = (StructuredToolInterface | RunnableToolLike) & {
|
|
15
|
+
mcp?: boolean;
|
|
16
|
+
};
|
|
15
17
|
export type ToolMap = Map<string, GenericTool>;
|
|
16
18
|
export type ToolRefs = {
|
|
17
19
|
tools: GenericTool[];
|
package/package.json
CHANGED
|
@@ -72,6 +72,7 @@ Requirements:
|
|
|
72
72
|
- Tools are pre-defined as async functions - DO NOT write function definitions
|
|
73
73
|
- Use await for all tool calls
|
|
74
74
|
- Use asyncio.gather() for parallel execution of independent calls
|
|
75
|
+
- DO NOT call asyncio.run() - an event loop is already running
|
|
75
76
|
- Only print() output flows back to the context window
|
|
76
77
|
- Tool results from programmatic calls do NOT consume context tokens`
|
|
77
78
|
),
|
|
@@ -234,6 +235,67 @@ export function filterToolsByUsage(
|
|
|
234
235
|
return toolDefs.filter((tool) => usedToolNames.has(tool.name));
|
|
235
236
|
}
|
|
236
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Fetches files from a previous session to make them available for the current execution.
|
|
240
|
+
* Files are returned as CodeEnvFile references to be included in the request.
|
|
241
|
+
* @param baseUrl - The base URL for the Code API
|
|
242
|
+
* @param apiKey - The API key for authentication
|
|
243
|
+
* @param sessionId - The session ID to fetch files from
|
|
244
|
+
* @param proxy - Optional HTTP proxy URL
|
|
245
|
+
* @returns Array of CodeEnvFile references, or empty array if fetch fails
|
|
246
|
+
*/
|
|
247
|
+
export async function fetchSessionFiles(
|
|
248
|
+
baseUrl: string,
|
|
249
|
+
apiKey: string,
|
|
250
|
+
sessionId: string,
|
|
251
|
+
proxy?: string
|
|
252
|
+
): Promise<t.CodeEnvFile[]> {
|
|
253
|
+
try {
|
|
254
|
+
const filesEndpoint = `${baseUrl}/files/${sessionId}?detail=full`;
|
|
255
|
+
const fetchOptions: RequestInit = {
|
|
256
|
+
method: 'GET',
|
|
257
|
+
headers: {
|
|
258
|
+
'User-Agent': 'LibreChat/1.0',
|
|
259
|
+
'X-API-Key': apiKey,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
if (proxy != null && proxy !== '') {
|
|
264
|
+
fetchOptions.agent = new HttpsProxyAgent(proxy);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const response = await fetch(filesEndpoint, fetchOptions);
|
|
268
|
+
if (!response.ok) {
|
|
269
|
+
throw new Error(`Failed to fetch files for session: ${response.status}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const files = await response.json();
|
|
273
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return files.map((file: Record<string, unknown>) => {
|
|
278
|
+
// Extract the ID from the file name (part after session ID prefix and before extension)
|
|
279
|
+
const nameParts = (file.name as string).split('/');
|
|
280
|
+
const id = nameParts.length > 1 ? nameParts[1].split('.')[0] : '';
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
session_id: sessionId,
|
|
284
|
+
id,
|
|
285
|
+
name: (file.metadata as Record<string, unknown>)[
|
|
286
|
+
'original-filename'
|
|
287
|
+
] as string,
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
} catch (error) {
|
|
291
|
+
// eslint-disable-next-line no-console
|
|
292
|
+
console.warn(
|
|
293
|
+
`Failed to fetch files for session: ${sessionId}, ${(error as Error).message}`
|
|
294
|
+
);
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
237
299
|
/**
|
|
238
300
|
* Makes an HTTP request to the Code API.
|
|
239
301
|
* @param endpoint - The API endpoint URL
|
|
@@ -274,9 +336,93 @@ export async function makeRequest(
|
|
|
274
336
|
return (await response.json()) as t.ProgrammaticExecutionResponse;
|
|
275
337
|
}
|
|
276
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Unwraps tool responses that may be formatted as tuples.
|
|
341
|
+
* MCP tools return [content, artifacts], we need to extract the raw data.
|
|
342
|
+
* @param result - The raw result from tool.invoke()
|
|
343
|
+
* @param isMCPTool - Whether this is an MCP tool (has mcp property)
|
|
344
|
+
* @returns Unwrapped raw data (string, object, or parsed JSON)
|
|
345
|
+
*/
|
|
346
|
+
export function unwrapToolResponse(
|
|
347
|
+
result: unknown,
|
|
348
|
+
isMCPTool: boolean
|
|
349
|
+
): unknown {
|
|
350
|
+
// Only unwrap if this is an MCP tool and result is a tuple
|
|
351
|
+
if (!isMCPTool) {
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Check if result is a tuple/array with [content, artifacts]
|
|
356
|
+
if (Array.isArray(result) && result.length >= 1) {
|
|
357
|
+
const [content] = result;
|
|
358
|
+
|
|
359
|
+
// If first element is a string, return it
|
|
360
|
+
if (typeof content === 'string') {
|
|
361
|
+
// Try to parse as JSON if it looks like JSON
|
|
362
|
+
if (typeof content === 'string' && content.trim().startsWith('{')) {
|
|
363
|
+
try {
|
|
364
|
+
return JSON.parse(content);
|
|
365
|
+
} catch {
|
|
366
|
+
return content;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return content;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// If first element is an array (content blocks), extract text/data
|
|
373
|
+
if (Array.isArray(content)) {
|
|
374
|
+
// If it's an array of content blocks (like [{ type: 'text', text: '...' }])
|
|
375
|
+
if (
|
|
376
|
+
content.length > 0 &&
|
|
377
|
+
typeof content[0] === 'object' &&
|
|
378
|
+
'type' in content[0]
|
|
379
|
+
) {
|
|
380
|
+
// Extract text from content blocks
|
|
381
|
+
const texts = content
|
|
382
|
+
.filter((block: unknown) => {
|
|
383
|
+
if (typeof block !== 'object' || block === null) return false;
|
|
384
|
+
const b = block as Record<string, unknown>;
|
|
385
|
+
return b.type === 'text' && typeof b.text === 'string';
|
|
386
|
+
})
|
|
387
|
+
.map((block: unknown) => {
|
|
388
|
+
const b = block as Record<string, unknown>;
|
|
389
|
+
return b.text as string;
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
if (texts.length > 0) {
|
|
393
|
+
const combined = texts.join('\n');
|
|
394
|
+
// Try to parse as JSON if it looks like JSON (objects or arrays)
|
|
395
|
+
if (
|
|
396
|
+
combined.trim().startsWith('{') ||
|
|
397
|
+
combined.trim().startsWith('[')
|
|
398
|
+
) {
|
|
399
|
+
try {
|
|
400
|
+
return JSON.parse(combined);
|
|
401
|
+
} catch {
|
|
402
|
+
return combined;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return combined;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// Otherwise return the content array as-is
|
|
409
|
+
return content;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// If first element is an object, return it
|
|
413
|
+
if (typeof content === 'object' && content !== null) {
|
|
414
|
+
return content;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Not a formatted response, return as-is
|
|
419
|
+
return result;
|
|
420
|
+
}
|
|
421
|
+
|
|
277
422
|
/**
|
|
278
423
|
* Executes tools in parallel when requested by the API.
|
|
279
424
|
* Uses Promise.all for parallel execution, catching individual errors.
|
|
425
|
+
* Unwraps formatted responses (e.g., MCP tool tuples) to raw data.
|
|
280
426
|
* @param toolCalls - Array of tool calls from the API
|
|
281
427
|
* @param toolMap - Map of tool names to executable tools
|
|
282
428
|
* @returns Array of tool results
|
|
@@ -301,9 +447,13 @@ export async function executeTools(
|
|
|
301
447
|
const result = await tool.invoke(call.input, {
|
|
302
448
|
metadata: { [Constants.PROGRAMMATIC_TOOL_CALLING]: true },
|
|
303
449
|
});
|
|
450
|
+
|
|
451
|
+
const isMCPTool = tool.mcp === true;
|
|
452
|
+
const unwrappedResult = unwrapToolResponse(result, isMCPTool);
|
|
453
|
+
|
|
304
454
|
return {
|
|
305
455
|
call_id: call.id,
|
|
306
|
-
result,
|
|
456
|
+
result: unwrappedResult,
|
|
307
457
|
is_error: false,
|
|
308
458
|
};
|
|
309
459
|
} catch (error) {
|
|
@@ -414,25 +564,21 @@ export function createProgrammaticToolCallingTool(
|
|
|
414
564
|
const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;
|
|
415
565
|
|
|
416
566
|
const description = `
|
|
417
|
-
Run tools
|
|
567
|
+
Run tools via Python code. Tools are injected as async functions—call with \`await\`.
|
|
418
568
|
|
|
419
|
-
|
|
569
|
+
Rules:
|
|
570
|
+
- Tools are pre-defined—DO NOT define them yourself
|
|
571
|
+
- Use \`await\` for calls, \`asyncio.gather()\` for parallel; NEVER call \`asyncio.run()\`
|
|
572
|
+
- Only \`print()\` output returns to the model; tool results are raw dicts/lists/strings
|
|
573
|
+
- Stateless: variables/imports don't persist; use \`session_id\` param for file access
|
|
574
|
+
- Files mount at \`/mnt/data/\` (READ-ONLY); write changes to NEW filenames
|
|
575
|
+
- Tool names normalized: hyphens→underscores, keywords get \`_tool\` suffix
|
|
420
576
|
|
|
421
|
-
|
|
422
|
-
- Tools are pre-defined as async functions - call them with await
|
|
423
|
-
- Use asyncio.gather() to run multiple tools in parallel
|
|
424
|
-
- Only print() output is returned - tool results stay in Python
|
|
577
|
+
When to use (vs. direct tool calls): loops, conditionals, parallel execution, aggregation.
|
|
425
578
|
|
|
426
579
|
Examples:
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
- Parallel: sf, ny = await asyncio.gather(get_weather(city="SF"), get_weather(city="NY"))
|
|
430
|
-
|
|
431
|
-
When to use this instead of calling tools directly:
|
|
432
|
-
- You need to call tools in a loop (process many items)
|
|
433
|
-
- You want parallel execution (asyncio.gather)
|
|
434
|
-
- You need conditionals based on tool results
|
|
435
|
-
- You want to aggregate/filter data before returning
|
|
580
|
+
result = await get_weather(city="NYC"); print(result)
|
|
581
|
+
sf, ny = await asyncio.gather(get_weather(city="SF"), get_weather(city="NY"))
|
|
436
582
|
`.trim();
|
|
437
583
|
|
|
438
584
|
return tool<typeof ProgrammaticToolCallingSchema>(
|
|
@@ -474,6 +620,12 @@ When to use this instead of calling tools directly:
|
|
|
474
620
|
);
|
|
475
621
|
}
|
|
476
622
|
|
|
623
|
+
// Fetch files from previous session if session_id is provided
|
|
624
|
+
let files: t.CodeEnvFile[] | undefined;
|
|
625
|
+
if (session_id != null && session_id.length > 0) {
|
|
626
|
+
files = await fetchSessionFiles(baseUrl, apiKey, session_id, proxy);
|
|
627
|
+
}
|
|
628
|
+
|
|
477
629
|
let response = await makeRequest(
|
|
478
630
|
EXEC_ENDPOINT,
|
|
479
631
|
apiKey,
|
|
@@ -482,6 +634,7 @@ When to use this instead of calling tools directly:
|
|
|
482
634
|
tools: effectiveTools,
|
|
483
635
|
session_id,
|
|
484
636
|
timeout,
|
|
637
|
+
...(files && files.length > 0 ? { files } : {}),
|
|
485
638
|
},
|
|
486
639
|
proxy
|
|
487
640
|
);
|
package/src/types/tools.ts
CHANGED
|
@@ -15,7 +15,9 @@ export type CustomToolCall = {
|
|
|
15
15
|
output?: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
export type GenericTool = StructuredToolInterface | RunnableToolLike
|
|
18
|
+
export type GenericTool = (StructuredToolInterface | RunnableToolLike) & {
|
|
19
|
+
mcp?: boolean;
|
|
20
|
+
};
|
|
19
21
|
|
|
20
22
|
export type ToolMap = Map<string, GenericTool>;
|
|
21
23
|
export type ToolRefs = {
|