@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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +588 -0
  3. package/dist/channels/base.cjs +58 -0
  4. package/dist/channels/base.d.ts +46 -0
  5. package/dist/channels/base.js +50 -0
  6. package/dist/channels/binop.cjs +70 -0
  7. package/dist/channels/binop.d.ts +16 -0
  8. package/dist/channels/binop.js +66 -0
  9. package/dist/channels/index.cjs +9 -0
  10. package/dist/channels/index.d.ts +1 -0
  11. package/dist/channels/index.js +1 -0
  12. package/dist/channels/last_value.cjs +53 -0
  13. package/dist/channels/last_value.d.ts +12 -0
  14. package/dist/channels/last_value.js +49 -0
  15. package/dist/channels/topic.cjs +90 -0
  16. package/dist/channels/topic.d.ts +19 -0
  17. package/dist/channels/topic.js +86 -0
  18. package/dist/checkpoint/base.cjs +32 -0
  19. package/dist/checkpoint/base.d.ts +47 -0
  20. package/dist/checkpoint/base.js +27 -0
  21. package/dist/checkpoint/index.cjs +8 -0
  22. package/dist/checkpoint/index.d.ts +2 -0
  23. package/dist/checkpoint/index.js +2 -0
  24. package/dist/checkpoint/memory.cjs +35 -0
  25. package/dist/checkpoint/memory.d.ts +8 -0
  26. package/dist/checkpoint/memory.js +31 -0
  27. package/dist/constants.cjs +5 -0
  28. package/dist/constants.d.ts +2 -0
  29. package/dist/constants.js +2 -0
  30. package/dist/graph/graph.cjs +175 -0
  31. package/dist/graph/graph.d.ts +30 -0
  32. package/dist/graph/graph.js +171 -0
  33. package/dist/graph/index.cjs +9 -0
  34. package/dist/graph/index.d.ts +2 -0
  35. package/dist/graph/index.js +2 -0
  36. package/dist/graph/state.cjs +108 -0
  37. package/dist/graph/state.d.ts +17 -0
  38. package/dist/graph/state.js +104 -0
  39. package/dist/index.cjs +8 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +1 -0
  42. package/dist/prebuilt/agent_executor.cjs +96 -0
  43. package/dist/prebuilt/agent_executor.d.ts +12 -0
  44. package/dist/prebuilt/agent_executor.js +92 -0
  45. package/dist/prebuilt/chat_agent_executor.cjs +130 -0
  46. package/dist/prebuilt/chat_agent_executor.d.ts +6 -0
  47. package/dist/prebuilt/chat_agent_executor.js +126 -0
  48. package/dist/prebuilt/index.cjs +9 -0
  49. package/dist/prebuilt/index.d.ts +3 -0
  50. package/dist/prebuilt/index.js +3 -0
  51. package/dist/prebuilt/tool_executor.cjs +63 -0
  52. package/dist/prebuilt/tool_executor.d.ts +27 -0
  53. package/dist/prebuilt/tool_executor.js +59 -0
  54. package/dist/pregel/debug.cjs +46 -0
  55. package/dist/pregel/debug.d.ts +4 -0
  56. package/dist/pregel/debug.js +41 -0
  57. package/dist/pregel/index.cjs +475 -0
  58. package/dist/pregel/index.d.ts +75 -0
  59. package/dist/pregel/index.js +469 -0
  60. package/dist/pregel/io.cjs +57 -0
  61. package/dist/pregel/io.d.ts +9 -0
  62. package/dist/pregel/io.js +52 -0
  63. package/dist/pregel/read.cjs +217 -0
  64. package/dist/pregel/read.d.ts +43 -0
  65. package/dist/pregel/read.js +211 -0
  66. package/dist/pregel/reserved.cjs +7 -0
  67. package/dist/pregel/reserved.d.ts +3 -0
  68. package/dist/pregel/reserved.js +4 -0
  69. package/dist/pregel/validate.cjs +90 -0
  70. package/dist/pregel/validate.d.ts +15 -0
  71. package/dist/pregel/validate.js +85 -0
  72. package/dist/pregel/write.cjs +54 -0
  73. package/dist/pregel/write.d.ts +13 -0
  74. package/dist/pregel/write.js +50 -0
  75. package/index.cjs +1 -0
  76. package/index.d.ts +1 -0
  77. package/index.js +1 -0
  78. package/package.json +100 -0
  79. package/prebuilt.cjs +1 -0
  80. package/prebuilt.d.ts +1 -0
  81. package/prebuilt.js +1 -0
  82. package/pregel.cjs +1 -0
  83. package/pregel.d.ts +1 -0
  84. package/pregel.js +1 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2023 LangChain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,588 @@
