@langchain/langgraph 0.0.1
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/LICENSE +21 -0
- package/README.md +588 -0
- package/dist/channels/base.cjs +58 -0
- package/dist/channels/base.d.ts +46 -0
- package/dist/channels/base.js +50 -0
- package/dist/channels/binop.cjs +70 -0
- package/dist/channels/binop.d.ts +16 -0
- package/dist/channels/binop.js +66 -0
- package/dist/channels/index.cjs +9 -0
- package/dist/channels/index.d.ts +1 -0
- package/dist/channels/index.js +1 -0
- package/dist/channels/last_value.cjs +53 -0
- package/dist/channels/last_value.d.ts +12 -0
- package/dist/channels/last_value.js +49 -0
- package/dist/channels/topic.cjs +90 -0
- package/dist/channels/topic.d.ts +19 -0
- package/dist/channels/topic.js +86 -0
- package/dist/checkpoint/base.cjs +32 -0
- package/dist/checkpoint/base.d.ts +47 -0
- package/dist/checkpoint/base.js +27 -0
- package/dist/checkpoint/index.cjs +8 -0
- package/dist/checkpoint/index.d.ts +2 -0
- package/dist/checkpoint/index.js +2 -0
- package/dist/checkpoint/memory.cjs +35 -0
- package/dist/checkpoint/memory.d.ts +8 -0
- package/dist/checkpoint/memory.js +31 -0
- package/dist/constants.cjs +5 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/graph/graph.cjs +175 -0
- package/dist/graph/graph.d.ts +30 -0
- package/dist/graph/graph.js +171 -0
- package/dist/graph/index.cjs +9 -0
- package/dist/graph/index.d.ts +2 -0
- package/dist/graph/index.js +2 -0
- package/dist/graph/state.cjs +108 -0
- package/dist/graph/state.d.ts +17 -0
- package/dist/graph/state.js +104 -0
- package/dist/index.cjs +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/prebuilt/agent_executor.cjs +96 -0
- package/dist/prebuilt/agent_executor.d.ts +12 -0
- package/dist/prebuilt/agent_executor.js +92 -0
- package/dist/prebuilt/chat_agent_executor.cjs +130 -0
- package/dist/prebuilt/chat_agent_executor.d.ts +6 -0
- package/dist/prebuilt/chat_agent_executor.js +126 -0
- package/dist/prebuilt/index.cjs +9 -0
- package/dist/prebuilt/index.d.ts +3 -0
- package/dist/prebuilt/index.js +3 -0
- package/dist/prebuilt/tool_executor.cjs +63 -0
- package/dist/prebuilt/tool_executor.d.ts +27 -0
- package/dist/prebuilt/tool_executor.js +59 -0
- package/dist/pregel/debug.cjs +46 -0
- package/dist/pregel/debug.d.ts +4 -0
- package/dist/pregel/debug.js +41 -0
- package/dist/pregel/index.cjs +475 -0
- package/dist/pregel/index.d.ts +75 -0
- package/dist/pregel/index.js +469 -0
- package/dist/pregel/io.cjs +57 -0
- package/dist/pregel/io.d.ts +9 -0
- package/dist/pregel/io.js +52 -0
- package/dist/pregel/read.cjs +217 -0
- package/dist/pregel/read.d.ts +43 -0
- package/dist/pregel/read.js +211 -0
- package/dist/pregel/reserved.cjs +7 -0
- package/dist/pregel/reserved.d.ts +3 -0
- package/dist/pregel/reserved.js +4 -0
- package/dist/pregel/validate.cjs +90 -0
- package/dist/pregel/validate.d.ts +15 -0
- package/dist/pregel/validate.js +85 -0
- package/dist/pregel/write.cjs +54 -0
- package/dist/pregel/write.d.ts +13 -0
- package/dist/pregel/write.js +50 -0
- package/index.cjs +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +100 -0
- package/prebuilt.cjs +1 -0
- package/prebuilt.d.ts +1 -0
- package/prebuilt.js +1 -0
- package/pregel.cjs +1 -0
- package/pregel.d.ts +1 -0
- package/pregel.js +1 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RunnableBinding, RunnableConfig } from "@langchain/core/runnables";
|
|
2
|
+
import { Tool } from "@langchain/core/tools";
|
|
3
|
+
export interface ToolExecutorArgs {
|
|
4
|
+
tools: Array<Tool>;
|
|
5
|
+
/**
|
|
6
|
+
* @default {INVALID_TOOL_MSG_TEMPLATE}
|
|
7
|
+
*/
|
|
8
|
+
invalidToolMsgTemplate?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Interface for invoking a tool
|
|
12
|
+
*/
|
|
13
|
+
export interface ToolInvocationInterface {
|
|
14
|
+
tool: string;
|
|
15
|
+
toolInput: string;
|
|
16
|
+
}
|
|
17
|
+
type ToolExecutorInputType = any;
|
|
18
|
+
type ToolExecutorOutputType = any;
|
|
19
|
+
export declare class ToolExecutor extends RunnableBinding<ToolExecutorInputType, ToolExecutorOutputType> {
|
|
20
|
+
lc_graph_name: string;
|
|
21
|
+
tools: Array<Tool>;
|
|
22
|
+
toolMap: Record<string, Tool>;
|
|
23
|
+
invalidToolMsgTemplate: string;
|
|
24
|
+
constructor(fields: ToolExecutorArgs);
|
|
25
|
+
_execute(toolInvocation: ToolInvocationInterface, _config?: RunnableConfig): Promise<string>;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { RunnableBinding, RunnableLambda, } from "@langchain/core/runnables";
|
|
2
|
+
const INVALID_TOOL_MSG_TEMPLATE = `{requestedToolName} is not a valid tool, try one of [availableToolNamesString].`;
|
|
3
|
+
export class ToolExecutor extends RunnableBinding {
|
|
4
|
+
constructor(fields) {
|
|
5
|
+
const fieldsWithDefaults = {
|
|
6
|
+
invalidToolMsgTemplate: INVALID_TOOL_MSG_TEMPLATE,
|
|
7
|
+
...fields,
|
|
8
|
+
};
|
|
9
|
+
const bound = new RunnableLambda({
|
|
10
|
+
func: async (input, options) => this._execute(input, options?.config),
|
|
11
|
+
});
|
|
12
|
+
super({
|
|
13
|
+
bound,
|
|
14
|
+
config: {},
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(this, "lc_graph_name", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: "ToolExecutor"
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "tools", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "toolMap", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: void 0
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(this, "invalidToolMsgTemplate", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: void 0
|
|
39
|
+
});
|
|
40
|
+
this.tools = fieldsWithDefaults.tools;
|
|
41
|
+
this.invalidToolMsgTemplate = fieldsWithDefaults.invalidToolMsgTemplate;
|
|
42
|
+
this.toolMap = this.tools.reduce((acc, tool) => {
|
|
43
|
+
acc[tool.name] = tool;
|
|
44
|
+
return acc;
|
|
45
|
+
}, {});
|
|
46
|
+
}
|
|
47
|
+
async _execute(toolInvocation, _config) {
|
|
48
|
+
if (!(toolInvocation.tool in this.toolMap)) {
|
|
49
|
+
return this.invalidToolMsgTemplate
|
|
50
|
+
.replace("{requestedToolName}", toolInvocation.tool)
|
|
51
|
+
.replace("{availableToolNamesString}", Object.keys(this.toolMap).join(", "));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const tool = this.toolMap[toolInvocation.tool];
|
|
55
|
+
const output = await tool.invoke(toolInvocation.toolInput);
|
|
56
|
+
return output;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printCheckpoint = exports.printStepStart = void 0;
|
|
4
|
+
const base_js_1 = require("../channels/base.cjs");
|
|
5
|
+
const COLORS_MAP = {
|
|
6
|
+
blue: {
|
|
7
|
+
start: "\x1b[34m",
|
|
8
|
+
end: "\x1b[0m",
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Wrap some text in a color for printing to the console.
|
|
13
|
+
*/
|
|
14
|
+
const wrap = (color, text) => `${color.start}${text}${color.end}`;
|
|
15
|
+
function printStepStart(step,
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
nextTasks) {
|
|
18
|
+
const nTasks = nextTasks.length;
|
|
19
|
+
console.log(`${wrap(COLORS_MAP.blue, "[pregel/step]")}`, `Starting step ${step} with ${nTasks} task${nTasks === 1 ? "" : "s"}. Next tasks:\n`, `\n${nextTasks
|
|
20
|
+
.map(([_, val, name]) => `- ${name}(${JSON.stringify(val, null, 2)})`)
|
|
21
|
+
.join("\n")}`);
|
|
22
|
+
}
|
|
23
|
+
exports.printStepStart = printStepStart;
|
|
24
|
+
function printCheckpoint(step, channels) {
|
|
25
|
+
console.log(`${wrap(COLORS_MAP.blue, "[pregel/checkpoint]")}`, `Finishing step ${step}. Channel values:\n`, `\n${JSON.stringify(Object.fromEntries(_readChannels(channels)), null, 2)}`);
|
|
26
|
+
}
|
|
27
|
+
exports.printCheckpoint = printCheckpoint;
|
|
28
|
+
function* _readChannels(channels
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
) {
|
|
31
|
+
for (const [name, channel] of Object.entries(channels)) {
|
|
32
|
+
try {
|
|
33
|
+
yield [name, channel.get()];
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.name === base_js_1.EmptyChannelError.name) {
|
|
38
|
+
// Skip the channel if it's empty
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw error; // Re-throw the error if it's not an EmptyChannelError
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Runnable } from "@langchain/core/runnables";
|
|
2
|
+
import { BaseChannel } from "../channels/base.js";
|
|
3
|
+
export declare function printStepStart(step: number, nextTasks: Array<[Runnable, any, string]>): void;
|
|
4
|
+
export declare function printCheckpoint<Value>(step: number, channels: Record<string, BaseChannel<Value>>): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EmptyChannelError } from "../channels/base.js";
|
|
2
|
+
const COLORS_MAP = {
|
|
3
|
+
blue: {
|
|
4
|
+
start: "\x1b[34m",
|
|
5
|
+
end: "\x1b[0m",
|
|
6
|
+
},
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Wrap some text in a color for printing to the console.
|
|
10
|
+
*/
|
|
11
|
+
const wrap = (color, text) => `${color.start}${text}${color.end}`;
|
|
12
|
+
export function printStepStart(step,
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
nextTasks) {
|
|
15
|
+
const nTasks = nextTasks.length;
|
|
16
|
+
console.log(`${wrap(COLORS_MAP.blue, "[pregel/step]")}`, `Starting step ${step} with ${nTasks} task${nTasks === 1 ? "" : "s"}. Next tasks:\n`, `\n${nextTasks
|
|
17
|
+
.map(([_, val, name]) => `- ${name}(${JSON.stringify(val, null, 2)})`)
|
|
18
|
+
.join("\n")}`);
|
|
19
|
+
}
|
|
20
|
+
export function printCheckpoint(step, channels) {
|
|
21
|
+
console.log(`${wrap(COLORS_MAP.blue, "[pregel/checkpoint]")}`, `Finishing step ${step}. Channel values:\n`, `\n${JSON.stringify(Object.fromEntries(_readChannels(channels)), null, 2)}`);
|
|
22
|
+
}
|
|
23
|
+
function* _readChannels(channels
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
) {
|
|
26
|
+
for (const [name, channel] of Object.entries(channels)) {
|
|
27
|
+
try {
|
|
28
|
+
yield [name, channel.get()];
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error.name === EmptyChannelError.name) {
|
|
33
|
+
// Skip the channel if it's empty
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
throw error; // Re-throw the error if it's not an EmptyChannelError
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Pregel = exports.Channel = exports.GraphRecursionError = void 0;
|
|
4
|
+
/* eslint-disable no-param-reassign */
|
|
5
|
+
const runnables_1 = require("@langchain/core/runnables");
|
|
6
|
+
const stream_1 = require("@langchain/core/utils/stream");
|
|
7
|
+
const base_js_1 = require("../channels/base.cjs");
|
|
8
|
+
const base_js_2 = require("../checkpoint/base.cjs");
|
|
9
|
+
const read_js_1 = require("./read.cjs");
|
|
10
|
+
const validate_js_1 = require("./validate.cjs");
|
|
11
|
+
const reserved_js_1 = require("./reserved.cjs");
|
|
12
|
+
const io_js_1 = require("./io.cjs");
|
|
13
|
+
const write_js_1 = require("./write.cjs");
|
|
14
|
+
const constants_js_1 = require("../constants.cjs");
|
|
15
|
+
const DEFAULT_RECURSION_LIMIT = 25;
|
|
16
|
+
class GraphRecursionError extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "GraphRecursionError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.GraphRecursionError = GraphRecursionError;
|
|
23
|
+
function _coerceWriteValue(value) {
|
|
24
|
+
if (!runnables_1.Runnable.isRunnable(value) && typeof value !== "function") {
|
|
25
|
+
return (0, runnables_1._coerceToRunnable)(() => value);
|
|
26
|
+
}
|
|
27
|
+
return (0, runnables_1._coerceToRunnable)(value);
|
|
28
|
+
}
|
|
29
|
+
function isString(value) {
|
|
30
|
+
return typeof value === "string";
|
|
31
|
+
}
|
|
32
|
+
class Channel {
|
|
33
|
+
static subscribeTo(channels, options) {
|
|
34
|
+
const { key, when, tags } = options ?? {};
|
|
35
|
+
if (Array.isArray(channels) && key !== undefined) {
|
|
36
|
+
throw new Error("Can't specify a key when subscribing to multiple channels");
|
|
37
|
+
}
|
|
38
|
+
let channelMappingOrString;
|
|
39
|
+
if (isString(channels)) {
|
|
40
|
+
if (key) {
|
|
41
|
+
channelMappingOrString = { [key]: channels };
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
channelMappingOrString = channels;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
channelMappingOrString = Object.fromEntries(channels.map((chan) => [chan, chan]));
|
|
49
|
+
}
|
|
50
|
+
const triggers = Array.isArray(channels) ? channels : [channels];
|
|
51
|
+
return new read_js_1.ChannelInvoke({
|
|
52
|
+
channels: channelMappingOrString,
|
|
53
|
+
triggers,
|
|
54
|
+
when,
|
|
55
|
+
tags,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
static subscribeToEach(inbox, key) {
|
|
59
|
+
return new read_js_1.ChannelBatch({
|
|
60
|
+
channel: inbox,
|
|
61
|
+
key,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
|
+
static writeTo(...args) {
|
|
66
|
+
// const channelPairs: Array<[string, WriteValue<RunInput, RunOutput>]> =
|
|
67
|
+
// channels.map((c) => [c, undefined]);
|
|
68
|
+
// return new ChannelWrite<RunInput, RunOutput>(channelPairs);
|
|
69
|
+
const channelPairs = [];
|
|
70
|
+
if (args.length === 1 && typeof args[0] === "object") {
|
|
71
|
+
// Handle the case where named arguments are passed as an object
|
|
72
|
+
const additionalArgs = args[0];
|
|
73
|
+
Object.entries(additionalArgs).forEach(([key, value]) => {
|
|
74
|
+
channelPairs.push([key, _coerceWriteValue(value)]);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
args.forEach((channel) => {
|
|
79
|
+
if (typeof channel === "string") {
|
|
80
|
+
channelPairs.push([channel, undefined]);
|
|
81
|
+
}
|
|
82
|
+
else if (typeof channel === "object") {
|
|
83
|
+
Object.entries(channel).forEach(([key, value]) => {
|
|
84
|
+
channelPairs.push([key, _coerceWriteValue(value)]);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return new write_js_1.ChannelWrite(channelPairs);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.Channel = Channel;
|
|
93
|
+
class Pregel extends runnables_1.Runnable {
|
|
94
|
+
constructor(fields) {
|
|
95
|
+
super();
|
|
96
|
+
// Because Pregel extends `Runnable`.
|
|
97
|
+
Object.defineProperty(this, "lc_namespace", {
|
|
98
|
+
enumerable: true,
|
|
99
|
+
configurable: true,
|
|
100
|
+
writable: true,
|
|
101
|
+
value: ["langgraph", "pregel"]
|
|
102
|
+
});
|
|
103
|
+
Object.defineProperty(this, "channels", {
|
|
104
|
+
enumerable: true,
|
|
105
|
+
configurable: true,
|
|
106
|
+
writable: true,
|
|
107
|
+
value: {}
|
|
108
|
+
});
|
|
109
|
+
Object.defineProperty(this, "output", {
|
|
110
|
+
enumerable: true,
|
|
111
|
+
configurable: true,
|
|
112
|
+
writable: true,
|
|
113
|
+
value: "output"
|
|
114
|
+
});
|
|
115
|
+
Object.defineProperty(this, "input", {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
configurable: true,
|
|
118
|
+
writable: true,
|
|
119
|
+
value: "input"
|
|
120
|
+
});
|
|
121
|
+
Object.defineProperty(this, "hidden", {
|
|
122
|
+
enumerable: true,
|
|
123
|
+
configurable: true,
|
|
124
|
+
writable: true,
|
|
125
|
+
value: []
|
|
126
|
+
});
|
|
127
|
+
Object.defineProperty(this, "debug", {
|
|
128
|
+
enumerable: true,
|
|
129
|
+
configurable: true,
|
|
130
|
+
writable: true,
|
|
131
|
+
value: false
|
|
132
|
+
});
|
|
133
|
+
Object.defineProperty(this, "nodes", {
|
|
134
|
+
enumerable: true,
|
|
135
|
+
configurable: true,
|
|
136
|
+
writable: true,
|
|
137
|
+
value: void 0
|
|
138
|
+
});
|
|
139
|
+
Object.defineProperty(this, "checkpointer", {
|
|
140
|
+
enumerable: true,
|
|
141
|
+
configurable: true,
|
|
142
|
+
writable: true,
|
|
143
|
+
value: void 0
|
|
144
|
+
});
|
|
145
|
+
Object.defineProperty(this, "stepTimeout", {
|
|
146
|
+
enumerable: true,
|
|
147
|
+
configurable: true,
|
|
148
|
+
writable: true,
|
|
149
|
+
value: void 0
|
|
150
|
+
});
|
|
151
|
+
Object.defineProperty(this, "interrupt", {
|
|
152
|
+
enumerable: true,
|
|
153
|
+
configurable: true,
|
|
154
|
+
writable: true,
|
|
155
|
+
value: []
|
|
156
|
+
});
|
|
157
|
+
this.channels = fields.channels ?? this.channels;
|
|
158
|
+
this.output = fields.output ?? this.output;
|
|
159
|
+
this.input = fields.input ?? this.input;
|
|
160
|
+
this.hidden = fields.hidden ?? this.hidden;
|
|
161
|
+
this.debug = fields.debug ?? this.debug;
|
|
162
|
+
this.nodes = fields.nodes;
|
|
163
|
+
this.checkpointer = fields.checkpointer;
|
|
164
|
+
this.stepTimeout = fields.stepTimeout;
|
|
165
|
+
this.interrupt = fields.interrupt ?? this.interrupt;
|
|
166
|
+
// Bind the method to the instance
|
|
167
|
+
this._transform = this._transform.bind(this);
|
|
168
|
+
(0, validate_js_1.validateGraph)({
|
|
169
|
+
nodes: this.nodes,
|
|
170
|
+
channels: this.channels,
|
|
171
|
+
output: this.output,
|
|
172
|
+
input: this.input,
|
|
173
|
+
hidden: this.hidden,
|
|
174
|
+
interrupt: this.interrupt,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async *_transform(input, runManager, config = {}) {
|
|
178
|
+
// assign defaults
|
|
179
|
+
let outputKeys = [];
|
|
180
|
+
if (Array.isArray(config.outputKeys) ||
|
|
181
|
+
typeof config.outputKeys === "string") {
|
|
182
|
+
outputKeys = config.outputKeys;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
for (const chan in this.channels) {
|
|
186
|
+
if (!this.hidden.includes(chan)) {
|
|
187
|
+
outputKeys.push(chan);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// copy nodes to ignore mutations during execution
|
|
192
|
+
const processes = { ...this.nodes };
|
|
193
|
+
// get checkpoint, or create an empty one
|
|
194
|
+
let checkpoint;
|
|
195
|
+
if (this.checkpointer) {
|
|
196
|
+
checkpoint = this.checkpointer.get(config);
|
|
197
|
+
}
|
|
198
|
+
checkpoint = checkpoint ?? (0, base_js_2.emptyCheckpoint)();
|
|
199
|
+
// create channels from checkpoint
|
|
200
|
+
const channels = (0, base_js_1.emptyChannels)(this.channels, checkpoint);
|
|
201
|
+
// map inputs to channel updates
|
|
202
|
+
const thisInput = this.input;
|
|
203
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
204
|
+
const inputPendingWrites = [];
|
|
205
|
+
for await (const c of input) {
|
|
206
|
+
for (const value of (0, io_js_1.mapInput)(thisInput, c)) {
|
|
207
|
+
inputPendingWrites.push(value);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
_applyWrites(checkpoint, channels, inputPendingWrites, config, 0);
|
|
211
|
+
const read = (chan) => _readChannel(channels, chan);
|
|
212
|
+
// Similarly to Bulk Synchronous Parallel / Pregel model
|
|
213
|
+
// computation proceeds in steps, while there are channel updates
|
|
214
|
+
// channel updates from step N are only visible in step N+1
|
|
215
|
+
// channels are guaranteed to be immutable for the duration of the step,
|
|
216
|
+
// with channel updates applied only at the transition between steps
|
|
217
|
+
const recursionLimit = config.recursionLimit ?? DEFAULT_RECURSION_LIMIT;
|
|
218
|
+
for (let step = 0; step < recursionLimit + 1; step += 1) {
|
|
219
|
+
const nextTasks = _prepareNextTasks(checkpoint, processes, channels);
|
|
220
|
+
// if no more tasks, we're done
|
|
221
|
+
if (nextTasks.length === 0) {
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
else if (step === config.recursionLimit) {
|
|
225
|
+
throw new GraphRecursionError(`Recursion limit of ${config.recursionLimit} reached without hitting a stop condition. You can increase the limit by setting the "recursionLimit" config key.`);
|
|
226
|
+
}
|
|
227
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
228
|
+
const pendingWrites = [];
|
|
229
|
+
const tasksWithConfig = nextTasks.map(([proc, input, name]) => [
|
|
230
|
+
proc,
|
|
231
|
+
input,
|
|
232
|
+
(0, runnables_1.patchConfig)(config, {
|
|
233
|
+
callbacks: runManager?.getChild(`graph:step:${step}`),
|
|
234
|
+
runName: name,
|
|
235
|
+
configurable: {
|
|
236
|
+
...config.configurable,
|
|
237
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
238
|
+
[constants_js_1.CONFIG_KEY_SEND]: (items) => pendingWrites.push(...items),
|
|
239
|
+
[constants_js_1.CONFIG_KEY_READ]: read,
|
|
240
|
+
},
|
|
241
|
+
}),
|
|
242
|
+
]);
|
|
243
|
+
// execute tasks, and wait for one to fail or all to finish.
|
|
244
|
+
// each task is independent from all other concurrent tasks
|
|
245
|
+
const tasks = tasksWithConfig.map(([proc, input, updatedConfig]) => () => proc.invoke(input, updatedConfig));
|
|
246
|
+
await executeTasks(tasks, this.stepTimeout);
|
|
247
|
+
// apply writes to channels
|
|
248
|
+
_applyWrites(checkpoint, channels, pendingWrites, config, step + 1);
|
|
249
|
+
// yield current value and checkpoint view
|
|
250
|
+
const stepOutput = (0, io_js_1.mapOutput)(outputKeys, pendingWrites, channels);
|
|
251
|
+
if (stepOutput) {
|
|
252
|
+
yield stepOutput;
|
|
253
|
+
if (typeof outputKeys !== "string") {
|
|
254
|
+
_applyWritesFromView(checkpoint, channels, stepOutput);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// save end of step checkpoint
|
|
258
|
+
if (this.checkpointer &&
|
|
259
|
+
this.checkpointer.at === base_js_2.CheckpointAt.END_OF_STEP) {
|
|
260
|
+
checkpoint = await (0, base_js_1.createCheckpoint)(checkpoint, channels);
|
|
261
|
+
this.checkpointer.put(config, checkpoint);
|
|
262
|
+
}
|
|
263
|
+
// interrupt if any channel written to is in interrupt list
|
|
264
|
+
if (pendingWrites.some(([chan]) => this.interrupt?.some((i) => i === chan))) {
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// save end of run checkpoint
|
|
269
|
+
if (this.checkpointer && this.checkpointer.at === base_js_2.CheckpointAt.END_OF_RUN) {
|
|
270
|
+
checkpoint = await (0, base_js_1.createCheckpoint)(checkpoint, channels);
|
|
271
|
+
this.checkpointer.put(config, checkpoint);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async invoke(input, config) {
|
|
275
|
+
if (!config?.outputKeys) {
|
|
276
|
+
if (!config) {
|
|
277
|
+
config = {};
|
|
278
|
+
}
|
|
279
|
+
config.outputKeys = this.output;
|
|
280
|
+
}
|
|
281
|
+
let latest;
|
|
282
|
+
for await (const chunk of await this.stream(input, config)) {
|
|
283
|
+
latest = chunk;
|
|
284
|
+
}
|
|
285
|
+
if (!latest) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
return latest;
|
|
289
|
+
}
|
|
290
|
+
async stream(input, config) {
|
|
291
|
+
const inputIterator = (async function* () {
|
|
292
|
+
yield input;
|
|
293
|
+
})();
|
|
294
|
+
return stream_1.IterableReadableStream.fromAsyncGenerator(this.transform(inputIterator, config));
|
|
295
|
+
}
|
|
296
|
+
async *transform(generator, config) {
|
|
297
|
+
for await (const chunk of this._transformStreamWithConfig(generator, this._transform, config)) {
|
|
298
|
+
yield chunk;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
exports.Pregel = Pregel;
|
|
303
|
+
function timeout(ms) {
|
|
304
|
+
return new Promise((reject) => {
|
|
305
|
+
setTimeout(reject, ms);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
async function executeTasks(tasks, stepTimeout) {
|
|
309
|
+
// Wrap each task in a Promise that respects the step timeout
|
|
310
|
+
const wrappedTasks = tasks.map((task) => stepTimeout
|
|
311
|
+
? Promise.race([
|
|
312
|
+
task(),
|
|
313
|
+
stepTimeout ? timeout(stepTimeout) : Promise.resolve(),
|
|
314
|
+
])
|
|
315
|
+
: task());
|
|
316
|
+
// Wait for all tasks to settle
|
|
317
|
+
const results = await Promise.allSettled(wrappedTasks);
|
|
318
|
+
// Process the results
|
|
319
|
+
for (const result of results) {
|
|
320
|
+
if (result.status === "rejected") {
|
|
321
|
+
// If any task failed, cancel all pending tasks and throw the error
|
|
322
|
+
throw result.reason;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function _readChannel(channels, chan) {
|
|
327
|
+
try {
|
|
328
|
+
return channels[chan].get();
|
|
329
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
330
|
+
}
|
|
331
|
+
catch (e) {
|
|
332
|
+
if (e.name === base_js_1.EmptyChannelError.name) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
throw e;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function _applyWrites(checkpoint, channels,
|
|
339
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
340
|
+
pendingWrites, config, forStep) {
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
342
|
+
const pendingWritesByChannel = {};
|
|
343
|
+
// Group writes by channel
|
|
344
|
+
for (const [chan, val] of pendingWrites) {
|
|
345
|
+
for (const c in reserved_js_1.ReservedChannels) {
|
|
346
|
+
if (chan === c) {
|
|
347
|
+
throw new Error(`Can't write to reserved channel ${chan}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (chan in pendingWritesByChannel) {
|
|
351
|
+
pendingWritesByChannel[chan].push(val);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
pendingWritesByChannel[chan] = [val];
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Update reserved channels
|
|
358
|
+
pendingWritesByChannel[reserved_js_1.ReservedChannels.isLastStep] = [
|
|
359
|
+
forStep + 1 === config.recursionLimit,
|
|
360
|
+
];
|
|
361
|
+
const updatedChannels = new Set();
|
|
362
|
+
// Apply writes to channels
|
|
363
|
+
for (const chan in pendingWritesByChannel) {
|
|
364
|
+
if (chan in pendingWritesByChannel) {
|
|
365
|
+
const vals = pendingWritesByChannel[chan];
|
|
366
|
+
if (chan in channels) {
|
|
367
|
+
channels[chan].update(vals);
|
|
368
|
+
if (checkpoint.channelVersions[chan] === undefined) {
|
|
369
|
+
checkpoint.channelVersions[chan] = 1;
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
checkpoint.channelVersions[chan] += 1;
|
|
373
|
+
}
|
|
374
|
+
updatedChannels.add(chan);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.warn(`Skipping write for channel ${chan} which has no readers`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// Channels that weren't updated in this step are notified of a new step
|
|
382
|
+
for (const chan in channels) {
|
|
383
|
+
if (!updatedChannels.has(chan)) {
|
|
384
|
+
channels[chan].update([]);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function _applyWritesFromView(checkpoint, channels, values) {
|
|
389
|
+
for (const [chan, val] of Object.entries(values)) {
|
|
390
|
+
if (val === _readChannel(channels, chan)) {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (channels[chan].lc_graph_name === "LastValue") {
|
|
394
|
+
throw new Error(`Can't modify channel ${chan} with LastValue`);
|
|
395
|
+
}
|
|
396
|
+
checkpoint.channelVersions[chan] += 1;
|
|
397
|
+
channels[chan].update([values[chan]]);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function _prepareNextTasks(checkpoint, processes, channels) {
|
|
401
|
+
const tasks = [];
|
|
402
|
+
// Check if any processes should be run in next step
|
|
403
|
+
// If so, prepare the values to be passed to them
|
|
404
|
+
for (const name in processes) {
|
|
405
|
+
if (Object.prototype.hasOwnProperty.call(processes, name)) {
|
|
406
|
+
const proc = processes[name];
|
|
407
|
+
let seen = checkpoint.versionsSeen[name];
|
|
408
|
+
if (!seen) {
|
|
409
|
+
checkpoint.versionsSeen[name] = {};
|
|
410
|
+
seen = checkpoint.versionsSeen[name];
|
|
411
|
+
}
|
|
412
|
+
if ("triggers" in proc) {
|
|
413
|
+
// If any of the channels read by this process were updated
|
|
414
|
+
if (proc.triggers.some((chan) => checkpoint.channelVersions[chan] > (seen[chan] ?? 0))) {
|
|
415
|
+
// If all channels subscribed by this process have been initialized
|
|
416
|
+
try {
|
|
417
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
418
|
+
let val = {};
|
|
419
|
+
if (typeof proc.channels === "string") {
|
|
420
|
+
val[proc.channels] = _readChannel(channels, proc.channels);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
for (const [k, chan] of Object.entries(proc.channels)) {
|
|
424
|
+
val[k] = _readChannel(channels, chan);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Processes that subscribe to a single keyless channel get
|
|
428
|
+
// the value directly, instead of a dict
|
|
429
|
+
if (typeof proc.channels === "string") {
|
|
430
|
+
val = val[proc.channels];
|
|
431
|
+
}
|
|
432
|
+
else if (Object.keys(proc.channels).length === 1 &&
|
|
433
|
+
proc.channels[Object.keys(proc.channels)[0]] === undefined) {
|
|
434
|
+
val = val[Object.keys(proc.channels)[0]];
|
|
435
|
+
}
|
|
436
|
+
// Update seen versions
|
|
437
|
+
proc.triggers.forEach((chan) => {
|
|
438
|
+
const version = checkpoint.channelVersions[chan];
|
|
439
|
+
if (version !== undefined) {
|
|
440
|
+
seen[chan] = version;
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
// skip if condition is not met
|
|
444
|
+
if (proc.when === undefined || proc.when(val)) {
|
|
445
|
+
tasks.push([proc, val, name]);
|
|
446
|
+
}
|
|
447
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
448
|
+
}
|
|
449
|
+
catch (error) {
|
|
450
|
+
if (error.name === base_js_1.EmptyChannelError.name) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
else if ("channel" in proc) {
|
|
460
|
+
// If the channel read by this process was updated
|
|
461
|
+
if (checkpoint.channelVersions[proc.channel] > (seen[proc.channel] ?? 0)) {
|
|
462
|
+
// Here we don't catch EmptyChannelError because the channel
|
|
463
|
+
// must be initialized if the previous `if` condition is true
|
|
464
|
+
let val = channels[proc.channel].get();
|
|
465
|
+
if (proc.key !== undefined) {
|
|
466
|
+
val = [{ [proc.key]: val }];
|
|
467
|
+
}
|
|
468
|
+
tasks.push([proc, val, name]);
|
|
469
|
+
seen[proc.channel] = checkpoint.channelVersions[proc.channel];
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return tasks;
|
|
475
|
+
}
|