@langchain/langgraph 0.0.30 → 0.0.32

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 (44) hide show
  1. package/README.md +75 -28
  2. package/dist/channels/base.cjs +14 -0
  3. package/dist/channels/base.d.ts +2 -0
  4. package/dist/channels/base.js +14 -0
  5. package/dist/graph/message.d.ts +1 -1
  6. package/dist/graph/state.cjs +36 -2
  7. package/dist/graph/state.d.ts +23 -9
  8. package/dist/graph/state.js +34 -1
  9. package/dist/index.cjs +2 -1
  10. package/dist/index.js +2 -1
  11. package/dist/prebuilt/agent_executor.d.ts +1 -1
  12. package/dist/pregel/index.cjs +26 -21
  13. package/dist/pregel/index.js +26 -21
  14. package/package.json +9 -16
  15. package/dist/tests/channels.test.d.ts +0 -1
  16. package/dist/tests/channels.test.js +0 -151
  17. package/dist/tests/chatbot.int.test.d.ts +0 -1
  18. package/dist/tests/chatbot.int.test.js +0 -66
  19. package/dist/tests/checkpoints.test.d.ts +0 -1
  20. package/dist/tests/checkpoints.test.js +0 -178
  21. package/dist/tests/diagrams.test.d.ts +0 -1
  22. package/dist/tests/diagrams.test.js +0 -25
  23. package/dist/tests/graph.test.d.ts +0 -1
  24. package/dist/tests/graph.test.js +0 -33
  25. package/dist/tests/prebuilt.int.test.d.ts +0 -1
  26. package/dist/tests/prebuilt.int.test.js +0 -207
  27. package/dist/tests/prebuilt.test.d.ts +0 -1
  28. package/dist/tests/prebuilt.test.js +0 -427
  29. package/dist/tests/pregel.io.test.d.ts +0 -1
  30. package/dist/tests/pregel.io.test.js +0 -332
  31. package/dist/tests/pregel.read.test.d.ts +0 -1
  32. package/dist/tests/pregel.read.test.js +0 -109
  33. package/dist/tests/pregel.test.d.ts +0 -1
  34. package/dist/tests/pregel.test.js +0 -1882
  35. package/dist/tests/pregel.validate.test.d.ts +0 -1
  36. package/dist/tests/pregel.validate.test.js +0 -198
  37. package/dist/tests/pregel.write.test.d.ts +0 -1
  38. package/dist/tests/pregel.write.test.js +0 -44
  39. package/dist/tests/tracing.int.test.d.ts +0 -1
  40. package/dist/tests/tracing.int.test.js +0 -450
  41. package/dist/tests/tracing.test.d.ts +0 -1
  42. package/dist/tests/tracing.test.js +0 -332
  43. package/dist/tests/utils.d.ts +0 -53
  44. package/dist/tests/utils.js +0 -167