1
+ # 🦜🕸️LangGraph.js
2
+
3
+ ⚡ Building language agents as graphs ⚡
4
+
5
+ ## Overview
6
+
7
+ LangGraph is a library for building stateful, multi-actor applications with LLMs, built on top of (and intended to be used with) [LangChain.js](https://github.com/langchain-ai/langchainjs).
8
+ It extends the [LangChain Expression Language](https://js.langchain.com/docs/expression_language/) with the ability to coordinate multiple chains (or actors) across multiple steps of computation in a cyclic manner.
9
+ It is inspired by [Pregel](https://research.google/pubs/pub37252/) and [Apache Beam](https://beam.apache.org/).
10
+ The current interface exposed is one inspired by [NetworkX](https://networkx.org/documentation/latest/).
11
+
12
+ The main use is for adding **cycles** to your LLM application.
13
+ Crucially, this is NOT a **DAG** framework.
14
+ If you want to build a DAG, you should use just use [LangChain Expression Language](https://js.langchain.com/docs/expression_language/).
15
+
16
+ Cycles are important for agent-like behaviors, where you call an LLM in a loop, asking it what action to take next.
17
+
18
+
19
+ > Looking for the Python version? Click [here](https://github.com/langchain-ai/langgraph).
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @langchain/langgraph
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ Here we will go over an example of recreating the [`AgentExecutor`](https://js.langchain.com/docs/modules/agents/concepts#agentexecutor) class from LangChain.
30
+ The benefits of creating it with LangGraph is that it is more modifiable.
31
+
32
+ We will also want to install some LangChain packages:
33
+
34
+ ```shell
35
+ npm install langchain @langchain/core @langchain/community @langchain/openai
36
+ ```
37
+
38
+ We also need to export some environment variables needed for our agent.
39
+
40
+ ```shell
41
+ export OPENAI_API_KEY=sk-...
42
+ export TAVILY_API_KEY=tvly-...
43
+ ```
44
+
45
+ Optionally, we can set up [LangSmith](https://docs.smith.langchain.com/) for best-in-class observability.
46
+
47
+ ```shell
48
+ export LANGCHAIN_TRACING_V2="true"
49
+ export LANGCHAIN_API_KEY=ls__...
50
+ export LANGCHAIN_ENDPOINT=https://api.langchain.com
51
+ ```
52
+
53
+ ### Set up the tools
54
+
55
+ We will first define the tools we want to use.
56
+ For this simple example, we will use a built-in search tool via Tavily.
57
+ However, it is really easy to create your own tools - see documentation [here](https://js.langchain.com/docs/modules/agents/tools/dynamic) on how to do that.
58
+
59
+ ```typescript
60
+ import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
61
+
62
+ const tools = [new TavilySearchResults({ maxResults: 1 })];
63
+ ```
64
+
65
+ We can now wrap these tools in a simple ToolExecutor.
66
+ This is a real simple class that takes in a ToolInvocation and calls that tool, returning the output.
67
+
68
+ A ToolInvocation is any type with `tool` and `toolInput` attribute.
69
+
70
+
71
+ ```typescript
72
+ import { ToolExecutor } from "@langchain/langgraph/prebuilt";
73
+
74
+ const toolExecutor = new ToolExecutor({
75
+ tools
76
+ });
77
+ ```
78
+
79
+ ### Set up the model
80
+
81
+ Now we need to load the chat model we want to use.
82
+ Importantly, this should satisfy two criteria:
83
+
84
+ 1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.
85
+ 2. It should work with OpenAI function calling. This means it should either be an OpenAI model or a model that exposes a similar interface.
86
+
87
+ Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example.
88
+
89
+ ```typescript
90
+ import { ChatOpenAI } from "@langchain/openai";
91
+
92
+ // We will set streaming=True so that we can stream tokens
93
+ // See the streaming section for more information on this.
94
+ const model = new ChatOpenAI({
95
+ temperature: 0,
96
+ streaming: true
97
+ });
98
+ ```
99
+
100
+ After we've done this, we should make sure the model knows that it has these tools available to call.
101
+ We can do this by converting the LangChain tools into the format for OpenAI function calling, and then bind them to the model class.
102
+
103
+ ```typescript
104
+ import { convertToOpenAIFunction } from "@langchain/core/utils/function_calling";
105
+
106
+ const toolsAsOpenAIFunctions = tools.map((tool) =>
107
+ convertToOpenAIFunction(tool)
108
+ );
109
+ const newModel = model.bind({
110
+ functions: toolsAsOpenAIFunctions,
111
+ });
112
+ ```
113
+
114
+ ### Define the agent state
115
+
116
+ The main type of graph in `langgraph` is the `StatefulGraph`.
117
+ This graph is parameterized by a state object that it passes around to each node.
118
+ Each node then returns operations to update that state.
119
+ These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute.
120
+ Whether to set or add is denoted by annotating the state object you construct the graph with.
121
+
122
+ For this example, the state we will track will just be a list of messages.
123
+ We want each node to just add messages to that list.
124
+ Therefore, we will use an object with one key (`messages`) with the value as an object: `{ value: Function, default?: () => any }`
125
+
126
+ The `default` key must be a factory that returns the default value for that attribute.
127
+
128
+ ```typescript
129
+ import { BaseMessage } from "@langchain/core/messages";
130
+
131
+ const agentState = {
132
+ messages: {
133
+ value: (x: BaseMessage[], y: BaseMessage[]) => x.concat(y),
134
+ default: () => [],
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### Define the nodes
140
+
141
+ We now need to define a few different nodes in our graph.
142
+ In `langgraph`, a node can be either a function or a [runnable](https://js.langchain.com/docs/expression_language/).
143
+ There are two main nodes we need for this:
144
+
145
+ 1. The agent: responsible for deciding what (if any) actions to take.
146
+ 2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.
147
+
148
+ We will also need to define some edges.
149
+ Some of these edges may be conditional.
150
+ The reason they are conditional is that based on the output of a node, one of several paths may be taken.
151
+ The path that is taken is not known until that node is run (the LLM decides).
152
+
153
+ 1. Conditional Edge: after the agent is called, we should either:
154
+ a. If the agent said to take an action, then the function to invoke tools should be called
155
+ b. If the agent said that it was finished, then it should finish
156
+ 2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next
157
+
158
+ Let's define the nodes, as well as a function to decide how what conditional edge to take.
159
+
160
+ ```typescript
161
+ import { FunctionMessage } from "@langchain/core/messages";
162
+ import { AgentAction } from "@langchain/core/agents";
163
+
164
+ // Define the function that determines whether to continue or not
165
+ const shouldContinue = (state: { messages: Array<BaseMessage> }) => {
166
+ const { messages } = state;
167
+ const lastMessage = messages[messages.length - 1];
168
+ // If there is no function call, then we finish
169
+ if (
170
+ !("function_call" in lastMessage.additional_kwargs) ||
171
+ !lastMessage.additional_kwargs.function_call
172
+ ) {
173
+ return "end";
174
+ }
175
+ // Otherwise if there is, we continue
176
+ return "continue";
177
+ };
178
+
179
+ // Define the function to execute tools
180
+ const _getAction = (state: { messages: Array<BaseMessage> }): AgentAction => {
181
+ const { messages } = state;
182
+ // Based on the continue condition
183
+ // we know the last message involves a function call
184
+ const lastMessage = messages[messages.length - 1];
185
+ if (!lastMessage) {
186
+ throw new Error("No messages found.");
187
+ }
188
+ if (!lastMessage.additional_kwargs.function_call) {
189
+ throw new Error("No function call found in message.");
190
+ }
191
+ // We construct an AgentAction from the function_call
192
+ return {
193
+ tool: lastMessage.additional_kwargs.function_call.name,
194
+ toolInput: JSON.stringify(
195
+ lastMessage.additional_kwargs.function_call.arguments
196
+ ),
197
+ log: "",
198
+ };
199
+ };
200
+
201
+ const callTool = async (state: { messages: Array<BaseMessage> }) => {
202
+ const action = _getAction(state);
203
+ // We call the tool_executor and get back a response
204
+ const response = await toolExecutor.invoke(action);
205
+ // We use the response to create a FunctionMessage
206
+ const functionMessage = new FunctionMessage({
207
+ content: response,
208
+ name: action.tool,
209
+ });
210
+ // We return a list, because this will get added to the existing list
211
+ return { messages: [functionMessage] };
212
+ };
213
+ ```
214
+
215
+ ### Define the graph
216
+
217
+ We can now put it all together and define the graph!
218
+
219
+ ```typescript
220
+ import { StateGraph, END } from "@langchain/langgraph";
221
+ import { RunnableLambda } from "@langchain/core/runnables";
222
+
223
+ // Define a new graph
224
+ const workflow = new StateGraph({
225
+ channels: agentState,
226
+ });
227
+
228
+ // Define the two nodes we will cycle between
229
+ workflow.addNode("agent", new RunnableLambda({ func: callModel }));
230
+ workflow.addNode("action", new RunnableLambda({ func: callTool }));
231
+
232
+ // Set the entrypoint as `agent`
233
+ // This means that this node is the first one called
234
+ workflow.setEntryPoint("agent");
235
+
236
+ // We now add a conditional edge
237
+ workflow.addConditionalEdges(
238
+ // First, we define the start node. We use `agent`.
239
+ // This means these are the edges taken after the `agent` node is called.
240
+ "agent",
241
+ // Next, we pass in the function that will determine which node is called next.
242
+ shouldContinue,
243
+ // Finally we pass in a mapping.
244
+ // The keys are strings, and the values are other nodes.
245
+ // END is a special node marking that the graph should finish.
246
+ // What will happen is we will call `should_continue`, and then the output of that
247
+ // will be matched against the keys in this mapping.
248
+ // Based on which one it matches, that node will then be called.
249
+ {
250
+ // If `tools`, then we call the tool node.
251
+ continue: "action",
252
+ // Otherwise we finish.
253
+ end: END
254
+ }
255
+ );
256
+
257
+ // We now add a normal edge from `tools` to `agent`.
258
+ // This means that after `tools` is called, `agent` node is called next.
259
+ workflow.addEdge("action", "agent");
260
+
261
+ // Finally, we compile it!
262
+ // This compiles it into a LangChain Runnable,
263
+ // meaning you can use it as you would any other runnable
264
+ const app = workflow.compile();
265
+ ```
266
+
267
+ ### Use it!
268
+
269
+ We can now use it!
270
+ This now exposes the [same interface](https://js.langchain.com/docs/expression_language/) as all other LangChain runnables.
271
+ This runnable accepts a list of messages.
272
+
273
+ ```typescript
274
+ import { HumanMessage } from "@langchain/core/messages";
275
+
276
+ const inputs = {
277
+ messages: [new HumanMessage("what is the weather in sf")]
278
+ }
279
+ const result = await app.invoke(inputs);
280
+ ```
281
+
282
+ See a LangSmith trace of this run [here](https://smith.langchain.com/public/2562d46e-da94-4c9d-9b14-3759a26aec9b/r).
283
+
284
+ This may take a little bit - it's making a few calls behind the scenes.
285
+ In order to start seeing some intermediate results as they happen, we can use streaming - see below for more information on that.
286
+
287
+ ## Streaming
288
+
289
+ LangGraph has support for several different types of streaming.
290
+
291
+ ### Streaming Node Output
292
+
293
+ One of the benefits of using LangGraph is that it is easy to stream output as it's produced by each node.
294
+
295
+ ```typescript
296
+ const inputs = {
297
+ messages: [new HumanMessage("what is the weather in sf")]
298
+ };
299
+ for await (const output of await app.stream(inputs)) {
300
+ console.log("output", output);
301
+ console.log("-----\n");
302
+ }
303
+ ```
304
+
305
+ See a LangSmith trace of this run [here](https://smith.langchain.com/public/9afacb13-b9dc-416e-abbe-6ed2a0811afe/r).
306
+
307
+ ## When to Use
308
+
309
+ When should you use this versus [LangChain Expression Language](https://js.langchain.com/docs/expression_language/)?
310
+
311
+ If you need cycles.
312
+
313
+ Langchain Expression Language allows you to easily define chains (DAGs) but does not have a good mechanism for adding in cycles.
314
+ `langgraph` adds that syntax.
315
+
316
+ ## Examples
317
+
318
+
319
+ ### ChatAgentExecutor: with function calling
320
+
321
+ This agent executor takes a list of messages as input and outputs a list of messages.
322
+ All agent state is represented as a list of messages.
323
+ This specifically uses OpenAI function calling.
324
+ This is recommended agent executor for newer chat based models that support function calling.
325
+
326
+ - [Getting Started Notebook](https://github.com/langchain-ai/langgraphjs/blob/main/examples/chat_agent_executor_with_function_calling/base.ipynb): Walks through creating this type of executor from scratch
327
+
328
+ ### AgentExecutor
329
+
330
+ This agent executor uses existing LangChain agents.
331
+
332
+ - [Getting Started Notebook](https://github.com/langchain-ai/langgraphjs/blob/main/examples/agent_executor/base.ipynb): Walks through creating this type of executor from scratch
333
+
334
+ ## Documentation
335
+
336
+ There are only a few new APIs to use.
337
+
338
+ ### StateGraph
339
+
340
+ The main entrypoint is `StateGraph`.
341
+
342
+ ```typescript
343
+ import { StateGraph } from "@langchain/langgraph";
344
+ ```
345
+
346
+ This class is responsible for constructing the graph.
347
+ It exposes an interface inspired by [NetworkX](https://networkx.org/documentation/latest/).
348
+ This graph is parameterized by a state object that it passes around to each node.
349
+
350
+
351
+ #### `constructor`
352
+
353
+ ```typescript
354
+ interface StateGraphArgs<T = any> {
355
+ channels: Record<
356
+ string,
357
+ {
358
+ value: BinaryOperator<T> | null;
359
+ default?: () => T;
360
+ }
361
+ >;
362
+ }
363
+
364
+ class StateGraph<T> extends Graph {
365
+ constructor(fields: StateGraphArgs<T>) {}
366
+ ```
367
+
368
+ When constructing the graph, you need to pass in a schema for a state.
369
+ Each node then returns operations to update that state.
370
+ These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute.
371
+ Whether to set or add is denoted by annotating the state object you construct the graph with.
372
+
373
+
374
+ Let's take a look at an example:
375
+
376
+ ```typescript
377
+ import { BaseMessage } from "@langchain/core/messages";
378
+
379
+ const schema = {
380
+ input: {
381
+ value: null,
382
+ },
383
+ agentOutcome: {
384
+ value: null,
385
+ },
386
+ steps: {
387
+ value: (x: Array<BaseMessage>, y: Array<BaseMessage>) => x.concat(y),
388
+ default: () => [],
389
+ },
390
+ };
391
+ ```
392
+
393
+ We can then use this like:
394
+
395
+ ```typescript
396
+ // Initialize the StateGraph with this state
397
+ const graph = new StateGraph({ channels: schema })
398
+ // Create nodes and edges
399
+ ...
400
+ // Compile the graph
401
+ const app = graph.compile()
402
+
403
+ // The inputs should be an object, because the schema is an object
404
+ const inputs = {
405
+ // Let's assume this the input
406
+ input: "hi"
407
+ // Let's assume agent_outcome is set by the graph as some point
408
+ // It doesn't need to be provided, and it will be null by default
409
+ }
410
+ ```
411
+
412
+ ### `.addNode`
413
+
414
+ ```typescript
415
+ addNode(key: string, action: RunnableLike<RunInput, RunOutput>): void
416
+ ```
417
+
418
+ This method adds a node to the graph.
419
+ It takes two arguments:
420
+
421
+ - `key`: A string representing the name of the node. This must be unique.
422
+ - `action`: The action to take when this node is called. This should either be a function or a runnable.
423
+
424
+ ### `.addEdge`
425
+
426
+ ```typescript
427
+ addEdge(startKey: string, endKey: string): void
428
+ ```
429
+
430
+ Creates an edge from one node to the next.
431
+ This means that output of the first node will be passed to the next node.
432
+ It takes two arguments.
433
+
434
+ - `startKey`: A string representing the name of the start node. This key must have already been registered in the graph.
435
+ - `endKey`: A string representing the name of the end node. This key must have already been registered in the graph.
436
+
437
+ ### `.addConditionalEdges`
438
+
439
+ ```typescript
440
+ addConditionalEdges(
441
+ startKey: string,
442
+ condition: CallableFunction,
443
+ conditionalEdgeMapping: Record<string, string>
444
+ ): void
445
+ ```
446
+
447
+ This method adds conditional edges.
448
+ What this means is that only one of the downstream edges will be taken, and which one that is depends on the results of the start node.
449
+ This takes three arguments:
450
+
451
+ - `startKey`: A string representing the name of the start node. This key must have already been registered in the graph.
452
+ - `condition`: A function to call to decide what to do next. The input will be the output of the start node. It should return a string that is present in `conditionalEdgeMapping` and represents the edge to take.
453
+ - `conditionalEdgeMapping`: A mapping of string to string. The keys should be strings that may be returned by `condition`. The values should be the downstream node to call if that condition is returned.
454
+
455
+ ### `.setEntryPoint`
456
+
457
+ ```typescript
458
+ setEntryPoint(key: string): void
459
+ ```
460
+
461
+ The entrypoint to the graph.
462
+ This is the node that is first called.
463
+ It only takes one argument:
464
+
465
+ - `key`: The name of the node that should be called first.
466
+
467
+ ### `.setFinishPoint`
468
+
469
+ ```typescript
470
+ setFinishPoint(key: string): void
471
+ ```
472
+
473
+ This is the exit point of the graph.
474
+ When this node is called, the results will be the final result from the graph.
475
+ It only has one argument:
476
+
477
+ - `key`: The name of the node that, when called, will return the results of calling it as the final output
478
+
479
+ Note: This does not need to be called if at any point you previously created an edge (conditional or normal) to `END`
480
+
481
+ ### `END`
482
+
483
+ ```typescript
484
+ import { END } from "@langchain/langgraph";
485
+ ```
486
+
487
+ This is a special node representing the end of the graph.
488
+ This means that anything passed to this node will be the final output of the graph.
489
+ It can be used in two places:
490
+
491
+ - As the `endKey` in `addEdge`
492
+ - As a value in `conditionalEdgeMapping` as passed to `addConditionalEdges`
493
+
494
+ ## When to Use
495
+
496
+ When should you use this versus [LangChain Expression Language](https://js.langchain.com/docs/expression_language/)?
497
+
498
+ If you need cycles.
499
+
500
+ Langchain Expression Language allows you to easily define chains (DAGs) but does not have a good mechanism for adding in cycles.
501
+ `langgraph` adds that syntax.
502
+
503
+ ## Examples
504
+
505
+ ### AgentExecutor
506
+
507
+ See the above Quick Start for an example of re-creating the LangChain [`AgentExecutor`](https://js.langchain.com/docs/modules/agents/concepts#agentexecutor) class.
508
+
509
+ ### Forced Function Calling
510
+
511
+ One simple modification of the above Graph is to modify it such that a certain tool is always called first.
512
+ This can be useful if you want to enforce a certain tool is called, but still want to enable agentic behavior after the fact.
513
+
514
+ Assuming you have done the above Quick Start, you can build off it like:
515
+
516
+ #### Define the first tool call
517
+
518
+ Here, we manually define the first tool call that we will make.
519
+ Notice that it does that same thing as `agent` would have done (adds the `agentOutcome` key).
520
+ This is so that we can easily plug it in.
521
+
522
+ ```typescript
523
+ import { AgentStep, AgentAction, AgentFinish } from "@langchain/core/agents";
524
+
525
+ // Define the data type that the agent will return.
526
+ type AgentData = {
527
+ input: string;
528
+ steps: Array<AgentStep>;
529
+ agentOutcome?: AgentAction | AgentFinish;
530
+ };
531
+
532
+ const firstAgent = (inputs: AgentData) => {
533
+ const newInputs = inputs;
534
+ const action = {
535
+ // We force call this tool
536
+ tool: "tavily_search_results_json",
537
+ // We just pass in the `input` key to this tool
538
+ toolInput: newInputs.input,
539
+ log: ""
540
+ };
541
+ newInputs.agentOutcome = action;
542
+ return newInputs;
543
+ };
544
+ ```
545
+
546
+ #### Create the graph
547
+
548
+ We can now create a new graph with this new node
549
+
550
+ ```typescript
551
+ const workflow = new Graph();
552
+
553
+ // Add the same nodes as before, plus this "first agent"
554
+ workflow.addNode("firstAgent", firstAgent);
555
+ workflow.addNode("agent", agent);
556
+ workflow.addNode("tools", executeTools);
557
+
558
+ // We now set the entry point to be this first agent
559
+ workflow.setEntryPoint("firstAgent");
560
+
561
+ // We define the same edges as before
562
+ workflow.addConditionalEdges("agent", shouldContinue, {
563
+ continue: "tools",
564
+ exit: END
565
+ });
566
+ workflow.addEdge("tools", "agent");
567
+
568
+ // We also define a new edge, from the "first agent" to the tools node
569
+ // This is so that we can call the tool
570
+ workflow.addEdge("firstAgent", "tools");
571
+
572
+ // We now compile the graph as before
573
+ const chain = workflow.compile();
574
+ ```
575
+
576
+ #### Use it!
577
+
578
+ We can now use it as before!
579
+ Depending on whether or not the first tool call is actually useful, this may save you an LLM call or two.
580
+
581
+ ```typescript
582
+ const result = await chain.invoke({
583
+ input: "what is the weather in sf",
584
+ steps: []
585
+ });
586
+ ```
587
+
588
+ You can see a LangSmith trace of this chain [here](https://smith.langchain.com/public/2e0a089f-8c05-405a-8404-b0a60b79a84a/r).
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCheckpoint = exports.emptyChannels = exports.InvalidUpdateError = exports.EmptyChannelError = exports.BaseChannel = void 0;
4
+ class BaseChannel {
5
+ }
6
+ exports.BaseChannel = BaseChannel;
7
+ class EmptyChannelError extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "EmptyChannelError";
11
+ }
12
+ }
13
+ exports.EmptyChannelError = EmptyChannelError;
14
+ class InvalidUpdateError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = "InvalidUpdateError";
18
+ }
19
+ }
20
+ exports.InvalidUpdateError = InvalidUpdateError;
21
+ function emptyChannels(channels, checkpoint) {
22
+ const newChannels = {};
23
+ for (const k in channels) {
24
+ if (Object.prototype.hasOwnProperty.call(channels, k)) {
25
+ const channelValue = checkpoint.channelValues[k];
26
+ newChannels[k] = channels[k].empty(channelValue);
27
+ }
28
+ }
29
+ return newChannels;
30
+ }
31
+ exports.emptyChannels = emptyChannels;
32
+ async function createCheckpoint(checkpoint, channels) {
33
+ const newCheckpoint = {
34
+ v: 1,
35
+ ts: new Date().toISOString(),
36
+ channelValues: { ...checkpoint.channelValues },
37
+ channelVersions: { ...checkpoint.channelVersions },
38
+ versionsSeen: { ...checkpoint.versionsSeen },
39
+ };
40
+ for (const k in channels) {
41
+ if (newCheckpoint.channelValues[k] === undefined) {
42
+ try {
43
+ newCheckpoint.channelValues[k] = await channels[k].checkpoint();
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ }
46
+ catch (error) {
47
+ if ("name" in error && error.name === EmptyChannelError.name) {
48
+ // no-op
49
+ }
50
+ else {
51
+ throw error; // Rethrow unexpected errors
52
+ }
53
+ }
54
+ }
55
+ }
56
+ return newCheckpoint;
57
+ }
58
+ exports.createCheckpoint = createCheckpoint;