@langchain/langgraph 0.0.11 → 0.0.13

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.
Files changed (134) hide show
  1. package/dist/channels/any_value.cjs +57 -0
  2. package/dist/channels/any_value.d.ts +16 -0
  3. package/dist/channels/any_value.js +53 -0
  4. package/dist/channels/base.cjs +19 -28
  5. package/dist/channels/base.d.ts +13 -19
  6. package/dist/channels/base.js +17 -24
  7. package/dist/channels/binop.cjs +4 -3
  8. package/dist/channels/binop.d.ts +1 -1
  9. package/dist/channels/binop.js +3 -2
  10. package/dist/channels/dynamic_barrier_value.cjs +88 -0
  11. package/dist/channels/dynamic_barrier_value.d.ts +26 -0
  12. package/dist/channels/dynamic_barrier_value.js +84 -0
  13. package/dist/channels/ephemeral_value.cjs +64 -0
  14. package/dist/channels/ephemeral_value.d.ts +14 -0
  15. package/dist/channels/ephemeral_value.js +60 -0
  16. package/dist/channels/index.cjs +1 -3
  17. package/dist/channels/index.d.ts +1 -1
  18. package/dist/channels/index.js +1 -1
  19. package/dist/channels/last_value.cjs +11 -5
  20. package/dist/channels/last_value.d.ts +5 -1
  21. package/dist/channels/last_value.js +9 -3
  22. package/dist/channels/named_barrier_value.cjs +71 -0
  23. package/dist/channels/named_barrier_value.d.ts +18 -0
  24. package/dist/channels/named_barrier_value.js +66 -0
  25. package/dist/channels/topic.cjs +5 -3
  26. package/dist/channels/topic.d.ts +3 -3
  27. package/dist/channels/topic.js +5 -3
  28. package/dist/checkpoint/base.cjs +30 -12
  29. package/dist/checkpoint/base.d.ts +39 -22
  30. package/dist/checkpoint/base.js +28 -11
  31. package/dist/checkpoint/id.cjs +40 -0
  32. package/dist/checkpoint/id.d.ts +2 -0
  33. package/dist/checkpoint/id.js +35 -0
  34. package/dist/checkpoint/index.cjs +2 -2
  35. package/dist/checkpoint/index.d.ts +2 -2
  36. package/dist/checkpoint/index.js +2 -2
  37. package/dist/checkpoint/memory.cjs +63 -49
  38. package/dist/checkpoint/memory.d.ts +7 -10
  39. package/dist/checkpoint/memory.js +62 -47
  40. package/dist/checkpoint/sqlite.cjs +170 -0
  41. package/dist/checkpoint/sqlite.d.ts +14 -0
  42. package/dist/checkpoint/sqlite.js +163 -0
  43. package/dist/constants.cjs +3 -1
  44. package/dist/constants.d.ts +2 -0
  45. package/dist/constants.js +2 -0
  46. package/dist/errors.cjs +31 -0
  47. package/dist/errors.d.ts +12 -0
  48. package/dist/errors.js +24 -0
  49. package/dist/graph/graph.cjs +234 -96
  50. package/dist/graph/graph.d.ts +52 -23
  51. package/dist/graph/graph.js +233 -97
  52. package/dist/graph/index.cjs +2 -2
  53. package/dist/graph/index.d.ts +2 -2
  54. package/dist/graph/index.js +2 -2
  55. package/dist/graph/message.cjs +4 -3
  56. package/dist/graph/message.d.ts +4 -1
  57. package/dist/graph/message.js +4 -3
  58. package/dist/graph/state.cjs +237 -102
  59. package/dist/graph/state.d.ts +41 -18
  60. package/dist/graph/state.js +238 -104
  61. package/dist/index.cjs +6 -2
  62. package/dist/index.d.ts +3 -2
  63. package/dist/index.js +2 -1
  64. package/dist/prebuilt/agent_executor.cjs +22 -36
  65. package/dist/prebuilt/agent_executor.d.ts +7 -10
  66. package/dist/prebuilt/agent_executor.js +23 -37
  67. package/dist/prebuilt/chat_agent_executor.cjs +13 -13
  68. package/dist/prebuilt/chat_agent_executor.d.ts +3 -1
  69. package/dist/prebuilt/chat_agent_executor.js +15 -15
  70. package/dist/prebuilt/index.cjs +4 -1
  71. package/dist/prebuilt/index.d.ts +1 -0
  72. package/dist/prebuilt/index.js +1 -0
  73. package/dist/prebuilt/tool_node.cjs +59 -0
  74. package/dist/prebuilt/tool_node.d.ts +17 -0
  75. package/dist/prebuilt/tool_node.js +54 -0
  76. package/dist/pregel/debug.cjs +6 -8
  77. package/dist/pregel/debug.d.ts +2 -2
  78. package/dist/pregel/debug.js +5 -7
  79. package/dist/pregel/index.cjs +406 -236
  80. package/dist/pregel/index.d.ts +77 -41
  81. package/dist/pregel/index.js +408 -241
  82. package/dist/pregel/io.cjs +117 -30
  83. package/dist/pregel/io.d.ts +11 -3
  84. package/dist/pregel/io.js +111 -28
  85. package/dist/pregel/read.cjs +126 -46
  86. package/dist/pregel/read.d.ts +27 -18
  87. package/dist/pregel/read.js +125 -45
  88. package/dist/pregel/types.cjs +2 -0
  89. package/dist/pregel/types.d.ts +32 -0
  90. package/dist/pregel/types.js +1 -0
  91. package/dist/pregel/validate.cjs +58 -51
  92. package/dist/pregel/validate.d.ts +14 -13
  93. package/dist/pregel/validate.js +56 -50
  94. package/dist/pregel/write.cjs +46 -30
  95. package/dist/pregel/write.d.ts +18 -8
  96. package/dist/pregel/write.js +45 -29
  97. package/dist/serde/base.cjs +2 -0
  98. package/dist/serde/base.d.ts +4 -0
  99. package/dist/serde/base.js +1 -0
  100. package/dist/setup/async_local_storage.cjs +2 -2
  101. package/dist/setup/async_local_storage.js +1 -1
  102. package/dist/tests/channels.test.d.ts +1 -0
  103. package/dist/tests/channels.test.js +151 -0
  104. package/dist/tests/chatbot.int.test.d.ts +1 -0
  105. package/dist/tests/chatbot.int.test.js +61 -0
  106. package/dist/tests/checkpoints.test.d.ts +1 -0
  107. package/dist/tests/checkpoints.test.js +190 -0
  108. package/dist/tests/graph.test.d.ts +1 -0
  109. package/dist/tests/graph.test.js +15 -0
  110. package/dist/tests/prebuilt.int.test.d.ts +1 -0
  111. package/dist/tests/prebuilt.int.test.js +101 -0
  112. package/dist/tests/prebuilt.test.d.ts +1 -0
  113. package/dist/tests/prebuilt.test.js +195 -0
  114. package/dist/tests/pregel.io.test.d.ts +1 -0
  115. package/dist/tests/pregel.io.test.js +332 -0
  116. package/dist/tests/pregel.read.test.d.ts +1 -0
  117. package/dist/tests/pregel.read.test.js +109 -0
  118. package/dist/tests/pregel.test.d.ts +1 -0
  119. package/dist/tests/pregel.test.js +1879 -0
  120. package/dist/tests/pregel.validate.test.d.ts +1 -0
  121. package/dist/tests/pregel.validate.test.js +198 -0
  122. package/dist/tests/pregel.write.test.d.ts +1 -0
  123. package/dist/tests/pregel.write.test.js +44 -0
  124. package/dist/tests/tracing.int.test.d.ts +1 -0
  125. package/dist/tests/tracing.int.test.js +449 -0
  126. package/dist/tests/utils.d.ts +22 -0
  127. package/dist/tests/utils.js +76 -0
  128. package/dist/utils.cjs +74 -0
  129. package/dist/utils.d.ts +18 -0
  130. package/dist/utils.js +70 -0
  131. package/package.json +12 -8
  132. package/dist/pregel/reserved.cjs +0 -6
  133. package/dist/pregel/reserved.d.ts +0 -3
  134. package/dist/pregel/reserved.js +0 -3
