@librechat/agents 3.4.5 → 3.4.7
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 +2 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +268 -14
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/langfuseTraceShaping.cjs +9 -6
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +10 -5
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/run.cjs +147 -14
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +93 -31
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/handlers.cjs +10 -0
- package/dist/cjs/session/handlers.cjs.map +1 -1
- package/dist/cjs/stream.cjs +17 -3
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -0
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +4 -2
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +1 -0
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +28 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/handlers.cjs +3 -0
- package/dist/cjs/utils/handlers.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +2 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +268 -14
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/langfuseTraceShaping.mjs +9 -6
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +10 -5
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/run.mjs +147 -14
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +93 -31
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/handlers.mjs +10 -0
- package/dist/esm/session/handlers.mjs.map +1 -1
- package/dist/esm/stream.mjs +17 -3
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -0
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +4 -2
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +1 -0
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +28 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/handlers.mjs +3 -0
- package/dist/esm/utils/handlers.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +87 -0
- package/dist/types/langfuseTraceShaping.d.ts +4 -3
- package/dist/types/run.d.ts +9 -1
- package/dist/types/session/types.d.ts +1 -1
- package/dist/types/summarization/node.d.ts +6 -0
- package/dist/types/types/graph.d.ts +3 -3
- package/dist/types/types/stream.d.ts +57 -0
- package/dist/types/utils/handlers.d.ts +1 -0
- package/package.json +1 -1
- package/src/aggregator.test.ts +127 -0
- package/src/common/enum.ts +2 -0
- package/src/graphs/Graph.ts +365 -7
- package/src/graphs/__tests__/Graph.closeRunStep.test.ts +385 -0
- package/src/langfuseTraceShaping.ts +14 -7
- package/src/llm/google/utils/common.test.ts +11 -0
- package/src/llm/google/utils/common.ts +6 -4
- package/src/run.ts +219 -41
- package/src/session/AgentSession.ts +105 -13
- package/src/session/handlers.ts +14 -2
- package/src/session/types.ts +1 -0
- package/src/specs/activity-label-observability.live.test.ts +262 -0
- package/src/specs/activity-label-observability.test.ts +167 -0
- package/src/specs/langfuse-trace-shaping.test.ts +32 -0
- package/src/specs/run-step-timestamps.test.ts +414 -0
- package/src/stream.ts +29 -6
- package/src/summarization/node.ts +11 -0
- package/src/tools/ToolNode.ts +2 -0
- package/src/tools/__tests__/SubagentExecutor.test.ts +52 -10
- package/src/tools/__tests__/handlers.test.ts +2 -0
- package/src/tools/handlers.ts +1 -0
- package/src/tools/subagent/SubagentExecutor.ts +46 -4
- package/src/types/graph.ts +9 -6
- package/src/types/stream.ts +65 -12
- package/src/utils/handlers.ts +13 -0
|
@@ -36,6 +36,9 @@ function createHandlers(callbacks) {
|
|
|
36
36
|
});
|
|
37
37
|
callbacks?.onRunStepDelta?.(event, data);
|
|
38
38
|
} },
|
|
39
|
+
["on_run_step_closed"]: { handle: (event, data) => {
|
|
40
|
+
callbacks?.onRunStepClosed?.(event, data);
|
|
41
|
+
} },
|
|
39
42
|
["on_message_delta"]: { handle: (event, data) => {
|
|
40
43
|
aggregateContent({
|
|
41
44
|
event,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.mjs","names":[],"sources":["../../../src/utils/handlers.ts"],"sourcesContent":["/**\n * Multi-Agent Handler Utilities\n *\n * Provides a simple helper to create handlers with content aggregation for multi-agent scripts.\n *\n * Usage:\n * ```typescript\n * const { contentParts, aggregateContent, handlers } = createHandlers();\n *\n * // With callbacks\n * const { contentParts, aggregateContent, handlers } = createHandlers({\n * onRunStep: (event, data) => console.log('Step:', data),\n * onRunStepCompleted: (event, data) => console.log('Completed:', data)\n * });\n * ```\n */\n\nimport type * as t from '@/types';\nimport { ChatModelStreamHandler, createContentAggregator } from '@/stream';\nimport { ToolEndHandler, ModelEndHandler } from '@/events';\nimport { GraphEvents } from '@/common';\n\ninterface HandlerCallbacks {\n onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;\n onRunStepCompleted?: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ) => void;\n onRunStepDelta?: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ) => void;\n onMessageDelta?: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ) => void;\n}\n\n/**\n * Creates handlers with content aggregation for multi-agent scripts\n */\nexport function createHandlers(callbacks?: HandlerCallbacks): {\n contentParts: Array<t.MessageContentComplex | undefined>;\n aggregateContent: ReturnType<\n typeof createContentAggregator\n >['aggregateContent'];\n handlers: Record<string, t.EventHandler>;\n} {\n // Set up content aggregator\n const { contentParts, aggregateContent } = createContentAggregator();\n\n // Create the handlers object\n const handlers = {\n [GraphEvents.TOOL_END]: new ToolEndHandler(),\n [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),\n [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),\n\n [GraphEvents.ON_RUN_STEP]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStep });\n callbacks?.onRunStep?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_COMPLETED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ): void => {\n aggregateContent({\n event,\n data: data as unknown as { result: t.ToolEndEvent },\n });\n callbacks?.onRunStepCompleted?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_DELTA]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStepDeltaEvent });\n callbacks?.onRunStepDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_MESSAGE_DELTA]: {\n handle: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.MessageDeltaEvent });\n callbacks?.onMessageDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_DELTA]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeDeltaData,\n });\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_COMPLETE]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeCompleteEvent,\n });\n },\n },\n };\n\n return {\n contentParts,\n aggregateContent,\n handlers,\n };\n}\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"handlers.mjs","names":[],"sources":["../../../src/utils/handlers.ts"],"sourcesContent":["/**\n * Multi-Agent Handler Utilities\n *\n * Provides a simple helper to create handlers with content aggregation for multi-agent scripts.\n *\n * Usage:\n * ```typescript\n * const { contentParts, aggregateContent, handlers } = createHandlers();\n *\n * // With callbacks\n * const { contentParts, aggregateContent, handlers } = createHandlers({\n * onRunStep: (event, data) => console.log('Step:', data),\n * onRunStepCompleted: (event, data) => console.log('Completed:', data)\n * });\n * ```\n */\n\nimport type * as t from '@/types';\nimport { ChatModelStreamHandler, createContentAggregator } from '@/stream';\nimport { ToolEndHandler, ModelEndHandler } from '@/events';\nimport { GraphEvents } from '@/common';\n\ninterface HandlerCallbacks {\n onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;\n onRunStepCompleted?: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ) => void;\n onRunStepDelta?: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ) => void;\n onRunStepClosed?: (\n event: GraphEvents.ON_RUN_STEP_CLOSED,\n data: t.RunStepClosedEvent\n ) => void;\n onMessageDelta?: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ) => void;\n}\n\n/**\n * Creates handlers with content aggregation for multi-agent scripts\n */\nexport function createHandlers(callbacks?: HandlerCallbacks): {\n contentParts: Array<t.MessageContentComplex | undefined>;\n aggregateContent: ReturnType<\n typeof createContentAggregator\n >['aggregateContent'];\n handlers: Record<string, t.EventHandler>;\n} {\n // Set up content aggregator\n const { contentParts, aggregateContent } = createContentAggregator();\n\n // Create the handlers object\n const handlers = {\n [GraphEvents.TOOL_END]: new ToolEndHandler(),\n [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),\n [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),\n\n [GraphEvents.ON_RUN_STEP]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStep });\n callbacks?.onRunStep?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_COMPLETED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ): void => {\n aggregateContent({\n event,\n data: data as unknown as { result: t.ToolEndEvent },\n });\n callbacks?.onRunStepCompleted?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_DELTA]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStepDeltaEvent });\n callbacks?.onRunStepDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_CLOSED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_CLOSED,\n data: t.StreamEventData\n ): void => {\n callbacks?.onRunStepClosed?.(event, data as t.RunStepClosedEvent);\n },\n },\n\n [GraphEvents.ON_MESSAGE_DELTA]: {\n handle: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.MessageDeltaEvent });\n callbacks?.onMessageDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_DELTA]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeDeltaData,\n });\n },\n },\n\n [GraphEvents.ON_SUMMARIZE_COMPLETE]: {\n handle: (event: string, data: t.StreamEventData): void => {\n aggregateContent({\n event: event as GraphEvents,\n data: data as t.SummarizeCompleteEvent,\n });\n },\n },\n };\n\n return {\n contentParts,\n aggregateContent,\n handlers,\n };\n}\n"],"mappings":";;;;;;;;AA6CA,SAAgB,eAAe,WAM7B;CAEA,MAAM,EAAE,cAAc,qBAAqB,wBAAwB;CA+EnE,OAAO;EACL;EACA;EACA,UAAA;oBA9EwB,IAAI,eAAe;0BACb,IAAI,gBAAgB;6BACjB,IAAI,uBAAuB;oBAEjC,EACzB,SACE,OACA,SACS;IACT,iBAAiB;KAAE;KAAa;IAAkB,CAAC;IACnD,WAAW,YAAY,OAAO,IAAI;GACpC,EACF;8BAEqC,EACnC,SACE,OACA,SACS;IACT,iBAAiB;KACf;KACM;IACR,CAAC;IACD,WAAW,qBAAqB,OAAO,IAAI;GAC7C,EACF;0BAEiC,EAC/B,SACE,OACA,SACS;IACT,iBAAiB;KAAE;KAAa;IAA4B,CAAC;IAC7D,WAAW,iBAAiB,OAAO,IAAI;GACzC,EACF;2BAEkC,EAChC,SACE,OACA,SACS;IACT,WAAW,kBAAkB,OAAO,IAA4B;GAClE,EACF;yBAEgC,EAC9B,SACE,OACA,SACS;IACT,iBAAiB;KAAE;KAAa;IAA4B,CAAC;IAC7D,WAAW,iBAAiB,OAAO,IAAI;GACzC,EACF;2BAEkC,EAChC,SAAS,OAAe,SAAkC;IACxD,iBAAiB;KACR;KACD;IACR,CAAC;GACH,EACF;8BAEqC,EACnC,SAAS,OAAe,SAAkC;IACxD,iBAAiB;KACR;KACD;IACR,CAAC;GACH,EACF;EAMO;CACT;AACF"}
|
|
@@ -13,6 +13,8 @@ export declare enum GraphEvents {
|
|
|
13
13
|
ON_RUN_STEP_DELTA = "on_run_step_delta",
|
|
14
14
|
/** [Custom] Completed event for run steps (tool calls) */
|
|
15
15
|
ON_RUN_STEP_COMPLETED = "on_run_step_completed",
|
|
16
|
+
/** [Custom] Terminal signal for a run step: closed with status + timestamps */
|
|
17
|
+
ON_RUN_STEP_CLOSED = "on_run_step_closed",
|
|
16
18
|
/** [Custom] Delta events for messages */
|
|
17
19
|
ON_MESSAGE_DELTA = "on_message_delta",
|
|
18
20
|
/** [Custom] Reasoning Delta events for messages */
|
|
@@ -45,6 +45,17 @@ export declare abstract class Graph<T extends t.BaseGraphState = t.BaseGraphStat
|
|
|
45
45
|
stepKeyIds: Map<string, string[]>;
|
|
46
46
|
contentIndexMap: Map<string, number>;
|
|
47
47
|
toolCallStepIds: Map<string, string>;
|
|
48
|
+
/** Step ID -> tool call IDs whose completions have not yet arrived. */
|
|
49
|
+
pendingToolCallsByStep: Map<string, Set<string>>;
|
|
50
|
+
/**
|
|
51
|
+
* Step ID -> latest producer completion time seen for that step. Parallel
|
|
52
|
+
* calls sharing a step can settle out of producer order when their host
|
|
53
|
+
* handlers differ in latency, so the call that happens to drain the set is
|
|
54
|
+
* not necessarily the one that finished last.
|
|
55
|
+
*/
|
|
56
|
+
latestCompletionByStep: Map<string, number>;
|
|
57
|
+
/** Agent key ('' for single-agent) -> currently open MESSAGE_CREATION step ID. */
|
|
58
|
+
openMessageStepByAgent: Map<string, string>;
|
|
48
59
|
/**
|
|
49
60
|
* Step IDs dispatched through the handler registry during this run.
|
|
50
61
|
* Event echo suppression is tracked separately so repeated deltas for
|
|
@@ -153,6 +164,16 @@ export declare abstract class Graph<T extends t.BaseGraphState = t.BaseGraphStat
|
|
|
153
164
|
clearHeavyState(): void;
|
|
154
165
|
getEagerEventToolUsageCount(agentId?: string): Map<string, number>;
|
|
155
166
|
protected clearEagerEventToolUsageCounts(): void;
|
|
167
|
+
/**
|
|
168
|
+
* Tracks a tool call whose completion must arrive before its step can be
|
|
169
|
+
* considered finished. Registered wherever `toolCallStepIds` gains entries,
|
|
170
|
+
* except cross-process subagent resume restoration, where pending state
|
|
171
|
+
* cannot be faithfully rebuilt and closes fall back to completions/sweep.
|
|
172
|
+
*/
|
|
173
|
+
registerPendingToolCall(toolCallId: string, stepId: string): void;
|
|
174
|
+
/** Lazily creates a step's pending-completions set; callers registering a
|
|
175
|
+
* batch hoist this lookup out of their per-call loop. */
|
|
176
|
+
protected getPendingToolCallSet(stepId: string): Set<string>;
|
|
156
177
|
markHandlerDispatchedEvent(eventName: string, stepId: string): () => void;
|
|
157
178
|
hasHandlerDispatchedEvent(eventName: string, stepId: string): boolean;
|
|
158
179
|
/**
|
|
@@ -415,6 +436,72 @@ export declare class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode>
|
|
|
415
436
|
releasePreemptSeal(): void;
|
|
416
437
|
getPreemptStats(): t.PreemptStats;
|
|
417
438
|
getRunStep(stepId: string): t.RunStep | undefined;
|
|
439
|
+
/**
|
|
440
|
+
* Derives the same lane key `dispatchRunStep` stamps as `runStep.agentId`.
|
|
441
|
+
* The multi-agent check gates the lookup because `getAgentContext` signals
|
|
442
|
+
* a miss by throwing: single-agent graphs key every step under `''`, so
|
|
443
|
+
* resolving the context could only ever produce a thrown-and-discarded
|
|
444
|
+
* Error on a per-model-call path.
|
|
445
|
+
*/
|
|
446
|
+
protected getStepAgentKey(metadata?: Record<string, unknown>): string;
|
|
447
|
+
/**
|
|
448
|
+
* O(1) reverse lookup: both dispatch funnels key the open-message map by
|
|
449
|
+
* `runStep.agentId ?? ''`, so the entry is addressable without scanning.
|
|
450
|
+
*/
|
|
451
|
+
private untrackRunStep;
|
|
452
|
+
/**
|
|
453
|
+
* Shared step accounting for both dispatch funnels: a successor step in
|
|
454
|
+
* the same agent lane marks the previous message step as finished — its
|
|
455
|
+
* CLOSED event must precede the successor's ON_RUN_STEP so hosts observe
|
|
456
|
+
* a consistent open-step timeline — then the step is registered in the
|
|
457
|
+
* content maps and tracked as the lane's open message step when
|
|
458
|
+
* applicable.
|
|
459
|
+
*/
|
|
460
|
+
protected trackDispatchedRunStep(runStep: t.RunStep, metadata?: Record<string, unknown>,
|
|
461
|
+
/**
|
|
462
|
+
* Summarization steps are typed MESSAGE_CREATION but own an explicit
|
|
463
|
+
* completion, and their model call emits `CHAT_MODEL_END` well before the
|
|
464
|
+
* summary is assembled. Tracking one as the lane's open step would let
|
|
465
|
+
* model-end publish an authoritative `completed` closure early — the
|
|
466
|
+
* measured duration would exclude the remaining work and a later
|
|
467
|
+
* post-model failure could no longer change the status. They close
|
|
468
|
+
* through `recordStepCompletion` instead.
|
|
469
|
+
*/
|
|
470
|
+
trackAsOpenMessageStep?: boolean): Promise<void>;
|
|
471
|
+
/**
|
|
472
|
+
* Closes a run step: stamps its terminal status + timestamp on the stored
|
|
473
|
+
* `RunStep` and emits `ON_RUN_STEP_CLOSED`. First close wins — later calls
|
|
474
|
+
* are no-ops — except a `restamp` close, which lets a `completed`
|
|
475
|
+
* TOOL_CALLS step refresh `completed_at` when a late-registered parallel
|
|
476
|
+
* tool call finishes after the step already closed (the eager-execution
|
|
477
|
+
* race). `cancelled`/`failed` are immutable once stamped.
|
|
478
|
+
*/
|
|
479
|
+
closeRunStep(stepId: string, status: Exclude<t.RunStepStatus, 'in_progress'>, options?: t.RunStepCloseOptions): Promise<boolean>;
|
|
480
|
+
/**
|
|
481
|
+
* Observes one `ON_RUN_STEP_COMPLETED` for a step and closes the step when
|
|
482
|
+
* no registered tool calls remain pending. Steps without pending tracking
|
|
483
|
+
* (summaries, cross-process resume) close on their first completion; the
|
|
484
|
+
* terminal-status guard in `closeRunStep` absorbs duplicate echoes.
|
|
485
|
+
*/
|
|
486
|
+
recordStepCompletion(stepId: string, options?: t.RecordStepCompletionOptions): Promise<void>;
|
|
487
|
+
/**
|
|
488
|
+
* Closes the tracked open MESSAGE_CREATION step for the event's agent lane.
|
|
489
|
+
* Fires on every model end, so the empty-map check short-circuits ahead of
|
|
490
|
+
* resolving the lane key — a turn whose message step already closed through
|
|
491
|
+
* successor-close does no work here.
|
|
492
|
+
*/
|
|
493
|
+
closeOpenMessageStep(metadata?: Record<string, unknown>,
|
|
494
|
+
/** Model-end time captured before host handlers ran, so a slow usage sink
|
|
495
|
+
* cannot inflate the step's measured duration. */
|
|
496
|
+
at?: number): Promise<void>;
|
|
497
|
+
/**
|
|
498
|
+
* End-of-run sweep: closes every step that never reached a terminal
|
|
499
|
+
* status. Dual-dispatches like any other close — the custom-event channel
|
|
500
|
+
* is usually already torn down here and `safeDispatchCustomEvent` reports
|
|
501
|
+
* that quietly, but callback-only subscribers still receive the terminal
|
|
502
|
+
* signal whenever it is alive.
|
|
503
|
+
*/
|
|
504
|
+
closeUnfinishedRunSteps(status: Exclude<t.RunStepStatus, 'in_progress'>, at?: number): Promise<void>;
|
|
418
505
|
getAgentContext(metadata: Record<string, unknown> | undefined): AgentContext;
|
|
419
506
|
getStepBaseKey(metadata: Record<string, unknown> | undefined): string;
|
|
420
507
|
getStepKey(metadata: Record<string, unknown> | undefined): string;
|
|
@@ -20,8 +20,9 @@ export declare function shouldDropLangfuseSpan(spanName: string): boolean;
|
|
|
20
20
|
* - Agent nodes become `agent` observations, while tool-dispatch nodes become
|
|
21
21
|
* stable `chain` observations whose input is scoped to the pending calls.
|
|
22
22
|
* Individual child calls remain `tool` observations (items 3 & 4).
|
|
23
|
-
* - Agent trace roots become `agent` observations
|
|
24
|
-
* `chain` observations. Root and trace input/output are
|
|
25
|
-
* question and assistant response when chat messages are
|
|
23
|
+
* - Agent trace roots become `agent` observations, while title and activity
|
|
24
|
+
* summary roots become `chain` observations. Root and trace input/output are
|
|
25
|
+
* reduced to the user question and assistant response when chat messages are
|
|
26
|
+
* available (item 2).
|
|
26
27
|
*/
|
|
27
28
|
export declare function shapeLangfuseSpan(span: ReadableSpan): void;
|
package/dist/types/run.d.ts
CHANGED
|
@@ -138,6 +138,14 @@ export declare class Run<_T extends t.BaseGraphState> {
|
|
|
138
138
|
private createCustomEventCallback;
|
|
139
139
|
private shouldClearHookSession;
|
|
140
140
|
private isAwaitingResume;
|
|
141
|
+
/**
|
|
142
|
+
* Terminal status for steps still open at end-of-run: `cancelled` for
|
|
143
|
+
* intentional stops (caller abort, hook halt), `failed` for unexpected
|
|
144
|
+
* stream errors, `completed` for a natural finish. Reads `_haltedReason`
|
|
145
|
+
* behind a method boundary on purpose — it is assigned inside the
|
|
146
|
+
* `consumeStream` closure, which control-flow narrowing cannot see.
|
|
147
|
+
*/
|
|
148
|
+
private resolveSweepStatus;
|
|
141
149
|
private getStreamLangfuseConfig;
|
|
142
150
|
private getStreamToolOutputTracingLangfuseConfig;
|
|
143
151
|
processStream(inputs: t.IState | Command, callerConfig: t.RunStreamConfig, streamOptions?: t.EventStreamOptions): Promise<MessageContentComplex[] | undefined>;
|
|
@@ -247,7 +255,7 @@ export declare class Run<_T extends t.BaseGraphState> {
|
|
|
247
255
|
}>;
|
|
248
256
|
/**
|
|
249
257
|
* Generates one parent summary for two or more logical activities. The
|
|
250
|
-
* summary model is traced as a dedicated activity-phase
|
|
258
|
+
* summary model is traced as a dedicated activity-phase chain root in the
|
|
251
259
|
* conversation session, with the model callback recorded as its generation
|
|
252
260
|
* child. No session id means no phase trace, avoiding orphan observations.
|
|
253
261
|
*/
|
|
@@ -150,7 +150,7 @@ export interface AgentSessionRunOptions {
|
|
|
150
150
|
streamOptions?: t.EventStreamOptions;
|
|
151
151
|
}
|
|
152
152
|
export interface AgentSessionStreamEvent {
|
|
153
|
-
type: 'run.started' | 'message.delta' | 'reasoning.delta' | 'tool.started' | 'tool.delta' | 'tool.completed' | 'usage.updated' | 'run.completed' | 'run.failed' | 'run.interrupted' | 'run.halted';
|
|
153
|
+
type: 'run.started' | 'message.delta' | 'reasoning.delta' | 'tool.started' | 'tool.delta' | 'tool.completed' | 'step.finished' | 'usage.updated' | 'run.completed' | 'run.failed' | 'run.interrupted' | 'run.halted';
|
|
154
154
|
sequence: number;
|
|
155
155
|
runId: string;
|
|
156
156
|
threadId: string;
|
|
@@ -21,6 +21,12 @@ interface CreateSummarizeNodeParams {
|
|
|
21
21
|
hookRegistry?: HookRegistry;
|
|
22
22
|
dispatchRunStep: (runStep: t.RunStep, config?: RunnableConfig) => Promise<void>;
|
|
23
23
|
dispatchRunStepCompleted: (stepId: string, result: t.StepCompleted, config?: RunnableConfig) => Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Terminal close for a summary step that ends without a completion —
|
|
26
|
+
* an errored or empty summary would otherwise stay `in_progress` until
|
|
27
|
+
* the run-end sweep reported it as `completed`.
|
|
28
|
+
*/
|
|
29
|
+
closeRunStep?: (stepId: string, status: Exclude<t.RunStepStatus, 'in_progress'>, config?: RunnableConfig) => Promise<void>;
|
|
24
30
|
/** The run's shared breaker signal, composed into every summarization
|
|
25
31
|
* model attempt so a sibling branch tripping a stream limit also
|
|
26
32
|
* cancels in-flight summaries. */
|
|
@@ -5,8 +5,8 @@ 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
7
|
import type { SummarizationNodeInput, SummarizeCompleteEvent, SummarizationConfig, SummarizeStartEvent, SummarizeDeltaEvent } from '@/types/summarize';
|
|
8
|
+
import type { RunStep, RunStepDeltaEvent, RunStepClosedEvent, MessageDeltaEvent, ReasoningDeltaEvent } from '@/types/stream';
|
|
8
9
|
import type { ToolMap, ToolSessionMap, ToolEndEvent, GenericTool, LCTool, ToolExecuteBatchRequest } from '@/types/tools';
|
|
9
|
-
import type { RunStep, RunStepDeltaEvent, MessageDeltaEvent, ReasoningDeltaEvent } from '@/types/stream';
|
|
10
10
|
import type { TokenCounter, StreamLimits, StreamPreemption, TokenBudgetBreakdown } from '@/types/run';
|
|
11
11
|
import type { Providers, Callback, GraphNodeKeys } from '@/common';
|
|
12
12
|
import type { StandardGraph, MultiAgentGraph } from '@/graphs';
|
|
@@ -73,7 +73,7 @@ export interface ContextUsageEvent {
|
|
|
73
73
|
calibrationRatio?: number;
|
|
74
74
|
}
|
|
75
75
|
export interface EventHandler {
|
|
76
|
-
handle(event: string, data: StreamEventData | ModelEndData | RunStep | RunStepDeltaEvent | MessageDeltaEvent | ReasoningDeltaEvent | SummarizeStartEvent | SummarizeDeltaEvent | SummarizeCompleteEvent | SubagentUpdateEvent | AgentLogEvent | ContextUsageEvent | ToolExecuteBatchRequest | {
|
|
76
|
+
handle(event: string, data: StreamEventData | ModelEndData | RunStep | RunStepDeltaEvent | RunStepClosedEvent | MessageDeltaEvent | ReasoningDeltaEvent | SummarizeStartEvent | SummarizeDeltaEvent | SummarizeCompleteEvent | SubagentUpdateEvent | AgentLogEvent | ContextUsageEvent | ToolExecuteBatchRequest | {
|
|
77
77
|
result: ToolEndEvent;
|
|
78
78
|
}, metadata?: Record<string, unknown>, graph?: StandardGraph | MultiAgentGraph): void | Promise<void>;
|
|
79
79
|
}
|
|
@@ -442,7 +442,7 @@ export type ExecutableSubagentConfig = ResolvedSubagentConfig | LazySingleAgentS
|
|
|
442
442
|
/** Graph-aware config accepted by the executor. Graph configs stay eager. */
|
|
443
443
|
export type ExecutableSubagentConfigEntry = ExecutableSubagentConfig | GraphSubagentConfig;
|
|
444
444
|
/** Lifecycle phase carried on {@link SubagentUpdateEvent}. */
|
|
445
|
-
export type SubagentUpdatePhase = 'start' | 'run_step' | 'run_step_delta' | 'run_step_completed' | 'message_delta' | 'reasoning_delta' | 'stop' | 'error';
|
|
445
|
+
export type SubagentUpdatePhase = 'start' | 'run_step' | 'run_step_delta' | 'run_step_completed' | 'run_step_closed' | 'message_delta' | 'reasoning_delta' | 'stop' | 'error';
|
|
446
446
|
export interface SubagentAncestryEntry {
|
|
447
447
|
readonly subagentRunId: string;
|
|
448
448
|
readonly subagentType: string;
|
|
@@ -2,6 +2,7 @@ import type { MessageContentImageUrl, MessageContentText, ToolMessage, BaseMessa
|
|
|
2
2
|
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
3
3
|
import type { LLMResult, Generation } from '@langchain/core/outputs';
|
|
4
4
|
import type { Command } from '@langchain/langgraph';
|
|
5
|
+
import type Anthropic from '@anthropic-ai/sdk';
|
|
5
6
|
import type { AnthropicContentBlock } from '@/llm/anthropic/types';
|
|
6
7
|
import type { AssistantTextPhase } from '@/types/assistantPhase';
|
|
7
8
|
import type { SummarizeCompleteEvent } from '@/types/summarize';
|
|
@@ -34,8 +35,25 @@ start, stream and end are associated with slightly different data payload.
|
|
|
34
35
|
|
|
35
36
|
Please see the documentation for EventData for more details. */
|
|
36
37
|
export type EventName = string;
|
|
38
|
+
export type RunStepStatus = 'in_progress' | 'completed' | 'cancelled' | 'failed';
|
|
37
39
|
export type RunStep = {
|
|
38
40
|
type: StepTypes;
|
|
41
|
+
/** Epoch ms when the step was dispatched. */
|
|
42
|
+
created_at?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Lifecycle status; terminal values are stamped when the step closes.
|
|
45
|
+
* Invariant (enforced by `closeRunStep`, not the type, to stay wire-compatible
|
|
46
|
+
* with the OpenAI Assistants shape): a terminal status sets exactly one
|
|
47
|
+
* matching `*_at` field; first close wins and `cancelled`/`failed` are
|
|
48
|
+
* immutable once stamped.
|
|
49
|
+
*/
|
|
50
|
+
status?: RunStepStatus;
|
|
51
|
+
/** Epoch ms when the step closed with status `completed`. */
|
|
52
|
+
completed_at?: number;
|
|
53
|
+
/** Epoch ms when the step closed with status `cancelled` (abort/halt). */
|
|
54
|
+
cancelled_at?: number;
|
|
55
|
+
/** Epoch ms when the step closed with status `failed`. */
|
|
56
|
+
failed_at?: number;
|
|
39
57
|
id: string;
|
|
40
58
|
runId?: string;
|
|
41
59
|
agentId?: string;
|
|
@@ -71,6 +89,41 @@ export interface RunStepDeltaEvent {
|
|
|
71
89
|
*/
|
|
72
90
|
delta: ToolCallDelta;
|
|
73
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Terminal signal for a run step, emitted exactly once per step when it
|
|
94
|
+
* finishes (`completed`), is aborted/halted (`cancelled`), or the run errors
|
|
95
|
+
* (`failed`). The `id` is top-level so callback echoes dedupe like other
|
|
96
|
+
* step-scoped events.
|
|
97
|
+
*/
|
|
98
|
+
export interface RunStepClosedEvent {
|
|
99
|
+
id: string;
|
|
100
|
+
index: number;
|
|
101
|
+
type: StepTypes;
|
|
102
|
+
status: Exclude<RunStepStatus, 'in_progress'>;
|
|
103
|
+
/** Epoch ms when the step was dispatched, when known. */
|
|
104
|
+
created_at?: number;
|
|
105
|
+
/** Epoch ms when the step reached its terminal status. */
|
|
106
|
+
closed_at: number;
|
|
107
|
+
runId?: string;
|
|
108
|
+
agentId?: string;
|
|
109
|
+
groupId?: number;
|
|
110
|
+
stepIndex?: number;
|
|
111
|
+
}
|
|
112
|
+
export type RecordStepCompletionOptions = {
|
|
113
|
+
/** The completing tool call, when the step tracks pending completions. */
|
|
114
|
+
toolCallId?: string;
|
|
115
|
+
metadata?: Record<string, unknown>;
|
|
116
|
+
/**
|
|
117
|
+
* Producer-stamped completion time (epoch ms). Carried through so a slow
|
|
118
|
+
* host completion handler cannot inflate the recorded step duration.
|
|
119
|
+
*/
|
|
120
|
+
at?: number;
|
|
121
|
+
};
|
|
122
|
+
export type RunStepCloseOptions = {
|
|
123
|
+
/** Epoch ms for the terminal stamp; defaults to `Date.now()` at close time. */
|
|
124
|
+
at?: number;
|
|
125
|
+
metadata?: Record<string, unknown>;
|
|
126
|
+
};
|
|
74
127
|
export type StepDetails = MessageCreationDetails | ToolCallsDetails;
|
|
75
128
|
export type SummaryCompleted = {
|
|
76
129
|
type: 'summary';
|
|
@@ -132,6 +185,8 @@ export type ToolCompleteEvent = ToolCallCompleted & {
|
|
|
132
185
|
/** The content index of the tool call */
|
|
133
186
|
index: number;
|
|
134
187
|
type: 'tool_call';
|
|
188
|
+
/** Epoch ms when this tool call's completion was dispatched. */
|
|
189
|
+
completed_at?: number;
|
|
135
190
|
};
|
|
136
191
|
export type ToolCallsDetails = {
|
|
137
192
|
type: StepTypes.TOOL_CALLS;
|
|
@@ -224,6 +279,8 @@ export type MessageDeltaUpdate = {
|
|
|
224
279
|
type: ContentTypes.TEXT;
|
|
225
280
|
text: string;
|
|
226
281
|
tool_call_ids?: string[];
|
|
282
|
+
/** Provider-supplied source citations, accumulated across deltas. */
|
|
283
|
+
citations?: Anthropic.TextCitation[];
|
|
227
284
|
};
|
|
228
285
|
export type ReasoningDeltaUpdate = {
|
|
229
286
|
type: ContentTypes.THINK;
|
|
@@ -21,6 +21,7 @@ interface HandlerCallbacks {
|
|
|
21
21
|
onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;
|
|
22
22
|
onRunStepCompleted?: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData) => void;
|
|
23
23
|
onRunStepDelta?: (event: GraphEvents.ON_RUN_STEP_DELTA, data: t.StreamEventData) => void;
|
|
24
|
+
onRunStepClosed?: (event: GraphEvents.ON_RUN_STEP_CLOSED, data: t.RunStepClosedEvent) => void;
|
|
24
25
|
onMessageDelta?: (event: GraphEvents.ON_MESSAGE_DELTA, data: t.StreamEventData) => void;
|
|
25
26
|
}
|
|
26
27
|
/**
|
package/package.json
CHANGED
package/src/aggregator.test.ts
CHANGED
|
@@ -1085,3 +1085,130 @@ describe('ContentAggregator multi-entry deltas', () => {
|
|
|
1085
1085
|
});
|
|
1086
1086
|
});
|
|
1087
1087
|
});
|
|
1088
|
+
|
|
1089
|
+
describe('ContentAggregator citation deltas', () => {
|
|
1090
|
+
const citation = (url: string, cited_text: string) => ({
|
|
1091
|
+
type: 'web_search_result_location',
|
|
1092
|
+
url,
|
|
1093
|
+
title: 'Example',
|
|
1094
|
+
cited_text,
|
|
1095
|
+
encrypted_index: 'enc',
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Anthropic emits a search turn's citations as their own `citations_delta`,
|
|
1100
|
+
* which `_makeMessageChunkFromAnthropicEvent` normalizes into a `text` part
|
|
1101
|
+
* carrying `citations` and **no** `text` key.
|
|
1102
|
+
*/
|
|
1103
|
+
const citationDelta = (id: string, ...citations: unknown[]) => ({
|
|
1104
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
1105
|
+
data: {
|
|
1106
|
+
id,
|
|
1107
|
+
delta: { content: [{ type: ContentTypes.TEXT, citations }] },
|
|
1108
|
+
} as t.MessageDeltaEvent,
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
const textDelta = (id: string, text: string) => ({
|
|
1112
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
1113
|
+
data: {
|
|
1114
|
+
id,
|
|
1115
|
+
delta: { content: [{ type: ContentTypes.TEXT, text }] },
|
|
1116
|
+
} as t.MessageDeltaEvent,
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
it('keeps citations that arrive without accompanying text', () => {
|
|
1120
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
1121
|
+
aggregateContent({
|
|
1122
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
1123
|
+
data: createRunStep('step_citations'),
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
aggregateContent(textDelta('step_citations', 'Answer.'));
|
|
1127
|
+
aggregateContent(
|
|
1128
|
+
citationDelta('step_citations', citation('https://a.example', 'quoted a'))
|
|
1129
|
+
);
|
|
1130
|
+
|
|
1131
|
+
expect(contentParts[0]).toEqual({
|
|
1132
|
+
type: ContentTypes.TEXT,
|
|
1133
|
+
text: 'Answer.',
|
|
1134
|
+
citations: [citation('https://a.example', 'quoted a')],
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
it('accumulates citations across successive deltas', () => {
|
|
1139
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
1140
|
+
aggregateContent({
|
|
1141
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
1142
|
+
data: createRunStep('step_multi'),
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
aggregateContent(textDelta('step_multi', 'Part one. '));
|
|
1146
|
+
aggregateContent(
|
|
1147
|
+
citationDelta('step_multi', citation('https://a.example', 'quoted a'))
|
|
1148
|
+
);
|
|
1149
|
+
aggregateContent(textDelta('step_multi', 'Part two.'));
|
|
1150
|
+
aggregateContent(
|
|
1151
|
+
citationDelta('step_multi', citation('https://b.example', 'quoted b'))
|
|
1152
|
+
);
|
|
1153
|
+
|
|
1154
|
+
expect(contentParts[0]).toEqual({
|
|
1155
|
+
type: ContentTypes.TEXT,
|
|
1156
|
+
text: 'Part one. Part two.',
|
|
1157
|
+
citations: [
|
|
1158
|
+
citation('https://a.example', 'quoted a'),
|
|
1159
|
+
citation('https://b.example', 'quoted b'),
|
|
1160
|
+
],
|
|
1161
|
+
});
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
it('leaves parts without citations untouched', () => {
|
|
1165
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
1166
|
+
aggregateContent({
|
|
1167
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
1168
|
+
data: createRunStep('step_plain'),
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
aggregateContent(textDelta('step_plain', 'Hello '));
|
|
1172
|
+
aggregateContent(textDelta('step_plain', 'world.'));
|
|
1173
|
+
|
|
1174
|
+
expect(contentParts[0]).toEqual({
|
|
1175
|
+
type: ContentTypes.TEXT,
|
|
1176
|
+
text: 'Hello world.',
|
|
1177
|
+
});
|
|
1178
|
+
expect(contentParts[0]).not.toHaveProperty('citations');
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
it('preserves tool_call_ids when a citations-only delta follows', () => {
|
|
1182
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
1183
|
+
aggregateContent({
|
|
1184
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
1185
|
+
data: createRunStep('step_tool_ids'),
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
aggregateContent({
|
|
1189
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
1190
|
+
data: {
|
|
1191
|
+
id: 'step_tool_ids',
|
|
1192
|
+
delta: {
|
|
1193
|
+
content: [
|
|
1194
|
+
{
|
|
1195
|
+
type: ContentTypes.TEXT,
|
|
1196
|
+
text: 'Answer.',
|
|
1197
|
+
tool_call_ids: ['call_1'],
|
|
1198
|
+
},
|
|
1199
|
+
],
|
|
1200
|
+
},
|
|
1201
|
+
} as t.MessageDeltaEvent,
|
|
1202
|
+
});
|
|
1203
|
+
aggregateContent(
|
|
1204
|
+
citationDelta('step_tool_ids', citation('https://a.example', 'quoted a'))
|
|
1205
|
+
);
|
|
1206
|
+
|
|
1207
|
+
expect(contentParts[0]).toEqual({
|
|
1208
|
+
type: ContentTypes.TEXT,
|
|
1209
|
+
text: 'Answer.',
|
|
1210
|
+
tool_call_ids: ['call_1'],
|
|
1211
|
+
citations: [citation('https://a.example', 'quoted a')],
|
|
1212
|
+
});
|
|
1213
|
+
});
|
|
1214
|
+
});
|
package/src/common/enum.ts
CHANGED
|
@@ -15,6 +15,8 @@ export enum GraphEvents {
|
|
|
15
15
|
ON_RUN_STEP_DELTA = 'on_run_step_delta',
|
|
16
16
|
/** [Custom] Completed event for run steps (tool calls) */
|
|
17
17
|
ON_RUN_STEP_COMPLETED = 'on_run_step_completed',
|
|
18
|
+
/** [Custom] Terminal signal for a run step: closed with status + timestamps */
|
|
19
|
+
ON_RUN_STEP_CLOSED = 'on_run_step_closed',
|
|
18
20
|
/** [Custom] Delta events for messages */
|
|
19
21
|
ON_MESSAGE_DELTA = 'on_message_delta',
|
|
20
22
|
/** [Custom] Reasoning Delta events for messages */
|