@librechat/agents 3.3.11 → 3.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/graphs/Graph.cjs +246 -25
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +1 -0
- package/dist/cjs/instrumentation.cjs.map +1 -1
- package/dist/cjs/langfuseSpanRegistry.cjs +6 -3
- package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +13 -2
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +146 -36
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +2 -2
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/streamLimits.cjs +723 -0
- package/dist/cjs/llm/streamLimits.cjs.map +1 -0
- package/dist/cjs/main.cjs +8 -0
- package/dist/cjs/run.cjs +6 -2
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +4 -1
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/stream.cjs +103 -12
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +157 -37
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -2
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +4 -3
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +5 -3
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +76 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +3 -2
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/search/crw-scraper.cjs +7 -1
- package/dist/cjs/tools/search/crw-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/crw-search.cjs +3 -1
- package/dist/cjs/tools/search/crw-search.cjs.map +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs +7 -1
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs +7 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-search.cjs +3 -1
- package/dist/cjs/tools/search/keenable-search.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +26 -8
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +30 -10
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/serper-scraper.cjs +7 -1
- package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs +7 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-search.cjs +3 -1
- package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +16 -2
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +54 -3
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +1 -0
- package/dist/cjs/utils/misc.cjs +12 -0
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/cjs/utils/proxy.cjs +63 -0
- package/dist/cjs/utils/proxy.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +245 -24
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +1 -0
- package/dist/esm/instrumentation.mjs.map +1 -1
- package/dist/esm/langfuseSpanRegistry.mjs +6 -3
- package/dist/esm/langfuseSpanRegistry.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +13 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +146 -36
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +2 -2
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/streamLimits.mjs +704 -0
- package/dist/esm/llm/streamLimits.mjs.map +1 -0
- package/dist/esm/main.mjs +4 -2
- package/dist/esm/run.mjs +6 -2
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +4 -1
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/stream.mjs +103 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +157 -37
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -2
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +4 -3
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +5 -3
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +76 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +3 -2
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/search/crw-scraper.mjs +7 -1
- package/dist/esm/tools/search/crw-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/crw-search.mjs +3 -1
- package/dist/esm/tools/search/crw-search.mjs.map +1 -1
- package/dist/esm/tools/search/firecrawl.mjs +7 -1
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-scraper.mjs +7 -1
- package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-search.mjs +3 -1
- package/dist/esm/tools/search/keenable-search.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs +26 -8
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +30 -10
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/serper-scraper.mjs +7 -1
- package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-scraper.mjs +7 -1
- package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-search.mjs +3 -1
- package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +16 -2
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +54 -3
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +1 -0
- package/dist/esm/utils/misc.mjs +12 -1
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/esm/utils/proxy.mjs +62 -0
- package/dist/esm/utils/proxy.mjs.map +1 -0
- package/dist/types/graphs/Graph.d.ts +53 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/llm/invoke.d.ts +25 -4
- package/dist/types/llm/openai/index.d.ts +3 -0
- package/dist/types/llm/streamLimits.d.ts +314 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/summarization/node.d.ts +27 -2
- package/dist/types/tools/BashExecutor.d.ts +2 -2
- package/dist/types/tools/CodeExecutor.d.ts +3 -3
- package/dist/types/tools/ToolNode.d.ts +11 -1
- package/dist/types/tools/search/crw-scraper.d.ts +2 -0
- package/dist/types/tools/search/firecrawl.d.ts +2 -0
- package/dist/types/tools/search/keenable-scraper.d.ts +2 -0
- package/dist/types/tools/search/rerankers.d.ts +9 -5
- package/dist/types/tools/search/serper-scraper.d.ts +2 -0
- package/dist/types/tools/search/tavily-scraper.d.ts +2 -0
- package/dist/types/tools/search/types.d.ts +28 -33
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +44 -0
- package/dist/types/types/graph.d.ts +12 -1
- package/dist/types/types/run.d.ts +46 -1
- package/dist/types/types/tools.d.ts +21 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/misc.d.ts +7 -0
- package/dist/types/utils/proxy.d.ts +31 -0
- package/package.json +2 -1
- package/src/__tests__/stream.eagerArgsDivergence.test.ts +158 -0
- package/src/__tests__/stream.eagerEventExecution.test.ts +1 -0
- package/src/__tests__/stream.streamLimits.test.ts +1982 -0
- package/src/graphs/Graph.ts +302 -27
- package/src/graphs/__tests__/Graph.breakerLifecycle.test.ts +234 -0
- package/src/index.ts +11 -0
- package/src/instrumentation.ts +1 -0
- package/src/langfuseSpanRegistry.ts +9 -0
- package/src/llm/bedrock/index.ts +22 -2
- package/src/llm/invoke.streamLimits.test.ts +142 -0
- package/src/llm/invoke.test.ts +89 -1
- package/src/llm/invoke.ts +197 -20
- package/src/llm/openai/cacheWriteTokens.test.ts +112 -0
- package/src/llm/openai/index.ts +14 -6
- package/src/llm/streamLimits.test.ts +450 -0
- package/src/llm/streamLimits.ts +1158 -0
- package/src/run.ts +4 -0
- package/src/session/AgentSession.ts +5 -0
- package/src/specs/langfuse-instrumentation.test.ts +26 -0
- package/src/specs/langfuse-span-registry.test.ts +17 -0
- package/src/specs/summarization.test.ts +5 -2
- package/src/stream.ts +141 -6
- package/src/summarization/__tests__/node.test.ts +269 -0
- package/src/summarization/chunkHandler.test.ts +196 -0
- package/src/summarization/node.ts +203 -6
- package/src/tools/BashExecutor.ts +4 -3
- package/src/tools/CodeExecutor.ts +5 -4
- package/src/tools/ProgrammaticToolCalling.ts +7 -5
- package/src/tools/ToolNode.ts +109 -6
- package/src/tools/ToolSearch.ts +4 -3
- package/src/tools/__tests__/BashExecutor.test.ts +2 -2
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +2 -4
- package/src/tools/__tests__/SubagentExecutor.test.ts +126 -0
- package/src/tools/__tests__/ToolNode.breakerSignal.test.ts +389 -0
- package/src/tools/__tests__/ToolNode.streamLimits.test.ts +69 -0
- package/src/tools/search/crw-scraper.ts +6 -0
- package/src/tools/search/crw-search.ts +6 -1
- package/src/tools/search/firecrawl.ts +6 -0
- package/src/tools/search/http-agent.test.ts +133 -0
- package/src/tools/search/keenable-scraper.ts +6 -0
- package/src/tools/search/keenable-search.ts +6 -1
- package/src/tools/search/rerankers.ts +36 -10
- package/src/tools/search/search.ts +29 -11
- package/src/tools/search/serper-scraper.ts +6 -0
- package/src/tools/search/tavily-scraper.ts +6 -0
- package/src/tools/search/tavily-search.ts +2 -0
- package/src/tools/search/tool.ts +16 -0
- package/src/tools/search/types.ts +31 -33
- package/src/tools/subagent/SubagentExecutor.ts +96 -3
- package/src/types/graph.ts +12 -0
- package/src/types/run.ts +49 -1
- package/src/types/tools.ts +21 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/misc.ts +19 -0
- package/src/utils/proxy.test.ts +176 -0
- package/src/utils/proxy.ts +93 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summarization streams replace `ChatModelStreamHandler` with an `onChunk`
|
|
3
|
+
* callback, so the stream circuit breakers are enforced inside
|
|
4
|
+
* `createSummarizationChunkHandler`: the per-generation event cap, and the
|
|
5
|
+
* argument byte cap for tool calls a tool-bound summarization model may
|
|
6
|
+
* emit. Keys derive from the attempt metadata `attemptInvoke` hands each
|
|
7
|
+
* chunk, so a fallback summary attempt never inherits the failed primary's
|
|
8
|
+
* counts.
|
|
9
|
+
*/
|
|
10
|
+
import { AIMessageChunk } from '@langchain/core/messages';
|
|
11
|
+
import { describe, it, expect } from '@jest/globals';
|
|
12
|
+
import type { StreamLimitState } from '@/llm/streamLimits';
|
|
13
|
+
import {
|
|
14
|
+
STREAM_LIMIT_ATTEMPT_KEY,
|
|
15
|
+
StreamLimitExceededError,
|
|
16
|
+
enforceStreamLimitsForWireChunk,
|
|
17
|
+
resolveStreamLimits,
|
|
18
|
+
} from '@/llm/streamLimits';
|
|
19
|
+
import { createSummarizationChunkHandler } from '@/summarization/node';
|
|
20
|
+
import { Providers } from '@/common';
|
|
21
|
+
|
|
22
|
+
const makeHandler = (graph: StreamLimitState) =>
|
|
23
|
+
createSummarizationChunkHandler({
|
|
24
|
+
stepId: 'step_1',
|
|
25
|
+
config: { metadata: { langgraph_node: 'summarize', langgraph_step: 4 } },
|
|
26
|
+
provider: Providers.OPENAI,
|
|
27
|
+
reasoningKey: 'reasoning_content',
|
|
28
|
+
graph,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const textChunk = (): AIMessageChunk => new AIMessageChunk({ content: 'part' });
|
|
32
|
+
|
|
33
|
+
describe('createSummarizationChunkHandler stream limits', () => {
|
|
34
|
+
it('enforces the event cap on summary streams', async () => {
|
|
35
|
+
const graph: StreamLimitState = {
|
|
36
|
+
streamLimits: resolveStreamLimits({ maxDeltaEventsPerTurn: 2 }),
|
|
37
|
+
};
|
|
38
|
+
const onChunk = makeHandler(graph);
|
|
39
|
+
expect(onChunk).toBeDefined();
|
|
40
|
+
await onChunk!(textChunk());
|
|
41
|
+
await onChunk!(textChunk());
|
|
42
|
+
await expect(async () => onChunk!(textChunk())).rejects.toThrow(
|
|
43
|
+
StreamLimitExceededError
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('keys each summary attempt by the metadata attemptInvoke hands the chunk', async () => {
|
|
48
|
+
const graph: StreamLimitState = {
|
|
49
|
+
streamLimits: resolveStreamLimits({ maxDeltaEventsPerTurn: 2 }),
|
|
50
|
+
};
|
|
51
|
+
const onChunk = makeHandler(graph);
|
|
52
|
+
const primary = {
|
|
53
|
+
langgraph_node: 'summarize',
|
|
54
|
+
langgraph_step: 4,
|
|
55
|
+
[STREAM_LIMIT_ATTEMPT_KEY]: 1,
|
|
56
|
+
};
|
|
57
|
+
const fallback = { ...primary, [STREAM_LIMIT_ATTEMPT_KEY]: 2 };
|
|
58
|
+
await onChunk!(textChunk(), primary);
|
|
59
|
+
await onChunk!(textChunk(), primary);
|
|
60
|
+
await onChunk!(textChunk(), fallback);
|
|
61
|
+
await onChunk!(textChunk(), fallback);
|
|
62
|
+
await expect(async () => onChunk!(textChunk(), fallback)).rejects.toThrow(
|
|
63
|
+
StreamLimitExceededError
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('enforces the argument byte cap on tool calls from tool-bound summary models', async () => {
|
|
68
|
+
const graph: StreamLimitState = {
|
|
69
|
+
streamLimits: resolveStreamLimits({ maxToolCallArgBytes: 50 }),
|
|
70
|
+
};
|
|
71
|
+
const onChunk = makeHandler(graph);
|
|
72
|
+
const toolChunk = (args: string): AIMessageChunk =>
|
|
73
|
+
new AIMessageChunk({
|
|
74
|
+
content: '',
|
|
75
|
+
tool_call_chunks: [
|
|
76
|
+
{ type: 'tool_call_chunk', name: 'db_query', args, index: 0 },
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
await onChunk!(toolChunk('x'.repeat(40)));
|
|
80
|
+
await expect(async () => onChunk!(toolChunk('x'.repeat(20)))).rejects.toThrow(
|
|
81
|
+
'(tool call: db_query)'
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('trips the run breaker when its own enforcement breaches', async () => {
|
|
86
|
+
const breaker = new AbortController();
|
|
87
|
+
const graph: StreamLimitState & {
|
|
88
|
+
getBreakerController?: () => AbortController;
|
|
89
|
+
} = {
|
|
90
|
+
streamLimits: resolveStreamLimits({ maxToolCallArgBytes: 50 }),
|
|
91
|
+
getBreakerController: () => breaker,
|
|
92
|
+
};
|
|
93
|
+
const onChunk = createSummarizationChunkHandler({
|
|
94
|
+
stepId: 'step_1',
|
|
95
|
+
config: { metadata: { langgraph_node: 'summarize', langgraph_step: 4 } },
|
|
96
|
+
provider: Providers.OPENAI,
|
|
97
|
+
reasoningKey: 'reasoning_content',
|
|
98
|
+
graph,
|
|
99
|
+
});
|
|
100
|
+
const toolChunk = (args: string): AIMessageChunk =>
|
|
101
|
+
new AIMessageChunk({
|
|
102
|
+
content: '',
|
|
103
|
+
tool_call_chunks: [
|
|
104
|
+
{ type: 'tool_call_chunk', name: 'db_query', args, index: 0 },
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/** This producer claim wins the race, so the wire consumer's
|
|
109
|
+
* breaker-aborting catch never fires for the same chunk — the trip has
|
|
110
|
+
* to happen here or sibling branches keep consuming quota. */
|
|
111
|
+
await expect(async () => onChunk!(toolChunk('x'.repeat(60)))).rejects.toThrow(
|
|
112
|
+
StreamLimitExceededError
|
|
113
|
+
);
|
|
114
|
+
expect(breaker.signal.aborted).toBe(true);
|
|
115
|
+
expect(breaker.signal.reason).toMatchObject({ kind: 'tool_call_args' });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('charges a chunk once when the run wire consumer also claims it', async () => {
|
|
119
|
+
const graph: StreamLimitState = {
|
|
120
|
+
streamLimits: resolveStreamLimits({ maxToolCallArgBytes: 100 }),
|
|
121
|
+
};
|
|
122
|
+
const onChunk = makeHandler(graph);
|
|
123
|
+
const metadata = { langgraph_node: 'summarize', langgraph_step: 4 };
|
|
124
|
+
const toolChunk = (args: string): AIMessageChunk =>
|
|
125
|
+
new AIMessageChunk({
|
|
126
|
+
content: '',
|
|
127
|
+
tool_call_chunks: [
|
|
128
|
+
{ type: 'tool_call_chunk', name: 'db_query', args, index: 0 },
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/** Inside a live run the provider callbacks route the same chunk object
|
|
133
|
+
* through the graph's registered stream consumer as well. Wire consumer
|
|
134
|
+
* first, then the summarization handler: 60 bytes must be tallied once
|
|
135
|
+
* (a second same-side claim would put the tally at 120 and trip). */
|
|
136
|
+
const first = toolChunk('x'.repeat(60));
|
|
137
|
+
enforceStreamLimitsForWireChunk({
|
|
138
|
+
graph,
|
|
139
|
+
metadata,
|
|
140
|
+
chunk: first,
|
|
141
|
+
side: 'consumer',
|
|
142
|
+
});
|
|
143
|
+
await onChunk!(first, metadata);
|
|
144
|
+
|
|
145
|
+
/** Opposite order for the next chunk: handler (producer) before wire
|
|
146
|
+
* consumer. 60 + 39 = 99 stays under the cap only when each chunk was
|
|
147
|
+
* charged exactly once. */
|
|
148
|
+
const second = toolChunk('x'.repeat(39));
|
|
149
|
+
await onChunk!(second, metadata);
|
|
150
|
+
enforceStreamLimitsForWireChunk({
|
|
151
|
+
graph,
|
|
152
|
+
metadata,
|
|
153
|
+
chunk: second,
|
|
154
|
+
side: 'consumer',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/** Enforcement stays armed: two more bytes cross the 100-byte cap. */
|
|
158
|
+
await expect(async () => onChunk!(toolChunk('xx'), metadata)).rejects.toThrow(
|
|
159
|
+
StreamLimitExceededError
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('counts dual-consumed events once against the event cap', async () => {
|
|
164
|
+
const graph: StreamLimitState = {
|
|
165
|
+
streamLimits: resolveStreamLimits({ maxDeltaEventsPerTurn: 2 }),
|
|
166
|
+
};
|
|
167
|
+
const onChunk = makeHandler(graph);
|
|
168
|
+
const metadata = { langgraph_node: 'summarize', langgraph_step: 4 };
|
|
169
|
+
const passBoth = async (chunk: AIMessageChunk): Promise<void> => {
|
|
170
|
+
enforceStreamLimitsForWireChunk({
|
|
171
|
+
graph,
|
|
172
|
+
metadata,
|
|
173
|
+
chunk,
|
|
174
|
+
side: 'consumer',
|
|
175
|
+
});
|
|
176
|
+
await onChunk!(chunk, metadata);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
await passBoth(textChunk());
|
|
180
|
+
await passBoth(textChunk());
|
|
181
|
+
await expect(async () => passBoth(textChunk())).rejects.toThrow(
|
|
182
|
+
StreamLimitExceededError
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('stays inert without graph state', async () => {
|
|
187
|
+
const onChunk = createSummarizationChunkHandler({
|
|
188
|
+
stepId: 'step_1',
|
|
189
|
+
config: { metadata: {} },
|
|
190
|
+
provider: Providers.OPENAI,
|
|
191
|
+
});
|
|
192
|
+
for (let i = 0; i < 10; i++) {
|
|
193
|
+
await onChunk!(textChunk());
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '@langchain/core/messages';
|
|
7
7
|
import type { UsageMetadata, BaseMessage } from '@langchain/core/messages';
|
|
8
8
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
9
|
+
import type { StreamLimitState } from '@/llm/streamLimits';
|
|
9
10
|
import type { AgentContext } from '@/agents/AgentContext';
|
|
10
11
|
import type { HookRegistry } from '@/hooks';
|
|
11
12
|
import type { OnChunk } from '@/llm/invoke';
|
|
@@ -33,6 +34,11 @@ import {
|
|
|
33
34
|
Providers,
|
|
34
35
|
} from '@/common';
|
|
35
36
|
import { safeDispatchCustomEvent, emitAgentLog } from '@/utils/events';
|
|
37
|
+
import {
|
|
38
|
+
enforceStreamLimitsForWireChunk,
|
|
39
|
+
StreamLimitExceededError,
|
|
40
|
+
STREAM_LIMIT_EPOCH_KEY,
|
|
41
|
+
} from '@/llm/streamLimits';
|
|
36
42
|
import { attemptInvoke, tryFallbackProviders } from '@/llm/invoke';
|
|
37
43
|
import { calculateMaxToolResultChars } from '@/utils/truncation';
|
|
38
44
|
import { createRemoveAllMessage } from '@/messages/reducer';
|
|
@@ -611,6 +617,8 @@ async function executeSummarizationWithFallback(params: {
|
|
|
611
617
|
stepId: string;
|
|
612
618
|
usePromptCache: boolean;
|
|
613
619
|
log: LogFn;
|
|
620
|
+
/** Carries the run's stream limits so the event cap covers summary streams. */
|
|
621
|
+
graph?: StreamLimitState & { getBreakerSignal?: () => AbortSignal };
|
|
614
622
|
}): Promise<{
|
|
615
623
|
text: string;
|
|
616
624
|
usage?: Partial<UsageMetadata>;
|
|
@@ -629,6 +637,7 @@ async function executeSummarizationWithFallback(params: {
|
|
|
629
637
|
stepId,
|
|
630
638
|
usePromptCache,
|
|
631
639
|
log,
|
|
640
|
+
graph,
|
|
632
641
|
} = params;
|
|
633
642
|
|
|
634
643
|
const priorSummaryText = agentContext.getSummaryText()?.trim() ?? '';
|
|
@@ -659,6 +668,7 @@ async function executeSummarizationWithFallback(params: {
|
|
|
659
668
|
stepId,
|
|
660
669
|
provider: clientConfig.provider as Providers,
|
|
661
670
|
reasoningKey: agentContext.reasoningKey,
|
|
671
|
+
graph,
|
|
662
672
|
usePromptCache,
|
|
663
673
|
promptCacheTtl:
|
|
664
674
|
(clientConfig.provider as Providers) === Providers.ANTHROPIC ||
|
|
@@ -686,6 +696,34 @@ async function executeSummarizationWithFallback(params: {
|
|
|
686
696
|
messagesToRefineCount: messages.length,
|
|
687
697
|
});
|
|
688
698
|
|
|
699
|
+
/**
|
|
700
|
+
* A tripped stream circuit breaker is a safety abort, not a
|
|
701
|
+
* summarization failure to recover from: retrying on fallback providers
|
|
702
|
+
* would spend more provider work after the limit fired, and degrading to
|
|
703
|
+
* the metadata stub would let the run continue (and periodic compaction
|
|
704
|
+
* commit the stub) despite the `StreamLimits` contract that a breach
|
|
705
|
+
* rejects the run. Rethrown so the summarize node fails the run with
|
|
706
|
+
* the actionable limit error, consistent with agent turns and subagents.
|
|
707
|
+
*/
|
|
708
|
+
if (primaryError instanceof StreamLimitExceededError) {
|
|
709
|
+
throw primaryError;
|
|
710
|
+
}
|
|
711
|
+
/** A parallel branch's trip aborts the composed summarization signal,
|
|
712
|
+
* and a provider can surface that as a generic abort error; entering
|
|
713
|
+
* fallback recovery or degrading to the metadata stub would continue
|
|
714
|
+
* work after the run-wide safety abort. Checked on BOTH channels: the
|
|
715
|
+
* graph's own breaker, and the composed config signal that carries a
|
|
716
|
+
* parent run's trip into subagent child graphs. */
|
|
717
|
+
{
|
|
718
|
+
const trippedReason = findStreamLimitAbortReason(
|
|
719
|
+
graph?.getBreakerSignal?.(),
|
|
720
|
+
summarizeConfig?.signal
|
|
721
|
+
);
|
|
722
|
+
if (trippedReason != null) {
|
|
723
|
+
throw trippedReason;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
689
727
|
const rawFallbacks = (
|
|
690
728
|
clientConfig.clientOptions as unknown as t.LLMConfig | undefined
|
|
691
729
|
)?.fallbacks;
|
|
@@ -697,6 +735,7 @@ async function executeSummarizationWithFallback(params: {
|
|
|
697
735
|
config: traceConfig(summarizeConfig, 'cache_hit_compaction'),
|
|
698
736
|
provider: clientConfig.provider as Providers,
|
|
699
737
|
reasoningKey: agentContext.reasoningKey,
|
|
738
|
+
graph,
|
|
700
739
|
});
|
|
701
740
|
const fbResult = await tryFallbackProviders({
|
|
702
741
|
fallbacks,
|
|
@@ -711,9 +750,13 @@ async function executeSummarizationWithFallback(params: {
|
|
|
711
750
|
)
|
|
712
751
|
),
|
|
713
752
|
],
|
|
714
|
-
config:
|
|
753
|
+
config: withBreakerSignal(
|
|
754
|
+
traceConfig(summarizeConfig, 'cache_hit_compaction'),
|
|
755
|
+
graph
|
|
756
|
+
),
|
|
715
757
|
primaryError,
|
|
716
758
|
onChunk,
|
|
759
|
+
streamLimitState: graph,
|
|
717
760
|
});
|
|
718
761
|
const fbMsg = fbResult?.messages?.[0];
|
|
719
762
|
if (fbMsg) {
|
|
@@ -722,6 +765,9 @@ async function executeSummarizationWithFallback(params: {
|
|
|
722
765
|
);
|
|
723
766
|
}
|
|
724
767
|
} catch (fbErr) {
|
|
768
|
+
if (fbErr instanceof StreamLimitExceededError) {
|
|
769
|
+
throw fbErr;
|
|
770
|
+
}
|
|
725
771
|
const fbDescribed = describeFallbackError(fbErr, fallbacks);
|
|
726
772
|
log('warn', `Fallback providers also failed ${fbDescribed.suffix}`, {
|
|
727
773
|
...fbDescribed.data,
|
|
@@ -858,13 +904,64 @@ interface CreateSummarizeNodeParams {
|
|
|
858
904
|
result: t.StepCompleted,
|
|
859
905
|
config?: RunnableConfig
|
|
860
906
|
) => Promise<void>;
|
|
861
|
-
|
|
907
|
+
/** The run's shared breaker signal, composed into every summarization
|
|
908
|
+
* model attempt so a sibling branch tripping a stream limit also
|
|
909
|
+
* cancels in-flight summaries. */
|
|
910
|
+
getBreakerSignal?: () => AbortSignal;
|
|
911
|
+
/** The run's shared breaker controller. Preferred over the bare signal
|
|
912
|
+
* accessor: the node captures it at entry so a breach detected by its
|
|
913
|
+
* own chunk handler trips the run that STARTED the summarization. */
|
|
914
|
+
getBreakerController?: () => AbortController;
|
|
915
|
+
/** The controller's epoch, captured at entry and stamped into summary
|
|
916
|
+
* attempt metadata so the wire consumer epoch-gates old-run summary
|
|
917
|
+
* chunks like model-attempt chunks. */
|
|
918
|
+
getBreakerEpoch?: () => number;
|
|
919
|
+
} & StreamLimitState;
|
|
862
920
|
generateStepId: (stepKey: string) => [string, number];
|
|
863
921
|
}
|
|
864
922
|
|
|
923
|
+
/** Composes the run's breaker signal (when the adapter provides one) into a
|
|
924
|
+
* summarization attempt's config, so breaches in sibling branches cancel
|
|
925
|
+
* summaries too. */
|
|
926
|
+
function withBreakerSignal<T extends { signal?: AbortSignal }>(
|
|
927
|
+
config: T | undefined,
|
|
928
|
+
graph?: { getBreakerSignal?: () => AbortSignal }
|
|
929
|
+
): T | undefined {
|
|
930
|
+
const breakerSignal = graph?.getBreakerSignal?.();
|
|
931
|
+
if (breakerSignal == null) {
|
|
932
|
+
return config;
|
|
933
|
+
}
|
|
934
|
+
if (config == null) {
|
|
935
|
+
return { signal: breakerSignal } as T;
|
|
936
|
+
}
|
|
937
|
+
const existing = config.signal;
|
|
938
|
+
if (existing == null || existing === breakerSignal) {
|
|
939
|
+
return { ...config, signal: breakerSignal };
|
|
940
|
+
}
|
|
941
|
+
return { ...config, signal: AbortSignal.any([existing, breakerSignal]) };
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/** First stream-limit reason found on any of the given signals. Checked
|
|
945
|
+
* alongside the graph's own breaker because a subagent child graph receives
|
|
946
|
+
* a ROOT sibling's trip through its constructor/invocation signal — the
|
|
947
|
+
* child's private breaker never fires for it. */
|
|
948
|
+
function findStreamLimitAbortReason(
|
|
949
|
+
...signals: Array<AbortSignal | undefined>
|
|
950
|
+
): StreamLimitExceededError | undefined {
|
|
951
|
+
for (const signal of signals) {
|
|
952
|
+
if (
|
|
953
|
+
signal?.aborted === true &&
|
|
954
|
+
signal.reason instanceof StreamLimitExceededError
|
|
955
|
+
) {
|
|
956
|
+
return signal.reason;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return undefined;
|
|
960
|
+
}
|
|
961
|
+
|
|
865
962
|
export function createSummarizeNode({
|
|
866
963
|
agentContext,
|
|
867
|
-
graph,
|
|
964
|
+
graph: adapterGraph,
|
|
868
965
|
generateStepId,
|
|
869
966
|
}: CreateSummarizeNodeParams) {
|
|
870
967
|
return async (
|
|
@@ -874,10 +971,45 @@ export function createSummarizeNode({
|
|
|
874
971
|
},
|
|
875
972
|
config?: RunnableConfig
|
|
876
973
|
): Promise<{ summarizationRequest: undefined; messages?: BaseMessage[] }> => {
|
|
974
|
+
/** The breaker signal is captured at node ENTRY, before dispatchRunStep,
|
|
975
|
+
* ON_SUMMARIZE_START, or PreCompact awaits: a sibling's trip plus a
|
|
976
|
+
* prompt next run can reset the controller during one of those awaits,
|
|
977
|
+
* and resolving the live accessor later would bind this summarization
|
|
978
|
+
* to the fresh controller. `Object.create` freezes the signal while
|
|
979
|
+
* DELEGATING every other adapter member — spreading would snapshot the
|
|
980
|
+
* charge-credit accessor pair and detach it from graph resets. */
|
|
981
|
+
const entryBreakerController = adapterGraph.getBreakerController?.();
|
|
982
|
+
const entryBreakerSignal =
|
|
983
|
+
entryBreakerController?.signal ?? adapterGraph.getBreakerSignal?.();
|
|
984
|
+
const entryBreakerEpoch = adapterGraph.getBreakerEpoch?.();
|
|
985
|
+
const graph =
|
|
986
|
+
entryBreakerSignal == null
|
|
987
|
+
? adapterGraph
|
|
988
|
+
: (Object.create(adapterGraph, {
|
|
989
|
+
getBreakerSignal: { value: (): AbortSignal => entryBreakerSignal },
|
|
990
|
+
...(entryBreakerController != null && {
|
|
991
|
+
getBreakerController: {
|
|
992
|
+
value: (): AbortController => entryBreakerController,
|
|
993
|
+
},
|
|
994
|
+
}),
|
|
995
|
+
}) as typeof adapterGraph);
|
|
877
996
|
const request = state.summarizationRequest;
|
|
878
997
|
if (request == null) {
|
|
879
998
|
return { summarizationRequest: undefined };
|
|
880
999
|
}
|
|
1000
|
+
/** Already-tripped-at-entry: a parallel sibling's breach failed the run
|
|
1001
|
+
* before this node was scheduled — via this graph's own breaker or, in
|
|
1002
|
+
* a subagent child graph, the parent trip on the invocation signal.
|
|
1003
|
+
* Rethrow before dispatching steps, hooks, or the model call — an
|
|
1004
|
+
* adapter that doesn't synchronously reject an aborted signal would
|
|
1005
|
+
* otherwise spend summarization provider work on a failed run. */
|
|
1006
|
+
const entryTripReason = findStreamLimitAbortReason(
|
|
1007
|
+
entryBreakerSignal,
|
|
1008
|
+
config?.signal
|
|
1009
|
+
);
|
|
1010
|
+
if (entryTripReason != null) {
|
|
1011
|
+
throw entryTripReason;
|
|
1012
|
+
}
|
|
881
1013
|
|
|
882
1014
|
/**
|
|
883
1015
|
* Overflow recovery routes through this node purely to get back to the
|
|
@@ -1111,10 +1243,31 @@ export function createSummarizeNode({
|
|
|
1111
1243
|
...(clientConfig.modelName != null && clientConfig.modelName !== ''
|
|
1112
1244
|
? { [Constants.INVOKED_MODEL]: clientConfig.modelName }
|
|
1113
1245
|
: {}),
|
|
1246
|
+
/** Entry-captured breaker epoch: the wire consumer epoch-gates
|
|
1247
|
+
* old-run summary chunks exactly like model-attempt chunks —
|
|
1248
|
+
* without the stamp, a straggling summary from a failed run
|
|
1249
|
+
* reads as current and could abort the next run's controller. */
|
|
1250
|
+
...(entryBreakerEpoch != null
|
|
1251
|
+
? { [STREAM_LIMIT_EPOCH_KEY]: entryBreakerEpoch }
|
|
1252
|
+
: {}),
|
|
1114
1253
|
},
|
|
1115
1254
|
}
|
|
1116
1255
|
: undefined;
|
|
1117
1256
|
|
|
1257
|
+
/** Rechecked after the pre-call awaits (dispatchRunStep,
|
|
1258
|
+
* ON_SUMMARIZE_START, PreCompact): a sibling can trip the breaker while
|
|
1259
|
+
* this node is paused in one of them, and a provider that doesn't
|
|
1260
|
+
* synchronously reject an aborted signal would still start the
|
|
1261
|
+
* summarization call. Mirrors the model node's pre-invoke recheck. */
|
|
1262
|
+
{
|
|
1263
|
+
const preCallTrip = findStreamLimitAbortReason(
|
|
1264
|
+
entryBreakerSignal,
|
|
1265
|
+
config?.signal
|
|
1266
|
+
);
|
|
1267
|
+
if (preCallTrip != null) {
|
|
1268
|
+
throw preCallTrip;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1118
1271
|
const {
|
|
1119
1272
|
text: rawText,
|
|
1120
1273
|
usage: summaryUsage,
|
|
@@ -1127,6 +1280,7 @@ export function createSummarizeNode({
|
|
|
1127
1280
|
stepId,
|
|
1128
1281
|
usePromptCache: isSelfSummarizeModel && hasPromptCache,
|
|
1129
1282
|
log,
|
|
1283
|
+
graph,
|
|
1130
1284
|
});
|
|
1131
1285
|
|
|
1132
1286
|
/**
|
|
@@ -1331,21 +1485,58 @@ function buildSummarizationInstruction(
|
|
|
1331
1485
|
}
|
|
1332
1486
|
|
|
1333
1487
|
/** Creates an `onChunk` callback that dispatches `ON_SUMMARIZE_DELTA` events for streaming. */
|
|
1334
|
-
function createSummarizationChunkHandler({
|
|
1488
|
+
export function createSummarizationChunkHandler({
|
|
1335
1489
|
stepId,
|
|
1336
1490
|
config,
|
|
1337
1491
|
provider,
|
|
1338
1492
|
reasoningKey = 'reasoning_content',
|
|
1493
|
+
graph,
|
|
1339
1494
|
}: {
|
|
1340
1495
|
stepId?: string;
|
|
1341
1496
|
config?: RunnableConfig;
|
|
1342
1497
|
provider?: Providers;
|
|
1343
1498
|
reasoningKey?: 'reasoning_content' | 'reasoning';
|
|
1499
|
+
graph?: StreamLimitState & { getBreakerController?: () => AbortController };
|
|
1344
1500
|
}): OnChunk | undefined {
|
|
1345
1501
|
if (stepId == null || stepId === '' || !config) {
|
|
1346
1502
|
return undefined;
|
|
1347
1503
|
}
|
|
1348
|
-
|
|
1504
|
+
const limitMetadata = config.metadata as Record<string, unknown> | undefined;
|
|
1505
|
+
return (chunk, attemptMetadata) => {
|
|
1506
|
+
/**
|
|
1507
|
+
* Charged as `producer` so it PAIRS with the run's registered stream
|
|
1508
|
+
* handler: inside a live run the provider callbacks route each chunk
|
|
1509
|
+
* through the graph's `on_chat_model_stream` consumer too, and two
|
|
1510
|
+
* same-side consumer claims would charge the chunk twice — halving an
|
|
1511
|
+
* opt-in event cap. Standalone callers (tests, custom hosts, runs with
|
|
1512
|
+
* no registered handler) still get single-sided enforcement from this
|
|
1513
|
+
* one claim. Summarization deliberately passes NO `context` to
|
|
1514
|
+
* `attemptInvoke`, whose onChunk branch would otherwise add a second
|
|
1515
|
+
* producer claim. A breach trips the run's shared breaker (the
|
|
1516
|
+
* ENTRY-captured controller when the adapter provides one) before
|
|
1517
|
+
* rethrowing: this producer claim wins the race, so the wire consumer's
|
|
1518
|
+
* breaker-aborting catch never fires for the same chunk — without the
|
|
1519
|
+
* trip here, sibling model calls, tools, and subagents would keep
|
|
1520
|
+
* consuming quota while the summary branch rejects.
|
|
1521
|
+
* `executeSummarizationWithFallback` then rethrows past fallbacks and
|
|
1522
|
+
* the metadata stub, failing the run like any other breach.
|
|
1523
|
+
*/
|
|
1524
|
+
if (graph != null) {
|
|
1525
|
+
try {
|
|
1526
|
+
enforceStreamLimitsForWireChunk({
|
|
1527
|
+
graph,
|
|
1528
|
+
metadata: attemptMetadata ?? limitMetadata,
|
|
1529
|
+
chunk: chunk as Parameters<
|
|
1530
|
+
typeof enforceStreamLimitsForWireChunk
|
|
1531
|
+
>[0]['chunk'],
|
|
1532
|
+
});
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
if (error instanceof StreamLimitExceededError) {
|
|
1535
|
+
graph.getBreakerController?.().abort(error);
|
|
1536
|
+
}
|
|
1537
|
+
throw error;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1349
1540
|
const chunkAny = chunk as Parameters<typeof getChunkContent>[0]['chunk'];
|
|
1350
1541
|
const raw = getChunkContent({ chunk: chunkAny, provider, reasoningKey });
|
|
1351
1542
|
if (raw == null || (typeof raw === 'string' && !raw)) {
|
|
@@ -1404,6 +1595,7 @@ async function summarizeWithCacheHit({
|
|
|
1404
1595
|
stepId,
|
|
1405
1596
|
provider,
|
|
1406
1597
|
reasoningKey,
|
|
1598
|
+
graph,
|
|
1407
1599
|
usePromptCache,
|
|
1408
1600
|
promptCacheTtl,
|
|
1409
1601
|
log,
|
|
@@ -1417,6 +1609,7 @@ async function summarizeWithCacheHit({
|
|
|
1417
1609
|
stepId?: string;
|
|
1418
1610
|
provider: Providers;
|
|
1419
1611
|
reasoningKey?: 'reasoning_content' | 'reasoning';
|
|
1612
|
+
graph?: StreamLimitState & { getBreakerSignal?: () => AbortSignal };
|
|
1420
1613
|
usePromptCache?: boolean;
|
|
1421
1614
|
promptCacheTtl?: PromptCacheTtl;
|
|
1422
1615
|
log?: LogFn;
|
|
@@ -1443,9 +1636,13 @@ async function summarizeWithCacheHit({
|
|
|
1443
1636
|
config: traceConfig(config, 'cache_hit_compaction'),
|
|
1444
1637
|
provider,
|
|
1445
1638
|
reasoningKey,
|
|
1639
|
+
graph,
|
|
1446
1640
|
}),
|
|
1641
|
+
/** Accounting lease only (summarization passes no `context` so its
|
|
1642
|
+
* producer claim never stacks with the onChunk handler's). */
|
|
1643
|
+
streamLimitState: graph,
|
|
1447
1644
|
},
|
|
1448
|
-
traceConfig(config, 'cache_hit_compaction')
|
|
1645
|
+
withBreakerSignal(traceConfig(config, 'cache_hit_compaction'), graph)
|
|
1449
1646
|
);
|
|
1450
1647
|
|
|
1451
1648
|
const responseMsg = result.messages?.[0];
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { config } from 'dotenv';
|
|
2
2
|
import fetch, { RequestInit } from 'node-fetch';
|
|
3
|
-
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
4
3
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
5
4
|
import type * as t from '@/types';
|
|
6
5
|
import {
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
normalizeCodeApiRequestError,
|
|
17
16
|
resolveCodeApiAuthHeaders,
|
|
18
17
|
} from './CodeExecutor';
|
|
18
|
+
import { resolveFetchProxyAgent } from '@/utils/proxy';
|
|
19
19
|
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
20
20
|
import { Constants } from '@/common';
|
|
21
21
|
|
|
@@ -253,8 +253,9 @@ function createBashExecutionTool(
|
|
|
253
253
|
body: JSON.stringify(postData),
|
|
254
254
|
};
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
const proxyAgent = resolveFetchProxyAgent(EXEC_ENDPOINT);
|
|
257
|
+
if (proxyAgent) {
|
|
258
|
+
fetchOptions.agent = proxyAgent;
|
|
258
259
|
}
|
|
259
260
|
const response = await fetch(EXEC_ENDPOINT, fetchOptions);
|
|
260
261
|
if (!response.ok) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { config } from 'dotenv';
|
|
2
2
|
import fetch, { RequestInit } from 'node-fetch';
|
|
3
|
-
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
4
3
|
import { getEnvironmentVariable } from '@langchain/core/utils/env';
|
|
5
4
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
6
5
|
import type * as t from '@/types';
|
|
7
6
|
import { appendCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
|
|
7
|
+
import { resolveFetchProxyAgent } from '@/utils/proxy';
|
|
8
8
|
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
9
9
|
import { EnvVar, Constants } from '@/common';
|
|
10
10
|
|
|
@@ -23,7 +23,7 @@ export const emptyOutputMessage =
|
|
|
23
23
|
'stdout: Empty. Ensure you\'re writing output explicitly.\n';
|
|
24
24
|
|
|
25
25
|
export const CODE_ARTIFACT_PATH_GUIDANCE =
|
|
26
|
-
'
|
|
26
|
+
'Anything a later call needs (data, helper scripts/modules, partial results) MUST be written under `/mnt/data` in the same call that produces it; `/tmp` never survives the call. `/mnt/data` keeps files with recognized extensions, covering common source, text, data, document, image, and archive formats (.py/.sh/.sql/.md/.json/.csv/.parquet/.png/.pdf/.zip and similar); extensionless or unusual extensions are not kept. Failed executions register nothing; fix the error and rerun before relying on new files.';
|
|
27
27
|
|
|
28
28
|
export const BASH_SHELL_GUIDANCE =
|
|
29
29
|
'Bash: multi-line files use heredoc/printf; run Python via python3 -c/heredoc, not bare Python.';
|
|
@@ -426,8 +426,9 @@ function createCodeExecutionTool(
|
|
|
426
426
|
body: JSON.stringify(postData),
|
|
427
427
|
};
|
|
428
428
|
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
const proxyAgent = resolveFetchProxyAgent(EXEC_ENDPOINT);
|
|
430
|
+
if (proxyAgent) {
|
|
431
|
+
fetchOptions.agent = proxyAgent;
|
|
431
432
|
}
|
|
432
433
|
const response = await fetch(EXEC_ENDPOINT, fetchOptions);
|
|
433
434
|
if (!response.ok) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// src/tools/ProgrammaticToolCalling.ts
|
|
2
2
|
import { config } from 'dotenv';
|
|
3
3
|
import fetch, { RequestInit } from 'node-fetch';
|
|
4
|
-
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
5
4
|
import { tool, DynamicStructuredTool } from '@langchain/core/tools';
|
|
6
5
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
7
6
|
import type { ProgrammaticToolCallingJsonSchema } from './ptcTimeout';
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
createCodeApiRunTimeoutSchema,
|
|
25
24
|
resolveCodeApiRunTimeoutMs,
|
|
26
25
|
} from './ptcTimeout';
|
|
26
|
+
import { resolveFetchProxyAgent } from '@/utils/proxy';
|
|
27
27
|
import { INTENT_PROPERTY } from '@/tools/intentArg';
|
|
28
28
|
import { Constants } from '@/common';
|
|
29
29
|
|
|
@@ -438,8 +438,9 @@ export async function fetchSessionFiles(
|
|
|
438
438
|
},
|
|
439
439
|
};
|
|
440
440
|
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
const proxyAgent = resolveFetchProxyAgent(filesEndpoint, proxy);
|
|
442
|
+
if (proxyAgent) {
|
|
443
|
+
fetchOptions.agent = proxyAgent;
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
const response = await fetch(filesEndpoint, fetchOptions);
|
|
@@ -491,8 +492,9 @@ export async function makeRequest(
|
|
|
491
492
|
body: JSON.stringify(body),
|
|
492
493
|
};
|
|
493
494
|
|
|
494
|
-
|
|
495
|
-
|
|
495
|
+
const proxyAgent = resolveFetchProxyAgent(endpoint, proxy);
|
|
496
|
+
if (proxyAgent) {
|
|
497
|
+
fetchOptions.agent = proxyAgent;
|
|
496
498
|
}
|
|
497
499
|
|
|
498
500
|
const response = await fetch(endpoint, fetchOptions);
|