@@ -0,0 +1,163 @@
1
+ import Database from "better-sqlite3";
2
+ import { BaseCheckpointSaver, } from "./base.js";
3
+ export class SqliteSaver extends BaseCheckpointSaver {
4
+ constructor(db, serde) {
5
+ super(serde);
6
+ Object.defineProperty(this, "db", {
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true,
10
+ value: void 0
11
+ });
12
+ Object.defineProperty(this, "isSetup", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: void 0
17
+ });
18
+ this.db = db;
19
+ this.isSetup = false;
20
+ }
21
+ static fromConnString(connStringOrLocalPath) {
22
+ return new SqliteSaver(new Database(connStringOrLocalPath));
23
+ }
24
+ setup() {
25
+ if (this.isSetup) {
26
+ return;
27
+ }
28
+ try {
29
+ this.db.pragma("journal_mode=WAL");
30
+ this.db.exec(`
31
+ CREATE TABLE IF NOT EXISTS checkpoints (
32
+ thread_id TEXT NOT NULL,
33
+ checkpoint_id TEXT NOT NULL,
34
+ parent_id TEXT,
35
+ checkpoint BLOB,
36
+ metadata BLOB,
37
+ PRIMARY KEY (thread_id, checkpoint_id)
38
+ );`);
39
+ }
40
+ catch (error) {
41
+ console.log("Error creating checkpoints table", error);
42
+ throw error;
43
+ }
44
+ this.isSetup = true;
45
+ }
46
+ async getTuple(config) {
47
+ this.setup();
48
+ const thread_id = config.configurable?.thread_id;
49
+ const checkpoint_id = config.configurable?.checkpoint_id;
50
+ if (checkpoint_id) {
51
+ try {
52
+ const row = this.db
53
+ .prepare(`SELECT checkpoint, parent_id, metadata FROM checkpoints WHERE thread_id = ? AND checkpoint_id = ?`)
54
+ .get(thread_id, checkpoint_id);
55
+ if (row) {
56
+ return {
57
+ config,
58
+ checkpoint: this.serde.parse(row.checkpoint),
59
+ metadata: this.serde.parse(row.metadata),
60
+ parentConfig: row.parent_id
61
+ ? {
62
+ configurable: {
63
+ thread_id,
64
+ checkpoint_id: row.parent_id,
65
+ },
66
+ }
67
+ : undefined,
68
+ };
69
+ }
70
+ }
71
+ catch (error) {
72
+ console.log("Error retrieving checkpoint", error);
73
+ throw error;
74
+ }
75
+ }
76
+ else {
77
+ const row = this.db
78
+ .prepare(`SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM checkpoints WHERE thread_id = ? ORDER BY checkpoint_id DESC LIMIT 1`)
79
+ .get(thread_id);
80
+ if (row) {
81
+ return {
82
+ config: {
83
+ configurable: {
84
+ thread_id: row.thread_id,
85
+ checkpoint_id: row.checkpoint_id,
86
+ },
87
+ },
88
+ checkpoint: this.serde.parse(row.checkpoint),
89
+ metadata: this.serde.parse(row.metadata),
90
+ parentConfig: row.parent_id
91
+ ? {
92
+ configurable: {
93
+ thread_id: row.thread_id,
94
+ checkpoint_id: row.parent_id,
95
+ },
96
+ }
97
+ : undefined,
98
+ };
99
+ }
100
+ }
101
+ return undefined;
102
+ }
103
+ async *list(config) {
104
+ this.setup();
105
+ const thread_id = config.configurable?.thread_id;
106
+ try {
107
+ const rows = this.db
108
+ .prepare(`SELECT thread_id, checkpoint_id, parent_id, checkpoint, metadata FROM checkpoints WHERE thread_id = ? ORDER BY checkpoint_id DESC`)
109
+ .all(thread_id);
110
+ if (rows) {
111
+ for (const row of rows) {
112
+ yield {
113
+ config: {
114
+ configurable: {
115
+ thread_id: row.thread_id,
116
+ checkpoint_id: row.checkpoint_id,
117
+ },
118
+ },
119
+ checkpoint: this.serde.parse(row.checkpoint),
120
+ metadata: this.serde.parse(row.metadata),
121
+ parentConfig: row.parent_id
122
+ ? {
123
+ configurable: {
124
+ thread_id: row.thread_id,
125
+ checkpoint_id: row.parent_id,
126
+ },
127
+ }
128
+ : undefined,
129
+ };
130
+ }
131
+ }
132
+ }
133
+ catch (error) {
134
+ console.log("Error listing checkpoints", error);
135
+ throw error;
136
+ }
137
+ }
138
+ async put(config, checkpoint, metadata) {
139
+ this.setup();
140
+ try {
141
+ const row = [
142
+ config.configurable?.thread_id,
143
+ checkpoint.id,
144
+ config.configurable?.checkpoint_id,
145
+ this.serde.stringify(checkpoint),
146
+ this.serde.stringify(metadata),
147
+ ];
148
+ this.db
149
+ .prepare(`INSERT OR REPLACE INTO checkpoints (thread_id, checkpoint_id, parent_id, checkpoint, metadata) VALUES (?, ?, ?, ?, ?)`)
150
+ .run(...row);
151
+ }
152
+ catch (error) {
153
+ console.log("Error saving checkpoint", error);
154
+ throw error;
155
+ }
156
+ return {
157
+ configurable: {
158
+ thread_id: config.configurable?.thread_id,
159
+ checkpoint_id: checkpoint.id,
160
+ },
161
+ };
162
+ }
163
+ }
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONFIG_KEY_READ = exports.CONFIG_KEY_SEND = void 0;
3
+ exports.TAG_HIDDEN = exports.INTERRUPT = exports.CONFIG_KEY_READ = exports.CONFIG_KEY_SEND = void 0;
4
4
  exports.CONFIG_KEY_SEND = "__pregel_send";
