@librechat/agents 3.1.85 → 3.1.87
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/README.md +69 -0
- package/dist/cjs/agents/AgentContext.cjs +7 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/events.cjs +23 -0
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +133 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +251 -53
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -5
- package/dist/cjs/llm/init.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +113 -24
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +3 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -5
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/openai/index.cjs +253 -0
- package/dist/cjs/openai/index.cjs.map +1 -0
- package/dist/cjs/responses/index.cjs +448 -0
- package/dist/cjs/responses/index.cjs.map +1 -0
- package/dist/cjs/run.cjs +108 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1057 -0
- package/dist/cjs/session/AgentSession.cjs.map +1 -0
- package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
- package/dist/cjs/session/handlers.cjs +221 -0
- package/dist/cjs/session/handlers.cjs.map +1 -0
- package/dist/cjs/session/ids.cjs +22 -0
- package/dist/cjs/session/ids.cjs.map +1 -0
- package/dist/cjs/session/messageSerialization.cjs +179 -0
- package/dist/cjs/session/messageSerialization.cjs.map +1 -0
- package/dist/cjs/stream.cjs +472 -11
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +177 -59
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
- package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
- package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +7 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/events.mjs +23 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +133 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +251 -53
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -5
- package/dist/esm/llm/init.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +113 -25
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -2
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/openai/index.mjs +246 -0
- package/dist/esm/openai/index.mjs.map +1 -0
- package/dist/esm/responses/index.mjs +440 -0
- package/dist/esm/responses/index.mjs.map +1 -0
- package/dist/esm/run.mjs +108 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1054 -0
- package/dist/esm/session/AgentSession.mjs.map +1 -0
- package/dist/esm/session/JsonlSessionStore.mjs +422 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
- package/dist/esm/session/handlers.mjs +219 -0
- package/dist/esm/session/handlers.mjs.map +1 -0
- package/dist/esm/session/ids.mjs +17 -0
- package/dist/esm/session/ids.mjs.map +1 -0
- package/dist/esm/session/messageSerialization.mjs +173 -0
- package/dist/esm/session/messageSerialization.mjs.map +1 -0
- package/dist/esm/stream.mjs +473 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +177 -59
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +107 -0
- package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
- package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
- package/dist/types/events.d.ts +1 -0
- package/dist/types/graphs/Graph.d.ts +24 -9
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +1 -0
- package/dist/types/openai/index.d.ts +75 -0
- package/dist/types/responses/index.d.ts +97 -0
- package/dist/types/run.d.ts +2 -0
- package/dist/types/session/AgentSession.d.ts +32 -0
- package/dist/types/session/JsonlSessionStore.d.ts +67 -0
- package/dist/types/session/handlers.d.ts +8 -0
- package/dist/types/session/ids.d.ts +4 -0
- package/dist/types/session/index.d.ts +5 -0
- package/dist/types/session/messageSerialization.d.ts +7 -0
- package/dist/types/session/types.d.ts +191 -0
- package/dist/types/tools/ToolNode.d.ts +12 -1
- package/dist/types/tools/eagerEventExecution.d.ts +23 -0
- package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
- package/dist/types/types/hitl.d.ts +4 -0
- package/dist/types/types/run.d.ts +11 -1
- package/dist/types/types/tools.d.ts +36 -0
- package/package.json +19 -2
- package/src/__tests__/stream.eagerEventExecution.test.ts +2458 -0
- package/src/agents/AgentContext.ts +7 -2
- package/src/agents/__tests__/AgentContext.test.ts +254 -5
- package/src/events.ts +29 -0
- package/src/graphs/Graph.ts +224 -50
- package/src/graphs/MultiAgentGraph.ts +1 -1
- package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
- package/src/index.ts +3 -0
- package/src/llm/anthropic/index.ts +356 -84
- package/src/llm/anthropic/llm.spec.ts +64 -0
- package/src/llm/custom-chat-models.smoke.test.ts +175 -4
- package/src/llm/openai/contentBlocks.test.ts +35 -0
- package/src/llm/openai/deepseek.test.ts +201 -2
- package/src/llm/openai/index.ts +171 -26
- package/src/llm/openai/utils/index.ts +22 -0
- package/src/llm/openrouter/index.ts +4 -2
- package/src/openai/__tests__/openai.test.ts +337 -0
- package/src/openai/index.ts +404 -0
- package/src/responses/__tests__/responses.test.ts +652 -0
- package/src/responses/index.ts +677 -0
- package/src/run.ts +158 -8
- package/src/scripts/compare_pi_vs_ours.ts +592 -173
- package/src/scripts/session_live.ts +548 -0
- package/src/session/AgentSession.ts +1432 -0
- package/src/session/JsonlSessionStore.ts +572 -0
- package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
- package/src/session/__tests__/handlers.test.ts +161 -0
- package/src/session/handlers.ts +272 -0
- package/src/session/ids.ts +17 -0
- package/src/session/index.ts +44 -0
- package/src/session/messageSerialization.ts +207 -0
- package/src/session/types.ts +275 -0
- package/src/specs/custom-event-await.test.ts +89 -0
- package/src/specs/summarization.test.ts +1 -1
- package/src/stream.ts +755 -48
- package/src/summarization/node.ts +1 -1
- package/src/tools/ToolNode.ts +299 -126
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
- package/src/tools/__tests__/handlers.test.ts +2 -1
- package/src/tools/__tests__/hitl.test.ts +206 -110
- package/src/tools/eagerEventExecution.ts +153 -0
- package/src/tools/handlers.ts +8 -4
- package/src/tools/streamedToolCallSeals.ts +57 -0
- package/src/types/hitl.ts +4 -0
- package/src/types/run.ts +11 -0
- package/src/types/tools.ts +36 -0
- package/dist/cjs/llm/text.cjs +0 -69
- package/dist/cjs/llm/text.cjs.map +0 -1
- package/dist/esm/llm/text.mjs +0 -67
- package/dist/esm/llm/text.mjs.map +0 -1
|
@@ -2,17 +2,15 @@ import { isBaseMessage, ToolMessage, HumanMessage, isAIMessage } from '@langchai
|
|
|
2
2
|
import { isCommand, isGraphInterrupt, interrupt, Command, Send, END } from '@langchain/langgraph';
|
|
3
3
|
import { AsyncLocalStorageProviderSingleton } from '@langchain/core/singletons';
|
|
4
4
|
import { Constants, CODE_EXECUTION_TOOLS, GraphEvents } from '../common/enum.mjs';
|
|
5
|
-
import '
|
|
6
|
-
import '../messages/core.mjs';
|
|
7
|
-
import { calculateMaxToolResultChars, truncateToolResultContent } from '../utils/truncation.mjs';
|
|
8
|
-
import { toLangChainContent } from '../messages/langchain.mjs';
|
|
9
|
-
import { safeDispatchCustomEvent } from '../utils/events.mjs';
|
|
10
|
-
import 'uuid';
|
|
5
|
+
import '../stream.mjs';
|
|
11
6
|
import { RunnableCallable } from '../utils/run.mjs';
|
|
12
7
|
import 'ai-tokenizer';
|
|
13
8
|
import 'zod-to-json-schema';
|
|
9
|
+
import { calculateMaxToolResultChars, truncateToolResultContent } from '../utils/truncation.mjs';
|
|
10
|
+
import { safeDispatchCustomEvent } from '../utils/events.mjs';
|
|
14
11
|
import { executeHooks } from '../hooks/executeHooks.mjs';
|
|
15
12
|
import '../hooks/createWorkspacePolicyHook.mjs';
|
|
13
|
+
import { toLangChainContent } from '../messages/langchain.mjs';
|
|
16
14
|
import { ToolOutputReferenceRegistry, buildReferenceKey } from './toolOutputReferences.mjs';
|
|
17
15
|
import './local/CompileCheckTool.mjs';
|
|
18
16
|
import 'path';
|
|
@@ -28,6 +26,7 @@ import './ProgrammaticToolCalling.mjs';
|
|
|
28
26
|
import './BashProgrammaticToolCalling.mjs';
|
|
29
27
|
import { resolveLocalToolRegistry, resolveLocalExecutionTools } from './local/resolveLocalExecutionTools.mjs';
|
|
30
28
|
import './local/attachments.mjs';
|
|
29
|
+
import { buildToolExecutionRequestPlan, recordArgsEqual } from './eagerEventExecution.mjs';
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
32
|
* Helper to check if a value is a Send object
|
|
@@ -258,6 +257,12 @@ class ToolNode extends RunnableCallable {
|
|
|
258
257
|
sessions;
|
|
259
258
|
/** When true, dispatches ON_TOOL_EXECUTE events instead of invoking tools directly */
|
|
260
259
|
eventDrivenMode = false;
|
|
260
|
+
/** Opt-in stream-layer prestart config for event-driven tools. */
|
|
261
|
+
eagerEventToolExecution;
|
|
262
|
+
/** Shared per-run prestarted tool registry populated by ChatModelStreamHandler. */
|
|
263
|
+
eagerEventToolExecutions;
|
|
264
|
+
/** Shared per-run per-tool turn counter used by eager and normal event dispatch. */
|
|
265
|
+
eagerEventToolUsageCount;
|
|
261
266
|
/** Agent ID for event-driven mode */
|
|
262
267
|
agentId;
|
|
263
268
|
/** Tool names that bypass event dispatch and execute directly (e.g., graph-managed handoff tools) */
|
|
@@ -301,7 +306,7 @@ class ToolNode extends RunnableCallable {
|
|
|
301
306
|
* other's in-flight state.
|
|
302
307
|
*/
|
|
303
308
|
anonBatchCounter = 0;
|
|
304
|
-
constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, toolRegistry, sessions, eventDrivenMode, agentId, directToolNames, maxContextTokens, maxToolResultChars, hookRegistry, humanInTheLoop, toolOutputReferences, toolOutputRegistry, toolExecution, fileCheckpointer, }) {
|
|
309
|
+
constructor({ tools, toolMap, name, tags, errorHandler, toolCallStepIds, handleToolErrors, loadRuntimeTools, toolRegistry, sessions, eventDrivenMode, eagerEventToolExecution, eagerEventToolExecutions, eagerEventToolUsageCount, agentId, directToolNames, maxContextTokens, maxToolResultChars, hookRegistry, humanInTheLoop, toolOutputReferences, toolOutputRegistry, toolExecution, fileCheckpointer, }) {
|
|
305
310
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
306
311
|
this.toolMap = toolMap ?? new Map(tools.map((tool) => [tool.name, tool]));
|
|
307
312
|
this.toolCallStepIds = toolCallStepIds;
|
|
@@ -315,6 +320,9 @@ class ToolNode extends RunnableCallable {
|
|
|
315
320
|
});
|
|
316
321
|
this.sessions = sessions;
|
|
317
322
|
this.eventDrivenMode = eventDrivenMode ?? false;
|
|
323
|
+
this.eagerEventToolExecution = eagerEventToolExecution;
|
|
324
|
+
this.eagerEventToolExecutions = eagerEventToolExecutions;
|
|
325
|
+
this.eagerEventToolUsageCount = eagerEventToolUsageCount;
|
|
318
326
|
this.agentId = agentId;
|
|
319
327
|
this.directToolNames = directToolNames;
|
|
320
328
|
this.maxToolResultChars =
|
|
@@ -493,6 +501,18 @@ class ToolNode extends RunnableCallable {
|
|
|
493
501
|
getToolUsageCounts() {
|
|
494
502
|
return new Map(this.toolUsageCount); // Return a copy
|
|
495
503
|
}
|
|
504
|
+
recordToolUsageTurn(toolName, turn, callId) {
|
|
505
|
+
this.toolUsageCount.set(toolName, Math.max(this.toolUsageCount.get(toolName) ?? 0, turn + 1));
|
|
506
|
+
if (callId != null && callId !== '') {
|
|
507
|
+
this.toolCallTurns.set(callId, turn);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
recordEventToolPlanningTurn(toolName, turn, callId) {
|
|
511
|
+
this.recordToolUsageTurn(toolName, turn, callId);
|
|
512
|
+
if (this.canConsumeEagerEventExecution()) {
|
|
513
|
+
this.eagerEventToolUsageCount?.set(toolName, Math.max(this.eagerEventToolUsageCount.get(toolName) ?? 0, turn + 1));
|
|
514
|
+
}
|
|
515
|
+
}
|
|
496
516
|
/**
|
|
497
517
|
* Runs a single tool call with error handling.
|
|
498
518
|
*
|
|
@@ -1298,7 +1318,7 @@ class ToolNode extends RunnableCallable {
|
|
|
1298
1318
|
* by `runTool`. Threaded as a local map (instead of instance state)
|
|
1299
1319
|
* so concurrent batches cannot read each other's entries.
|
|
1300
1320
|
*/
|
|
1301
|
-
handleRunToolCompletions(calls, outputs, config, resolvedArgsByCallId) {
|
|
1321
|
+
async handleRunToolCompletions(calls, outputs, config, resolvedArgsByCallId) {
|
|
1302
1322
|
for (let i = 0; i < calls.length; i++) {
|
|
1303
1323
|
const call = calls[i];
|
|
1304
1324
|
const output = outputs[i];
|
|
@@ -1344,7 +1364,7 @@ class ToolNode extends RunnableCallable {
|
|
|
1344
1364
|
output: contentString,
|
|
1345
1365
|
progress: 1,
|
|
1346
1366
|
};
|
|
1347
|
-
safeDispatchCustomEvent(GraphEvents.ON_RUN_STEP_COMPLETED, {
|
|
1367
|
+
await safeDispatchCustomEvent(GraphEvents.ON_RUN_STEP_COMPLETED, {
|
|
1348
1368
|
result: {
|
|
1349
1369
|
id: stepId,
|
|
1350
1370
|
index: turn,
|
|
@@ -1513,9 +1533,9 @@ class ToolNode extends RunnableCallable {
|
|
|
1513
1533
|
reason,
|
|
1514
1534
|
});
|
|
1515
1535
|
};
|
|
1516
|
-
const flushDeferredBlockedSideEffects = () => {
|
|
1536
|
+
const flushDeferredBlockedSideEffects = async () => {
|
|
1517
1537
|
for (const item of deferredBlockedSideEffects) {
|
|
1518
|
-
this.dispatchStepCompleted(item.callId, item.toolName, item.args, item.contentString, config);
|
|
1538
|
+
await this.dispatchStepCompleted(item.callId, item.toolName, item.args, item.contentString, config);
|
|
1519
1539
|
if (hookRegistry.hasHookFor('PermissionDenied', runId)) {
|
|
1520
1540
|
executeHooks({
|
|
1521
1541
|
registry: hookRegistry,
|
|
@@ -1738,7 +1758,7 @@ class ToolNode extends RunnableCallable {
|
|
|
1738
1758
|
* no risk of being rolled back by a subsequent throw, so
|
|
1739
1759
|
* no risk of a duplicate `ON_RUN_STEP_COMPLETED` event.
|
|
1740
1760
|
*/
|
|
1741
|
-
this.dispatchStepCompleted(entry.call.id, entry.call.name, entry.args, truncatedResponse, config);
|
|
1761
|
+
await this.dispatchStepCompleted(entry.call.id, entry.call.name, entry.args, truncatedResponse, config);
|
|
1742
1762
|
continue;
|
|
1743
1763
|
}
|
|
1744
1764
|
if (decision.type === 'edit') {
|
|
@@ -1804,7 +1824,7 @@ class ToolNode extends RunnableCallable {
|
|
|
1804
1824
|
* dispatches in the same relative position as the pre-deferral
|
|
1805
1825
|
* code did (after hook processing, before tool execution).
|
|
1806
1826
|
*/
|
|
1807
|
-
flushDeferredBlockedSideEffects();
|
|
1827
|
+
await flushDeferredBlockedSideEffects();
|
|
1808
1828
|
}
|
|
1809
1829
|
else {
|
|
1810
1830
|
approvedEntries.push(...preToolCalls);
|
|
@@ -1812,50 +1832,92 @@ class ToolNode extends RunnableCallable {
|
|
|
1812
1832
|
const injected = [];
|
|
1813
1833
|
const batchIndexByCallId = new Map();
|
|
1814
1834
|
if (approvedEntries.length > 0) {
|
|
1815
|
-
const
|
|
1816
|
-
|
|
1817
|
-
|
|
1835
|
+
const plan = buildToolExecutionRequestPlan({
|
|
1836
|
+
toolCalls: approvedEntries.map((entry) => {
|
|
1837
|
+
const codeSessionContext = CODE_EXECUTION_TOOLS.has(entry.call.name) ||
|
|
1838
|
+
entry.call.name === Constants.SKILL_TOOL ||
|
|
1839
|
+
entry.call.name === Constants.READ_FILE
|
|
1840
|
+
? this.getCodeSessionContext()
|
|
1841
|
+
: undefined;
|
|
1842
|
+
return {
|
|
1843
|
+
id: entry.call.id,
|
|
1844
|
+
name: entry.call.name,
|
|
1845
|
+
args: entry.args,
|
|
1846
|
+
stepId: entry.stepId,
|
|
1847
|
+
codeSessionContext,
|
|
1848
|
+
};
|
|
1849
|
+
}),
|
|
1850
|
+
usageCount: this.toolUsageCount,
|
|
1851
|
+
invalidArgsBehavior: 'error-result',
|
|
1852
|
+
recordTurn: (toolName, reservedTurn, callId) => {
|
|
1853
|
+
this.recordEventToolPlanningTurn(toolName, reservedTurn, callId);
|
|
1854
|
+
},
|
|
1855
|
+
});
|
|
1856
|
+
if (plan == null) {
|
|
1857
|
+
throw new Error('Unable to build event tool execution request plan');
|
|
1858
|
+
}
|
|
1859
|
+
const requests = plan.requests;
|
|
1860
|
+
for (const entry of approvedEntries) {
|
|
1818
1861
|
if (entry.batchIndex != null && entry.call.id != null) {
|
|
1819
1862
|
batchIndexByCallId.set(entry.call.id, entry.batchIndex);
|
|
1820
1863
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
* - `SKILL_TOOL` — skill files live alongside code-env state.
|
|
1833
|
-
* - `READ_FILE` — when the requested path is a code-env artifact
|
|
1834
|
-
* (e.g. `/mnt/data/...`) the host falls back to reading via the
|
|
1835
|
-
* same sandbox session; without the seeded `session_id` /
|
|
1836
|
-
* `_injected_files` here, that fallback can't see prior-turn
|
|
1837
|
-
* artifacts on the very first call of a turn.
|
|
1838
|
-
*/
|
|
1839
|
-
if (CODE_EXECUTION_TOOLS.has(entry.call.name) ||
|
|
1840
|
-
entry.call.name === Constants.SKILL_TOOL ||
|
|
1841
|
-
entry.call.name === Constants.READ_FILE) {
|
|
1842
|
-
request.codeSessionContext = this.getCodeSessionContext();
|
|
1864
|
+
}
|
|
1865
|
+
for (const result of plan.rejectedResults) {
|
|
1866
|
+
this.eagerEventToolExecutions?.delete(result.toolCallId);
|
|
1867
|
+
}
|
|
1868
|
+
const requestMap = new Map(plan.allRequests.map((r) => [r.id, r]));
|
|
1869
|
+
const eagerExecutions = [];
|
|
1870
|
+
const dispatchRequests = [];
|
|
1871
|
+
for (const request of requests) {
|
|
1872
|
+
const eagerExecution = this.takeMatchingEagerEventExecution(request);
|
|
1873
|
+
if (eagerExecution != null) {
|
|
1874
|
+
eagerExecutions.push({ request, execution: eagerExecution });
|
|
1843
1875
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1876
|
+
else {
|
|
1877
|
+
dispatchRequests.push(request);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
const dispatchPromise = dispatchRequests.length === 0
|
|
1881
|
+
? Promise.resolve([])
|
|
1882
|
+
: new Promise((resolve, reject) => {
|
|
1883
|
+
let dispatchSettled = false;
|
|
1884
|
+
let resultSettled = false;
|
|
1885
|
+
let settledResults;
|
|
1886
|
+
const maybeResolve = () => {
|
|
1887
|
+
if (dispatchSettled && resultSettled) {
|
|
1888
|
+
resolve(settledResults ?? []);
|
|
1889
|
+
}
|
|
1890
|
+
};
|
|
1891
|
+
const batchRequest = {
|
|
1892
|
+
toolCalls: dispatchRequests,
|
|
1893
|
+
userId: config.configurable?.user_id,
|
|
1894
|
+
agentId: this.agentId,
|
|
1895
|
+
configurable: config.configurable,
|
|
1896
|
+
metadata: config.metadata,
|
|
1897
|
+
resolve: (results) => {
|
|
1898
|
+
resultSettled = true;
|
|
1899
|
+
settledResults = results;
|
|
1900
|
+
maybeResolve();
|
|
1901
|
+
},
|
|
1902
|
+
reject,
|
|
1903
|
+
};
|
|
1904
|
+
void safeDispatchCustomEvent(GraphEvents.ON_TOOL_EXECUTE, batchRequest, config)
|
|
1905
|
+
.then(() => {
|
|
1906
|
+
dispatchSettled = true;
|
|
1907
|
+
maybeResolve();
|
|
1908
|
+
})
|
|
1909
|
+
.catch(reject);
|
|
1910
|
+
});
|
|
1911
|
+
const eagerResultsPromise = Promise.all(eagerExecutions.map(({ request, execution }) => this.resolveEagerEventExecution(request, execution))).then((results) => results.flat());
|
|
1912
|
+
const [eagerResults, dispatchedResults] = await Promise.all([
|
|
1913
|
+
eagerResultsPromise,
|
|
1914
|
+
dispatchPromise,
|
|
1915
|
+
]);
|
|
1916
|
+
const results = [
|
|
1917
|
+
...plan.rejectedResults,
|
|
1918
|
+
...eagerResults,
|
|
1919
|
+
...dispatchedResults,
|
|
1920
|
+
];
|
|
1859
1921
|
this.storeCodeSessionFromResults(results, requestMap);
|
|
1860
1922
|
const hasPostHook = this.hookRegistry?.hasHookFor('PostToolUse', runId) === true;
|
|
1861
1923
|
const hasFailureHook = this.hookRegistry?.hasHookFor('PostToolUseFailure', runId) === true;
|
|
@@ -1994,7 +2056,7 @@ class ToolNode extends RunnableCallable {
|
|
|
1994
2056
|
}),
|
|
1995
2057
|
});
|
|
1996
2058
|
}
|
|
1997
|
-
this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn);
|
|
2059
|
+
await this.dispatchStepCompleted(result.toolCallId, toolName, request?.args ?? {}, contentString, config, request?.turn);
|
|
1998
2060
|
postToolBatchEntryByCallId.set(result.toolCallId, {
|
|
1999
2061
|
toolName,
|
|
2000
2062
|
toolInput: request?.args ?? {},
|
|
@@ -2022,6 +2084,62 @@ class ToolNode extends RunnableCallable {
|
|
|
2022
2084
|
});
|
|
2023
2085
|
return { toolMessages, injected };
|
|
2024
2086
|
}
|
|
2087
|
+
canConsumeEagerEventExecution() {
|
|
2088
|
+
return (this.eventDrivenMode &&
|
|
2089
|
+
this.eagerEventToolExecution?.enabled === true &&
|
|
2090
|
+
this.hookRegistry == null &&
|
|
2091
|
+
this.humanInTheLoop?.enabled !== true &&
|
|
2092
|
+
this.toolOutputRegistry == null);
|
|
2093
|
+
}
|
|
2094
|
+
takeMatchingEagerEventExecution(request) {
|
|
2095
|
+
if (!this.canConsumeEagerEventExecution()) {
|
|
2096
|
+
return undefined;
|
|
2097
|
+
}
|
|
2098
|
+
const execution = this.eagerEventToolExecutions?.get(request.id);
|
|
2099
|
+
if (execution == null) {
|
|
2100
|
+
return undefined;
|
|
2101
|
+
}
|
|
2102
|
+
this.eagerEventToolExecutions?.delete(request.id);
|
|
2103
|
+
if (execution.toolName !== request.name ||
|
|
2104
|
+
!recordArgsEqual(execution.args, request.args) ||
|
|
2105
|
+
execution.request.turn !== request.turn) {
|
|
2106
|
+
return {
|
|
2107
|
+
toolCallId: request.id,
|
|
2108
|
+
toolName: request.name,
|
|
2109
|
+
args: request.args,
|
|
2110
|
+
request,
|
|
2111
|
+
promise: Promise.resolve({
|
|
2112
|
+
results: [
|
|
2113
|
+
{
|
|
2114
|
+
toolCallId: request.id,
|
|
2115
|
+
status: 'error',
|
|
2116
|
+
content: '',
|
|
2117
|
+
errorMessage: 'Tool call changed after eager execution started; refusing to re-run the tool to avoid duplicate side effects.',
|
|
2118
|
+
},
|
|
2119
|
+
],
|
|
2120
|
+
}),
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
return execution;
|
|
2124
|
+
}
|
|
2125
|
+
async resolveEagerEventExecution(request, execution) {
|
|
2126
|
+
const outcome = await execution.promise;
|
|
2127
|
+
if (outcome.error != null) {
|
|
2128
|
+
throw outcome.error;
|
|
2129
|
+
}
|
|
2130
|
+
const results = outcome.results.filter((result) => result.toolCallId === request.id);
|
|
2131
|
+
if (results.length > 0) {
|
|
2132
|
+
return results;
|
|
2133
|
+
}
|
|
2134
|
+
return [
|
|
2135
|
+
{
|
|
2136
|
+
toolCallId: request.id,
|
|
2137
|
+
status: 'error',
|
|
2138
|
+
content: '',
|
|
2139
|
+
errorMessage: 'Tool execution completed without a result for this tool call',
|
|
2140
|
+
},
|
|
2141
|
+
];
|
|
2142
|
+
}
|
|
2025
2143
|
/**
|
|
2026
2144
|
* Fires the `PostToolBatch` hook (if registered) and appends the
|
|
2027
2145
|
* accumulated batch-level `additionalContext` strings to `injected`
|
|
@@ -2087,7 +2205,7 @@ class ToolNode extends RunnableCallable {
|
|
|
2087
2205
|
}));
|
|
2088
2206
|
}
|
|
2089
2207
|
}
|
|
2090
|
-
dispatchStepCompleted(toolCallId, toolName, args, output, config, turn) {
|
|
2208
|
+
async dispatchStepCompleted(toolCallId, toolName, args, output, config, turn) {
|
|
2091
2209
|
const stepId = this.toolCallStepIds?.get(toolCallId) ?? '';
|
|
2092
2210
|
if (!stepId) {
|
|
2093
2211
|
// eslint-disable-next-line no-console
|
|
@@ -2095,7 +2213,7 @@ class ToolNode extends RunnableCallable {
|
|
|
2095
2213
|
'This indicates a race between the stream consumer and graph execution. ' +
|
|
2096
2214
|
`Map size: ${this.toolCallStepIds?.size ?? 0}`);
|
|
2097
2215
|
}
|
|
2098
|
-
safeDispatchCustomEvent(GraphEvents.ON_RUN_STEP_COMPLETED, {
|
|
2216
|
+
await safeDispatchCustomEvent(GraphEvents.ON_RUN_STEP_COMPLETED, {
|
|
2099
2217
|
result: {
|
|
2100
2218
|
id: stepId,
|
|
2101
2219
|
index: turn ?? this.toolUsageCount.get(toolName) ?? 0,
|
|
@@ -2214,7 +2332,7 @@ class ToolNode extends RunnableCallable {
|
|
|
2214
2332
|
}),
|
|
2215
2333
|
]
|
|
2216
2334
|
: [sendOutput];
|
|
2217
|
-
this.handleRunToolCompletions([input.lg_tool_call],
|
|
2335
|
+
await this.handleRunToolCompletions([input.lg_tool_call],
|
|
2218
2336
|
// Pass only the tool output to completion handling; the
|
|
2219
2337
|
// HumanMessage isn't a tool result.
|
|
2220
2338
|
[sendOutput], config, resolvedArgsByCallId);
|
|
@@ -2338,7 +2456,7 @@ class ToolNode extends RunnableCallable {
|
|
|
2338
2456
|
})))
|
|
2339
2457
|
: [];
|
|
2340
2458
|
if (directCalls.length > 0 && directOutputs.length > 0) {
|
|
2341
|
-
this.handleRunToolCompletions(directCalls, directOutputs, config, resolvedArgsByCallId);
|
|
2459
|
+
await this.handleRunToolCompletions(directCalls, directOutputs, config, resolvedArgsByCallId);
|
|
2342
2460
|
}
|
|
2343
2461
|
const eventResult = eventCalls.length > 0
|
|
2344
2462
|
? await this.dispatchToolEvents(eventCalls, config, {
|
|
@@ -2386,7 +2504,7 @@ class ToolNode extends RunnableCallable {
|
|
|
2386
2504
|
preBatchSnapshot,
|
|
2387
2505
|
additionalContextsSink: directAdditionalContexts,
|
|
2388
2506
|
})));
|
|
2389
|
-
this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId);
|
|
2507
|
+
await this.handleRunToolCompletions(filteredCalls, toolOutputs, config, resolvedArgsByCallId);
|
|
2390
2508
|
// Append accumulated additionalContexts as a single
|
|
2391
2509
|
// HumanMessage so the next model turn sees them. Codex P2 #39.
|
|
2392
2510
|
outputs =
|