@librechat/agents 3.7.8 → 3.7.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs +3 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/constants.cjs +2 -0
- package/dist/cjs/common/constants.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +40 -6
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
- package/dist/cjs/hooks/executeHooks.cjs +8 -0
- package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
- package/dist/cjs/hooks/index.cjs +2 -0
- package/dist/cjs/hooks/index.cjs.map +1 -1
- package/dist/cjs/hooks/types.cjs +1 -0
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +43 -3
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +2 -6
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/request.cjs +6 -0
- package/dist/cjs/llm/request.cjs.map +1 -1
- package/dist/cjs/main.cjs +19 -11
- package/dist/cjs/messages/format.cjs +95 -18
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +1 -1
- package/dist/cjs/run.cjs +201 -46
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +1 -1
- package/dist/cjs/summarization/index.cjs +1 -0
- package/dist/cjs/summarization/index.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +24 -85
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/summarization/semanticIndex.cjs +7 -3
- package/dist/cjs/summarization/semanticIndex.cjs.map +1 -1
- package/dist/cjs/summarization/shared.cjs +83 -0
- package/dist/cjs/summarization/shared.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +5 -5
- package/dist/cjs/tools/local/CompileCheckTool.cjs +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +1 -1
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +1 -1
- package/dist/cjs/tools/runStepResume.cjs +1 -1
- package/dist/cjs/tools/runStepResume.cjs.map +1 -1
- package/dist/cjs/tools/search/metrics.cjs +234 -0
- package/dist/cjs/tools/search/metrics.cjs.map +1 -0
- package/dist/cjs/tools/search/rerankers.cjs +80 -68
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +95 -45
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +77 -49
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +8 -12
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +8 -1
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +3 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/constants.mjs +2 -1
- package/dist/esm/common/constants.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +40 -6
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
- package/dist/esm/hooks/executeHooks.mjs +8 -1
- package/dist/esm/hooks/executeHooks.mjs.map +1 -1
- package/dist/esm/hooks/index.mjs +2 -1
- package/dist/esm/hooks/index.mjs.map +1 -1
- package/dist/esm/hooks/types.mjs +1 -0
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +43 -3
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +2 -6
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/request.mjs +6 -1
- package/dist/esm/llm/request.mjs.map +1 -1
- package/dist/esm/main.mjs +13 -12
- package/dist/esm/messages/format.mjs +96 -19
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +1 -1
- package/dist/esm/run.mjs +201 -46
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +1 -1
- package/dist/esm/summarization/index.mjs +1 -0
- package/dist/esm/summarization/index.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +21 -82
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/summarization/semanticIndex.mjs +7 -4
- package/dist/esm/summarization/semanticIndex.mjs.map +1 -1
- package/dist/esm/summarization/shared.mjs +79 -0
- package/dist/esm/summarization/shared.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +5 -5
- package/dist/esm/tools/local/CompileCheckTool.mjs +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +1 -1
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +1 -1
- package/dist/esm/tools/runStepResume.mjs +1 -1
- package/dist/esm/tools/runStepResume.mjs.map +1 -1
- package/dist/esm/tools/search/metrics.mjs +234 -0
- package/dist/esm/tools/search/metrics.mjs.map +1 -0
- package/dist/esm/tools/search/rerankers.mjs +80 -68
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +96 -46
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +77 -49
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +8 -12
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +8 -2
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/common/constants.d.ts +7 -0
- package/dist/types/graphs/Graph.d.ts +9 -0
- package/dist/types/hooks/executeHooks.d.ts +5 -2
- package/dist/types/hooks/index.d.ts +7 -2
- package/dist/types/hooks/types.d.ts +45 -7
- package/dist/types/langfuse.d.ts +3 -1
- package/dist/types/llm/request.d.ts +10 -0
- package/dist/types/messages/format.d.ts +8 -2
- package/dist/types/run.d.ts +2 -1
- package/dist/types/summarization/index.d.ts +6 -0
- package/dist/types/summarization/node.d.ts +0 -4
- package/dist/types/summarization/semanticIndex.d.ts +2 -0
- package/dist/types/summarization/shared.d.ts +25 -0
- package/dist/types/tools/search/metrics.d.ts +15 -0
- package/dist/types/tools/search/rerankers.d.ts +30 -5
- package/dist/types/tools/search/tool.d.ts +7 -1
- package/dist/types/tools/search/types.d.ts +88 -4
- package/dist/types/tools/search/utils.d.ts +2 -10
- package/dist/types/types/run.d.ts +8 -0
- package/dist/types/types/stream.d.ts +6 -0
- package/dist/types/types/summarize.d.ts +6 -0
- package/dist/types/utils/tokens.d.ts +9 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +2 -5
- package/src/common/constants.ts +8 -0
- package/src/graphs/Graph.ts +45 -0
- package/src/hooks/HookRegistry.ts +3 -1
- package/src/hooks/executeHooks.ts +20 -2
- package/src/hooks/index.ts +12 -1
- package/src/hooks/types.ts +47 -4
- package/src/langfuse.ts +70 -0
- package/src/llm/invoke.ts +13 -29
- package/src/llm/request.ts +24 -0
- package/src/messages/format.ts +213 -27
- package/src/run.ts +366 -186
- package/src/summarization/index.ts +11 -0
- package/src/summarization/node.ts +117 -130
- package/src/summarization/semanticIndex.ts +16 -7
- package/src/summarization/shared.ts +130 -0
- package/src/tools/runStepResume.ts +9 -0
- package/src/tools/search/metrics.ts +400 -0
- package/src/tools/search/rerankers.ts +160 -97
- package/src/tools/search/search.ts +139 -56
- package/src/tools/search/tool.ts +126 -62
- package/src/tools/search/types.ts +104 -4
- package/src/tools/search/utils.ts +2 -10
- package/src/tools/subagent/SubagentExecutor.ts +4 -27
- package/src/types/run.ts +8 -0
- package/src/types/stream.ts +6 -0
- package/src/types/summarize.ts +7 -0
- package/src/utils/tokens.ts +31 -8
package/src/run.ts
CHANGED
|
@@ -3,27 +3,28 @@ import { nanoid } from 'nanoid';
|
|
|
3
3
|
import { PromptTemplate } from '@langchain/core/prompts';
|
|
4
4
|
import { RunnableLambda } from '@langchain/core/runnables';
|
|
5
5
|
import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
|
|
6
|
-
import {
|
|
7
|
-
Command,
|
|
8
|
-
INTERRUPT,
|
|
9
|
-
MemorySaver,
|
|
10
|
-
isInterrupted,
|
|
11
|
-
} from '@langchain/langgraph';
|
|
12
6
|
import {
|
|
13
7
|
AIMessage,
|
|
14
8
|
BaseMessage,
|
|
15
9
|
HumanMessage,
|
|
16
10
|
SystemMessage,
|
|
17
11
|
} from '@langchain/core/messages';
|
|
12
|
+
import {
|
|
13
|
+
Command,
|
|
14
|
+
INTERRUPT,
|
|
15
|
+
MemorySaver,
|
|
16
|
+
Overwrite,
|
|
17
|
+
isInterrupted,
|
|
18
|
+
} from '@langchain/langgraph';
|
|
18
19
|
import type {
|
|
19
20
|
MessageContentComplex,
|
|
20
21
|
UsageMetadata,
|
|
21
22
|
} from '@langchain/core/messages';
|
|
22
23
|
import type { StringPromptValue } from '@langchain/core/prompt_values';
|
|
23
24
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
25
|
+
import type { AggregatedHookResult, HookRegistry } from '@/hooks';
|
|
24
26
|
import type { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
|
|
25
27
|
import type { StandardGraph } from '@/graphs/Graph';
|
|
26
|
-
import type { HookRegistry } from '@/hooks';
|
|
27
28
|
import type * as t from '@/types';
|
|
28
29
|
import {
|
|
29
30
|
Callback,
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
REASONING_LABEL_RUN_NAME,
|
|
34
35
|
ACTIVITY_PHASE_RUN_NAME,
|
|
35
36
|
ACTIVITY_PHASE_LABEL_RUN_NAME,
|
|
37
|
+
DEFAULT_MAX_STOP_CONTINUATIONS,
|
|
36
38
|
DEFAULT_RECURSION_LIMIT,
|
|
37
39
|
} from '@/common';
|
|
38
40
|
import {
|
|
@@ -94,15 +96,17 @@ import { LANGFUSE_OPERATION_METADATA_KEY } from '@/langfuseOperation';
|
|
|
94
96
|
import { createTokenCounter, encodingForModel } from '@/utils/tokens';
|
|
95
97
|
import { stampSyntheticProviderMessage } from '@/messages/provenance';
|
|
96
98
|
import { isOpenAILike, isLibreChatOpenAIModel } from '@/utils/llm';
|
|
99
|
+
import { executeHooks, mergeAggregatedHookResults } from '@/hooks';
|
|
100
|
+
import { convertInjectedMessages } from '@/messages/injected';
|
|
97
101
|
import { initializeLangfuseTracing } from './instrumentation';
|
|
98
102
|
import { seedRunInitialSessions } from '@/utils/toolSessions';
|
|
99
103
|
import { getTraceIdSeed } from '@/langfuseRuntimeContext';
|
|
104
|
+
import { resolveClientOptionsModel } from '@/llm/request';
|
|
100
105
|
import { createGraph } from '@/graphs/createGraph';
|
|
101
106
|
import { resolveMaxSeals } from '@/llm/preempt';
|
|
102
107
|
import { isBuiltRuntime } from '@/lazyRequire';
|
|
103
108
|
import { initializeModel } from '@/llm/init';
|
|
104
109
|
import { HandlerRegistry } from '@/events';
|
|
105
|
-
import { executeHooks } from '@/hooks';
|
|
106
110
|
|
|
107
111
|
/** Source-mode runs have no dist siblings for the lazy-loading seam, so every
|
|
108
112
|
* lazily loadable module is imported through the active loader before the
|
|
@@ -132,6 +136,60 @@ const ACTIVITY_PHASE_TRACE_NAME = 'LibreChat Activity Phase';
|
|
|
132
136
|
const REASONING_LABEL_TRACE_NAME = 'LibreChat Reasoning Label';
|
|
133
137
|
const OUTPUT_TRUNCATED_HALT_REASON = 'output_truncated';
|
|
134
138
|
|
|
139
|
+
function resolveMaxStopContinuations(value: number | undefined): number {
|
|
140
|
+
if (value == null || !Number.isFinite(value)) {
|
|
141
|
+
return DEFAULT_MAX_STOP_CONTINUATIONS;
|
|
142
|
+
}
|
|
143
|
+
return Math.max(0, Math.floor(value));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function materializeStopContinuation(
|
|
147
|
+
result: AggregatedHookResult
|
|
148
|
+
): BaseMessage[] {
|
|
149
|
+
const messages: BaseMessage[] = [];
|
|
150
|
+
const contexts = result.additionalContexts.filter(
|
|
151
|
+
(context) => context.trim() !== ''
|
|
152
|
+
);
|
|
153
|
+
if (contexts.length > 0) {
|
|
154
|
+
messages.push(
|
|
155
|
+
stampSyntheticProviderMessage(
|
|
156
|
+
new HumanMessage({
|
|
157
|
+
content: contexts.join('\n\n'),
|
|
158
|
+
additional_kwargs: {
|
|
159
|
+
role: 'system',
|
|
160
|
+
isMeta: true,
|
|
161
|
+
source: 'hook',
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
messages.push(...convertInjectedMessages(result.injectedMessages));
|
|
168
|
+
return messages;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function advanceCheckpointCursor(
|
|
172
|
+
config: t.RunStreamConfig
|
|
173
|
+
): t.RunStreamConfig {
|
|
174
|
+
const configurable = { ...config.configurable };
|
|
175
|
+
delete configurable.checkpoint_id;
|
|
176
|
+
delete configurable.checkpoint_map;
|
|
177
|
+
return { ...config, configurable };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function assertFinalAdmissionSucceeded(result: AggregatedHookResult): void {
|
|
181
|
+
if (result.hasHookFailures !== true && result.errors.length === 0) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const detail =
|
|
185
|
+
result.errors.length > 0
|
|
186
|
+
? result.errors.join('; ')
|
|
187
|
+
: 'one or more internal finalizers failed';
|
|
188
|
+
throw new Error(
|
|
189
|
+
`StopFinalize terminal admission failed: ${detail}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
135
193
|
const CUSTOM_GRAPH_EVENTS = new Set<string>([
|
|
136
194
|
GraphEvents.ON_AGENT_UPDATE,
|
|
137
195
|
GraphEvents.ON_RUN_STEP,
|
|
@@ -228,7 +286,10 @@ function getInterruptHookSessionId(payload: unknown): string | undefined {
|
|
|
228
286
|
|
|
229
287
|
type InterruptStateSnapshot = {
|
|
230
288
|
config?: RunnableConfig;
|
|
231
|
-
values?: {
|
|
289
|
+
values?: {
|
|
290
|
+
messages?: BaseMessage[];
|
|
291
|
+
runStepState?: t.RunStepResumeState;
|
|
292
|
+
};
|
|
232
293
|
tasks?: Array<{
|
|
233
294
|
interrupts?: Array<{ id?: string; value?: unknown }>;
|
|
234
295
|
state?: RunnableConfig | InterruptStateSnapshot;
|
|
@@ -241,10 +302,79 @@ type WorkflowWithStateHistory = {
|
|
|
241
302
|
options?: { subgraphs?: boolean }
|
|
242
303
|
): Promise<InterruptStateSnapshot>;
|
|
243
304
|
getStateHistory?(
|
|
244
|
-
config: RunnableConfig
|
|
305
|
+
config: RunnableConfig,
|
|
306
|
+
options?: { filter?: Record<string, unknown>; limit?: number }
|
|
245
307
|
): AsyncIterableIterator<InterruptStateSnapshot>;
|
|
246
308
|
};
|
|
247
309
|
|
|
310
|
+
async function resolveCompletedSegmentConfig(
|
|
311
|
+
workflow: t.CompiledStateWorkflow,
|
|
312
|
+
config: t.RunStreamConfig,
|
|
313
|
+
executionId: string,
|
|
314
|
+
streamSegment: number
|
|
315
|
+
): Promise<t.RunStreamConfig> {
|
|
316
|
+
const stateWorkflow = workflow as t.CompiledStateWorkflow &
|
|
317
|
+
WorkflowWithStateHistory;
|
|
318
|
+
const stateHistory = stateWorkflow.getStateHistory;
|
|
319
|
+
if (typeof stateHistory !== 'function') {
|
|
320
|
+
throw new Error(
|
|
321
|
+
'Cannot continue a checkpointed run without exact state history.'
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
const lookup = advanceCheckpointCursor(config);
|
|
325
|
+
|
|
326
|
+
const resolveSnapshot = (
|
|
327
|
+
snapshot: InterruptStateSnapshot | undefined
|
|
328
|
+
): t.RunStreamConfig | undefined => {
|
|
329
|
+
if (snapshot == null) {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
const runStepState =
|
|
333
|
+
snapshot.values?.runStepState ??
|
|
334
|
+
snapshot.tasks
|
|
335
|
+
?.flatMap((task) => task.interrupts ?? [])
|
|
336
|
+
.map((pendingInterrupt) =>
|
|
337
|
+
getRunStepResumeState(pendingInterrupt.value)
|
|
338
|
+
)
|
|
339
|
+
.find((state): state is t.RunStepResumeState => state != null);
|
|
340
|
+
if (
|
|
341
|
+
runStepState?.stopContinuationExecutionId !== executionId ||
|
|
342
|
+
runStepState.streamSegment !== streamSegment
|
|
343
|
+
) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
const checkpointId = snapshot.config?.configurable?.checkpoint_id;
|
|
347
|
+
if (typeof checkpointId !== 'string' || checkpointId.length === 0) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
...config,
|
|
352
|
+
configurable: {
|
|
353
|
+
...config.configurable,
|
|
354
|
+
...snapshot.config?.configurable,
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
if (typeof stateWorkflow.getState === 'function') {
|
|
360
|
+
const latest = await stateWorkflow.getState(lookup);
|
|
361
|
+
const latestConfig = resolveSnapshot(latest);
|
|
362
|
+
if (latestConfig != null) {
|
|
363
|
+
return latestConfig;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for await (const snapshot of stateHistory.call(workflow, lookup)) {
|
|
368
|
+
const snapshotConfig = resolveSnapshot(snapshot);
|
|
369
|
+
if (snapshotConfig != null) {
|
|
370
|
+
return snapshotConfig;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
throw new Error(
|
|
374
|
+
'Cannot identify the checkpoint committed by the completed graph segment.'
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
248
378
|
function getFirstPersistedInterrupt(
|
|
249
379
|
snapshot: InterruptStateSnapshot
|
|
250
380
|
): { id: string; value: unknown } | undefined {
|
|
@@ -284,6 +414,27 @@ function getPersistedMessages(
|
|
|
284
414
|
|
|
285
415
|
type ResumeCommandUpdate = ConstructorParameters<typeof Command>[0]['update'];
|
|
286
416
|
|
|
417
|
+
function overwriteResumeRunStepState(
|
|
418
|
+
command: Command,
|
|
419
|
+
state: t.RunStepResumeState
|
|
420
|
+
): Command {
|
|
421
|
+
const overwrite = new Overwrite(state);
|
|
422
|
+
const update = Array.isArray(command.update)
|
|
423
|
+
? [
|
|
424
|
+
...command.update.filter(([key]) => key !== 'runStepState'),
|
|
425
|
+
['runStepState', overwrite] as [string, unknown],
|
|
426
|
+
]
|
|
427
|
+
: { ...(command.update ?? {}), runStepState: overwrite };
|
|
428
|
+
const resumed = new Command({
|
|
429
|
+
...(command.graph == null ? {} : { graph: command.graph }),
|
|
430
|
+
...(command.resume === undefined ? {} : { resume: command.resume }),
|
|
431
|
+
update,
|
|
432
|
+
...(command.goto === undefined ? {} : { goto: command.goto }),
|
|
433
|
+
});
|
|
434
|
+
resumed.lc_direct_tool_output = command.lc_direct_tool_output;
|
|
435
|
+
return resumed;
|
|
436
|
+
}
|
|
437
|
+
|
|
287
438
|
function getResumeUpdateMessages(
|
|
288
439
|
update: ResumeCommandUpdate
|
|
289
440
|
): BaseMessage[] | undefined {
|
|
@@ -316,6 +467,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
316
467
|
private toolExecution?: t.ToolExecutionConfig;
|
|
317
468
|
private subagentUsageSink?: t.SubagentUsageSink;
|
|
318
469
|
private preemption?: t.StreamPreemption;
|
|
470
|
+
private maxStopContinuations: number;
|
|
319
471
|
private streamLimits?: t.StreamLimits;
|
|
320
472
|
private subagentTasks?: t.SubagentTaskConfig;
|
|
321
473
|
private indexTokenCountMap?: Record<string, number>;
|
|
@@ -387,6 +539,9 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
387
539
|
this.subagentUsageSink = config.subagentUsageSink;
|
|
388
540
|
this.subagentTasks = config.subagentTasks;
|
|
389
541
|
this.preemption = config.preemption;
|
|
542
|
+
this.maxStopContinuations = resolveMaxStopContinuations(
|
|
543
|
+
config.maxStopContinuations
|
|
544
|
+
);
|
|
390
545
|
this.streamLimits = config.streamLimits;
|
|
391
546
|
|
|
392
547
|
if (!config.graphConfig) {
|
|
@@ -729,12 +884,16 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
729
884
|
config: t.RunConfig
|
|
730
885
|
): Promise<Run<T>> {
|
|
731
886
|
await ensureSourceModeProviders();
|
|
732
|
-
/** Create tokenCounter if indexTokenCountMap is provided but tokenCounter is
|
|
887
|
+
/** Create tokenCounter if indexTokenCountMap is provided but tokenCounter is
|
|
888
|
+
* not. The model is read through both option keys: `modelName` is
|
|
889
|
+
* LangChain's alias for `model`, so consulting one alone would give a
|
|
890
|
+
* Claude-backed agent an `o200k_base` counter and undercount every message
|
|
891
|
+
* it measures. */
|
|
733
892
|
if (config.indexTokenCountMap && !config.tokenCounter) {
|
|
734
893
|
const gc = config.graphConfig;
|
|
735
894
|
const clientOpts =
|
|
736
895
|
'agents' in gc ? gc.agents[0]?.clientOptions : gc.clientOptions;
|
|
737
|
-
const model = (clientOpts
|
|
896
|
+
const model = resolveClientOptionsModel(clientOpts) ?? '';
|
|
738
897
|
config.tokenCounter = await createTokenCounter(encodingForModel(model));
|
|
739
898
|
}
|
|
740
899
|
return new Run<T>(config);
|
|
@@ -1055,11 +1214,16 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1055
1214
|
delete config.configurable?.[SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY];
|
|
1056
1215
|
delete config.configurable?.[SUBAGENT_RESUME_MANIFEST_CONFIG_KEY];
|
|
1057
1216
|
}
|
|
1217
|
+
let overwriteLegacyResumeState = false;
|
|
1058
1218
|
if (isResume) {
|
|
1059
1219
|
await this.restoreInterruptFromCheckpoint(
|
|
1060
1220
|
config,
|
|
1061
1221
|
(inputs as Command).update
|
|
1062
1222
|
);
|
|
1223
|
+
if (graph.getStopContinuationExecutionId() === '') {
|
|
1224
|
+
graph.startStopContinuationExecution(nanoid());
|
|
1225
|
+
overwriteLegacyResumeState = this.hasCheckpointer;
|
|
1226
|
+
}
|
|
1063
1227
|
}
|
|
1064
1228
|
|
|
1065
1229
|
/**
|
|
@@ -1110,6 +1274,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1110
1274
|
checkpointId === '' ? 0 : ++this.checkpointForkSeq,
|
|
1111
1275
|
]);
|
|
1112
1276
|
graph.resetValues(streamOptions?.keepContent, checkpointScope);
|
|
1277
|
+
graph.startStopContinuationExecution(nanoid());
|
|
1113
1278
|
}
|
|
1114
1279
|
this._interrupt = undefined;
|
|
1115
1280
|
this._haltedReason = undefined;
|
|
@@ -1175,6 +1340,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1175
1340
|
: undefined,
|
|
1176
1341
|
traceAnchor: graph.langfuseTraceAnchor,
|
|
1177
1342
|
runId: graph.langfuseScopeRunId,
|
|
1343
|
+
deferRootRunId: this.id,
|
|
1178
1344
|
// The aggregate multi-agent policy from the runtime scope — the
|
|
1179
1345
|
// handler must restore THIS (not the primary agent's config-derived
|
|
1180
1346
|
// policy) when rejecting a foreign scope.
|
|
@@ -1189,6 +1355,7 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1189
1355
|
throw new Error('Run ID not provided');
|
|
1190
1356
|
}
|
|
1191
1357
|
|
|
1358
|
+
config.runId = this.id;
|
|
1192
1359
|
config.run_id = this.id;
|
|
1193
1360
|
config.configurable = Object.assign(config.configurable ?? {}, {
|
|
1194
1361
|
run_id: this.id,
|
|
@@ -1237,144 +1404,92 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1237
1404
|
let terminalAt: number | undefined;
|
|
1238
1405
|
|
|
1239
1406
|
const consumeStream = async (): Promise<void> => {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
* Custom events are already handled via our createCustomEventCallback()
|
|
1254
|
-
* which routes them through the handlerRegistry.
|
|
1255
|
-
* Without this flag, EventStreamCallbackHandler throws errors when
|
|
1256
|
-
* custom events are dispatched for run IDs not in its internal map
|
|
1257
|
-
* (due to timing issues in parallel execution or after run cleanup).
|
|
1258
|
-
*/
|
|
1259
|
-
ignoreCustomEvent: true,
|
|
1260
|
-
}
|
|
1261
|
-
);
|
|
1407
|
+
let streamInputs: t.IState | Command = inputs;
|
|
1408
|
+
if (!isResume && this.hasCheckpointer) {
|
|
1409
|
+
streamInputs = {
|
|
1410
|
+
...(inputs as t.IState),
|
|
1411
|
+
runStepState: new Overwrite(graph.createRunStepResumeState()),
|
|
1412
|
+
} as unknown as t.IState;
|
|
1413
|
+
} else if (overwriteLegacyResumeState) {
|
|
1414
|
+
streamInputs = overwriteResumeRunStepState(
|
|
1415
|
+
inputs as Command,
|
|
1416
|
+
graph.createRunStepResumeState()
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
let streamConfig = config;
|
|
1262
1420
|
|
|
1263
|
-
for
|
|
1264
|
-
|
|
1421
|
+
for (;;) {
|
|
1422
|
+
/**
|
|
1423
|
+
* `streamEvents` accepts both state inputs and `Command` (resume) at
|
|
1424
|
+
* runtime, but our `CompiledStateWorkflow` type narrows the first
|
|
1425
|
+
* arg to `BaseGraphState`. Cast on the call so the resume path
|
|
1426
|
+
* type-checks without widening the wrapper for every caller.
|
|
1427
|
+
*/
|
|
1428
|
+
const stream = graphRunnable.streamEvents(
|
|
1429
|
+
streamInputs as t.IState,
|
|
1430
|
+
{ ...streamConfig, runName: graph.runName },
|
|
1431
|
+
{
|
|
1432
|
+
raiseError: true,
|
|
1433
|
+
/** Custom events are handled by createCustomEventCallback(). */
|
|
1434
|
+
ignoreCustomEvent: true,
|
|
1435
|
+
}
|
|
1436
|
+
);
|
|
1265
1437
|
|
|
1266
|
-
const
|
|
1438
|
+
for await (const event of stream) {
|
|
1439
|
+
const { data, metadata, ...info } = event;
|
|
1440
|
+
const eventName: t.EventName = info.event;
|
|
1267
1441
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
}
|
|
1442
|
+
if (CUSTOM_GRAPH_EVENTS.has(eventName)) {
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1272
1445
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
data.chunk != null &&
|
|
1288
|
-
isInterrupted<unknown>(data.chunk)
|
|
1289
|
-
) {
|
|
1290
|
-
const interrupts = data.chunk[INTERRUPT];
|
|
1291
|
-
if (interrupts.length > 0) {
|
|
1292
|
-
const first = interrupts[0];
|
|
1293
|
-
/**
|
|
1294
|
-
* Capture the interrupt unconditionally — `interrupt(null)`
|
|
1295
|
-
* and `interrupt(undefined)` are valid pauses (a custom
|
|
1296
|
-
* node may want to pause without metadata) and the host
|
|
1297
|
-
* still needs to know the run is awaiting resume. Gating
|
|
1298
|
-
* on `payload != null` would silently downgrade a paused
|
|
1299
|
-
* run to "completed" and let the `Stop` hook fire,
|
|
1300
|
-
* breaking host resume handling.
|
|
1301
|
-
*/
|
|
1302
|
-
this._interrupt = {
|
|
1303
|
-
interruptId: first.id ?? '',
|
|
1304
|
-
threadId,
|
|
1305
|
-
payload: first.value,
|
|
1306
|
-
};
|
|
1446
|
+
if (
|
|
1447
|
+
this._interrupt == null &&
|
|
1448
|
+
data.chunk != null &&
|
|
1449
|
+
isInterrupted<unknown>(data.chunk)
|
|
1450
|
+
) {
|
|
1451
|
+
const interrupts = data.chunk[INTERRUPT];
|
|
1452
|
+
if (interrupts.length > 0) {
|
|
1453
|
+
const first = interrupts[0];
|
|
1454
|
+
this._interrupt = {
|
|
1455
|
+
interruptId: first.id ?? '',
|
|
1456
|
+
threadId,
|
|
1457
|
+
payload: first.value,
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1307
1460
|
}
|
|
1308
|
-
}
|
|
1309
1461
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
eventName === GraphEvents.CHAT_MODEL_END ? Date.now() : undefined;
|
|
1317
|
-
const handler = this.handlerRegistry?.getHandler(eventName);
|
|
1318
|
-
if (handler) {
|
|
1319
|
-
await handler.handle(eventName, data, metadata, this.Graph);
|
|
1320
|
-
}
|
|
1462
|
+
const modelEndAt =
|
|
1463
|
+
eventName === GraphEvents.CHAT_MODEL_END ? Date.now() : undefined;
|
|
1464
|
+
const handler = this.handlerRegistry?.getHandler(eventName);
|
|
1465
|
+
if (handler) {
|
|
1466
|
+
await handler.handle(eventName, data, metadata, this.Graph);
|
|
1467
|
+
}
|
|
1321
1468
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
* CHAT_MODEL_END handler with their own instance, which would
|
|
1326
|
-
* silently drop the close.
|
|
1327
|
-
*/
|
|
1328
|
-
if (eventName === GraphEvents.CHAT_MODEL_END && this.Graph != null) {
|
|
1329
|
-
await this.Graph.closeOpenMessageStep(metadata, modelEndAt);
|
|
1330
|
-
}
|
|
1469
|
+
if (eventName === GraphEvents.CHAT_MODEL_END && this.Graph != null) {
|
|
1470
|
+
await this.Graph.closeOpenMessageStep(metadata, modelEndAt);
|
|
1471
|
+
}
|
|
1331
1472
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
* stream events and break out as soon as one is set so the
|
|
1338
|
-
* graph doesn't take another model turn after the halting
|
|
1339
|
-
* operation completes.
|
|
1340
|
-
*
|
|
1341
|
-
* This `break` is NOT graceful, despite what a `continue: false`
|
|
1342
|
-
* reading suggests. Leaving the `for await` calls the iterator's
|
|
1343
|
-
* `return()`, which cancels the reader
|
|
1344
|
-
* (`@langchain/core/utils/stream`), and langgraph's stream wrapper
|
|
1345
|
-
* turns that cancel into `_abortController.abort()`
|
|
1346
|
-
* (`pregel/stream.js`). The in-flight model call or tool batch is
|
|
1347
|
-
* torn down where it stands — it does not finish first.
|
|
1348
|
-
*
|
|
1349
|
-
* A halt is therefore the wrong tool for "stop generating but keep
|
|
1350
|
-
* what you have". That is what `RunConfig.preemption` is for: it
|
|
1351
|
-
* seals the stream at a provider-safe boundary and keeps the run.
|
|
1352
|
-
*/
|
|
1353
|
-
const haltSignal = this.hookRegistry?.getHaltSignal(this.id);
|
|
1354
|
-
if (haltSignal != null) {
|
|
1355
|
-
this._haltedReason = haltSignal.reason;
|
|
1356
|
-
break;
|
|
1473
|
+
const haltSignal = this.hookRegistry?.getHaltSignal(this.id);
|
|
1474
|
+
if (haltSignal != null) {
|
|
1475
|
+
this._haltedReason = haltSignal.reason;
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1357
1478
|
}
|
|
1358
|
-
}
|
|
1359
1479
|
|
|
1360
|
-
|
|
1480
|
+
terminalAt = Date.now();
|
|
1361
1481
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1482
|
+
if (this._interrupt != null) {
|
|
1483
|
+
const interruptConfig = this.hasCheckpointer
|
|
1484
|
+
? advanceCheckpointCursor(streamConfig)
|
|
1485
|
+
: streamConfig;
|
|
1486
|
+
await this.resolveInterruptResumeConfig(interruptConfig);
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
if (this._haltedReason != null) {
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1365
1492
|
|
|
1366
|
-
/**
|
|
1367
|
-
* Skip the Stop hook when the run paused on a HITL interrupt
|
|
1368
|
-
* (still pending human input) or was halted by a hook (the host
|
|
1369
|
-
* already chose to stop, so a Stop hook firing now would be
|
|
1370
|
-
* misleading). The host fires Stop on the resumed-and-completed
|
|
1371
|
-
* run instead.
|
|
1372
|
-
*/
|
|
1373
|
-
if (
|
|
1374
|
-
this._interrupt == null &&
|
|
1375
|
-
this._haltedReason == null &&
|
|
1376
|
-
this.hookRegistry?.hasHookFor('Stop', this.id) === true
|
|
1377
|
-
) {
|
|
1378
1493
|
let stopReason = graph.preemptHaltReason;
|
|
1379
1494
|
if (stopReason == null && graph.preemptIncomplete) {
|
|
1380
1495
|
stopReason = 'preempt_incomplete';
|
|
@@ -1382,56 +1497,116 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1382
1497
|
if (stopReason == null && graph.outputTruncatedIncomplete) {
|
|
1383
1498
|
stopReason = OUTPUT_TRUNCATED_HALT_REASON;
|
|
1384
1499
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1500
|
+
|
|
1501
|
+
const stopMessages = graph.getRunMessages() ?? stateInputs?.messages ?? [];
|
|
1502
|
+
const stopContinuationCount = graph.getStopContinuationCount();
|
|
1503
|
+
const continuationBudgetRemaining = Math.max(
|
|
1504
|
+
0,
|
|
1505
|
+
this.maxStopContinuations - stopContinuationCount
|
|
1506
|
+
);
|
|
1507
|
+
let stopResult: AggregatedHookResult | undefined;
|
|
1508
|
+
if (this.hookRegistry?.hasHookFor('Stop', this.id) === true) {
|
|
1509
|
+
stopResult = await executeHooks({
|
|
1510
|
+
registry: this.hookRegistry,
|
|
1511
|
+
input: {
|
|
1512
|
+
hook_event_name: 'Stop',
|
|
1513
|
+
runId: this.id,
|
|
1514
|
+
threadId,
|
|
1515
|
+
agentId: graph.defaultAgentId,
|
|
1516
|
+
messages: stopMessages,
|
|
1517
|
+
stopReason,
|
|
1518
|
+
stopHookActive: stopContinuationCount > 0,
|
|
1519
|
+
continuationCount: stopContinuationCount,
|
|
1520
|
+
continuationBudgetRemaining,
|
|
1521
|
+
},
|
|
1522
|
+
sessionId: this.id,
|
|
1523
|
+
signal: config.signal,
|
|
1524
|
+
}).catch((): undefined => undefined);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
if (this.hookRegistry?.hasHookFor('StopFinalize', this.id) === true) {
|
|
1528
|
+
const stopInjected =
|
|
1529
|
+
stopResult == null ? [] : materializeStopContinuation(stopResult);
|
|
1530
|
+
const continuationPrevented =
|
|
1531
|
+
stopReason != null || stopResult?.preventContinuation === true;
|
|
1532
|
+
const finalized = await executeHooks({
|
|
1533
|
+
registry: this.hookRegistry,
|
|
1534
|
+
input: {
|
|
1535
|
+
hook_event_name: 'StopFinalize',
|
|
1536
|
+
runId: this.id,
|
|
1537
|
+
threadId,
|
|
1538
|
+
agentId: graph.defaultAgentId,
|
|
1539
|
+
messages: stopMessages,
|
|
1540
|
+
stopReason,
|
|
1541
|
+
stopHookActive: stopContinuationCount > 0,
|
|
1542
|
+
continuationCount: stopContinuationCount,
|
|
1543
|
+
continuationBudgetRemaining,
|
|
1544
|
+
continuationPlanned:
|
|
1545
|
+
!continuationPrevented &&
|
|
1546
|
+
stopResult?.stopDecision === 'block' &&
|
|
1547
|
+
stopInjected.length > 0 &&
|
|
1548
|
+
continuationBudgetRemaining > 0,
|
|
1549
|
+
continuationPrevented,
|
|
1550
|
+
},
|
|
1551
|
+
sessionId: this.id,
|
|
1552
|
+
signal: config.signal,
|
|
1553
|
+
});
|
|
1554
|
+
assertFinalAdmissionSucceeded(finalized);
|
|
1555
|
+
stopResult = mergeAggregatedHookResults(stopResult, finalized);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
if (stopResult?.preventContinuation === true) {
|
|
1559
|
+
this._haltedReason =
|
|
1560
|
+
stopResult.stopReason ?? stopResult.reason ?? 'preventContinuation';
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
const requestsContinuation =
|
|
1565
|
+
stopReason == null && stopResult?.stopDecision === 'block';
|
|
1566
|
+
if (requestsContinuation && stopResult != null) {
|
|
1567
|
+
const injected = materializeStopContinuation(stopResult);
|
|
1568
|
+
if (injected.length > 0) {
|
|
1569
|
+
if (stopContinuationCount >= this.maxStopContinuations) {
|
|
1570
|
+
throw new Error(
|
|
1571
|
+
'Stop hook attempted to inject messages after the terminal continuation budget was exhausted.'
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
const completedSegmentConfig = this.hasCheckpointer
|
|
1575
|
+
? await resolveCompletedSegmentConfig(
|
|
1576
|
+
graphRunnable,
|
|
1577
|
+
streamConfig,
|
|
1578
|
+
graph.getStopContinuationExecutionId(),
|
|
1579
|
+
graph.getStreamSegment()
|
|
1580
|
+
)
|
|
1581
|
+
: streamConfig;
|
|
1582
|
+
const nextContinuationCount = stopContinuationCount + 1;
|
|
1583
|
+
graph.setStopContinuationCount(nextContinuationCount);
|
|
1584
|
+
graph.advanceStreamSegment();
|
|
1393
1585
|
/**
|
|
1394
|
-
* A
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
1397
|
-
*
|
|
1398
|
-
* is reserved for the boundary that simply had nothing to inject.
|
|
1586
|
+
* A checkpointer already owns the completed graph state, so only
|
|
1587
|
+
* the delta is submitted. Without one, each invocation starts from
|
|
1588
|
+
* empty state and must be seeded with the live full transcript.
|
|
1589
|
+
* Graph sidecars and the outer Run stay intact in both cases.
|
|
1399
1590
|
*/
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1591
|
+
streamInputs = {
|
|
1592
|
+
messages: this.hasCheckpointer
|
|
1593
|
+
? injected
|
|
1594
|
+
: [...graph.messages, ...injected],
|
|
1595
|
+
runStepState: graph.createRunStepResumeState(),
|
|
1596
|
+
};
|
|
1597
|
+
streamConfig = completedSegmentConfig;
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1408
1601
|
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
*
|
|
1418
|
-
* An empty boundary — sealed, but nothing to inject because the host's
|
|
1419
|
-
* queue was drained or cancelled in the meantime — cut the answer short
|
|
1420
|
-
* just as surely, only without a hook-supplied reason. It surfaces
|
|
1421
|
-
* through the same channel under the same name the `Stop` dispatch
|
|
1422
|
-
* already used for its `stopReason`, so terminal consumers
|
|
1423
|
-
* (`AgentSession` emits `run.halted`, not `run.completed`) cannot
|
|
1424
|
-
* finalize a truncated answer as a natural finish.
|
|
1425
|
-
*/
|
|
1426
|
-
if (this._haltedReason == null && graph.preemptHaltReason != null) {
|
|
1427
|
-
this._haltedReason = graph.preemptHaltReason;
|
|
1428
|
-
} else if (this._haltedReason == null && graph.preemptIncomplete) {
|
|
1429
|
-
this._haltedReason = 'preempt_incomplete';
|
|
1430
|
-
} else if (
|
|
1431
|
-
this._haltedReason == null &&
|
|
1432
|
-
graph.outputTruncatedIncomplete
|
|
1433
|
-
) {
|
|
1434
|
-
this._haltedReason = OUTPUT_TRUNCATED_HALT_REASON;
|
|
1602
|
+
if (graph.preemptHaltReason != null) {
|
|
1603
|
+
this._haltedReason = graph.preemptHaltReason;
|
|
1604
|
+
} else if (graph.preemptIncomplete) {
|
|
1605
|
+
this._haltedReason = 'preempt_incomplete';
|
|
1606
|
+
} else if (graph.outputTruncatedIncomplete) {
|
|
1607
|
+
this._haltedReason = OUTPUT_TRUNCATED_HALT_REASON;
|
|
1608
|
+
}
|
|
1609
|
+
return;
|
|
1435
1610
|
}
|
|
1436
1611
|
};
|
|
1437
1612
|
|
|
@@ -1455,6 +1630,10 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1455
1630
|
} catch (err) {
|
|
1456
1631
|
terminalAt = Date.now();
|
|
1457
1632
|
streamThrew = true;
|
|
1633
|
+
await langfuseHandler?.handleChainError(
|
|
1634
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
1635
|
+
this.id
|
|
1636
|
+
);
|
|
1458
1637
|
/**
|
|
1459
1638
|
* Corroborate cancellation against an actually-aborted signal. A
|
|
1460
1639
|
* provider SDK or host handler can reject with an `AbortError` while
|
|
@@ -1859,7 +2038,8 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
1859
2038
|
return;
|
|
1860
2039
|
}
|
|
1861
2040
|
this.Graph?.restoreRunStepResumeState(
|
|
1862
|
-
getRunStepResumeState(persistedInterrupt.value)
|
|
2041
|
+
getRunStepResumeState(persistedInterrupt.value) ??
|
|
2042
|
+
snapshot.values?.runStepState
|
|
1863
2043
|
);
|
|
1864
2044
|
const persistedMessages = getPersistedMessages(snapshot);
|
|
1865
2045
|
if (persistedMessages != null) {
|