5
5
  exports.CONFIG_KEY_READ = "__pregel_read";
6
+ exports.INTERRUPT = "__interrupt__";
7
+ exports.TAG_HIDDEN = "langsmith:hidden";
@@ -1,2 +1,4 @@
1
1
  export declare const CONFIG_KEY_SEND = "__pregel_send";
2
2
  export declare const CONFIG_KEY_READ = "__pregel_read";
3
+ export declare const INTERRUPT = "__interrupt__";
4
+ export declare const TAG_HIDDEN = "langsmith:hidden";
package/dist/constants.js CHANGED
@@ -1,2 +1,4 @@
1
1
  export const CONFIG_KEY_SEND = "__pregel_send";
2
2
  export const CONFIG_KEY_READ = "__pregel_read";
3
+ export const INTERRUPT = "__interrupt__";
4
+ export const TAG_HIDDEN = "langsmith:hidden";
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidUpdateError = exports.EmptyChannelError = exports.GraphValueError = exports.GraphRecursionError = void 0;
4
+ class GraphRecursionError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "GraphRecursionError";
8
+ }
9
+ }
10
+ exports.GraphRecursionError = GraphRecursionError;
11
+ class GraphValueError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "GraphValueError";
15
+ }
16
+ }
17
+ exports.GraphValueError = GraphValueError;
18
+ class EmptyChannelError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = "EmptyChannelError";
22
+ }
23
+ }
24
+ exports.EmptyChannelError = EmptyChannelError;
25
+ class InvalidUpdateError extends Error {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = "InvalidUpdateError";
29
+ }
30
+ }
31
+ exports.InvalidUpdateError = InvalidUpdateError;
@@ -0,0 +1,12 @@
1
+ export declare class GraphRecursionError extends Error {
2
+ constructor(message?: string);
3
+ }
4
+ export declare class GraphValueError extends Error {
5
+ constructor(message?: string);
6
+ }
7
+ export declare class EmptyChannelError extends Error {
8
+ constructor(message?: string);
9
+ }
10
+ export declare class InvalidUpdateError extends Error {
11
+ constructor(message?: string);
12
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,24 @@
1
+ export class GraphRecursionError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "GraphRecursionError";
5
+ }
6
+ }
7
+ export class GraphValueError extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "GraphValueError";
11
+ }
12
+ }
13
+ export class EmptyChannelError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "EmptyChannelError";
17
+ }
18
+ }
19
+ export class InvalidUpdateError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "InvalidUpdateError";
23
+ }
24
+ }
@@ -1,11 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Graph = exports.END = void 0;
3
+ exports.CompiledGraph = exports.Graph = exports.Branch = exports.END = exports.START = void 0;
4
+ /* eslint-disable @typescript-eslint/no-use-before-define */
4
5
  const runnables_1 = require("@langchain/core/runnables");
