@librechat/agents 3.1.83 → 3.1.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs +26 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +1 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +2 -1
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +5 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -2
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/main.cjs +4 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +5 -2
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +26 -24
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +28 -2
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +130 -56
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +7 -5
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +52 -13
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ptcTimeout.cjs +56 -0
- package/dist/cjs/tools/ptcTimeout.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +27 -4
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +1 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +2 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +5 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +3 -2
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/main.mjs +3 -3
- package/dist/esm/tools/BashExecutor.mjs +6 -3
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +26 -25
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +27 -3
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +131 -58
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +7 -5
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +54 -15
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ptcTimeout.mjs +50 -0
- package/dist/esm/tools/ptcTimeout.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +3 -1
- package/dist/types/common/enum.d.ts +2 -1
- package/dist/types/tools/BashProgrammaticToolCalling.d.ts +4 -36
- package/dist/types/tools/CodeExecutor.d.ts +5 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +18 -39
- package/dist/types/tools/ptcTimeout.d.ts +25 -0
- package/dist/types/types/tools.d.ts +8 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +32 -3
- package/src/agents/__tests__/AgentContext.test.ts +36 -3
- package/src/common/enum.ts +1 -0
- package/src/events.ts +4 -1
- package/src/graphs/MultiAgentGraph.ts +3 -2
- package/src/graphs/__tests__/composition.smoke.test.ts +84 -2
- package/src/tools/BashExecutor.ts +14 -3
- package/src/tools/BashProgrammaticToolCalling.ts +37 -25
- package/src/tools/CodeExecutor.ts +36 -2
- package/src/tools/ProgrammaticToolCalling.ts +206 -53
- package/src/tools/ToolNode.ts +3 -4
- package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +424 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +31 -1
- package/src/tools/local/LocalProgrammaticToolCalling.ts +94 -13
- package/src/tools/ptcTimeout.ts +89 -0
- package/src/types/tools.ts +12 -0
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ANTHROPIC_TOOL_TOKEN_MULTIPLIER,
|
|
14
14
|
DEFAULT_TOOL_TOKEN_MULTIPLIER,
|
|
15
15
|
ContentTypes,
|
|
16
|
+
Constants,
|
|
16
17
|
Providers,
|
|
17
18
|
} from '@/common';
|
|
18
19
|
import { createSchemaOnlyTools } from '@/tools/schema';
|
|
@@ -389,7 +390,7 @@ export class AgentContext {
|
|
|
389
390
|
/**
|
|
390
391
|
* Builds instructions text for tools that are ONLY callable via programmatic code execution.
|
|
391
392
|
* These tools cannot be called directly by the LLM but are available through the
|
|
392
|
-
*
|
|
393
|
+
* configured programmatic tool.
|
|
393
394
|
*
|
|
394
395
|
* Includes:
|
|
395
396
|
* - Code_execution-only tools that are NOT deferred
|
|
@@ -416,6 +417,7 @@ export class AgentContext {
|
|
|
416
417
|
|
|
417
418
|
if (programmaticOnlyTools.length === 0) return '';
|
|
418
419
|
|
|
420
|
+
const programmaticTool = this.getProgrammaticToolInstructionTarget();
|
|
419
421
|
const toolDescriptions = programmaticOnlyTools
|
|
420
422
|
.map((tool) => {
|
|
421
423
|
let desc = `- **${tool.name}**`;
|
|
@@ -431,12 +433,39 @@ export class AgentContext {
|
|
|
431
433
|
|
|
432
434
|
return (
|
|
433
435
|
'\n\n## Programmatic-Only Tools\n\n' +
|
|
434
|
-
|
|
435
|
-
|
|
436
|
+
`The following tools are available exclusively through the \`${programmaticTool.name}\` tool. ` +
|
|
437
|
+
`You cannot call these tools directly; instead, use \`${programmaticTool.name}\` with ${programmaticTool.language} code that invokes them.\n\n` +
|
|
436
438
|
toolDescriptions
|
|
437
439
|
);
|
|
438
440
|
}
|
|
439
441
|
|
|
442
|
+
private getProgrammaticToolInstructionTarget(): {
|
|
443
|
+
name: string;
|
|
444
|
+
language: 'bash' | 'Python';
|
|
445
|
+
} {
|
|
446
|
+
if (this.hasAvailableTool(Constants.BASH_PROGRAMMATIC_TOOL_CALLING)) {
|
|
447
|
+
return {
|
|
448
|
+
name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
|
|
449
|
+
language: 'bash',
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (this.hasAvailableTool(Constants.PROGRAMMATIC_TOOL_CALLING)) {
|
|
454
|
+
return { name: Constants.PROGRAMMATIC_TOOL_CALLING, language: 'Python' };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return { name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING, language: 'bash' };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private hasAvailableTool(name: string): boolean {
|
|
461
|
+
if (this.toolDefinitions?.some((tool) => tool.name === name)) return true;
|
|
462
|
+
if (this.tools?.some((tool) => 'name' in tool && tool.name === name)) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
if (this.toolMap?.has(name)) return true;
|
|
466
|
+
return this.toolRegistry?.has(name) === true;
|
|
467
|
+
}
|
|
468
|
+
|
|
440
469
|
/**
|
|
441
470
|
* Gets the system runnable, creating it lazily if needed.
|
|
442
471
|
* Includes stable instructions, dynamic additional instructions, and
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/agents/__tests__/AgentContext.test.ts
|
|
2
2
|
import { AIMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
|
|
3
3
|
import { AgentContext } from '../AgentContext';
|
|
4
|
-
import { Providers } from '@/common';
|
|
4
|
+
import { Constants, Providers } from '@/common';
|
|
5
5
|
import { addBedrockCacheControl } from '@/messages/cache';
|
|
6
6
|
import type * as t from '@/types';
|
|
7
7
|
|
|
@@ -593,7 +593,7 @@ describe('AgentContext', () => {
|
|
|
593
593
|
});
|
|
594
594
|
|
|
595
595
|
describe('buildProgrammaticOnlyToolsInstructions', () => {
|
|
596
|
-
it('includes code_execution-only tools in system message', () => {
|
|
596
|
+
it('includes code_execution-only tools in system message', async () => {
|
|
597
597
|
const toolRegistry: t.LCToolRegistry = new Map([
|
|
598
598
|
[
|
|
599
599
|
'programmatic_tool',
|
|
@@ -606,11 +606,44 @@ describe('AgentContext', () => {
|
|
|
606
606
|
]);
|
|
607
607
|
|
|
608
608
|
const ctx = createBasicContext({
|
|
609
|
-
agentConfig: {
|
|
609
|
+
agentConfig: {
|
|
610
|
+
instructions: 'Base',
|
|
611
|
+
toolDefinitions: [{ name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING }],
|
|
612
|
+
toolRegistry,
|
|
613
|
+
},
|
|
610
614
|
});
|
|
611
615
|
|
|
612
616
|
const runnable = ctx.systemRunnable;
|
|
613
617
|
expect(runnable).toBeDefined();
|
|
618
|
+
const result = await runnable!.invoke([]);
|
|
619
|
+
expect(result[0].content).toContain('run_tools_with_bash');
|
|
620
|
+
expect(result[0].content).not.toContain('run_tools_with_code');
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it('uses Python PTC guidance when only run_tools_with_code is available', async () => {
|
|
624
|
+
const toolRegistry: t.LCToolRegistry = new Map([
|
|
625
|
+
[
|
|
626
|
+
'programmatic_tool',
|
|
627
|
+
{
|
|
628
|
+
name: 'programmatic_tool',
|
|
629
|
+
description: 'Only callable via code execution',
|
|
630
|
+
allowed_callers: ['code_execution'],
|
|
631
|
+
},
|
|
632
|
+
],
|
|
633
|
+
]);
|
|
634
|
+
|
|
635
|
+
const ctx = createBasicContext({
|
|
636
|
+
agentConfig: {
|
|
637
|
+
instructions: 'Base',
|
|
638
|
+
toolDefinitions: [{ name: Constants.PROGRAMMATIC_TOOL_CALLING }],
|
|
639
|
+
toolRegistry,
|
|
640
|
+
},
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
const result = await ctx.systemRunnable!.invoke([]);
|
|
644
|
+
expect(result[0].content).toContain('run_tools_with_code');
|
|
645
|
+
expect(result[0].content).toContain('Python code');
|
|
646
|
+
expect(result[0].content).not.toContain('run_tools_with_bash');
|
|
614
647
|
});
|
|
615
648
|
|
|
616
649
|
it('excludes direct-callable tools from programmatic section', () => {
|
package/src/common/enum.ts
CHANGED
package/src/events.ts
CHANGED
|
@@ -90,7 +90,10 @@ export class ToolEndHandler implements t.EventHandler {
|
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true ||
|
|
95
|
+
metadata[Constants.BASH_PROGRAMMATIC_TOOL_CALLING] === true
|
|
96
|
+
) {
|
|
94
97
|
return;
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -1074,10 +1074,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1074
1074
|
* to pass filtered messages + prompt to the destination agent
|
|
1075
1075
|
*/
|
|
1076
1076
|
const filteredMessages = state.messages.slice(0, this.startIndex);
|
|
1077
|
+
const promptMessage = new HumanMessage(promptText);
|
|
1077
1078
|
return {
|
|
1078
|
-
messages: [
|
|
1079
|
+
messages: [promptMessage],
|
|
1079
1080
|
agentMessages: messagesStateReducer(filteredMessages, [
|
|
1080
|
-
|
|
1081
|
+
promptMessage,
|
|
1081
1082
|
]),
|
|
1082
1083
|
};
|
|
1083
1084
|
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { HumanMessage } from '@langchain/core/messages';
|
|
2
|
-
import type {
|
|
1
|
+
import { HumanMessage, getBufferString } from '@langchain/core/messages';
|
|
2
|
+
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
3
3
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
4
|
+
import type { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
5
|
+
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
6
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
4
7
|
import type * as t from '@/types';
|
|
5
8
|
import { MultiAgentGraph } from '../MultiAgentGraph';
|
|
6
9
|
import { Constants, Providers } from '@/common';
|
|
10
|
+
import { FakeChatModel } from '@/llm/fake';
|
|
7
11
|
import { StandardGraph } from '../Graph';
|
|
8
12
|
|
|
13
|
+
const CHAIN_PROMPT_PREFIX = 'Previous context:\n';
|
|
14
|
+
|
|
9
15
|
const makeAgent = (agentId: string): t.AgentInputs => ({
|
|
10
16
|
agentId,
|
|
11
17
|
provider: Providers.OPENAI,
|
|
@@ -29,6 +35,36 @@ const getAiContents = (messages: t.BaseGraphState['messages']): string[] =>
|
|
|
29
35
|
.map((message) => message.content)
|
|
30
36
|
.filter((content): content is string => typeof content === 'string');
|
|
31
37
|
|
|
38
|
+
const getChainPromptContent = (messages: BaseMessage[]): string => {
|
|
39
|
+
const promptMessage = messages.find(
|
|
40
|
+
(message) =>
|
|
41
|
+
message.getType() === 'human' &&
|
|
42
|
+
typeof message.content === 'string' &&
|
|
43
|
+
message.content.startsWith(CHAIN_PROMPT_PREFIX)
|
|
44
|
+
);
|
|
45
|
+
if (promptMessage == null || typeof promptMessage.content !== 'string') {
|
|
46
|
+
throw new Error('Expected chain prompt message');
|
|
47
|
+
}
|
|
48
|
+
return promptMessage.content;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
class CapturingChatModel extends FakeChatModel {
|
|
52
|
+
readonly invocations: BaseMessage[][] = [];
|
|
53
|
+
|
|
54
|
+
constructor(responses: string[]) {
|
|
55
|
+
super({ responses });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override async *_streamResponseChunks(
|
|
59
|
+
messages: BaseMessage[],
|
|
60
|
+
options: this['ParsedCallOptions'],
|
|
61
|
+
runManager?: CallbackManagerForLLMRun
|
|
62
|
+
): AsyncGenerator<ChatGenerationChunk> {
|
|
63
|
+
this.invocations.push(messages);
|
|
64
|
+
yield* super._streamResponseChunks(messages, options, runManager);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
32
68
|
const expectCompiledWorkflow = (
|
|
33
69
|
workflow: t.CompiledWorkflow | t.CompiledMultiAgentWorkflow
|
|
34
70
|
): void => {
|
|
@@ -119,6 +155,52 @@ describe('LangGraph composition smoke tests', () => {
|
|
|
119
155
|
expect(getAiContents(result.messages)).toEqual(['from A', 'from B']);
|
|
120
156
|
});
|
|
121
157
|
|
|
158
|
+
it('does not duplicate excludeResults chain prompt history for downstream agents', async () => {
|
|
159
|
+
const model = new CapturingChatModel(['from A', 'from B', 'from C']);
|
|
160
|
+
const prompt = (messages: BaseMessage[], startIndex: number): string =>
|
|
161
|
+
`${CHAIN_PROMPT_PREFIX}${getBufferString(messages.slice(startIndex))}`;
|
|
162
|
+
const graph = new MultiAgentGraph({
|
|
163
|
+
runId: 'exclude-results-chain-smoke',
|
|
164
|
+
agents: [makeAgent('A'), makeAgent('B'), makeAgent('C')],
|
|
165
|
+
edges: [
|
|
166
|
+
{
|
|
167
|
+
from: 'A',
|
|
168
|
+
to: 'B',
|
|
169
|
+
edgeType: 'direct',
|
|
170
|
+
prompt,
|
|
171
|
+
excludeResults: true,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
from: 'B',
|
|
175
|
+
to: 'C',
|
|
176
|
+
edgeType: 'direct',
|
|
177
|
+
prompt,
|
|
178
|
+
excludeResults: true,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
});
|
|
182
|
+
graph.overrideModel = model;
|
|
183
|
+
|
|
184
|
+
const result = await graph
|
|
185
|
+
.createWorkflow()
|
|
186
|
+
.invoke(
|
|
187
|
+
{ messages: [new HumanMessage('start')] },
|
|
188
|
+
makeConfig('exclude-results-chain-smoke')
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expect(getAiContents(result.messages)).toEqual([
|
|
192
|
+
'from A',
|
|
193
|
+
'from B',
|
|
194
|
+
'from C',
|
|
195
|
+
]);
|
|
196
|
+
expect(model.invocations).toHaveLength(3);
|
|
197
|
+
|
|
198
|
+
const downstreamPrompt = getChainPromptContent(model.invocations[2]);
|
|
199
|
+
const previousPromptCount =
|
|
200
|
+
downstreamPrompt.match(/Human: Previous context:/g)?.length ?? 0;
|
|
201
|
+
expect(previousPromptCount).toBe(1);
|
|
202
|
+
});
|
|
203
|
+
|
|
122
204
|
it('compiles and invokes a handoff edge using graph-managed transfer tools', async () => {
|
|
123
205
|
const transferToolCall: ToolCall = {
|
|
124
206
|
id: 'call_transfer_to_B',
|
|
@@ -3,7 +3,12 @@ import fetch, { RequestInit } from 'node-fetch';
|
|
|
3
3
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
4
4
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
5
5
|
import type * as t from '@/types';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
emptyOutputMessage,
|
|
8
|
+
buildCodeApiHttpErrorMessage,
|
|
9
|
+
getCodeBaseURL,
|
|
10
|
+
resolveCodeApiAuthHeaders,
|
|
11
|
+
} from './CodeExecutor';
|
|
7
12
|
import { Constants } from '@/common';
|
|
8
13
|
|
|
9
14
|
config();
|
|
@@ -104,6 +109,7 @@ function createBashExecutionTool(
|
|
|
104
109
|
): DynamicStructuredTool {
|
|
105
110
|
return tool(
|
|
106
111
|
async (rawInput, config) => {
|
|
112
|
+
const { authHeaders, ...executionParams } = params ?? {};
|
|
107
113
|
const { command, ...rest } = rawInput as {
|
|
108
114
|
command: string;
|
|
109
115
|
args?: string[];
|
|
@@ -117,7 +123,7 @@ function createBashExecutionTool(
|
|
|
117
123
|
lang: 'bash',
|
|
118
124
|
code: command,
|
|
119
125
|
...rest,
|
|
120
|
-
...
|
|
126
|
+
...executionParams,
|
|
121
127
|
};
|
|
122
128
|
|
|
123
129
|
/* See `CodeExecutor.ts` for the rationale — `/files/<session_id>`
|
|
@@ -137,11 +143,14 @@ function createBashExecutionTool(
|
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
try {
|
|
146
|
+
const resolvedAuthHeaders =
|
|
147
|
+
await resolveCodeApiAuthHeaders(authHeaders);
|
|
140
148
|
const fetchOptions: RequestInit = {
|
|
141
149
|
method: 'POST',
|
|
142
150
|
headers: {
|
|
143
151
|
'Content-Type': 'application/json',
|
|
144
152
|
'User-Agent': 'LibreChat/1.0',
|
|
153
|
+
...resolvedAuthHeaders,
|
|
145
154
|
},
|
|
146
155
|
body: JSON.stringify(postData),
|
|
147
156
|
};
|
|
@@ -151,7 +160,9 @@ function createBashExecutionTool(
|
|
|
151
160
|
}
|
|
152
161
|
const response = await fetch(EXEC_ENDPOINT, fetchOptions);
|
|
153
162
|
if (!response.ok) {
|
|
154
|
-
throw new Error(
|
|
163
|
+
throw new Error(
|
|
164
|
+
await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)
|
|
165
|
+
);
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
const result: t.ExecuteResult = await response.json();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { config } from 'dotenv';
|
|
2
2
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
3
3
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
4
|
+
import type { ProgrammaticToolCallingJsonSchema } from './ptcTimeout';
|
|
4
5
|
import type * as t from '@/types';
|
|
5
6
|
import {
|
|
6
7
|
makeRequest,
|
|
@@ -8,6 +9,11 @@ import {
|
|
|
8
9
|
formatCompletedResponse,
|
|
9
10
|
} from './ProgrammaticToolCalling';
|
|
10
11
|
import { getCodeBaseURL } from './CodeExecutor';
|
|
12
|
+
import {
|
|
13
|
+
clampCodeApiRunTimeoutMs,
|
|
14
|
+
createCodeApiRunTimeoutSchema,
|
|
15
|
+
resolveCodeApiRunTimeoutMs,
|
|
16
|
+
} from './ptcTimeout';
|
|
11
17
|
import { Constants } from '@/common';
|
|
12
18
|
|
|
13
19
|
config();
|
|
@@ -17,7 +23,7 @@ config();
|
|
|
17
23
|
// ============================================================================
|
|
18
24
|
|
|
19
25
|
const DEFAULT_MAX_ROUND_TRIPS = 20;
|
|
20
|
-
const
|
|
26
|
+
const DEFAULT_RUN_TIMEOUT_MS = resolveCodeApiRunTimeoutMs();
|
|
21
27
|
|
|
22
28
|
/** Bash reserved words that get `_tool` suffix when used as function names */
|
|
23
29
|
const BASH_RESERVED = new Set([
|
|
@@ -60,7 +66,8 @@ const CORE_RULES = `Rules:
|
|
|
60
66
|
- Tools are pre-defined as bash functions—DO NOT redefine them
|
|
61
67
|
- Each tool function accepts a JSON string argument
|
|
62
68
|
- Only echo/printf output returns to the model
|
|
63
|
-
- Generated files are automatically available in /mnt/data/ for subsequent executions
|
|
69
|
+
- Generated files are automatically available in /mnt/data/ for subsequent executions
|
|
70
|
+
- timeout caps one sandbox run/replay iteration, not the total multi-round-trip workflow`;
|
|
64
71
|
|
|
65
72
|
const ADDITIONAL_RULES =
|
|
66
73
|
'- Tool names normalized: hyphens→underscores, reserved words get `_tool` suffix';
|
|
@@ -92,25 +99,25 @@ ${CORE_RULES}`;
|
|
|
92
99
|
// Schema
|
|
93
100
|
// ============================================================================
|
|
94
101
|
|
|
95
|
-
export
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
default: DEFAULT_TIMEOUT,
|
|
108
|
-
description:
|
|
109
|
-
'Maximum execution time in milliseconds. Default: 60 seconds. Max: 5 minutes.',
|
|
102
|
+
export function createBashProgrammaticToolCallingSchema(
|
|
103
|
+
maxRunTimeoutMs = DEFAULT_RUN_TIMEOUT_MS
|
|
104
|
+
): ProgrammaticToolCallingJsonSchema {
|
|
105
|
+
return {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
code: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
minLength: 1,
|
|
111
|
+
description: CODE_PARAM_DESCRIPTION,
|
|
112
|
+
},
|
|
113
|
+
timeout: createCodeApiRunTimeoutSchema(maxRunTimeoutMs),
|
|
110
114
|
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
115
|
+
required: ['code'],
|
|
116
|
+
} as const;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const BashProgrammaticToolCallingSchema =
|
|
120
|
+
createBashProgrammaticToolCallingSchema();
|
|
114
121
|
|
|
115
122
|
export const BashProgrammaticToolCallingName =
|
|
116
123
|
Constants.BASH_PROGRAMMATIC_TOOL_CALLING;
|
|
@@ -242,6 +249,7 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
242
249
|
): DynamicStructuredTool {
|
|
243
250
|
const baseUrl = initParams.baseUrl ?? getCodeBaseURL();
|
|
244
251
|
const maxRoundTrips = initParams.maxRoundTrips ?? DEFAULT_MAX_ROUND_TRIPS;
|
|
252
|
+
const maxRunTimeoutMs = resolveCodeApiRunTimeoutMs(initParams.runTimeoutMs);
|
|
245
253
|
const proxy = initParams.proxy ?? process.env.PROXY;
|
|
246
254
|
const debug = initParams.debug ?? process.env.BASH_PTC_DEBUG === 'true';
|
|
247
255
|
const EXEC_ENDPOINT = `${baseUrl}/exec/programmatic`;
|
|
@@ -249,7 +257,8 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
249
257
|
return tool(
|
|
250
258
|
async (rawParams, config) => {
|
|
251
259
|
const params = rawParams as { code: string; timeout?: number };
|
|
252
|
-
const { code
|
|
260
|
+
const { code } = params;
|
|
261
|
+
const timeout = clampCodeApiRunTimeoutMs(params.timeout, maxRunTimeoutMs);
|
|
253
262
|
|
|
254
263
|
const toolCall = (config.toolCall ?? {}) as ToolCall &
|
|
255
264
|
Partial<t.ProgrammaticCache> & {
|
|
@@ -312,7 +321,8 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
312
321
|
timeout,
|
|
313
322
|
...(files && files.length > 0 ? { files } : {}),
|
|
314
323
|
},
|
|
315
|
-
proxy
|
|
324
|
+
proxy,
|
|
325
|
+
initParams.authHeaders
|
|
316
326
|
);
|
|
317
327
|
|
|
318
328
|
// ====================================================================
|
|
@@ -339,7 +349,8 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
339
349
|
|
|
340
350
|
const toolResults = await executeTools(
|
|
341
351
|
response.tool_calls ?? [],
|
|
342
|
-
toolMap
|
|
352
|
+
toolMap,
|
|
353
|
+
Constants.BASH_PROGRAMMATIC_TOOL_CALLING
|
|
343
354
|
);
|
|
344
355
|
|
|
345
356
|
response = await makeRequest(
|
|
@@ -348,7 +359,8 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
348
359
|
continuation_token: response.continuation_token,
|
|
349
360
|
tool_results: toolResults,
|
|
350
361
|
},
|
|
351
|
-
proxy
|
|
362
|
+
proxy,
|
|
363
|
+
initParams.authHeaders
|
|
352
364
|
);
|
|
353
365
|
}
|
|
354
366
|
|
|
@@ -379,7 +391,7 @@ export function createBashProgrammaticToolCallingTool(
|
|
|
379
391
|
{
|
|
380
392
|
name: Constants.BASH_PROGRAMMATIC_TOOL_CALLING,
|
|
381
393
|
description: BashProgrammaticToolCallingDescription,
|
|
382
|
-
schema:
|
|
394
|
+
schema: createBashProgrammaticToolCallingSchema(maxRunTimeoutMs),
|
|
383
395
|
responseFormat: Constants.CONTENT_AND_ARTIFACT,
|
|
384
396
|
}
|
|
385
397
|
);
|
|
@@ -70,6 +70,34 @@ const EXEC_ENDPOINT = `${baseEndpoint}/exec`;
|
|
|
70
70
|
|
|
71
71
|
type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
|
|
72
72
|
|
|
73
|
+
export async function resolveCodeApiAuthHeaders(
|
|
74
|
+
authHeaders?: t.CodeApiAuthHeaders
|
|
75
|
+
): Promise<t.CodeApiAuthHeaderMap> {
|
|
76
|
+
if (authHeaders == null) {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
if (typeof authHeaders === 'function') {
|
|
80
|
+
return authHeaders();
|
|
81
|
+
}
|
|
82
|
+
return authHeaders;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function buildCodeApiHttpErrorMessage(
|
|
86
|
+
method: string,
|
|
87
|
+
endpoint: string,
|
|
88
|
+
response: { status: number; text: () => Promise<string> }
|
|
89
|
+
): Promise<string> {
|
|
90
|
+
let responseBody = '';
|
|
91
|
+
try {
|
|
92
|
+
responseBody = await response.text();
|
|
93
|
+
} catch {
|
|
94
|
+
responseBody = '';
|
|
95
|
+
}
|
|
96
|
+
const body = responseBody.trim();
|
|
97
|
+
const bodySuffix = body === '' ? '' : `, body: ${body.slice(0, 1000)}`;
|
|
98
|
+
return `CodeAPI request failed: ${method} ${endpoint} returned ${response.status}${bodySuffix}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
73
101
|
export const CodeExecutionToolDescription = `
|
|
74
102
|
Runs code and returns stdout/stderr output from a stateless execution environment, similar to running scripts in a command-line interface. Each execution is isolated and independent.
|
|
75
103
|
|
|
@@ -92,6 +120,7 @@ function createCodeExecutionTool(
|
|
|
92
120
|
): DynamicStructuredTool {
|
|
93
121
|
return tool(
|
|
94
122
|
async (rawInput, config) => {
|
|
123
|
+
const { authHeaders, ...executionParams } = params ?? {};
|
|
95
124
|
const { lang, code, ...rest } = rawInput as {
|
|
96
125
|
lang: SupportedLanguage;
|
|
97
126
|
code: string;
|
|
@@ -111,7 +140,7 @@ function createCodeExecutionTool(
|
|
|
111
140
|
lang,
|
|
112
141
|
code,
|
|
113
142
|
...rest,
|
|
114
|
-
...
|
|
143
|
+
...executionParams,
|
|
115
144
|
};
|
|
116
145
|
|
|
117
146
|
/* File injection: `_injected_files` from ToolNode (set when host
|
|
@@ -135,11 +164,14 @@ function createCodeExecutionTool(
|
|
|
135
164
|
}
|
|
136
165
|
|
|
137
166
|
try {
|
|
167
|
+
const resolvedAuthHeaders =
|
|
168
|
+
await resolveCodeApiAuthHeaders(authHeaders);
|
|
138
169
|
const fetchOptions: RequestInit = {
|
|
139
170
|
method: 'POST',
|
|
140
171
|
headers: {
|
|
141
172
|
'Content-Type': 'application/json',
|
|
142
173
|
'User-Agent': 'LibreChat/1.0',
|
|
174
|
+
...resolvedAuthHeaders,
|
|
143
175
|
},
|
|
144
176
|
body: JSON.stringify(postData),
|
|
145
177
|
};
|
|
@@ -149,7 +181,9 @@ function createCodeExecutionTool(
|
|
|
149
181
|
}
|
|
150
182
|
const response = await fetch(EXEC_ENDPOINT, fetchOptions);
|
|
151
183
|
if (!response.ok) {
|
|
152
|
-
throw new Error(
|
|
184
|
+
throw new Error(
|
|
185
|
+
await buildCodeApiHttpErrorMessage('POST', EXEC_ENDPOINT, response)
|
|
186
|
+
);
|
|
153
187
|
}
|
|
154
188
|
|
|
155
189
|
const result: t.ExecuteResult = await response.json();
|