@@ -1,450 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable no-process-env */
3
- import { test } from "@jest/globals";
4
- import { pull } from "langchain/hub";
5
- import { ChatOpenAI } from "@langchain/openai";
6
- import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
7
- import { ChatPromptTemplate, MessagesPlaceholder, } from "@langchain/core/prompts";
8
- import { HumanMessage } from "@langchain/core/messages";
9
- import { RunnableLambda } from "@langchain/core/runnables";
10
- import { AgentExecutor, createOpenAIFunctionsAgent, createOpenAIToolsAgent, } from "langchain/agents";
11
- import { JsonOutputFunctionsParser, JsonOutputToolsParser, } from "langchain/output_parsers";
12
- import { createOpenAIFnRunnable } from "langchain/chains/openai_functions";
13
- import { zodToJsonSchema } from "zod-to-json-schema";
14
- import { z } from "zod";
15
- import { ToolExecutor } from "../prebuilt/tool_executor.js";
16
- import { createAgentExecutor } from "../prebuilt/agent_executor.js";
17
- // Import from main `@langchain/langgraph` endpoint to turn on automatic config passing
18
- import { StateGraph, END, START } from "../index.js";
19
- test.skip("Can invoke with tracing", async () => {
20
- const tools = [new TavilySearchResults({ maxResults: 1 })];
21
- // Get the prompt to use - you can modify this!
22
- const prompt = await pull("hwchase17/openai-functions-agent");
23
- // Choose the LLM that will drive the agent
24
- const llm = new ChatOpenAI({
25
- modelName: "gpt-4-1106-preview",
26
- temperature: 0,
27
- });
28
- // Construct the OpenAI Functions agent
29
- const agentRunnable = await createOpenAIFunctionsAgent({
30
- llm,
31
- tools,
32
- prompt,
33
- });
34
- const toolExecutor = new ToolExecutor({
35
- tools,
36
- });
37
- // Define logic that will be used to determine which conditional edge to go down
38
- const shouldContinue = (data) => {
39
- if (data.agentOutcome && "returnValues" in data.agentOutcome) {
40
- return "end";
41
- }
42
- return "continue";
43
- };
44
- const runAgent = async (data) => {
45
- const agentOutcome = await agentRunnable.invoke(data);
46
- return {
47
- agentOutcome,
48
- };
49
- };
50
- const executeTools = async (data) => {
51
- const agentAction = data.agentOutcome;
52
- if (!agentAction || "returnValues" in agentAction) {
53
- throw new Error("Agent has not been run yet");
54
- }
55
- const output = await toolExecutor.invoke(agentAction);
56
- return {
57
- steps: [{ action: agentAction, observation: JSON.stringify(output) }],
58
- };
59
- };
60
- // Define a new graph
61
- const workflow = new StateGraph({
62
- channels: {
63
- input: null,
64
- steps: {
65
- value: (x, y) => x.concat(y),
66
- default: () => [],
67
- },
68
- agentOutcome: null,
69
- },
70
- })
71
- // Define the two nodes we will cycle between
72
- .addNode("agent", new RunnableLambda({ func: runAgent }))
73
- .addNode("action", new RunnableLambda({ func: executeTools }))
74
- // Set the entrypoint as `agent`
75
- // This means that this node is the first one called
76
- .addEdge(START, "agent")
77
- // We now add a conditional edge
78
- .addConditionalEdges(
79
- // First, we define the start node. We use `agent`.
80
- // This means these are the edges taken after the `agent` node is called.
81
- "agent",
82
- // Next, we pass in the function that will determine which node is called next.
83
- shouldContinue,
84
- // Finally we pass in a mapping.
85
- // The keys are strings, and the values are other nodes.
86
- // END is a special node marking that the graph should finish.
87
- // What will happen is we will call `should_continue`, and then the output of that
88
- // will be matched against the keys in this mapping.
89
- // Based on which one it matches, that node will then be called.
90
- {
91
- // If `tools`, then we call the tool node.
92
- continue: "action",
93
- // Otherwise we finish.
94
- end: END,
95
- })
96
- // We now add a normal edge from `tools` to `agent`.
97
- // This means that after `tools` is called, `agent` node is called next.
98
- .addEdge("action", "agent");
99
- const app = workflow.compile();
100
- const inputs = { input: "what is the weather in sf" };
101
- for await (const s of await app.stream(inputs)) {
102
- console.log(s);
103
- console.log("----\n");
104
- }
105
- });
106
- test.skip("Can nest an agent executor", async () => {
107
- const tools = [new TavilySearchResults({ maxResults: 3 })];
108
- const llm = new ChatOpenAI({
109
- modelName: "gpt-4-1106-preview",
110
- temperature: 0,
111
- });
112
- const systemPrompt = `You are a web researcher. You may use the Tavily search engine to search the web for important information.`;
113
- // Each worker node will be given a name and some tools.
114
- const prompt = ChatPromptTemplate.fromMessages([
115
- ["system", systemPrompt],
116
- new MessagesPlaceholder("messages"),
117
- new MessagesPlaceholder("agent_scratchpad"),
118
- ]);
119
- const agent = await createOpenAIToolsAgent({ llm, tools, prompt });
120
- const executor = new AgentExecutor({ agent, tools });
121
- const researcherNode = async (state) => {
122
- console.log("STATE", state);
123
- const result = await executor.invoke(state);
124
- return {
125
- messages: [
126
- new HumanMessage({ content: result.output, name: "researcher" }),
127
- ],
128
- };
129
- };
130
- // Define the routing function
131
- const functionDef = {
132
- name: "route",
133
- description: "Select the next role.",
134
- parameters: {
135
- title: "routeSchema",
136
- type: "object",
137
- properties: {
138
- next: {
139
- title: "Next",
140
- anyOf: [{ enum: ["FINISH", "researcher"] }],
141
- },
142
- },
143
- required: ["next"],
144
- },
145
- };
146
- const toolDef = {
147
- type: "function",
148
- function: functionDef,
149
- };
150
- const supervisorPrompt = ChatPromptTemplate.fromMessages([
151
- ["system", systemPrompt],
152
- new MessagesPlaceholder("messages"),
153
- [
154
- "system",
155
- "Given the conversation above, who should act next? Or should we FINISH? Select one of: {options}",
156
- ],
157
- ]);
158
- const formattedPrompt = await supervisorPrompt.partial({
159
- options: ["FINISH", "researcher"].join(", "),
160
- });
161
- const supervisorChain = formattedPrompt
162
- .pipe(llm.bind({
163
- tools: [toolDef],
164
- tool_choice: { type: "function", function: { name: "route" } },
165
- }))
166
- .pipe(new JsonOutputToolsParser())
167
- // select the first one
168
- .pipe((x) => x[0].args);
169
- // 1. Create the graph
170
- const workflow = new StateGraph({
171
- channels: {
172
- messages: {
173
- value: (x, y) => x.concat(y),
174
- default: () => [],
175
- },
176
- next: null,
177
- },
178
- })
179
- // 2. Add the nodes; these will do the work
180
- .addNode("researcher", researcherNode)
181
- .addNode("supervisor", supervisorChain)
182
- // 3. Define the edges. We will define both regular and conditional ones
183
- // After a worker completes, report to supervisor
184
- .addEdge("researcher", "supervisor")
185
- // When the supervisor returns, route to the agent identified in the supervisor's output
186
- .addConditionalEdges("supervisor", (x) => x.next, {
187
- researcher: "researcher",
188
- // Or end work if done
189
- FINISH: END,
190
- })
191
- .addEdge(START, "supervisor");
192
- const graph = workflow.compile();
193
- const streamResults = graph.stream({
194
- messages: [
195
- new HumanMessage({
196
- content: "Who is the current prime minister of malaysia?",
197
- }),
198
- ],
199
- }, { tags: ["outer_tag"], recursionLimit: 100 });
200
- for await (const output of await streamResults) {
201
- if (!output?.__end__) {
202
- console.log(output);
203
- console.log("----");
204
- }
205
- }
206
- });
207
- test.skip("Can nest a graph within a graph", async () => {
208
- const tools = [new TavilySearchResults({ maxResults: 3 })];
209
- const llm = new ChatOpenAI({
210
- modelName: "gpt-4-1106-preview",
211
- temperature: 0,
212
- });
213
- const systemPrompt = `You are a web researcher. You may use the Tavily search engine to search the web for important information.`;
214
- // Each worker node will be given a name and some tools.
215
- const prompt = ChatPromptTemplate.fromMessages([
216
- ["system", systemPrompt],
217
- new MessagesPlaceholder("messages"),
218
- new MessagesPlaceholder("agent_scratchpad"),
219
- ]);
220
- const agent = await createOpenAIToolsAgent({ llm, tools, prompt });
221
- const executor = new AgentExecutor({ agent, tools });
222
- const researcherNode = async (state) => {
223
- const result = await executor.invoke(state);
224
- return {
225
- messages: [
226
- new HumanMessage({ content: result.output, name: "researcher" }),
227
- ],
228
- };
229
- };
230
- // Define the routing function
231
- const functionDef = {
232
- name: "route",
233
- description: "Select the next role.",
234
- parameters: {
235
- title: "routeSchema",
236
- type: "object",
237
- properties: {
238
- next: {
239
- title: "Next",
240
- anyOf: [{ enum: ["FINISH", "researcher"] }],
241
- },
242
- },
243
- required: ["next"],
244
- },
245
- };
246
- const toolDef = {
247
- type: "function",
248
- function: functionDef,
249
- };
250
- const supervisorPrompt = ChatPromptTemplate.fromMessages([
251
- ["system", systemPrompt],
252
- new MessagesPlaceholder("messages"),
253
- [
254
- "system",
255
- "Given the conversation above, who should act next? Or should we FINISH? Select one of: {options}",
256
- ],
257
- ]);
258
- const formattedPrompt = await supervisorPrompt.partial({
259
- options: ["FINISH", "researcher"].join(", "),
260
- });
261
- const supervisorChain = formattedPrompt
262
- .pipe(llm.bind({
263
- tools: [toolDef],
264
- tool_choice: { type: "function", function: { name: "route" } },
265
- }))
266
- .pipe(new JsonOutputToolsParser())
267
- // select the first one
268
- .pipe((x) => x[0].args);
269
- // 1. Create the graph
270
- const workflow = new StateGraph({
271
- channels: {
272
- messages: {
273
- value: (x, y) => x.concat(y),
274
- default: () => [],
275
- },
276
- next: null,
277
- },
278
- })
279
- // 2. Add the nodes; these will do the work
280
- .addNode("researcher", researcherNode)
281
- .addNode("supervisor", supervisorChain)
282
- // 3. Define the edges. We will define both regular and conditional ones
283
- // After a worker completes, report to supervisor
284
- .addEdge("researcher", "supervisor")
285
- // When the supervisor returns, route to the agent identified in the supervisor's output
286
- .addConditionalEdges("supervisor", (x) => x.next, {
287
- researcher: "researcher",
288
- FINISH: END,
289
- })
290
- .addEdge(START, "supervisor");
291
- const graph = workflow.compile();
292
- const streamResults = graph.stream({
293
- messages: [
294
- new HumanMessage({
295
- content: "Who is the current prime minister of malaysia?",
296
- }),
297
- ],
298
- }, { tags: ["outer_tag"], recursionLimit: 100 });
299
- for await (const output of await streamResults) {
300
- if (!output?.__end__) {
301
- console.log(output);
302
- console.log("----");
303
- }
304
- }
305
- });
306
- test.skip("Should trace plan and execute flow", async () => {
307
- const tools = [new TavilySearchResults({ maxResults: 3 })];
308
- // Get the prompt to use - you can modify this!
309
- const prompt = await pull("hwchase17/openai-functions-agent");
310
- // Choose the LLM that will drive the agent
311
- const llm = new ChatOpenAI({ modelName: "gpt-4-0125-preview" });
312
- // Construct the OpenAI Functions agent
313
- const agentRunnable = await createOpenAIFunctionsAgent({
314
- llm,
315
- tools,
316
- prompt,
317
- });
318
- const agentExecutor = createAgentExecutor({
319
- agentRunnable,
320
- tools,
321
- });
322
- const plan = zodToJsonSchema(z.object({
323
- steps: z
324
- .array(z.string())
325
- .describe("different steps to follow, should be in sorted order"),
326
- }));
327
- const planFunction = {
328
- name: "plan",
329
- description: "This tool is used to plan the steps to follow",
330
- parameters: plan,
331
- };
332
- const plannerPrompt = ChatPromptTemplate.fromTemplate(`For the given objective, come up with a simple step by step plan. \
333
- This plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps. \
334
- The result of the final step should be the final answer. Make sure that each step has all the information needed - do not skip steps.
335
-
336
- {objective}`);
337
- const model = new ChatOpenAI({
338
- modelName: "gpt-4-0125-preview",
339
- }).bind({
340
- functions: [planFunction],
341
- function_call: planFunction,
342
- });
343
- const parserSingle = new JsonOutputFunctionsParser({
344
- argsOnly: true,
345
- });
346
- const planner = plannerPrompt.pipe(model).pipe(parserSingle);
347
- const response = zodToJsonSchema(z.object({
348
- response: z.string().describe("Response to user."),
349
- }));
350
- const responseFunction = {
351
- name: "response",
352
- description: "Response to user.",
353
- parameters: response,
354
- };
355
- const replannerPrompt = ChatPromptTemplate.fromTemplate(`For the given objective, come up with a simple step by step plan.
356
- This plan should involve individual tasks, that if executed correctly will yield the correct answer. Do not add any superfluous steps.
357
- The result of the final step should be the final answer. Make sure that each step has all the information needed - do not skip steps.
358
-
359
- Your objective was this:
360
- {input}
361
-
362
- Your original plan was this:
363
- {plan}
364
-
365
- You have currently done the follow steps:
366
- {pastSteps}
367
-
368
- Update your plan accordingly. If no more steps are needed and you can return to the user, then respond with that and use the 'response' function.
369
- Otherwise, fill out the plan.
370
- Only add steps to the plan that still NEED to be done. Do not return previously done steps as part of the plan.`);
371
- const parser = new JsonOutputFunctionsParser();
372
- const replanner = createOpenAIFnRunnable({
373
- functions: [planFunction, responseFunction],
374
- outputParser: parser,
375
- llm: new ChatOpenAI({
376
- modelName: "gpt-4-0125-preview",
377
- }),
378
- prompt: replannerPrompt,
379
- });
380
- async function executeStep(state) {
381
- const task = state.input;
382
- const agentResponse = await agentExecutor.invoke({ input: task });
383
- return {
384
- pastSteps: [task, agentResponse.agentOutcome.returnValues.output],
385
- };
386
- }
387
- async function planStep(state) {
388
- if (!state.input) {
389
- throw new Error("No input.");
390
- }
391
- const plan = await planner.invoke({ objective: state.input });
392
- return { plan: plan.steps };
393
- }
394
- async function replanStep(state) {
395
- const output = await replanner.invoke({
396
- input: state.input,
397
- plan: state.plan ? state.plan.join("\n") : "",
398
- pastSteps: state.pastSteps.join("\n"),
399
- });
400
- if (output.response !== undefined) {
401
- return { response: output.response };
402
- }
403
- return { plan: output.steps };
404
- }
405
- function shouldEnd(state) {
406
- if (state.response) {
407
- return "true";
408
- }
409
- return "false";
410
- }
411
- const workflow = new StateGraph({
412
- channels: {
413
- input: null,
414
- plan: null,
415
- pastSteps: {
416
- reducer: (x, y) => x.concat(y),
417
- default: () => [],
418
- },
419
- response: null,
420
- },
421
- })
422
- // Add the plan node
423
- .addNode("planner", planStep)
424
- // Add the execution step
425
- .addNode("agent", executeStep)
426
- // Add a replan node
427
- .addNode("replan", replanStep)
428
- .addEdge(START, "planner")
429
- // From plan we go to agent
430
- .addEdge("planner", "agent")
431
- // From agent, we replan
432
- .addEdge("agent", "replan")
433
- .addConditionalEdges("replan",
434
- // Next, we pass in the function that will determine which node is called next.
435
- shouldEnd, {
436
- true: END,
437
- false: "planner",
438
- });
439
- // Finally, we compile it!
440
- // This compiles it into a LangChain Runnable,
441
- // meaning you can use it as you would any other runnable
442
- const app = workflow.compile();
443
- const config = { recursionLimit: 50 };
444
- const inputs = {
445
- input: "what is the hometown of the 2024 Australia open winner?",
446
- };
447
- for await (const event of await app.stream(inputs, config)) {
448
- console.log(event);
449
- }
450
- });
@@ -1 +0,0 @@
1
- export {};