6
+ const read_js_1 = require("../pregel/read.cjs");
5
7
  const index_js_1 = require("../pregel/index.cjs");
8
+ const ephemeral_value_js_1 = require("../channels/ephemeral_value.cjs");
9
+ const write_js_1 = require("../pregel/write.cjs");
10
+ const constants_js_1 = require("../constants.cjs");
11
+ const utils_js_1 = require("../utils.cjs");
12
+ exports.START = "__start__";
6
13
  exports.END = "__end__";
7
14
  class Branch {
8
- constructor(condition, ends) {
15
+ constructor(options) {
9
16
  Object.defineProperty(this, "condition", {
10
17
  enumerable: true,
11
18
  configurable: true,
@@ -18,22 +25,42 @@ class Branch {
18
25
  writable: true,
19
26
  value: void 0
20
27
  });
21
- this.condition = condition;
22
- this.ends = ends;
28
+ Object.defineProperty(this, "then", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
34
+ this.condition = options.path;
35
+ this.ends = Array.isArray(options.pathMap)
36
+ ? options.pathMap.reduce((acc, n) => {
37
+ acc[n] = n;
38
+ return acc;
39
+ }, {})
40
+ : options.pathMap;
41
+ this.then = options.then;
42
+ }
43
+ compile(writer, reader) {
44
+ return write_js_1.ChannelWrite.registerWriter(new utils_js_1.RunnableCallable({
45
+ func: (input, config) => this._route(input, config, writer, reader),
46
+ }));
23
47
  }
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
- runnable(input, options) {
26
- const result = this.condition(input, options?.config);
27
- let destination;
48
+ async _route(input, config, writer, reader) {
49
+ let result = await this.condition(reader ? reader(config) : input, config);
50
+ if (!Array.isArray(result)) {
51
+ result = [result];
52
+ }
53
+ let destinations;
28
54
  if (this.ends) {
29
- destination = this.ends[result];
55
+ destinations = result.map((r) => this.ends[r]);
30
56
  }
31
57
  else {
32
- destination = result;
58
+ destinations = result;
33
59
  }
34
- return index_js_1.Channel.writeTo(destination !== exports.END ? `${destination}:inbox` : exports.END);
60
+ return writer(destinations);
35
61
  }
36
62
  }
63
+ exports.Branch = Branch;
37
64
  class Graph {
38
65
  constructor() {
39
66
  Object.defineProperty(this, "nodes", {
@@ -66,6 +93,12 @@ class Graph {
66
93
  writable: true,
67
94
  value: false
68
95
  });
96
+ Object.defineProperty(this, "supportMultipleEdges", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: false
101
+ });
69
102
  this.nodes = {};
70
103
  this.edges = new Set();
71
104
  this.branches = {};
@@ -75,129 +108,175 @@ class Graph {
75
108
  console.warn(message);
76
109
  }
77
110
  }
111
+ get allEdges() {
112
+ return this.edges;
113
+ }
78
114
  addNode(key, action) {
79
115
  this.warnIfCompiled(`Adding a node to a graph that has already been compiled. This will not be reflected in the compiled graph.`);
80
- if (this.nodes[key]) {
116
+ if (key in this.nodes) {
81
117
  throw new Error(`Node \`${key}\` already present.`);
82
118
  }
83
119
  if (key === exports.END) {
84
120
  throw new Error(`Node \`${key}\` is reserved.`);
85
121
  }
86
122
  this.nodes[key] = (0, runnables_1._coerceToRunnable)(action);
123
+ return this;
87
124
  }
88
125
  addEdge(startKey, endKey) {
89
126
  this.warnIfCompiled(`Adding an edge to a graph that has already been compiled. This will not be reflected in the compiled graph.`);
90
127
  if (startKey === exports.END) {
91
128
  throw new Error("END cannot be a start node");
92
129
  }
93
- if (!this.nodes[startKey]) {
94
- throw new Error(`Need to addNode \`${startKey}\` first`);
95
- }
96
- if (!this.nodes[endKey] && endKey !== exports.END) {
97
- throw new Error(`Need to addNode \`${endKey}\` first`);
130
+ if (endKey === exports.START) {
131
+ throw new Error("START cannot be an end node");
98
132
  }
99
- // TODO: support multiple message passing
100
- if (Array.from(this.edges).some(([start]) => start === startKey)) {
133
+ if (!this.supportMultipleEdges &&
134
+ Array.from(this.edges).some(([start]) => start === startKey)) {
101
135
  throw new Error(`Already found path for ${startKey}`);
102
136
  }
103
137
  this.edges.add([startKey, endKey]);
138
+ return this;
104
139
  }
105
- addConditionalEdges(startKey, condition, conditionalEdgeMapping) {
140
+ addConditionalEdges(source, path, pathMap) {
141
+ const options = typeof source === "object" ? source : { source, path: path, pathMap };
106
142
  this.warnIfCompiled("Adding an edge to a graph that has already been compiled. This will not be reflected in the compiled graph.");
107
- if (!this.nodes[startKey]) {
108
- throw new Error(`Need to addNode \`${startKey}\` first`);
109
- }
110
- if (condition.constructor.name === "AsyncFunction") {
111
- throw new Error("Condition cannot be an async function");
112
- }
113
- if (conditionalEdgeMapping) {
114
- const mappingValues = Array.from(Object.values(conditionalEdgeMapping));
115
- const nodesValues = Object.keys(this.nodes);
116
- const endExcluded = mappingValues.filter((value) => value !== exports.END);
117
- const difference = endExcluded.filter((value) => !nodesValues.some((nv) => nv === value));
118
- if (difference.length > 0) {
119
- throw new Error(`Missing nodes which are in conditional edge mapping.\nMapping contains possible destinations: ${mappingValues.join(", ")}.\nPossible nodes are ${nodesValues.join(", ")}.`);
120
- }
143
+ // find a name for condition
144
+ const name = options.path.name || "condition";
145
+ // validate condition
146
+ if (this.branches[options.source] && this.branches[options.source][name]) {
147
+ throw new Error(`Condition \`${name}\` already present for node \`${source}\``);
121
148
  }
122
- if (!this.branches[startKey]) {
123
- this.branches[startKey] = [];
149
+ // save it
150
+ if (!this.branches[options.source]) {
151
+ this.branches[options.source] = {};
124
152
  }
125
- this.branches[startKey].push(new Branch(condition, conditionalEdgeMapping));
153
+ this.branches[options.source][name] = new Branch(options);
154
+ return this;
126
155
  }
156
+ /**
157
+ * @deprecated use `addEdge(START, key)` instead
158
+ */
127
159
  setEntryPoint(key) {
128
160
  this.warnIfCompiled("Setting the entry point of a graph that has already been compiled. This will not be reflected in the compiled graph.");
129
- if (!this.nodes[key]) {
130
- throw new Error(`Need to addNode \`${key}\` first`);
131
- }
132
- this.entryPoint = key;
161
+ return this.addEdge(exports.START, key);
133
162
  }
163
+ /**
164
+ * @deprecated use `addEdge(key, END)` instead
165
+ */
134
166
  setFinishPoint(key) {
135
167
  this.warnIfCompiled("Setting a finish point of a graph that has already been compiled. This will not be reflected in the compiled graph.");
136
- this.addEdge(key, exports.END);
137
- }
138
- compile(checkpointer) {
139
- this.validate();
140
- const outgoingEdges = {};
141
- this.edges.forEach(([start, end]) => {
142
- if (!outgoingEdges[start]) {
143
- outgoingEdges[start] = [];
144
- }
145
- outgoingEdges[start].push(end !== exports.END ? `${end}:inbox` : exports.END);
168
+ return this.addEdge(key, exports.END);
169
+ }
170
+ compile({ checkpointer, interruptBefore, interruptAfter, } = {}) {
171
+ // validate the graph
172
+ this.validate([
173
+ ...(Array.isArray(interruptBefore) ? interruptBefore : []),
174
+ ...(Array.isArray(interruptAfter) ? interruptAfter : []),
175
+ ]);
176
+ // create empty compiled graph
177
+ const compiled = new CompiledGraph({
178
+ builder: this,
179
+ checkpointer,
180
+ interruptAfter,
181
+ interruptBefore,
182
+ autoValidate: false,
183
+ nodes: {},
184
+ channels: {
185
+ [exports.START]: new ephemeral_value_js_1.EphemeralValue(),
186
+ [exports.END]: new ephemeral_value_js_1.EphemeralValue(),
187
+ },
188
+ inputs: exports.START,
189
+ outputs: exports.END,
190
+ streamChannels: [],
191
+ streamMode: "values",
146
192
  });
147
- const nodes = {};
193
+ // attach nodes, edges and branches
148
194
  for (const [key, node] of Object.entries(this.nodes)) {
149
- nodes[key] = index_js_1.Channel.subscribeTo(`${key}:inbox`)
150
- .pipe(node)
151
- .pipe(index_js_1.Channel.writeTo(key));
152
- }
153
- for (const key of Object.keys(this.nodes)) {
154
- const outgoing = outgoingEdges[key];
155
- const edgesKey = `${key}:edges`;
156
- if (outgoing || this.branches[key]) {
157
- nodes[edgesKey] = index_js_1.Channel.subscribeTo(key, {
158
- tags: ["langsmith:hidden"],
159
- });
195
+ compiled.attachNode(key, node);
196
+ }
197
+ for (const [start, end] of this.edges) {
198
+ compiled.attachEdge(start, end);
199
+ }
200
+ for (const [start, branches] of Object.entries(this.branches)) {
201
+ for (const [name, branch] of Object.entries(branches)) {
202
+ compiled.attachBranch(start, name, branch);
160
203
  }
161
- if (outgoing) {
162
- nodes[edgesKey] = nodes[edgesKey].pipe(index_js_1.Channel.writeTo(...outgoing));
204
+ }
205
+ return compiled.validate();
206
+ }
207
+ validate(interrupt) {
208
+ // assemble sources
209
+ const allSources = new Set([...this.allEdges].map(([src, _]) => src));
210
+ for (const [start, branches] of Object.entries(this.branches)) {
211
+ allSources.add(start);
212
+ for (const branch of Object.values(branches)) {
213
+ if (branch.then) {
214
+ if (branch.ends) {
215
+ for (const end of Object.values(branch.ends)) {
216
+ if (end !== exports.END) {
217
+ allSources.add(end);
218
+ }
219
+ }
220
+ }
221
+ else {
222
+ for (const node of Object.keys(this.nodes)) {
223
+ if (node !== start) {
224
+ allSources.add(node);
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ }
231
+ // validate sources
232
+ for (const node of Object.keys(this.nodes)) {
233
+ if (!allSources.has(node)) {
234
+ throw new Error(`Node \`${node}\` is a dead-end`);
163
235
  }
164
- if (this.branches[key]) {
165
- this.branches[key].forEach((branch) => {
166
- const runnableLambda = new runnables_1.RunnableLambda({
167
- func: (input) => branch.runnable(input),
168
- });
169
- nodes[edgesKey] = nodes[edgesKey].pipe(runnableLambda);
170
- });
236
+ }
237
+ for (const source of allSources) {
238
+ if (source !== exports.START && !(source in this.nodes)) {
239
+ throw new Error(`Found edge starting at unknown node \`${source}\``);
171
240
  }
172
241
  }
173
- const hidden = Object.keys(this.nodes).map((node) => `${node}:inbox`);
174
- if (!this.entryPoint) {
175
- throw new Error("Entry point not set");
242
+ // assemble targets
243
+ const allTargets = new Set([...this.allEdges].map(([_, target]) => target));
244
+ for (const [start, branches] of Object.entries(this.branches)) {
245
+ for (const branch of Object.values(branches)) {
246
+ if (branch.then) {
247
+ allTargets.add(branch.then);
248
+ }
249
+ if (branch.ends) {
250
+ for (const end of Object.values(branch.ends)) {
251
+ allTargets.add(end);
252
+ }
253
+ }
254
+ else {
255
+ allTargets.add(exports.END);
256
+ for (const node of Object.keys(this.nodes)) {
257
+ if (node !== start && node !== branch.then) {
258
+ allTargets.add(node);
259
+ }
260
+ }
261
+ }
262
+ }
176
263
  }
177
- return new index_js_1.Pregel({
178
- nodes,
179
- input: `${this.entryPoint}:inbox`,
180
- output: exports.END,
181
- hidden,
182
- checkpointer,
183
- });
184
- }
185
- validate() {
186
- const allStarts = new Set([...this.edges].map(([src, _]) => src).concat(Object.keys(this.branches)));
264
+ // validate targets
187
265
  for (const node of Object.keys(this.nodes)) {
188
- if (!allStarts.has(node)) {
189
- throw new Error(`Node \`${node}\` is a dead-end`);
266
+ if (!allTargets.has(node)) {
267
+ throw new Error(`Node \`${node}\` is not reachable`);
268
+ }
269
+ }
270
+ for (const target of allTargets) {
271
+ if (target !== exports.END && !(target in this.nodes)) {
272
+ throw new Error(`Found edge ending at unknown node \`${target}\``);
190
273
  }
191
274
  }
192
- const allEndsAreDefined = Object.values(this.branches).every((branchList) => branchList.every((branch) => branch.ends));
193
- if (allEndsAreDefined) {
194
- const allEnds = new Set([...this.edges]
195
- .map(([_, end]) => end)
196
- .concat(...Object.values(this.branches).flatMap((branchList) => branchList.flatMap((branch) => Object.values(branch.ends ?? {}))))
197
- .concat(this.entryPoint ? [this.entryPoint] : []));
198
- for (const node of Object.keys(this.nodes)) {
199
- if (!allEnds.has(node)) {
200
- throw new Error(`Node \`${node}\` is not reachable`);
275
+ // validate interrupts
276
+ if (interrupt) {
277
+ for (const node of interrupt) {
278
+ if (!(node in this.nodes)) {
279
+ throw new Error(`Interrupt node \`${node}\` is not present`);
201
280
  }
202
281
  }
203
282
  }
@@ -205,3 +284,62 @@ class Graph {
205
284
  }
206
285
  }
207
286
  exports.Graph = Graph;
287
+ class CompiledGraph extends index_js_1.Pregel {
288
+ constructor({ builder, ...rest }) {
289
+ super(rest);
290
+ Object.defineProperty(this, "builder", {
291
+ enumerable: true,
292
+ configurable: true,
293
+ writable: true,
294
+ value: void 0
295
+ });
296
+ this.builder = builder;
297
+ }
298
+ attachNode(key, node) {
299
+ this.channels[key] = new ephemeral_value_js_1.EphemeralValue();
300
+ this.nodes[key] = new read_js_1.PregelNode({
301
+ channels: [],
302
+ triggers: [],
303
+ })
304
+ .pipe(node)
305
+ .pipe(new write_js_1.ChannelWrite([{ channel: key, value: write_js_1.PASSTHROUGH }], [constants_js_1.TAG_HIDDEN]));
306
+ this.streamChannels.push(key);
307
+ }
308
+ attachEdge(start, end) {
309
+ if (end === exports.END) {
310
+ if (start === exports.START) {
311
+ throw new Error("Cannot have an edge from START to END");
312
+ }
313
+ this.nodes[start].writers.push(new write_js_1.ChannelWrite([{ channel: exports.END, value: write_js_1.PASSTHROUGH }], [constants_js_1.TAG_HIDDEN]));
314
+ }
315
+ else {
316
+ this.nodes[end].triggers.push(start);
317
+ this.nodes[end].channels.push(start);
318
+ }
319
+ }
320
+ attachBranch(start, name, branch) {
321
+ // add hidden start node
322
+ if (start === exports.START && this.nodes[exports.START]) {
323
+ this.nodes[exports.START] = index_js_1.Channel.subscribeTo(exports.START, { tags: [constants_js_1.TAG_HIDDEN] });
324
+ }
325
+ // attach branch writer
326
+ this.nodes[start].pipe(branch.compile((dests) => {
327
+ const channels = dests.map((dest) => dest === exports.END ? exports.END : `branch:${start}:${name}:${dest}`);
328
+ return new write_js_1.ChannelWrite(channels.map((channel) => ({ channel, value: write_js_1.PASSTHROUGH })), [constants_js_1.TAG_HIDDEN]);
329
+ }));
330
+ // attach branch readers
331
+ const ends = branch.ends
332
+ ? Object.values(branch.ends)
333
+ : Object.keys(this.nodes);
334
+ for (const end of ends) {
335
+ if (end !== exports.END) {
336
+ const channelName = `branch:${start}:${name}:${end}`;
337
+ this.channels[channelName] =
338
+ new ephemeral_value_js_1.EphemeralValue();
339
+ this.nodes[end].triggers.push(channelName);
340
+ this.nodes[end].channels.push(channelName);
341
+ }
342
+ }
343
+ }
344
+ }
345
+ exports.CompiledGraph = CompiledGraph;