@librechat/agents 3.9.7 → 3.9.8
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/common/enum.cjs +1 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +4 -3
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/instrumentation.cjs +3 -3
- package/dist/cjs/langfuse.cjs +3 -3
- package/dist/cjs/langfuseRuntimeScope.cjs +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +2 -2
- package/dist/cjs/llm/invoke.cjs +1 -1
- package/dist/cjs/run.cjs +4 -3
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +7 -4
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +1 -1
- package/dist/cjs/tools/ToolNode.cjs +20 -6
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +2 -2
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +22 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/events.cjs +20 -2
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +1 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +4 -3
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/instrumentation.mjs +3 -3
- package/dist/esm/langfuse.mjs +3 -3
- package/dist/esm/langfuseRuntimeScope.mjs +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +2 -2
- package/dist/esm/llm/invoke.mjs +1 -1
- package/dist/esm/run.mjs +4 -3
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +11 -7
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +1 -1
- package/dist/esm/tools/ToolNode.mjs +24 -9
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +2 -2
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +22 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/events.mjs +20 -3
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +2 -2
- package/dist/types/tools/handlers.d.ts +2 -1
- package/dist/types/types/graph.d.ts +3 -3
- package/dist/types/types/stream.d.ts +24 -0
- package/dist/types/types/tools.d.ts +2 -0
- package/dist/types/utils/events.d.ts +4 -0
- package/package.json +1 -1
- package/src/common/enum.ts +2 -0
- package/src/graphs/Graph.ts +5 -2
- package/src/run.ts +9 -7
- package/src/stream.ts +12 -3
- package/src/tools/ToolNode.ts +32 -6
- package/src/tools/handlers.ts +4 -1
- package/src/tools/subagent/SubagentExecutor.ts +24 -0
- package/src/types/graph.ts +3 -0
- package/src/types/stream.ts +21 -0
- package/src/types/tools.ts +2 -0
- package/src/utils/events.ts +21 -2
|
@@ -10,9 +10,14 @@ async function safeDispatchCustomEvent(event, payload, config) {
|
|
|
10
10
|
payload = {
|
|
11
11
|
...request,
|
|
12
12
|
resolve: (results) => {
|
|
13
|
-
|
|
13
|
+
const receivedAt = Date.now();
|
|
14
|
+
const stamped = results.map((result) => ({
|
|
15
|
+
...result,
|
|
16
|
+
received_at: receivedAt
|
|
17
|
+
}));
|
|
18
|
+
traceHostToolResults(request, results, config).then(() => request.resolve(stamped), () => {
|
|
14
19
|
console.warn("Failed to record host tool execution metadata");
|
|
15
|
-
request.resolve(
|
|
20
|
+
request.resolve(stamped);
|
|
16
21
|
});
|
|
17
22
|
}
|
|
18
23
|
};
|
|
@@ -25,6 +30,18 @@ async function safeDispatchCustomEvent(event, payload, config) {
|
|
|
25
30
|
return false;
|
|
26
31
|
}
|
|
27
32
|
}
|
|
33
|
+
function createToolCallsDispatchedEvent(config, calls) {
|
|
34
|
+
const runId = config.configurable?.run_id;
|
|
35
|
+
return {
|
|
36
|
+
dispatched_at: Date.now(),
|
|
37
|
+
...typeof runId === "string" ? { runId } : {},
|
|
38
|
+
toolCalls: calls.map(({ id, name, stepId }) => ({
|
|
39
|
+
id,
|
|
40
|
+
name,
|
|
41
|
+
stepId
|
|
42
|
+
}))
|
|
43
|
+
};
|
|
44
|
+
}
|
|
28
45
|
function emitAgentLog(config, level, scope, message, data, meta, options) {
|
|
29
46
|
if (!config) return;
|
|
30
47
|
if (level === "debug" && !(options?.force ?? false) && process.env.AGENT_DEBUG_LOGGING !== "true") return;
|
|
@@ -37,6 +54,6 @@ function emitAgentLog(config, level, scope, message, data, meta, options) {
|
|
|
37
54
|
}, config);
|
|
38
55
|
}
|
|
39
56
|
//#endregion
|
|
40
|
-
export { emitAgentLog, safeDispatchCustomEvent };
|
|
57
|
+
export { createToolCallsDispatchedEvent, emitAgentLog, safeDispatchCustomEvent };
|
|
41
58
|
|
|
42
59
|
//# sourceMappingURL=events.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.mjs","names":[],"sources":["../../../src/utils/events.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"events.mjs","names":[],"sources":["../../../src/utils/events.ts"],"mappings":";;;;;AAcA,eAAsB,wBACpB,OACA,SACA,QACyB;CACzB,IAAI;EACF,IAAI,UAAA,mBAAuC;GACzC,MAAM,UAAU;GAChB,UAAU;IACR,GAAG;IACH,UACE,YACS;KACT,MAAM,aAAa,KAAK,IAAI;KAC5B,MAAM,UAAU,QAAQ,KAAK,YAAY;MAAE,GAAG;MAAQ,aAAa;KAAW,EAAE;KAChF,qBAA0B,SAAS,SAAS,MAAM,CAAC,CAAC,WAC5C,QAAQ,QAAQ,OAAO,SACvB;MACJ,QAAQ,KAAK,+CAA+C;MAC5D,QAAQ,QAAQ,OAAO;KACzB,CACF;IACF;GACF;EACF;EACA,MAAM,oBAAoB,OAAO,SAAS,MAAM;EAChD,OAAO;CACT,SAAS,GAAG;EAEV,IACE,aAAa,SACb,EAAE,QAAQ,SAAS,2BAA2B,KAC9C,EAAE,QAAQ,SAAS,sBAAsB,GAKzC,OAAO;EAGT,QAAQ,MAAM,mCAAmC,CAAC;EAClD,OAAO;CACT;AACF;AAGA,SAAgB,+BACd,QACA,OAI0B;CAC1B,MAAM,QAAQ,OAAO,cAAc;CACnC,OAAO;EACL,eAAe,KAAK,IAAI;EACxB,GAAI,OAAO,UAAU,WAAW,EAAE,MAAM,IAAI,CAAC;EAC7C,WAAW,MAAM,KAAK,EAAE,IAAI,MAAM,cAAc;GAAE;GAAI;GAAM;EAAO,EAAE;CACvE;AACF;AAQA,SAAgB,aACd,QACA,OACA,OACA,SACA,MACA,MACA,SACM;CACN,IAAI,CAAC,QAAQ;CACb,IACE,UAAU,WACV,EAAE,SAAS,SAAS,UACpB,QAAQ,IAAI,wBAAwB,QAEpC;CACF,wBAAK,gBAEH;EAAE;EAAO;EAAO;EAAS;EAAM,GAAG;CAAK,GACvC,MACF;AACF"}
|
|
@@ -23,6 +23,8 @@ export declare enum GraphEvents {
|
|
|
23
23
|
ON_MESSAGE_DELTA = "on_message_delta",
|
|
24
24
|
/** [Custom] Reasoning Delta events for messages */
|
|
25
25
|
ON_REASONING_DELTA = "on_reasoning_delta",
|
|
26
|
+
/** [Custom] Per-call handoff boundary between streamed tool preparation and execution */
|
|
27
|
+
ON_TOOL_CALLS_DISPATCHED = "on_tool_calls_dispatched",
|
|
26
28
|
/** [Custom] Request to execute tools - dispatched by ToolNode, handled by host */
|
|
27
29
|
ON_TOOL_EXECUTE = "on_tool_execute",
|
|
28
30
|
/** [Custom] Emitted when the summarize node begins generating a summary */
|
|
@@ -38,7 +38,7 @@ export declare abstract class Graph<T extends t.BaseGraphState = t.BaseGraphStat
|
|
|
38
38
|
abstract getStepIdByKey(stepKey: string, index?: number): string;
|
|
39
39
|
abstract getRunStep(stepId: string): t.RunStep | undefined;
|
|
40
40
|
abstract dispatchRunStep(stepKey: string, stepDetails: t.StepDetails, metadata?: Record<string, unknown>): Promise<string>;
|
|
41
|
-
abstract dispatchRunStepDelta(id: string, delta: t.ToolCallDelta, metadata?: Record<string, unknown
|
|
41
|
+
abstract dispatchRunStepDelta(id: string, delta: t.ToolCallDelta, metadata?: Record<string, unknown>, observedAt?: number): Promise<void>;
|
|
42
42
|
abstract dispatchMessageDelta(id: string, delta: t.MessageDelta, metadata?: Record<string, unknown>): Promise<void>;
|
|
43
43
|
abstract dispatchReasoningDelta(stepId: string, delta: t.ReasoningDelta, metadata?: Record<string, unknown>): Promise<void>;
|
|
44
44
|
abstract createCallModel(agentId?: string, currentModel?: t.ChatModel): (state: t.AgentSubgraphState, config?: RunnableConfig) => Promise<Partial<t.AgentSubgraphState>>;
|
|
@@ -761,7 +761,7 @@ export declare class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode>
|
|
|
761
761
|
* Kept for backward compatibility
|
|
762
762
|
*/
|
|
763
763
|
handleToolCallError(data: t.ToolErrorData, metadata?: Record<string, unknown>): Promise<boolean>;
|
|
764
|
-
dispatchRunStepDelta(id: string, delta: t.ToolCallDelta, metadata?: Record<string, unknown
|
|
764
|
+
dispatchRunStepDelta(id: string, delta: t.ToolCallDelta, metadata?: Record<string, unknown>, observedAt?: number): Promise<void>;
|
|
765
765
|
dispatchMessageDelta(id: string, delta: t.MessageDelta, metadata?: Record<string, unknown>): Promise<void>;
|
|
766
766
|
dispatchReasoningDelta: (stepId: string, delta: t.ReasoningDelta, metadata?: Record<string, unknown>) => Promise<void>;
|
|
767
767
|
}
|
|
@@ -2,11 +2,12 @@ import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
|
2
2
|
import type { Graph, MultiAgentGraph, StandardGraph } from '@/graphs';
|
|
3
3
|
import type { AgentContext } from '@/agents/AgentContext';
|
|
4
4
|
import type * as t from '@/types';
|
|
5
|
-
export declare function handleToolCallChunks({ graph, stepKey, toolCallChunks, metadata, }: {
|
|
5
|
+
export declare function handleToolCallChunks({ graph, stepKey, toolCallChunks, metadata, observedAt, }: {
|
|
6
6
|
graph: StandardGraph | MultiAgentGraph;
|
|
7
7
|
stepKey: string;
|
|
8
8
|
toolCallChunks: ToolCallChunk[];
|
|
9
9
|
metadata?: Record<string, unknown>;
|
|
10
|
+
observedAt?: number;
|
|
10
11
|
}): Promise<void>;
|
|
11
12
|
export declare const handleToolCalls: (toolCalls?: ToolCall[], metadata?: Record<string, unknown>, graph?: Graph | StandardGraph | MultiAgentGraph) => Promise<void>;
|
|
12
13
|
export declare const toolResultTypes: Set<string>;
|
|
@@ -4,7 +4,7 @@ import type { START, StateGraph, StateGraphArgs } from '@langchain/langgraph';
|
|
|
4
4
|
import type { RunnableConfig, Runnable } from '@langchain/core/runnables';
|
|
5
5
|
import type { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
6
6
|
import type { GoogleAIToolType } from '@langchain/google-common';
|
|
7
|
-
import type { RunStep, ModelResponseEvent, ModelToolsClaimedEvent, RunStepDeltaEvent, RunStepResumeState, RunStepClosedEvent, MessageDeltaEvent, ReasoningDeltaEvent } from '@/types/stream';
|
|
7
|
+
import type { RunStep, ModelResponseEvent, ModelToolsClaimedEvent, RunStepDeltaEvent, RunStepResumeState, RunStepClosedEvent, ToolCallsDispatchedEvent, MessageDeltaEvent, ReasoningDeltaEvent } from '@/types/stream';
|
|
8
8
|
import type { SummarizationNodeInput, SummarizeCompleteEvent, CompactionSemanticIndex, SummarizationConfig, SummarizeStartEvent, SummarizeDeltaEvent } from '@/types/summarize';
|
|
9
9
|
import type { ToolMap, ToolSessionMap, ToolEndEvent, GenericTool, LCTool, ToolExecutionConfig, ToolExecuteBatchRequest } from '@/types/tools';
|
|
10
10
|
import type { TokenCounter, StreamLimits, StreamPreemption, TokenBudgetBreakdown } from '@/types/run';
|
|
@@ -139,7 +139,7 @@ export interface FadingTier {
|
|
|
139
139
|
/** Latched fading tiers keyed by agent ID. */
|
|
140
140
|
export type FadingTiers = Record<string, FadingTier>;
|
|
141
141
|
export interface EventHandler {
|
|
142
|
-
handle(event: string, data: StreamEventData | ModelEndData | ModelResponseEvent | ModelToolsClaimedEvent | RunStep | RunStepDeltaEvent | RunStepClosedEvent | MessageDeltaEvent | ReasoningDeltaEvent | SummarizeStartEvent | SummarizeDeltaEvent | SummarizeCompleteEvent | SubagentUpdateEvent | AgentLogEvent | ContextUsageEvent | ToolExecuteBatchRequest | {
|
|
142
|
+
handle(event: string, data: StreamEventData | ModelEndData | ModelResponseEvent | ModelToolsClaimedEvent | RunStep | RunStepDeltaEvent | RunStepClosedEvent | ToolCallsDispatchedEvent | MessageDeltaEvent | ReasoningDeltaEvent | SummarizeStartEvent | SummarizeDeltaEvent | SummarizeCompleteEvent | SubagentUpdateEvent | AgentLogEvent | ContextUsageEvent | ToolExecuteBatchRequest | {
|
|
143
143
|
result: ToolEndEvent;
|
|
144
144
|
}, metadata?: Record<string, unknown>, graph?: StandardGraph | MultiAgentGraph): void | Promise<void>;
|
|
145
145
|
}
|
|
@@ -531,7 +531,7 @@ export type ExecutableSubagentConfig = ResolvedSubagentConfig | LazySingleAgentS
|
|
|
531
531
|
/** Graph-aware config accepted by the executor. Graph configs stay eager. */
|
|
532
532
|
export type ExecutableSubagentConfigEntry = ExecutableSubagentConfig | GraphSubagentConfig;
|
|
533
533
|
/** Lifecycle phase carried on {@link SubagentUpdateEvent}. */
|
|
534
|
-
export type SubagentUpdatePhase = 'start' | 'run_step' | 'run_step_delta' | 'run_step_completed' | 'run_step_closed' | 'message_delta' | 'reasoning_delta' | 'control' | 'stop' | 'error';
|
|
534
|
+
export type SubagentUpdatePhase = 'start' | 'run_step' | 'run_step_delta' | 'tool_calls_dispatched' | 'run_step_completed' | 'run_step_closed' | 'message_delta' | 'reasoning_delta' | 'control' | 'stop' | 'error';
|
|
535
535
|
export interface SubagentAncestryEntry {
|
|
536
536
|
readonly subagentRunId: string;
|
|
537
537
|
readonly subagentType: string;
|
|
@@ -127,6 +127,8 @@ export interface RunStepDeltaEvent {
|
|
|
127
127
|
* The delta containing the fields that have changed on the run step.
|
|
128
128
|
*/
|
|
129
129
|
delta: ToolCallDelta;
|
|
130
|
+
/** Epoch ms when the SDK received this tool-call fragment, before step dispatch. */
|
|
131
|
+
observed_at?: number;
|
|
130
132
|
}
|
|
131
133
|
/**
|
|
132
134
|
* Terminal signal for a run step, emitted exactly once per step when it
|
|
@@ -218,6 +220,28 @@ export type ToolCallCompleted = {
|
|
|
218
220
|
type: 'tool_call';
|
|
219
221
|
tool_call: ProcessedToolCall;
|
|
220
222
|
};
|
|
223
|
+
/**
|
|
224
|
+
* A tool call has crossed from model-authored argument preparation into SDK
|
|
225
|
+
* execution. Emitted immediately before host dispatch or direct invocation;
|
|
226
|
+
* it does not claim the host has begun its own work. Paired with the tool
|
|
227
|
+
* completion `completed_at`, the stamp measures SDK handoff-to-result time,
|
|
228
|
+
* not MCP or database execution time. The earliest `observed_at` tool-call
|
|
229
|
+
* delta for a call bounds its argument preparation time; consumers correlate
|
|
230
|
+
* deltas by step/index until an ID is known. For a call without streamed
|
|
231
|
+
* fragments, the preparation start is unknown. Never infer a missing start.
|
|
232
|
+
* An eager call may dispatch before it has a step ID, and this observational
|
|
233
|
+
* event may arrive after completion: correlate by tool call ID and timestamps,
|
|
234
|
+
* not arrival order.
|
|
235
|
+
*/
|
|
236
|
+
export type ToolCallsDispatchedEvent = {
|
|
237
|
+
dispatched_at: number;
|
|
238
|
+
runId?: string;
|
|
239
|
+
toolCalls: Array<{
|
|
240
|
+
id: string;
|
|
241
|
+
name: string;
|
|
242
|
+
stepId?: string;
|
|
243
|
+
}>;
|
|
244
|
+
};
|
|
221
245
|
export type ToolCompleteEvent = ToolCallCompleted & {
|
|
222
246
|
/** The Step Id of the Tool Call */
|
|
223
247
|
id: string;
|
|
@@ -644,6 +644,8 @@ export type OutcomePatch = {
|
|
|
644
644
|
export type ToolExecuteResult = {
|
|
645
645
|
/** Matches ToolCallRequest.id */
|
|
646
646
|
toolCallId: string;
|
|
647
|
+
/** SDK-stamped result receipt time; never host-supplied or inferred from handler cleanup. */
|
|
648
|
+
received_at?: number;
|
|
647
649
|
/** Tool output content */
|
|
648
650
|
content: string | unknown[];
|
|
649
651
|
/** Optional artifact (for content_and_artifact format) */
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
2
|
+
import type { ToolCallsDispatchedEvent } from '@/types/stream';
|
|
3
|
+
import type { ToolExecuteBatchRequest } from '@/types/tools';
|
|
2
4
|
import type { AgentLogEvent } from '@/types/graph';
|
|
3
5
|
/**
|
|
4
6
|
* Safely dispatches a custom event and properly awaits it to avoid
|
|
5
7
|
* race conditions where events are dispatched after run cleanup.
|
|
6
8
|
*/
|
|
7
9
|
export declare function safeDispatchCustomEvent(event: string, payload: unknown, config?: RunnableConfig): Promise<boolean | void>;
|
|
10
|
+
/** Builds an argument-free snapshot once per handoff, not once per result. */
|
|
11
|
+
export declare function createToolCallsDispatchedEvent(config: RunnableConfig, calls: readonly Pick<ToolExecuteBatchRequest['toolCalls'][number], 'id' | 'name' | 'stepId'>[]): ToolCallsDispatchedEvent;
|
|
8
12
|
/**
|
|
9
13
|
* Fire-and-forget diagnostic log event.
|
|
10
14
|
* Debug-level logs are gated behind AGENT_DEBUG_LOGGING=true to avoid
|
package/package.json
CHANGED
package/src/common/enum.ts
CHANGED
|
@@ -26,6 +26,8 @@ export enum GraphEvents {
|
|
|
26
26
|
ON_MESSAGE_DELTA = 'on_message_delta',
|
|
27
27
|
/** [Custom] Reasoning Delta events for messages */
|
|
28
28
|
ON_REASONING_DELTA = 'on_reasoning_delta',
|
|
29
|
+
/** [Custom] Per-call handoff boundary between streamed tool preparation and execution */
|
|
30
|
+
ON_TOOL_CALLS_DISPATCHED = 'on_tool_calls_dispatched',
|
|
29
31
|
/** [Custom] Request to execute tools - dispatched by ToolNode, handled by host */
|
|
30
32
|
ON_TOOL_EXECUTE = 'on_tool_execute',
|
|
31
33
|
/** [Custom] Emitted when the summarize node begins generating a summary */
|
package/src/graphs/Graph.ts
CHANGED
|
@@ -766,7 +766,8 @@ export abstract class Graph<
|
|
|
766
766
|
abstract dispatchRunStepDelta(
|
|
767
767
|
id: string,
|
|
768
768
|
delta: t.ToolCallDelta,
|
|
769
|
-
metadata?: Record<string, unknown
|
|
769
|
+
metadata?: Record<string, unknown>,
|
|
770
|
+
observedAt?: number
|
|
770
771
|
): Promise<void>;
|
|
771
772
|
abstract dispatchMessageDelta(
|
|
772
773
|
id: string,
|
|
@@ -6130,7 +6131,8 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
6130
6131
|
async dispatchRunStepDelta(
|
|
6131
6132
|
id: string,
|
|
6132
6133
|
delta: t.ToolCallDelta,
|
|
6133
|
-
metadata?: Record<string, unknown
|
|
6134
|
+
metadata?: Record<string, unknown>,
|
|
6135
|
+
observedAt?: number
|
|
6134
6136
|
): Promise<void> {
|
|
6135
6137
|
if (!this.config) {
|
|
6136
6138
|
throw new Error('No config provided');
|
|
@@ -6140,6 +6142,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
6140
6142
|
const runStepDelta: t.RunStepDeltaEvent = {
|
|
6141
6143
|
id,
|
|
6142
6144
|
delta,
|
|
6145
|
+
...(observedAt != null && { observed_at: observedAt }),
|
|
6143
6146
|
};
|
|
6144
6147
|
const handler = this.handlerRegistry?.getHandler(
|
|
6145
6148
|
GraphEvents.ON_RUN_STEP_DELTA
|
package/src/run.ts
CHANGED
|
@@ -37,13 +37,6 @@ import {
|
|
|
37
37
|
DEFAULT_MAX_STOP_CONTINUATIONS,
|
|
38
38
|
DEFAULT_RECURSION_LIMIT,
|
|
39
39
|
} from '@/common';
|
|
40
|
-
import {
|
|
41
|
-
ACTIVITY_PHASE_LABEL_PROMPT,
|
|
42
|
-
ACTIVITY_LABEL_PROMPT,
|
|
43
|
-
buildActivityLabelPrompt,
|
|
44
|
-
buildActivityPhaseLabelPrompt,
|
|
45
|
-
normalizeActivityPhaseLabel,
|
|
46
|
-
} from '@/prompts/activityLabel';
|
|
47
40
|
import {
|
|
48
41
|
createLangfuseTraceMetadata,
|
|
49
42
|
createLangfuseHandler,
|
|
@@ -53,6 +46,13 @@ import {
|
|
|
53
46
|
isLangfuseCallbackHandler,
|
|
54
47
|
withLangfuseAttributes,
|
|
55
48
|
} from '@/langfuse';
|
|
49
|
+
import {
|
|
50
|
+
ACTIVITY_PHASE_LABEL_PROMPT,
|
|
51
|
+
ACTIVITY_LABEL_PROMPT,
|
|
52
|
+
buildActivityLabelPrompt,
|
|
53
|
+
buildActivityPhaseLabelPrompt,
|
|
54
|
+
normalizeActivityPhaseLabel,
|
|
55
|
+
} from '@/prompts/activityLabel';
|
|
56
56
|
import {
|
|
57
57
|
requireValidSubagentResumeManifest,
|
|
58
58
|
SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY,
|
|
@@ -204,6 +204,7 @@ const CUSTOM_GRAPH_EVENTS = new Set<string>([
|
|
|
204
204
|
GraphEvents.ON_RUN_STEP_DELTA,
|
|
205
205
|
GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
206
206
|
GraphEvents.ON_RUN_STEP_CLOSED,
|
|
207
|
+
GraphEvents.ON_TOOL_CALLS_DISPATCHED,
|
|
207
208
|
GraphEvents.ON_MESSAGE_DELTA,
|
|
208
209
|
GraphEvents.ON_REASONING_DELTA,
|
|
209
210
|
GraphEvents.ON_TOOL_EXECUTE,
|
|
@@ -1095,6 +1096,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1095
1096
|
| t.RunStep
|
|
1096
1097
|
| t.RunStepDeltaEvent
|
|
1097
1098
|
| t.RunStepClosedEvent
|
|
1099
|
+
| t.ToolCallsDispatchedEvent
|
|
1098
1100
|
| t.MessageDeltaEvent
|
|
1099
1101
|
| t.ReasoningDeltaEvent
|
|
1100
1102
|
| { result: t.ToolEndEvent },
|
package/src/stream.ts
CHANGED
|
@@ -55,13 +55,13 @@ import {
|
|
|
55
55
|
calculateMaxToolResultChars,
|
|
56
56
|
truncateToolResultContent,
|
|
57
57
|
} from '@/utils/truncation';
|
|
58
|
+
import { createToolCallsDispatchedEvent, safeDispatchCustomEvent } from '@/utils/events';
|
|
58
59
|
import { resolveToolOutcome, outcomeFieldsFromResult } from '@/tools/intentArg';
|
|
59
|
-
import { formatToolErrorContent } from '@/tools/toolErrorContent';
|
|
60
60
|
import { snapshotValidatedModelChunk } from '@/graphs/acceptedModelResponse';
|
|
61
61
|
import { TOOL_OUTPUT_REF_PATTERN } from '@/tools/toolOutputReferences';
|
|
62
|
+
import { formatToolErrorContent } from '@/tools/toolErrorContent';
|
|
62
63
|
import { PreparedSubagentError } from '@/tools/preparedSubagents';
|
|
63
64
|
import { isReasoningContentBlock } from '@/messages/core';
|
|
64
|
-
import { safeDispatchCustomEvent } from '@/utils/events';
|
|
65
65
|
import { composeAbortSignals } from '@/utils/misc';
|
|
66
66
|
import { isGoogleLike } from '@/utils/llm';
|
|
67
67
|
import { getMessageId } from '@/messages';
|
|
@@ -871,6 +871,13 @@ function startEagerToolExecutions(args: {
|
|
|
871
871
|
reject,
|
|
872
872
|
};
|
|
873
873
|
|
|
874
|
+
if (graph.config != null) {
|
|
875
|
+
void safeDispatchCustomEvent(
|
|
876
|
+
GraphEvents.ON_TOOL_CALLS_DISPATCHED,
|
|
877
|
+
createToolCallsDispatchedEvent(graph.config, batchRequest.toolCalls),
|
|
878
|
+
graph.config
|
|
879
|
+
);
|
|
880
|
+
}
|
|
874
881
|
void safeDispatchCustomEvent(
|
|
875
882
|
GraphEvents.ON_TOOL_EXECUTE,
|
|
876
883
|
batchRequest,
|
|
@@ -980,7 +987,7 @@ async function dispatchEagerToolCompletions(args: {
|
|
|
980
987
|
progress: 1,
|
|
981
988
|
...(outcome != null && { outcome }),
|
|
982
989
|
} as t.ProcessedToolCall,
|
|
983
|
-
completed_at: Date.now(),
|
|
990
|
+
completed_at: result.received_at ?? Date.now(),
|
|
984
991
|
},
|
|
985
992
|
},
|
|
986
993
|
graph.config
|
|
@@ -1701,6 +1708,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
1701
1708
|
// Callback delivery can beat the producer's iterator. Validate before
|
|
1702
1709
|
// accounting, run steps, or eager dispatch reads raw tool descriptors.
|
|
1703
1710
|
chunk = snapshotValidatedModelChunk(chunk as AIMessageChunk);
|
|
1711
|
+
const observedAt = Date.now();
|
|
1704
1712
|
|
|
1705
1713
|
/**
|
|
1706
1714
|
* Enforced before every content-specific early return below
|
|
@@ -1923,6 +1931,7 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
1923
1931
|
stepKey,
|
|
1924
1932
|
toolCallChunks: chunk.tool_call_chunks,
|
|
1925
1933
|
metadata,
|
|
1934
|
+
observedAt,
|
|
1926
1935
|
});
|
|
1927
1936
|
if (canStreamEager) {
|
|
1928
1937
|
if (runScopeInvalidated()) {
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -170,7 +170,7 @@ function stripHostExecutionConfig(
|
|
|
170
170
|
}
|
|
171
171
|
import { convertInjectedMessages } from '@/messages/injected';
|
|
172
172
|
import { stampSyntheticProviderMessage } from '@/messages/provenance';
|
|
173
|
-
import { safeDispatchCustomEvent } from '@/utils/events';
|
|
173
|
+
import { createToolCallsDispatchedEvent, safeDispatchCustomEvent } from '@/utils/events';
|
|
174
174
|
import { RunnableCallable, composeAbortSignals } from '@/utils';
|
|
175
175
|
import {
|
|
176
176
|
executeHooks,
|
|
@@ -1672,6 +1672,15 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1672
1672
|
};
|
|
1673
1673
|
this.throwIfBreakerTripped(config);
|
|
1674
1674
|
config.signal?.throwIfAborted();
|
|
1675
|
+
if (call.id != null && call.id !== '') {
|
|
1676
|
+
void safeDispatchCustomEvent(
|
|
1677
|
+
GraphEvents.ON_TOOL_CALLS_DISPATCHED,
|
|
1678
|
+
createToolCallsDispatchedEvent(config, [
|
|
1679
|
+
{ id: call.id, name: call.name, stepId: this.toolCallStepIds?.get(call.id) },
|
|
1680
|
+
]),
|
|
1681
|
+
config
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1675
1684
|
return tool.invoke(invokeParams, runtime);
|
|
1676
1685
|
}
|
|
1677
1686
|
|
|
@@ -4334,7 +4343,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4334
4343
|
const dispatchRequestById = new Map(
|
|
4335
4344
|
dispatchRequests.map((request) => [request.id, request])
|
|
4336
4345
|
);
|
|
4346
|
+
const receivedAtById = new Map<string, number>();
|
|
4337
4347
|
const onResult = (result: t.ToolExecuteResult): void => {
|
|
4348
|
+
const receivedAt = Date.now();
|
|
4338
4349
|
/** Runtime-honest widening: hosts may omit the id despite the
|
|
4339
4350
|
* field type. */
|
|
4340
4351
|
const resultToolCallId = result.toolCallId as string | undefined;
|
|
@@ -4349,8 +4360,9 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4349
4360
|
return;
|
|
4350
4361
|
}
|
|
4351
4362
|
earlyCompletionDispatchedIds.add(result.toolCallId);
|
|
4363
|
+
receivedAtById.set(result.toolCallId, receivedAt);
|
|
4352
4364
|
earlyCompletionDispatches.push(
|
|
4353
|
-
this.dispatchEarlyToolCompletion(result, request, config).then(
|
|
4365
|
+
this.dispatchEarlyToolCompletion({ ...result, received_at: receivedAt }, request, config).then(
|
|
4354
4366
|
(dispatched) => {
|
|
4355
4367
|
if (!dispatched) {
|
|
4356
4368
|
earlyCompletionDispatchedIds.delete(result.toolCallId);
|
|
@@ -4402,6 +4414,12 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4402
4414
|
| undefined,
|
|
4403
4415
|
signal: config.signal,
|
|
4404
4416
|
resolve: (results): void => {
|
|
4417
|
+
const receivedAt = Date.now();
|
|
4418
|
+
for (const result of results) {
|
|
4419
|
+
if (dispatchRequestById.has(result.toolCallId)) {
|
|
4420
|
+
receivedAtById.set(result.toolCallId, result.received_at ?? receivedAt);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4405
4423
|
resultSettled = true;
|
|
4406
4424
|
settledResults = results;
|
|
4407
4425
|
maybeResolve();
|
|
@@ -4410,6 +4428,11 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4410
4428
|
...(canEmitEarlyCompletions && { onResult }),
|
|
4411
4429
|
};
|
|
4412
4430
|
|
|
4431
|
+
void safeDispatchCustomEvent(
|
|
4432
|
+
GraphEvents.ON_TOOL_CALLS_DISPATCHED,
|
|
4433
|
+
createToolCallsDispatchedEvent(config, dispatchRequests),
|
|
4434
|
+
config
|
|
4435
|
+
);
|
|
4413
4436
|
void safeDispatchCustomEvent(
|
|
4414
4437
|
GraphEvents.ON_TOOL_EXECUTE,
|
|
4415
4438
|
batchRequest,
|
|
@@ -4695,7 +4718,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4695
4718
|
request?.turn,
|
|
4696
4719
|
resolveToolOutcome(request?.args, outcomeFieldsFromResult(result), {
|
|
4697
4720
|
isError: result.status === 'error',
|
|
4698
|
-
})
|
|
4721
|
+
}),
|
|
4722
|
+
receivedAtById.get(result.toolCallId) ?? result.received_at
|
|
4699
4723
|
);
|
|
4700
4724
|
}
|
|
4701
4725
|
|
|
@@ -4997,7 +5021,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
4997
5021
|
output: string,
|
|
4998
5022
|
config: RunnableConfig,
|
|
4999
5023
|
turn?: number,
|
|
5000
|
-
outcome?: string
|
|
5024
|
+
outcome?: string,
|
|
5025
|
+
receivedAt?: number
|
|
5001
5026
|
): Promise<boolean> {
|
|
5002
5027
|
const stepId = this.toolCallStepIds?.get(toolCallId) ?? '';
|
|
5003
5028
|
if (!stepId) {
|
|
@@ -5024,7 +5049,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5024
5049
|
progress: 1,
|
|
5025
5050
|
...(outcome != null && { outcome }),
|
|
5026
5051
|
} as t.ProcessedToolCall,
|
|
5027
|
-
completed_at: Date.now(),
|
|
5052
|
+
completed_at: receivedAt ?? Date.now(),
|
|
5028
5053
|
},
|
|
5029
5054
|
},
|
|
5030
5055
|
config
|
|
@@ -5064,7 +5089,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
5064
5089
|
request.turn,
|
|
5065
5090
|
resolveToolOutcome(request.args, outcomeFieldsFromResult(result), {
|
|
5066
5091
|
isError: result.status === 'error',
|
|
5067
|
-
})
|
|
5092
|
+
}),
|
|
5093
|
+
result.received_at
|
|
5068
5094
|
);
|
|
5069
5095
|
}
|
|
5070
5096
|
|
package/src/tools/handlers.ts
CHANGED
|
@@ -26,11 +26,13 @@ export async function handleToolCallChunks({
|
|
|
26
26
|
stepKey,
|
|
27
27
|
toolCallChunks,
|
|
28
28
|
metadata,
|
|
29
|
+
observedAt = Date.now(),
|
|
29
30
|
}: {
|
|
30
31
|
graph: StandardGraph | MultiAgentGraph;
|
|
31
32
|
stepKey: string;
|
|
32
33
|
toolCallChunks: ToolCallChunk[];
|
|
33
34
|
metadata?: Record<string, unknown>;
|
|
35
|
+
observedAt?: number;
|
|
34
36
|
}): Promise<void> {
|
|
35
37
|
let prevStepId: string;
|
|
36
38
|
let prevRunStep: t.RunStep | undefined;
|
|
@@ -124,7 +126,8 @@ export async function handleToolCallChunks({
|
|
|
124
126
|
type: StepTypes.TOOL_CALLS,
|
|
125
127
|
tool_calls: toolCallChunks,
|
|
126
128
|
},
|
|
127
|
-
metadata
|
|
129
|
+
metadata,
|
|
130
|
+
observedAt
|
|
128
131
|
);
|
|
129
132
|
}
|
|
130
133
|
|
|
@@ -3463,6 +3463,10 @@ export class SubagentExecutor {
|
|
|
3463
3463
|
scheduleWrap(eventName, 'run_step', data, memberAgentId);
|
|
3464
3464
|
return;
|
|
3465
3465
|
}
|
|
3466
|
+
if (eventName === GraphEvents.ON_TOOL_CALLS_DISPATCHED) {
|
|
3467
|
+
scheduleWrap(eventName, 'tool_calls_dispatched', data, memberAgentId);
|
|
3468
|
+
return;
|
|
3469
|
+
}
|
|
3466
3470
|
if (eventName === GraphEvents.ON_RUN_STEP_DELTA) {
|
|
3467
3471
|
scheduleWrap(eventName, 'run_step_delta', data, memberAgentId);
|
|
3468
3472
|
return;
|
|
@@ -3816,6 +3820,26 @@ export function sanitizeForwardedSubagentUpdateData(
|
|
|
3816
3820
|
if (eventName === GraphEvents.ON_RUN_STEP_DELTA) {
|
|
3817
3821
|
return sanitizeRunStepDeltaUpdateData(data);
|
|
3818
3822
|
}
|
|
3823
|
+
if (eventName === GraphEvents.ON_TOOL_CALLS_DISPATCHED) {
|
|
3824
|
+
if (!isObjectLike(data)) return undefined;
|
|
3825
|
+
const event = data as { dispatched_at?: number; toolCalls?: unknown };
|
|
3826
|
+
if (!Array.isArray(event.toolCalls) || typeof event.dispatched_at !== 'number') {
|
|
3827
|
+
return undefined;
|
|
3828
|
+
}
|
|
3829
|
+
return {
|
|
3830
|
+
dispatched_at: event.dispatched_at,
|
|
3831
|
+
toolCalls: (event.toolCalls as unknown[]).flatMap((call) => {
|
|
3832
|
+
if (!isObjectLike(call)) return [];
|
|
3833
|
+
const entry = call as Record<string, unknown>;
|
|
3834
|
+
if (typeof entry.id !== 'string' || typeof entry.name !== 'string') return [];
|
|
3835
|
+
return [{
|
|
3836
|
+
id: entry.id,
|
|
3837
|
+
name: entry.name,
|
|
3838
|
+
...(typeof entry.stepId === 'string' ? { stepId: entry.stepId } : {}),
|
|
3839
|
+
}];
|
|
3840
|
+
}),
|
|
3841
|
+
};
|
|
3842
|
+
}
|
|
3819
3843
|
if (eventName === GraphEvents.ON_RUN_STEP_COMPLETED) {
|
|
3820
3844
|
return sanitizeRunStepCompletedUpdateData(data);
|
|
3821
3845
|
}
|
package/src/types/graph.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
RunStepDeltaEvent,
|
|
18
18
|
RunStepResumeState,
|
|
19
19
|
RunStepClosedEvent,
|
|
20
|
+
ToolCallsDispatchedEvent,
|
|
20
21
|
MessageDeltaEvent,
|
|
21
22
|
ReasoningDeltaEvent,
|
|
22
23
|
} from '@/types/stream';
|
|
@@ -209,6 +210,7 @@ export interface EventHandler {
|
|
|
209
210
|
| RunStep
|
|
210
211
|
| RunStepDeltaEvent
|
|
211
212
|
| RunStepClosedEvent
|
|
213
|
+
| ToolCallsDispatchedEvent
|
|
212
214
|
| MessageDeltaEvent
|
|
213
215
|
| ReasoningDeltaEvent
|
|
214
216
|
| SummarizeStartEvent
|
|
@@ -694,6 +696,7 @@ export type SubagentUpdatePhase =
|
|
|
694
696
|
| 'start'
|
|
695
697
|
| 'run_step'
|
|
696
698
|
| 'run_step_delta'
|
|
699
|
+
| 'tool_calls_dispatched'
|
|
697
700
|
| 'run_step_completed'
|
|
698
701
|
| 'run_step_closed'
|
|
699
702
|
| 'message_delta'
|
package/src/types/stream.ts
CHANGED
|
@@ -158,6 +158,8 @@ export interface RunStepDeltaEvent {
|
|
|
158
158
|
* The delta containing the fields that have changed on the run step.
|
|
159
159
|
*/
|
|
160
160
|
delta: ToolCallDelta;
|
|
161
|
+
/** Epoch ms when the SDK received this tool-call fragment, before step dispatch. */
|
|
162
|
+
observed_at?: number;
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
/**
|
|
@@ -264,6 +266,25 @@ export type ToolCallCompleted = {
|
|
|
264
266
|
tool_call: ProcessedToolCall;
|
|
265
267
|
};
|
|
266
268
|
|
|
269
|
+
/**
|
|
270
|
+
* A tool call has crossed from model-authored argument preparation into SDK
|
|
271
|
+
* execution. Emitted immediately before host dispatch or direct invocation;
|
|
272
|
+
* it does not claim the host has begun its own work. Paired with the tool
|
|
273
|
+
* completion `completed_at`, the stamp measures SDK handoff-to-result time,
|
|
274
|
+
* not MCP or database execution time. The earliest `observed_at` tool-call
|
|
275
|
+
* delta for a call bounds its argument preparation time; consumers correlate
|
|
276
|
+
* deltas by step/index until an ID is known. For a call without streamed
|
|
277
|
+
* fragments, the preparation start is unknown. Never infer a missing start.
|
|
278
|
+
* An eager call may dispatch before it has a step ID, and this observational
|
|
279
|
+
* event may arrive after completion: correlate by tool call ID and timestamps,
|
|
280
|
+
* not arrival order.
|
|
281
|
+
*/
|
|
282
|
+
export type ToolCallsDispatchedEvent = {
|
|
283
|
+
dispatched_at: number;
|
|
284
|
+
runId?: string;
|
|
285
|
+
toolCalls: Array<{ id: string; name: string; stepId?: string }>;
|
|
286
|
+
};
|
|
287
|
+
|
|
267
288
|
export type ToolCompleteEvent = ToolCallCompleted & {
|
|
268
289
|
/** The Step Id of the Tool Call */
|
|
269
290
|
id: string;
|
package/src/types/tools.ts
CHANGED
|
@@ -700,6 +700,8 @@ export type OutcomePatch = {
|
|
|
700
700
|
export type ToolExecuteResult = {
|
|
701
701
|
/** Matches ToolCallRequest.id */
|
|
702
702
|
toolCallId: string;
|
|
703
|
+
/** SDK-stamped result receipt time; never host-supplied or inferred from handler cleanup. */
|
|
704
|
+
received_at?: number;
|
|
703
705
|
/** Tool output content */
|
|
704
706
|
content: string | unknown[];
|
|
705
707
|
/** Optional artifact (for content_and_artifact format) */
|
package/src/utils/events.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// src/utils/events.ts
|
|
3
3
|
import { dispatchCustomEvent } from '@langchain/core/callbacks/dispatch';
|
|
4
4
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
5
|
+
import type { ToolCallsDispatchedEvent } from '@/types/stream';
|
|
5
6
|
import type { ToolExecuteBatchRequest } from '@/types/tools';
|
|
6
7
|
import type { AgentLogEvent } from '@/types/graph';
|
|
7
8
|
import { traceHostToolResults } from '@/langfuse';
|
|
@@ -24,11 +25,13 @@ export async function safeDispatchCustomEvent(
|
|
|
24
25
|
resolve: (
|
|
25
26
|
results: Parameters<ToolExecuteBatchRequest['resolve']>[0]
|
|
26
27
|
): void => {
|
|
28
|
+
const receivedAt = Date.now();
|
|
29
|
+
const stamped = results.map((result) => ({ ...result, received_at: receivedAt }));
|
|
27
30
|
void traceHostToolResults(request, results, config).then(
|
|
28
|
-
() => request.resolve(
|
|
31
|
+
() => request.resolve(stamped),
|
|
29
32
|
() => {
|
|
30
33
|
console.warn('Failed to record host tool execution metadata');
|
|
31
|
-
request.resolve(
|
|
34
|
+
request.resolve(stamped);
|
|
32
35
|
}
|
|
33
36
|
);
|
|
34
37
|
},
|
|
@@ -54,6 +57,22 @@ export async function safeDispatchCustomEvent(
|
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
/** Builds an argument-free snapshot once per handoff, not once per result. */
|
|
61
|
+
export function createToolCallsDispatchedEvent(
|
|
62
|
+
config: RunnableConfig,
|
|
63
|
+
calls: readonly Pick<
|
|
64
|
+
ToolExecuteBatchRequest['toolCalls'][number],
|
|
65
|
+
'id' | 'name' | 'stepId'
|
|
66
|
+
>[]
|
|
67
|
+
): ToolCallsDispatchedEvent {
|
|
68
|
+
const runId = config.configurable?.run_id;
|
|
69
|
+
return {
|
|
70
|
+
dispatched_at: Date.now(),
|
|
71
|
+
...(typeof runId === 'string' ? { runId } : {}),
|
|
72
|
+
toolCalls: calls.map(({ id, name, stepId }) => ({ id, name, stepId })),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
57
76
|
/**
|
|
58
77
|
* Fire-and-forget diagnostic log event.
|
|
59
78
|
* Debug-level logs are gated behind AGENT_DEBUG_LOGGING=true to avoid
|