@langchain/langgraph 0.2.31 → 0.2.33
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/constants.cjs +84 -5
- package/dist/constants.d.ts +64 -3
- package/dist/constants.js +82 -3
- package/dist/errors.cjs +11 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +9 -0
- package/dist/graph/graph.cjs +9 -1
- package/dist/graph/graph.d.ts +1 -1
- package/dist/graph/graph.js +10 -2
- package/dist/graph/state.cjs +73 -31
- package/dist/graph/state.d.ts +1 -1
- package/dist/graph/state.js +75 -33
- package/dist/prebuilt/react_agent_executor.cjs +2 -2
- package/dist/prebuilt/react_agent_executor.d.ts +5 -4
- package/dist/prebuilt/react_agent_executor.js +3 -3
- package/dist/prebuilt/tool_node.cjs +15 -2
- package/dist/prebuilt/tool_node.js +15 -2
- package/dist/pregel/index.cjs +53 -1
- package/dist/pregel/index.js +53 -1
- package/dist/pregel/io.cjs +4 -5
- package/dist/pregel/io.d.ts +1 -1
- package/dist/pregel/io.js +4 -5
- package/dist/pregel/loop.cjs +19 -11
- package/dist/pregel/loop.d.ts +1 -1
- package/dist/pregel/loop.js +20 -12
- package/dist/pregel/write.cjs +64 -30
- package/dist/pregel/write.d.ts +7 -3
- package/dist/pregel/write.js +64 -30
- package/dist/web.cjs +2 -1
- package/dist/web.d.ts +1 -1
- package/dist/web.js +1 -1
- package/package.json +2 -2
package/dist/graph/state.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import { _coerceToRunnable, Runnable, } from "@langchain/core/runnables";
|
|
3
3
|
import { isBaseChannel } from "../channels/base.js";
|
|
4
4
|
import { END, CompiledGraph, Graph, START, Branch, } from "./graph.js";
|
|
5
|
-
import { ChannelWrite, PASSTHROUGH,
|
|
5
|
+
import { ChannelWrite, PASSTHROUGH, } from "../pregel/write.js";
|
|
6
6
|
import { ChannelRead, PregelNode } from "../pregel/read.js";
|
|
7
7
|
import { NamedBarrierValue } from "../channels/named_barrier_value.js";
|
|
8
8
|
import { EphemeralValue } from "../channels/ephemeral_value.js";
|
|
9
9
|
import { RunnableCallable } from "../utils.js";
|
|
10
|
-
import {
|
|
10
|
+
import { isCommand, _isSend, CHECKPOINT_NAMESPACE_END, CHECKPOINT_NAMESPACE_SEPARATOR, Command, SELF, TAG_HIDDEN, } from "../constants.js";
|
|
11
11
|
import { InvalidUpdateError, ParentCommand } from "../errors.js";
|
|
12
12
|
import { getChannel, } from "./annotation.js";
|
|
13
13
|
import { isConfiguredManagedValue } from "../managed/base.js";
|
|
@@ -343,59 +343,101 @@ function _getChannels(schema) {
|
|
|
343
343
|
*/
|
|
344
344
|
export class CompiledStateGraph extends CompiledGraph {
|
|
345
345
|
attachNode(key, node) {
|
|
346
|
-
|
|
346
|
+
let outputKeys;
|
|
347
|
+
if (key === START) {
|
|
348
|
+
// Get input schema keys excluding managed values
|
|
349
|
+
outputKeys = Object.entries(this.builder._schemaDefinitions.get(this.builder._inputDefinition))
|
|
350
|
+
.filter(([_, v]) => !isConfiguredManagedValue(v))
|
|
351
|
+
.map(([k]) => k);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
outputKeys = Object.keys(this.builder.channels);
|
|
355
|
+
}
|
|
356
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
347
357
|
function _getRoot(input) {
|
|
348
|
-
if (
|
|
358
|
+
if (isCommand(input)) {
|
|
349
359
|
if (input.graph === Command.PARENT) {
|
|
350
|
-
return
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
return input._updateAsTuples();
|
|
363
|
+
}
|
|
364
|
+
else if (Array.isArray(input) &&
|
|
365
|
+
input.length > 0 &&
|
|
366
|
+
input.some((i) => isCommand(i))) {
|
|
367
|
+
const updates = [];
|
|
368
|
+
for (const i of input) {
|
|
369
|
+
if (isCommand(i)) {
|
|
370
|
+
if (i.graph === Command.PARENT) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
updates.push(...i._updateAsTuples());
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
updates.push([ROOT, i]);
|
|
377
|
+
}
|
|
351
378
|
}
|
|
352
|
-
return
|
|
379
|
+
return updates;
|
|
353
380
|
}
|
|
354
|
-
|
|
381
|
+
else if (input != null) {
|
|
382
|
+
return [[ROOT, input]];
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
355
385
|
}
|
|
356
386
|
// to avoid name collision below
|
|
357
387
|
const nodeKey = key;
|
|
358
|
-
|
|
388
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
389
|
+
function _getUpdates(input) {
|
|
359
390
|
if (!input) {
|
|
360
|
-
return
|
|
391
|
+
return null;
|
|
361
392
|
}
|
|
362
|
-
else if (
|
|
393
|
+
else if (isCommand(input)) {
|
|
363
394
|
if (input.graph === Command.PARENT) {
|
|
364
|
-
return
|
|
395
|
+
return null;
|
|
365
396
|
}
|
|
366
|
-
return
|
|
397
|
+
return input._updateAsTuples();
|
|
367
398
|
}
|
|
368
|
-
else if (
|
|
399
|
+
else if (Array.isArray(input) &&
|
|
400
|
+
input.length > 0 &&
|
|
401
|
+
input.some(isCommand)) {
|
|
402
|
+
const updates = [];
|
|
403
|
+
for (const item of input) {
|
|
404
|
+
if (isCommand(item)) {
|
|
405
|
+
if (item.graph === Command.PARENT) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
updates.push(...item._updateAsTuples());
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
const itemUpdates = _getUpdates(item);
|
|
412
|
+
if (itemUpdates) {
|
|
413
|
+
updates.push(...(itemUpdates ?? []));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return updates;
|
|
418
|
+
}
|
|
419
|
+
else if (typeof input === "object" && !Array.isArray(input)) {
|
|
420
|
+
return Object.entries(input).filter(([k]) => outputKeys.includes(k));
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
369
423
|
const typeofInput = Array.isArray(input) ? "array" : typeof input;
|
|
370
424
|
throw new InvalidUpdateError(`Expected node "${nodeKey.toString()}" to return an object, received ${typeofInput}`, {
|
|
371
425
|
lc_error_code: "INVALID_GRAPH_NODE_RETURN_VALUE",
|
|
372
426
|
});
|
|
373
427
|
}
|
|
374
|
-
else {
|
|
375
|
-
return key in input ? input[key] : SKIP_WRITE;
|
|
376
|
-
}
|
|
377
428
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
? {
|
|
381
|
-
channel: key,
|
|
429
|
+
const stateWriteEntries = [
|
|
430
|
+
{
|
|
382
431
|
value: PASSTHROUGH,
|
|
383
|
-
skipNone: true,
|
|
384
432
|
mapper: new RunnableCallable({
|
|
385
|
-
func:
|
|
433
|
+
func: outputKeys.length && outputKeys[0] === ROOT
|
|
434
|
+
? _getRoot
|
|
435
|
+
: _getUpdates,
|
|
386
436
|
trace: false,
|
|
387
437
|
recurse: false,
|
|
388
438
|
}),
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
channel: key,
|
|
392
|
-
value: PASSTHROUGH,
|
|
393
|
-
mapper: new RunnableCallable({
|
|
394
|
-
func: getStateKey.bind(null, key),
|
|
395
|
-
trace: false,
|
|
396
|
-
recurse: false,
|
|
397
|
-
}),
|
|
398
|
-
});
|
|
439
|
+
},
|
|
440
|
+
];
|
|
399
441
|
// add node and output channel
|
|
400
442
|
if (key === START) {
|
|
401
443
|
this.nodes[key] = new PregelNode({
|
|
@@ -536,7 +578,7 @@ function _controlBranch(value) {
|
|
|
536
578
|
if (_isSend(value)) {
|
|
537
579
|
return [value];
|
|
538
580
|
}
|
|
539
|
-
if (!
|
|
581
|
+
if (!isCommand(value)) {
|
|
540
582
|
return [];
|
|
541
583
|
}
|
|
542
584
|
if (value.graph === Command.PARENT) {
|
|
@@ -108,7 +108,7 @@ function _getModelPreprocessingRunnable(stateModifier, messageModifier) {
|
|
|
108
108
|
* ```
|
|
109
109
|
*/
|
|
110
110
|
function createReactAgent(params) {
|
|
111
|
-
const { llm, tools, messageModifier, stateModifier, checkpointSaver, interruptBefore, interruptAfter, store, } = params;
|
|
111
|
+
const { llm, tools, messageModifier, stateModifier, stateSchema, checkpointSaver, interruptBefore, interruptAfter, store, } = params;
|
|
112
112
|
let toolClasses;
|
|
113
113
|
if (!Array.isArray(tools)) {
|
|
114
114
|
toolClasses = tools.tools;
|
|
@@ -138,7 +138,7 @@ function createReactAgent(params) {
|
|
|
138
138
|
// TODO: Auto-promote streaming.
|
|
139
139
|
return { messages: [await modelRunnable.invoke(state, config)] };
|
|
140
140
|
};
|
|
141
|
-
const workflow = new index_js_1.StateGraph(messages_annotation_js_1.MessagesAnnotation)
|
|
141
|
+
const workflow = new index_js_1.StateGraph(stateSchema ?? messages_annotation_js_1.MessagesAnnotation)
|
|
142
142
|
.addNode("agent", callModel)
|
|
143
143
|
.addNode("tools", new tool_node_js_1.ToolNode(toolClasses))
|
|
144
144
|
.addEdge(index_js_1.START, "agent")
|
|
@@ -3,7 +3,7 @@ import { BaseMessage, BaseMessageLike, SystemMessage } from "@langchain/core/mes
|
|
|
3
3
|
import { Runnable, RunnableToolLike } from "@langchain/core/runnables";
|
|
4
4
|
import { StructuredToolInterface } from "@langchain/core/tools";
|
|
5
5
|
import { All, BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
|
|
6
|
-
import { START, CompiledStateGraph } from "../graph/index.js";
|
|
6
|
+
import { START, CompiledStateGraph, AnnotationRoot } from "../graph/index.js";
|
|
7
7
|
import { MessagesAnnotation } from "../graph/messages_annotation.js";
|
|
8
8
|
import { ToolNode } from "./tool_node.js";
|
|
9
9
|
import { LangGraphRunnableConfig } from "../pregel/runnable_types.js";
|
|
@@ -14,11 +14,11 @@ export type N = typeof START | "agent" | "tools";
|
|
|
14
14
|
export type StateModifier = SystemMessage | string | ((state: typeof MessagesAnnotation.State, config: LangGraphRunnableConfig) => BaseMessageLike[]) | ((state: typeof MessagesAnnotation.State, config: LangGraphRunnableConfig) => Promise<BaseMessageLike[]>) | Runnable;
|
|
15
15
|
/** @deprecated Use StateModifier instead. */
|
|
16
16
|
export type MessageModifier = SystemMessage | string | ((messages: BaseMessage[]) => BaseMessage[]) | ((messages: BaseMessage[]) => Promise<BaseMessage[]>) | Runnable;
|
|
17
|
-
export type CreateReactAgentParams = {
|
|
17
|
+
export type CreateReactAgentParams<A extends AnnotationRoot<any> = AnnotationRoot<any>> = {
|
|
18
18
|
/** The chat model that can utilize OpenAI-style tool calling. */
|
|
19
19
|
llm: BaseChatModel;
|
|
20
20
|
/** A list of tools or a ToolNode. */
|
|
21
|
-
tools: ToolNode
|
|
21
|
+
tools: ToolNode | (StructuredToolInterface | RunnableToolLike)[];
|
|
22
22
|
/**
|
|
23
23
|
* @deprecated
|
|
24
24
|
* Use stateModifier instead. stateModifier works the same as
|
|
@@ -73,6 +73,7 @@ export type CreateReactAgentParams = {
|
|
|
73
73
|
* - Runnable: This runnable should take in full graph state and the output is then passed to the language model.
|
|
74
74
|
*/
|
|
75
75
|
stateModifier?: StateModifier;
|
|
76
|
+
stateSchema?: A;
|
|
76
77
|
/** An optional checkpoint saver to persist the agent's state. */
|
|
77
78
|
checkpointSaver?: BaseCheckpointSaver;
|
|
78
79
|
/** An optional list of node names to interrupt before running. */
|
|
@@ -123,4 +124,4 @@ export type CreateReactAgentParams = {
|
|
|
123
124
|
* // Returns the messages in the state at each step of execution
|
|
124
125
|
* ```
|
|
125
126
|
*/
|
|
126
|
-
export declare function createReactAgent(params: CreateReactAgentParams): CompiledStateGraph<(typeof MessagesAnnotation)["State"], (typeof MessagesAnnotation)["Update"], typeof START | "agent" | "tools">;
|
|
127
|
+
export declare function createReactAgent<A extends AnnotationRoot<any> = AnnotationRoot<any>>(params: CreateReactAgentParams<A>): CompiledStateGraph<(typeof MessagesAnnotation)["State"], (typeof MessagesAnnotation)["Update"], typeof START | "agent" | "tools", typeof MessagesAnnotation.spec & A["spec"], typeof MessagesAnnotation.spec & A["spec"]>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isAIMessage, isBaseMessage, SystemMessage, } from "@langchain/core/messages";
|
|
2
2
|
import { Runnable, RunnableLambda, } from "@langchain/core/runnables";
|
|
3
|
-
import { END, START, StateGraph } from "../graph/index.js";
|
|
3
|
+
import { END, START, StateGraph, } from "../graph/index.js";
|
|
4
4
|
import { MessagesAnnotation } from "../graph/messages_annotation.js";
|
|
5
5
|
import { ToolNode } from "./tool_node.js";
|
|
6
6
|
function _convertMessageModifierToStateModifier(messageModifier) {
|
|
@@ -105,7 +105,7 @@ function _getModelPreprocessingRunnable(stateModifier, messageModifier) {
|
|
|
105
105
|
* ```
|
|
106
106
|
*/
|
|
107
107
|
export function createReactAgent(params) {
|
|
108
|
-
const { llm, tools, messageModifier, stateModifier, checkpointSaver, interruptBefore, interruptAfter, store, } = params;
|
|
108
|
+
const { llm, tools, messageModifier, stateModifier, stateSchema, checkpointSaver, interruptBefore, interruptAfter, store, } = params;
|
|
109
109
|
let toolClasses;
|
|
110
110
|
if (!Array.isArray(tools)) {
|
|
111
111
|
toolClasses = tools.tools;
|
|
@@ -135,7 +135,7 @@ export function createReactAgent(params) {
|
|
|
135
135
|
// TODO: Auto-promote streaming.
|
|
136
136
|
return { messages: [await modelRunnable.invoke(state, config)] };
|
|
137
137
|
};
|
|
138
|
-
const workflow = new StateGraph(MessagesAnnotation)
|
|
138
|
+
const workflow = new StateGraph(stateSchema ?? MessagesAnnotation)
|
|
139
139
|
.addNode("agent", callModel)
|
|
140
140
|
.addNode("tools", new ToolNode(toolClasses))
|
|
141
141
|
.addEdge(START, "agent")
|
|
@@ -5,6 +5,7 @@ const messages_1 = require("@langchain/core/messages");
|
|
|
5
5
|
const utils_js_1 = require("../utils.cjs");
|
|
6
6
|
const graph_js_1 = require("../graph/graph.cjs");
|
|
7
7
|
const errors_js_1 = require("../errors.cjs");
|
|
8
|
+
const constants_js_1 = require("../constants.cjs");
|
|
8
9
|
/**
|
|
9
10
|
* A node that runs the tools requested in the last AIMessage. It can be used
|
|
10
11
|
* either in StateGraph with a "messages" key or in MessageGraph. If multiple
|
|
@@ -163,7 +164,8 @@ class ToolNode extends utils_js_1.RunnableCallable {
|
|
|
163
164
|
throw new Error(`Tool "${call.name}" not found.`);
|
|
164
165
|
}
|
|
165
166
|
const output = await tool.invoke({ ...call, type: "tool_call" }, config);
|
|
166
|
-
if ((0, messages_1.isBaseMessage)(output) && output._getType() === "tool")
|
|
167
|
+
if (((0, messages_1.isBaseMessage)(output) && output._getType() === "tool") ||
|
|
168
|
+
(0, constants_js_1.isCommand)(output)) {
|
|
167
169
|
return output;
|
|
168
170
|
}
|
|
169
171
|
else {
|
|
@@ -192,7 +194,18 @@ class ToolNode extends utils_js_1.RunnableCallable {
|
|
|
192
194
|
});
|
|
193
195
|
}
|
|
194
196
|
}) ?? []);
|
|
195
|
-
|
|
197
|
+
// Preserve existing behavior for non-command tool outputs for backwards compatibility
|
|
198
|
+
if (!outputs.some(constants_js_1.isCommand)) {
|
|
199
|
+
return (Array.isArray(input) ? outputs : { messages: outputs });
|
|
200
|
+
}
|
|
201
|
+
// Handle mixed Command and non-Command outputs
|
|
202
|
+
const combinedOutputs = outputs.map((output) => {
|
|
203
|
+
if ((0, constants_js_1.isCommand)(output)) {
|
|
204
|
+
return output;
|
|
205
|
+
}
|
|
206
|
+
return Array.isArray(input) ? [output] : { messages: [output] };
|
|
207
|
+
});
|
|
208
|
+
return combinedOutputs;
|
|
196
209
|
}
|
|
197
210
|
}
|
|
198
211
|
exports.ToolNode = ToolNode;
|
|
@@ -2,6 +2,7 @@ import { ToolMessage, isBaseMessage, } from "@langchain/core/messages";
|
|
|
2
2
|
import { RunnableCallable } from "../utils.js";
|
|
3
3
|
import { END } from "../graph/graph.js";
|
|
4
4
|
import { isGraphInterrupt } from "../errors.js";
|
|
5
|
+
import { isCommand } from "../constants.js";
|
|
5
6
|
/**
|
|
6
7
|
* A node that runs the tools requested in the last AIMessage. It can be used
|
|
7
8
|
* either in StateGraph with a "messages" key or in MessageGraph. If multiple
|
|
@@ -160,7 +161,8 @@ export class ToolNode extends RunnableCallable {
|
|
|
160
161
|
throw new Error(`Tool "${call.name}" not found.`);
|
|
161
162
|
}
|
|
162
163
|
const output = await tool.invoke({ ...call, type: "tool_call" }, config);
|
|
163
|
-
if (isBaseMessage(output) && output._getType() === "tool")
|
|
164
|
+
if ((isBaseMessage(output) && output._getType() === "tool") ||
|
|
165
|
+
isCommand(output)) {
|
|
164
166
|
return output;
|
|
165
167
|
}
|
|
166
168
|
else {
|
|
@@ -189,7 +191,18 @@ export class ToolNode extends RunnableCallable {
|
|
|
189
191
|
});
|
|
190
192
|
}
|
|
191
193
|
}) ?? []);
|
|
192
|
-
|
|
194
|
+
// Preserve existing behavior for non-command tool outputs for backwards compatibility
|
|
195
|
+
if (!outputs.some(isCommand)) {
|
|
196
|
+
return (Array.isArray(input) ? outputs : { messages: outputs });
|
|
197
|
+
}
|
|
198
|
+
// Handle mixed Command and non-Command outputs
|
|
199
|
+
const combinedOutputs = outputs.map((output) => {
|
|
200
|
+
if (isCommand(output)) {
|
|
201
|
+
return output;
|
|
202
|
+
}
|
|
203
|
+
return Array.isArray(input) ? [output] : { messages: [output] };
|
|
204
|
+
});
|
|
205
|
+
return combinedOutputs;
|
|
193
206
|
}
|
|
194
207
|
}
|
|
195
208
|
export function toolsCondition(state) {
|
package/dist/pregel/index.cjs
CHANGED
|
@@ -345,6 +345,19 @@ class Pregel extends runnables_1.Runnable {
|
|
|
345
345
|
});
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
|
+
// apply pending writes
|
|
349
|
+
const nullWrites = (saved.pendingWrites ?? [])
|
|
350
|
+
.filter((w) => w[0] === constants_js_1.NULL_TASK_ID)
|
|
351
|
+
.map((w) => w.slice(1));
|
|
352
|
+
if (nullWrites.length > 0) {
|
|
353
|
+
(0, algo_js_1._applyWrites)(saved.checkpoint, channels, [
|
|
354
|
+
{
|
|
355
|
+
name: constants_js_1.INPUT,
|
|
356
|
+
writes: nullWrites,
|
|
357
|
+
triggers: [],
|
|
358
|
+
},
|
|
359
|
+
]);
|
|
360
|
+
}
|
|
348
361
|
// assemble the state snapshot
|
|
349
362
|
return {
|
|
350
363
|
values: (0, io_js_1.readChannels)(channels, this.streamChannelsAsIs),
|
|
@@ -505,7 +518,7 @@ class Pregel extends runnables_1.Runnable {
|
|
|
505
518
|
// apply null writes
|
|
506
519
|
const nullWrites = (saved.pendingWrites || [])
|
|
507
520
|
.filter((w) => w[0] === constants_js_1.NULL_TASK_ID)
|
|
508
|
-
.
|
|
521
|
+
.map((w) => w.slice(1));
|
|
509
522
|
if (nullWrites.length > 0) {
|
|
510
523
|
(0, algo_js_1._applyWrites)(saved.checkpoint, channels, [
|
|
511
524
|
{
|
|
@@ -547,6 +560,45 @@ class Pregel extends runnables_1.Runnable {
|
|
|
547
560
|
}, {});
|
|
548
561
|
return (0, index_js_1.patchCheckpointMap)(nextConfig, saved ? saved.metadata : undefined);
|
|
549
562
|
}
|
|
563
|
+
// apply pending writes, if not on specific checkpoint
|
|
564
|
+
if (config.configurable?.checkpoint_id === undefined &&
|
|
565
|
+
saved?.pendingWrites !== undefined &&
|
|
566
|
+
saved.pendingWrites.length > 0) {
|
|
567
|
+
// tasks for this checkpoint
|
|
568
|
+
const nextTasks = (0, algo_js_1._prepareNextTasks)(checkpoint, saved.pendingWrites, this.nodes, channels, managed, saved.config, true, {
|
|
569
|
+
store: this.store,
|
|
570
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
571
|
+
checkpointer: this.checkpointer,
|
|
572
|
+
step: (saved.metadata?.step ?? -1) + 1,
|
|
573
|
+
});
|
|
574
|
+
// apply null writes
|
|
575
|
+
const nullWrites = (saved.pendingWrites ?? [])
|
|
576
|
+
.filter((w) => w[0] === constants_js_1.NULL_TASK_ID)
|
|
577
|
+
.map((w) => w.slice(1));
|
|
578
|
+
if (nullWrites.length > 0) {
|
|
579
|
+
(0, algo_js_1._applyWrites)(saved.checkpoint, channels, [
|
|
580
|
+
{
|
|
581
|
+
name: constants_js_1.INPUT,
|
|
582
|
+
writes: nullWrites,
|
|
583
|
+
triggers: [],
|
|
584
|
+
},
|
|
585
|
+
]);
|
|
586
|
+
}
|
|
587
|
+
// apply writes
|
|
588
|
+
for (const [tid, k, v] of saved.pendingWrites) {
|
|
589
|
+
if ([constants_js_1.ERROR, constants_js_1.INTERRUPT, langgraph_checkpoint_1.SCHEDULED].includes(k) ||
|
|
590
|
+
nextTasks[tid] === undefined) {
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
nextTasks[tid].writes.push([k, v]);
|
|
594
|
+
}
|
|
595
|
+
const tasks = Object.values(nextTasks).filter((task) => {
|
|
596
|
+
return task.writes.length > 0;
|
|
597
|
+
});
|
|
598
|
+
if (tasks.length > 0) {
|
|
599
|
+
(0, algo_js_1._applyWrites)(checkpoint, channels, tasks);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
550
602
|
const nonNullVersion = Object.values(checkpoint.versions_seen)
|
|
551
603
|
.map((seenVersions) => {
|
|
552
604
|
return Object.values(seenVersions);
|
package/dist/pregel/index.js
CHANGED
|
@@ -341,6 +341,19 @@ export class Pregel extends Runnable {
|
|
|
341
341
|
});
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
|
+
// apply pending writes
|
|
345
|
+
const nullWrites = (saved.pendingWrites ?? [])
|
|
346
|
+
.filter((w) => w[0] === NULL_TASK_ID)
|
|
347
|
+
.map((w) => w.slice(1));
|
|
348
|
+
if (nullWrites.length > 0) {
|
|
349
|
+
_applyWrites(saved.checkpoint, channels, [
|
|
350
|
+
{
|
|
351
|
+
name: INPUT,
|
|
352
|
+
writes: nullWrites,
|
|
353
|
+
triggers: [],
|
|
354
|
+
},
|
|
355
|
+
]);
|
|
356
|
+
}
|
|
344
357
|
// assemble the state snapshot
|
|
345
358
|
return {
|
|
346
359
|
values: readChannels(channels, this.streamChannelsAsIs),
|
|
@@ -501,7 +514,7 @@ export class Pregel extends Runnable {
|
|
|
501
514
|
// apply null writes
|
|
502
515
|
const nullWrites = (saved.pendingWrites || [])
|
|
503
516
|
.filter((w) => w[0] === NULL_TASK_ID)
|
|
504
|
-
.
|
|
517
|
+
.map((w) => w.slice(1));
|
|
505
518
|
if (nullWrites.length > 0) {
|
|
506
519
|
_applyWrites(saved.checkpoint, channels, [
|
|
507
520
|
{
|
|
@@ -543,6 +556,45 @@ export class Pregel extends Runnable {
|
|
|
543
556
|
}, {});
|
|
544
557
|
return patchCheckpointMap(nextConfig, saved ? saved.metadata : undefined);
|
|
545
558
|
}
|
|
559
|
+
// apply pending writes, if not on specific checkpoint
|
|
560
|
+
if (config.configurable?.checkpoint_id === undefined &&
|
|
561
|
+
saved?.pendingWrites !== undefined &&
|
|
562
|
+
saved.pendingWrites.length > 0) {
|
|
563
|
+
// tasks for this checkpoint
|
|
564
|
+
const nextTasks = _prepareNextTasks(checkpoint, saved.pendingWrites, this.nodes, channels, managed, saved.config, true, {
|
|
565
|
+
store: this.store,
|
|
566
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
567
|
+
checkpointer: this.checkpointer,
|
|
568
|
+
step: (saved.metadata?.step ?? -1) + 1,
|
|
569
|
+
});
|
|
570
|
+
// apply null writes
|
|
571
|
+
const nullWrites = (saved.pendingWrites ?? [])
|
|
572
|
+
.filter((w) => w[0] === NULL_TASK_ID)
|
|
573
|
+
.map((w) => w.slice(1));
|
|
574
|
+
if (nullWrites.length > 0) {
|
|
575
|
+
_applyWrites(saved.checkpoint, channels, [
|
|
576
|
+
{
|
|
577
|
+
name: INPUT,
|
|
578
|
+
writes: nullWrites,
|
|
579
|
+
triggers: [],
|
|
580
|
+
},
|
|
581
|
+
]);
|
|
582
|
+
}
|
|
583
|
+
// apply writes
|
|
584
|
+
for (const [tid, k, v] of saved.pendingWrites) {
|
|
585
|
+
if ([ERROR, INTERRUPT, SCHEDULED].includes(k) ||
|
|
586
|
+
nextTasks[tid] === undefined) {
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
nextTasks[tid].writes.push([k, v]);
|
|
590
|
+
}
|
|
591
|
+
const tasks = Object.values(nextTasks).filter((task) => {
|
|
592
|
+
return task.writes.length > 0;
|
|
593
|
+
});
|
|
594
|
+
if (tasks.length > 0) {
|
|
595
|
+
_applyWrites(checkpoint, channels, tasks);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
546
598
|
const nonNullVersion = Object.values(checkpoint.versions_seen)
|
|
547
599
|
.map((seenVersions) => {
|
|
548
600
|
return Object.values(seenVersions);
|
package/dist/pregel/io.cjs
CHANGED
|
@@ -72,17 +72,16 @@ function* mapCommand(cmd, pendingWrites) {
|
|
|
72
72
|
throw new Error(`In Command.send, expected Send or string, got ${typeof send}`);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
// TODO: handle goto str for state graph
|
|
76
75
|
}
|
|
77
76
|
if (cmd.resume) {
|
|
78
77
|
if (typeof cmd.resume === "object" &&
|
|
79
|
-
!!cmd.resume &&
|
|
80
78
|
Object.keys(cmd.resume).length &&
|
|
81
79
|
Object.keys(cmd.resume).every(uuid_1.validate)) {
|
|
82
80
|
for (const [tid, resume] of Object.entries(cmd.resume)) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
const existing = pendingWrites
|
|
82
|
+
.filter((w) => w[0] === tid && w[1] === constants_js_1.RESUME)
|
|
83
|
+
.map((w) => w[2])
|
|
84
|
+
.slice(0, 1) ?? [];
|
|
86
85
|
existing.push(resume);
|
|
87
86
|
yield [tid, constants_js_1.RESUME, existing];
|
|
88
87
|
}
|
package/dist/pregel/io.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare function readChannels<C extends PropertyKey>(channels: Record<C,
|
|
|
7
7
|
/**
|
|
8
8
|
* Map input chunk to a sequence of pending writes in the form (channel, value).
|
|
9
9
|
*/
|
|
10
|
-
export declare function mapCommand(cmd: Command, pendingWrites: CheckpointPendingWrite
|
|
10
|
+
export declare function mapCommand(cmd: Command, pendingWrites: CheckpointPendingWrite[]): Generator<[string, string, unknown]>;
|
|
11
11
|
/**
|
|
12
12
|
* Map input chunk to a sequence of pending writes in the form [channel, value].
|
|
13
13
|
*/
|
package/dist/pregel/io.js
CHANGED
|
@@ -67,17 +67,16 @@ export function* mapCommand(cmd, pendingWrites) {
|
|
|
67
67
|
throw new Error(`In Command.send, expected Send or string, got ${typeof send}`);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
-
// TODO: handle goto str for state graph
|
|
71
70
|
}
|
|
72
71
|
if (cmd.resume) {
|
|
73
72
|
if (typeof cmd.resume === "object" &&
|
|
74
|
-
!!cmd.resume &&
|
|
75
73
|
Object.keys(cmd.resume).length &&
|
|
76
74
|
Object.keys(cmd.resume).every(validate)) {
|
|
77
75
|
for (const [tid, resume] of Object.entries(cmd.resume)) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
const existing = pendingWrites
|
|
77
|
+
.filter((w) => w[0] === tid && w[1] === RESUME)
|
|
78
|
+
.map((w) => w[2])
|
|
79
|
+
.slice(0, 1) ?? [];
|
|
81
80
|
existing.push(resume);
|
|
82
81
|
yield [tid, RESUME, existing];
|
|
83
82
|
}
|
package/dist/pregel/loop.cjs
CHANGED
|
@@ -409,14 +409,9 @@ class PregelLoop {
|
|
|
409
409
|
}
|
|
410
410
|
// save writes
|
|
411
411
|
for (const [c, v] of writesCopy) {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
this.checkpointPendingWrites[idx] = [taskId, c, v];
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
419
|
-
}
|
|
412
|
+
const idx = this.checkpointPendingWrites.findIndex((w) => w[0] === taskId && w[1] === c);
|
|
413
|
+
if (c in langgraph_checkpoint_1.WRITES_IDX_MAP && idx !== -1) {
|
|
414
|
+
this.checkpointPendingWrites[idx] = [taskId, c, v];
|
|
420
415
|
}
|
|
421
416
|
else {
|
|
422
417
|
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
@@ -586,14 +581,14 @@ class PregelLoop {
|
|
|
586
581
|
/**
|
|
587
582
|
* Resuming from previous checkpoint requires
|
|
588
583
|
* - finding a previous checkpoint
|
|
589
|
-
* - receiving
|
|
584
|
+
* - receiving null input (outer graph) or RESUMING flag (subgraph)
|
|
590
585
|
*/
|
|
591
586
|
async _first(inputKeys) {
|
|
592
587
|
const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
|
|
593
588
|
(this.config.configurable?.[constants_js_1.CONFIG_KEY_RESUMING] !== undefined ||
|
|
594
589
|
this.input === null ||
|
|
595
|
-
(0, constants_js_1.
|
|
596
|
-
if ((0, constants_js_1.
|
|
590
|
+
(0, constants_js_1.isCommand)(this.input));
|
|
591
|
+
if ((0, constants_js_1.isCommand)(this.input)) {
|
|
597
592
|
const writes = {};
|
|
598
593
|
// group writes by task id
|
|
599
594
|
for (const [tid, key, value] of (0, io_js_1.mapCommand)(this.input, this.checkpointPendingWrites)) {
|
|
@@ -610,6 +605,19 @@ class PregelLoop {
|
|
|
610
605
|
this.putWrites(tid, ws);
|
|
611
606
|
}
|
|
612
607
|
}
|
|
608
|
+
// apply null writes
|
|
609
|
+
const nullWrites = (this.checkpointPendingWrites ?? [])
|
|
610
|
+
.filter((w) => w[0] === constants_js_1.NULL_TASK_ID)
|
|
611
|
+
.map((w) => w.slice(1));
|
|
612
|
+
if (nullWrites.length > 0) {
|
|
613
|
+
(0, algo_js_1._applyWrites)(this.checkpoint, this.channels, [
|
|
614
|
+
{
|
|
615
|
+
name: constants_js_1.INPUT,
|
|
616
|
+
writes: nullWrites,
|
|
617
|
+
triggers: [],
|
|
618
|
+
},
|
|
619
|
+
], this.checkpointerGetNextVersion);
|
|
620
|
+
}
|
|
613
621
|
if (isResuming) {
|
|
614
622
|
for (const channelName of Object.keys(this.channels)) {
|
|
615
623
|
if (this.checkpoint.channel_versions[channelName] !== undefined) {
|
package/dist/pregel/loop.d.ts
CHANGED
|
@@ -116,7 +116,7 @@ export declare class PregelLoop {
|
|
|
116
116
|
/**
|
|
117
117
|
* Resuming from previous checkpoint requires
|
|
118
118
|
* - finding a previous checkpoint
|
|
119
|
-
* - receiving
|
|
119
|
+
* - receiving null input (outer graph) or RESUMING flag (subgraph)
|
|
120
120
|
*/
|
|
121
121
|
protected _first(inputKeys: string | string[]): Promise<void>;
|
|
122
122
|
protected _emit(values: [StreamMode, unknown][]): void;
|
package/dist/pregel/loop.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IterableReadableStream } from "@langchain/core/utils/stream";
|
|
2
2
|
import { copyCheckpoint, emptyCheckpoint, AsyncBatchedStore, WRITES_IDX_MAP, } from "@langchain/langgraph-checkpoint";
|
|
3
3
|
import { createCheckpoint, emptyChannels, } from "../channels/base.js";
|
|
4
|
-
import {
|
|
4
|
+
import { isCommand, CHECKPOINT_NAMESPACE_SEPARATOR, CONFIG_KEY_CHECKPOINT_MAP, CONFIG_KEY_READ, CONFIG_KEY_RESUMING, CONFIG_KEY_STREAM, ERROR, INPUT, INTERRUPT, NULL_TASK_ID, RESUME, TAG_HIDDEN, } from "../constants.js";
|
|
5
5
|
import { _applyWrites, _prepareNextTasks, increment, shouldInterrupt, } from "./algo.js";
|
|
6
6
|
import { gatherIterator, gatherIteratorSync, prefixGenerator, } from "../utils.js";
|
|
7
7
|
import { mapCommand, mapInput, mapOutputUpdates, mapOutputValues, readChannels, } from "./io.js";
|
|
@@ -405,14 +405,9 @@ export class PregelLoop {
|
|
|
405
405
|
}
|
|
406
406
|
// save writes
|
|
407
407
|
for (const [c, v] of writesCopy) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
this.checkpointPendingWrites[idx] = [taskId, c, v];
|
|
412
|
-
}
|
|
413
|
-
else {
|
|
414
|
-
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
415
|
-
}
|
|
408
|
+
const idx = this.checkpointPendingWrites.findIndex((w) => w[0] === taskId && w[1] === c);
|
|
409
|
+
if (c in WRITES_IDX_MAP && idx !== -1) {
|
|
410
|
+
this.checkpointPendingWrites[idx] = [taskId, c, v];
|
|
416
411
|
}
|
|
417
412
|
else {
|
|
418
413
|
this.checkpointPendingWrites.push([taskId, c, v]);
|
|
@@ -582,14 +577,14 @@ export class PregelLoop {
|
|
|
582
577
|
/**
|
|
583
578
|
* Resuming from previous checkpoint requires
|
|
584
579
|
* - finding a previous checkpoint
|
|
585
|
-
* - receiving
|
|
580
|
+
* - receiving null input (outer graph) or RESUMING flag (subgraph)
|
|
586
581
|
*/
|
|
587
582
|
async _first(inputKeys) {
|
|
588
583
|
const isResuming = Object.keys(this.checkpoint.channel_versions).length !== 0 &&
|
|
589
584
|
(this.config.configurable?.[CONFIG_KEY_RESUMING] !== undefined ||
|
|
590
585
|
this.input === null ||
|
|
591
|
-
|
|
592
|
-
if (
|
|
586
|
+
isCommand(this.input));
|
|
587
|
+
if (isCommand(this.input)) {
|
|
593
588
|
const writes = {};
|
|
594
589
|
// group writes by task id
|
|
595
590
|
for (const [tid, key, value] of mapCommand(this.input, this.checkpointPendingWrites)) {
|
|
@@ -606,6 +601,19 @@ export class PregelLoop {
|
|
|
606
601
|
this.putWrites(tid, ws);
|
|
607
602
|
}
|
|
608
603
|
}
|
|
604
|
+
// apply null writes
|
|
605
|
+
const nullWrites = (this.checkpointPendingWrites ?? [])
|
|
606
|
+
.filter((w) => w[0] === NULL_TASK_ID)
|
|
607
|
+
.map((w) => w.slice(1));
|
|
608
|
+
if (nullWrites.length > 0) {
|
|
609
|
+
_applyWrites(this.checkpoint, this.channels, [
|
|
610
|
+
{
|
|
611
|
+
name: INPUT,
|
|
612
|
+
writes: nullWrites,
|
|
613
|
+
triggers: [],
|
|
614
|
+
},
|
|
615
|
+
], this.checkpointerGetNextVersion);
|
|
616
|
+
}
|
|
609
617
|
if (isResuming) {
|
|
610
618
|
for (const channelName of Object.keys(this.channels)) {
|
|
611
619
|
if (this.checkpoint.channel_versions[channelName] !== undefined) {
|