@langchain/langgraph 0.0.12 → 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 +235 -95
  50. package/dist/graph/graph.d.ts +52 -23
  51. package/dist/graph/graph.js +234 -96
  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 +11 -7
  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,23 +25,42 @@ class Branch {
18
25
  writable: true,
19
26
  value: void 0
20
27
  });
21
- this.condition = condition;
22
- this.ends = ends;
23
- }
24
- async runnable(
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- input, options) {
27
- const result = await this.condition(input, options?.config);
28
- let destination;
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
+ }));
47
+ }
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;
29
54
  if (this.ends) {
30
- destination = this.ends[result];
55
+ destinations = result.map((r) => this.ends[r]);
31
56
  }
32
57
  else {
33
- destination = result;
58
+ destinations = result;
34
59
  }
35
- return index_js_1.Channel.writeTo(destination !== exports.END ? `${destination}:inbox` : exports.END);
60
+ return writer(destinations);
36
61
  }
37
62
  }
63
+ exports.Branch = Branch;
38
64
  class Graph {
39
65
  constructor() {
40
66
  Object.defineProperty(this, "nodes", {
@@ -67,6 +93,12 @@ class Graph {
67
93
  writable: true,
68
94
  value: false
69
95
  });
96
+ Object.defineProperty(this, "supportMultipleEdges", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: false
101
+ });
70
102
  this.nodes = {};
71
103
  this.edges = new Set();
72
104
  this.branches = {};
@@ -76,126 +108,175 @@ class Graph {
76
108
  console.warn(message);
77
109
  }
78
110
  }
111
+ get allEdges() {
112
+ return this.edges;
113
+ }
79
114
  addNode(key, action) {
80
115
  this.warnIfCompiled(`Adding a node to a graph that has already been compiled. This will not be reflected in the compiled graph.`);
81
- if (this.nodes[key]) {
116
+ if (key in this.nodes) {
82
117
  throw new Error(`Node \`${key}\` already present.`);
83
118
  }
84
119
  if (key === exports.END) {
85
120
  throw new Error(`Node \`${key}\` is reserved.`);
86
121
  }
87
122
  this.nodes[key] = (0, runnables_1._coerceToRunnable)(action);
123
+ return this;
88
124
  }
89
125
  addEdge(startKey, endKey) {
90
126
  this.warnIfCompiled(`Adding an edge to a graph that has already been compiled. This will not be reflected in the compiled graph.`);
91
127
  if (startKey === exports.END) {
92
128
  throw new Error("END cannot be a start node");
93
129
  }
94
- if (!this.nodes[startKey]) {
95
- throw new Error(`Need to addNode \`${startKey}\` first`);
96
- }
97
- if (!this.nodes[endKey] && endKey !== exports.END) {
98
- throw new Error(`Need to addNode \`${endKey}\` first`);
130
+ if (endKey === exports.START) {
131
+ throw new Error("START cannot be an end node");
99
132
  }
100
- // TODO: support multiple message passing
101
- if (Array.from(this.edges).some(([start]) => start === startKey)) {
133
+ if (!this.supportMultipleEdges &&
134
+ Array.from(this.edges).some(([start]) => start === startKey)) {
102
135
  throw new Error(`Already found path for ${startKey}`);
103
136
  }
104
137
  this.edges.add([startKey, endKey]);
138
+ return this;
105
139
  }
106
- addConditionalEdges(startKey, condition, conditionalEdgeMapping) {
140
+ addConditionalEdges(source, path, pathMap) {
141
+ const options = typeof source === "object" ? source : { source, path: path, pathMap };
107
142
  this.warnIfCompiled("Adding an edge to a graph that has already been compiled. This will not be reflected in the compiled graph.");
108
- if (!this.nodes[startKey]) {
109
- throw new Error(`Need to addNode \`${startKey}\` first`);
110
- }
111
- if (conditionalEdgeMapping) {
112
- const mappingValues = Array.from(Object.values(conditionalEdgeMapping));
113
- const nodesValues = Object.keys(this.nodes);
114
- const endExcluded = mappingValues.filter((value) => value !== exports.END);
115
- const difference = endExcluded.filter((value) => !nodesValues.some((nv) => nv === value));
116
- if (difference.length > 0) {
117
- throw new Error(`Missing nodes which are in conditional edge mapping.\nMapping contains possible destinations: ${mappingValues.join(", ")}.\nPossible nodes are ${nodesValues.join(", ")}.`);
118
- }
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}\``);
119
148
  }
120
- if (!this.branches[startKey]) {
121
- this.branches[startKey] = [];
149
+ // save it
150
+ if (!this.branches[options.source]) {
151
+ this.branches[options.source] = {};
122
152
  }
123
- this.branches[startKey].push(new Branch(condition, conditionalEdgeMapping));
153
+ this.branches[options.source][name] = new Branch(options);
154
+ return this;
124
155
  }
156
+ /**
157
+ * @deprecated use `addEdge(START, key)` instead
158
+ */
125
159
  setEntryPoint(key) {
126
160
  this.warnIfCompiled("Setting the entry point of a graph that has already been compiled. This will not be reflected in the compiled graph.");
127
- if (!this.nodes[key]) {
128
- throw new Error(`Need to addNode \`${key}\` first`);
129
- }
130
- this.entryPoint = key;
161
+ return this.addEdge(exports.START, key);
131
162
  }
163
+ /**
164
+ * @deprecated use `addEdge(key, END)` instead
165
+ */
132
166
  setFinishPoint(key) {
133
167
  this.warnIfCompiled("Setting a finish point of a graph that has already been compiled. This will not be reflected in the compiled graph.");
134
- this.addEdge(key, exports.END);
135
- }
136
- compile(checkpointer) {
137
- this.validate();
138
- const outgoingEdges = {};
139
- this.edges.forEach(([start, end]) => {
140
- if (!outgoingEdges[start]) {
141
- outgoingEdges[start] = [];
142
- }
143
- 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",
144
192
  });
145
- const nodes = {};
193
+ // attach nodes, edges and branches
146
194
  for (const [key, node] of Object.entries(this.nodes)) {
147
- nodes[key] = index_js_1.Channel.subscribeTo(`${key}:inbox`)
148
- .pipe(node)
149
- .pipe(index_js_1.Channel.writeTo(key));
150
- }
151
- for (const key of Object.keys(this.nodes)) {
152
- const outgoing = outgoingEdges[key];
153
- const edgesKey = `${key}:edges`;
154
- if (outgoing || this.branches[key]) {
155
- nodes[edgesKey] = index_js_1.Channel.subscribeTo(key, {
156
- tags: ["langsmith:hidden"],
157
- });
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);
158
203
  }
159
- if (outgoing) {
160
- 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
+ }
161
229
  }
162
- if (this.branches[key]) {
163
- this.branches[key].forEach((branch) => {
164
- const runnableLambda = new runnables_1.RunnableLambda({
165
- func: (input) => branch.runnable(input),
166
- });
167
- nodes[edgesKey] = nodes[edgesKey].pipe(runnableLambda);
168
- });
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`);
169
235
  }
170
236
  }
171
- const hidden = Object.keys(this.nodes).map((node) => `${node}:inbox`);
172
- if (!this.entryPoint) {
173
- throw new Error("Entry point not set");
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}\``);
240
+ }
174
241
  }
175
- return new index_js_1.Pregel({
176
- nodes,
177
- input: `${this.entryPoint}:inbox`,
178
- output: exports.END,
179
- hidden,
180
- checkpointer,
181
- });
182
- }
183
- validate() {
184
- const allStarts = new Set([...this.edges].map(([src, _]) => src).concat(Object.keys(this.branches)));
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
+ }
263
+ }
264
+ // validate targets
185
265
  for (const node of Object.keys(this.nodes)) {
186
- if (!allStarts.has(node)) {
187
- 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}\``);
188
273
  }
189
274
  }
190
- const allEndsAreDefined = Object.values(this.branches).every((branchList) => branchList.every((branch) => branch.ends));
191
- if (allEndsAreDefined) {
192
- const allEnds = new Set([...this.edges]
193
- .map(([_, end]) => end)
194
- .concat(...Object.values(this.branches).flatMap((branchList) => branchList.flatMap((branch) => Object.values(branch.ends ?? {}))))
195
- .concat(this.entryPoint ? [this.entryPoint] : []));
196
- for (const node of Object.keys(this.nodes)) {
197
- if (!allEnds.has(node)) {
198
- 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`);
199
280
  }
200
281
  }
201
282
  }
@@ -203,3 +284,62 @@ class Graph {
203
284
  }
204
285
  }
205
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;