@langchain/langgraph 0.0.26 → 0.0.27

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.
@@ -4,7 +4,6 @@ import { PromptTemplate } from "@langchain/core/prompts";
4
4
  import { StructuredTool, Tool } from "@langchain/core/tools";
5
5
  import { FakeStreamingLLM } from "@langchain/core/utils/testing";
6
6
  import { AIMessage, HumanMessage, SystemMessage, ToolMessage, } from "@langchain/core/messages";
7
- import { RunnableLambda } from "@langchain/core/runnables";
8
7
  import { z } from "zod";
9
8
  import { FakeToolCallingChatModel } from "./utils.js";
10
9
  import { createAgentExecutor, createReactAgent } from "../prebuilt/index.js";
@@ -227,6 +226,38 @@ describe("createReactAgent", () => {
227
226
  return `result for ${input?.query}`;
228
227
  }
229
228
  }
229
+ class SearchAPIWithArtifact extends StructuredTool {
230
+ constructor() {
231
+ super(...arguments);
232
+ Object.defineProperty(this, "name", {
233
+ enumerable: true,
234
+ configurable: true,
235
+ writable: true,
236
+ value: "search_api"
237
+ });
238
+ Object.defineProperty(this, "description", {
239
+ enumerable: true,
240
+ configurable: true,
241
+ writable: true,
242
+ value: "A simple API that returns the input string."
243
+ });
244
+ Object.defineProperty(this, "schema", {
245
+ enumerable: true,
246
+ configurable: true,
247
+ writable: true,
248
+ value: searchSchema
249
+ });
250
+ Object.defineProperty(this, "responseFormat", {
251
+ enumerable: true,
252
+ configurable: true,
253
+ writable: true,
254
+ value: "content_and_artifact"
255
+ });
256
+ }
257
+ async _call(_) {
258
+ return ["some response format", Buffer.from("123")];
259
+ }
260
+ }
230
261
  const tools = [new SearchAPI()];
231
262
  it("Can use string message modifier", async () => {
232
263
  const llm = new FakeToolCallingChatModel({
@@ -299,100 +330,41 @@ describe("createReactAgent", () => {
299
330
  new AIMessage("result2"),
300
331
  ]);
301
332
  });
302
- it("Can use custom function message modifier", async () => {
303
- const aiM1 = new AIMessage({
304
- content: "result1",
305
- tool_calls: [
306
- { name: "search_api", id: "tool_abcd123", args: { query: "foo" } },
307
- ],
308
- });
309
- const aiM2 = new AIMessage("result2");
333
+ it("Works with tools that return content_and_artifact response format", async () => {
310
334
  const llm = new FakeToolCallingChatModel({
311
- responses: [aiM1, aiM2],
312
- });
313
- const messageModifier = (messages) => [
314
- new SystemMessage("You are a helpful assistant"),
315
- ...messages,
316
- ];
317
- const agent = createReactAgent({ llm, tools, messageModifier });
318
- const result = await agent.invoke({
319
- messages: [new HumanMessage("Hello Input!")],
320
- });
321
- expect(result.messages).toEqual([
322
- new HumanMessage("Hello Input!"),
323
- aiM1,
324
- new ToolMessage({
325
- name: "search_api",
326
- content: "result for foo",
327
- tool_call_id: "tool_abcd123",
328
- }),
329
- aiM2,
330
- ]);
331
- });
332
- it("Can use async custom function message modifier", async () => {
333
- const aiM1 = new AIMessage({
334
- content: "result1",
335
- tool_calls: [
336
- { name: "search_api", id: "tool_abcd123", args: { query: "foo" } },
335
+ responses: [
336
+ new AIMessage({
337
+ content: "result1",
338
+ tool_calls: [
339
+ { name: "search_api", id: "tool_abcd123", args: { query: "foo" } },
340
+ ],
341
+ }),
342
+ new AIMessage("result2"),
337
343
  ],
338
344
  });
339
- const aiM2 = new AIMessage("result2");
340
- const llm = new FakeToolCallingChatModel({
341
- responses: [aiM1, aiM2],
345
+ const agent = createReactAgent({
346
+ llm,
347
+ tools: [new SearchAPIWithArtifact()],
348
+ messageModifier: "You are a helpful assistant",
342
349
  });
343
- const messageModifier = async (messages) => [
344
- new SystemMessage("You are a helpful assistant"),
345
- ...messages,
346
- ];
347
- const agent = createReactAgent({ llm, tools, messageModifier });
348
350
  const result = await agent.invoke({
349
351
  messages: [new HumanMessage("Hello Input!")],
350
352
  });
351
353
  expect(result.messages).toEqual([
352
354
  new HumanMessage("Hello Input!"),
353
- aiM1,
354
- new ToolMessage({
355
- name: "search_api",
356
- content: "result for foo",
357
- tool_call_id: "tool_abcd123",
355
+ new AIMessage({
356
+ content: "result1",
357
+ tool_calls: [
358
+ { name: "search_api", id: "tool_abcd123", args: { query: "foo" } },
359
+ ],
358
360
  }),
359
- aiM2,
360
- ]);
361
- });
362
- it("Can use RunnableLambda message modifier", async () => {
363
- const aiM1 = new AIMessage({
364
- content: "result1",
365
- tool_calls: [
366
- { name: "search_api", id: "tool_abcd123", args: { query: "foo" } },
367
- ],
368
- });
369
- const aiM2 = new AIMessage("result2");
370
- const llm = new FakeToolCallingChatModel({
371
- responses: [aiM1, aiM2],
372
- });
373
- const messageModifier = new RunnableLambda({
374
- func: (messages) => [
375
- new SystemMessage("You are a helpful assistant"),
376
- ...messages,
377
- ],
378
- });
379
- const agent = createReactAgent({ llm, tools, messageModifier });
380
- const result = await agent.invoke({
381
- messages: [
382
- new HumanMessage("Hello Input!"),
383
- new HumanMessage("Another Input!"),
384
- ],
385
- });
386
- expect(result.messages).toEqual([
387
- new HumanMessage("Hello Input!"),
388
- new HumanMessage("Another Input!"),
389
- aiM1,
390
361
  new ToolMessage({
391
362
  name: "search_api",
392
- content: "result for foo",
363
+ content: "some response format",
393
364
  tool_call_id: "tool_abcd123",
365
+ artifact: Buffer.from("123"),
394
366
  }),
395
- aiM2,
367
+ new AIMessage("result2"),
396
368
  ]);
397
369
  });
398
370
  });
@@ -53,7 +53,12 @@ it("should pass config through if importing from the primary entrypoint", async
53
53
  name: "__start__",
54
54
  tags: ["graph:step:0", "langsmith:hidden"],
55
55
  run_id: expect.any(String),
56
- metadata: {},
56
+ metadata: {
57
+ langgraph_node: "__start__",
58
+ langgraph_step: 0,
59
+ langgraph_task_idx: 0,
60
+ langgraph_triggers: ["__pregel_tasks"],
61
+ },
57
62
  },
58
63
  {
59
64
  event: "on_chain_end",
@@ -66,7 +71,12 @@ it("should pass config through if importing from the primary entrypoint", async
66
71
  run_id: expect.any(String),
67
72
  name: "__start__",
68
73
  tags: ["graph:step:0", "langsmith:hidden"],
69
- metadata: {},
74
+ metadata: {
75
+ langgraph_node: "__start__",
76
+ langgraph_step: 0,
77
+ langgraph_task_idx: 0,
78
+ langgraph_triggers: ["__pregel_tasks"],
79
+ },
70
80
  },
71
81
  {
72
82
  event: "on_chain_start",
@@ -78,7 +88,12 @@ it("should pass config through if importing from the primary entrypoint", async
78
88
  name: "testnode",
79
89
  tags: ["graph:step:1"],
80
90
  run_id: expect.any(String),
81
- metadata: {},
91
+ metadata: {
92
+ langgraph_node: "testnode",
93
+ langgraph_step: 1,
94
+ langgraph_task_idx: 0,
95
+ langgraph_triggers: ["__pregel_tasks"],
96
+ },
82
97
  },
83
98
  {
84
99
  event: "on_chain_start",
@@ -90,7 +105,12 @@ it("should pass config through if importing from the primary entrypoint", async
90
105
  name: "RunnableLambda",
91
106
  tags: ["seq:step:1"],
92
107
  run_id: expect.any(String),
93
- metadata: {},
108
+ metadata: {
109
+ langgraph_node: "testnode",
110
+ langgraph_step: 1,
111
+ langgraph_task_idx: 0,
112
+ langgraph_triggers: ["__pregel_tasks"],
113
+ },
94
114
  },
95
115
  {
96
116
  event: "on_chat_model_start",
@@ -103,6 +123,10 @@ it("should pass config through if importing from the primary entrypoint", async
103
123
  tags: [],
104
124
  run_id: expect.any(String),
105
125
  metadata: {
126
+ langgraph_node: "testnode",
127
+ langgraph_step: 1,
128
+ langgraph_task_idx: 0,
129
+ langgraph_triggers: ["__pregel_tasks"],
106
130
  ls_model_type: "chat",
107
131
  ls_stop: undefined,
108
132
  },
@@ -119,6 +143,10 @@ it("should pass config through if importing from the primary entrypoint", async
119
143
  name: "model_call",
120
144
  tags: [],
121
145
  metadata: {
146
+ langgraph_node: "testnode",
147
+ langgraph_step: 1,
148
+ langgraph_task_idx: 0,
149
+ langgraph_triggers: ["__pregel_tasks"],
122
150
  ls_model_type: "chat",
123
151
  ls_stop: undefined,
124
152
  },
@@ -136,7 +164,12 @@ it("should pass config through if importing from the primary entrypoint", async
136
164
  run_id: expect.any(String),
137
165
  name: "RunnableLambda",
138
166
  tags: ["seq:step:1"],
139
- metadata: {},
167
+ metadata: {
168
+ langgraph_node: "testnode",
169
+ langgraph_step: 1,
170
+ langgraph_task_idx: 0,
171
+ langgraph_triggers: ["__pregel_tasks"],
172
+ },
140
173
  },
141
174
  {
142
175
  event: "on_chain_start",
@@ -148,7 +181,12 @@ it("should pass config through if importing from the primary entrypoint", async
148
181
  name: "ChannelWrite<messages,testnode>",
149
182
  tags: ["seq:step:2", "langsmith:hidden"],
150
183
  run_id: expect.any(String),
151
- metadata: {},
184
+ metadata: {
185
+ langgraph_node: "testnode",
186
+ langgraph_step: 1,
187
+ langgraph_task_idx: 0,
188
+ langgraph_triggers: ["__pregel_tasks"],
189
+ },
152
190
  },
153
191
  {
154
192
  event: "on_chain_end",
@@ -161,7 +199,12 @@ it("should pass config through if importing from the primary entrypoint", async
161
199
  run_id: expect.any(String),
162
200
  name: "ChannelWrite<messages,testnode>",
163
201
  tags: ["seq:step:2", "langsmith:hidden"],
164
- metadata: {},
202
+ metadata: {
203
+ langgraph_node: "testnode",
204
+ langgraph_step: 1,
205
+ langgraph_task_idx: 0,
206
+ langgraph_triggers: ["__pregel_tasks"],
207
+ },
165
208
  },
166
209
  {
167
210
  event: "on_chain_start",
@@ -173,7 +216,12 @@ it("should pass config through if importing from the primary entrypoint", async
173
216
  name: "func",
174
217
  tags: ["seq:step:3"],
175
218
  run_id: expect.any(String),
176
- metadata: {},
219
+ metadata: {
220
+ langgraph_node: "testnode",
221
+ langgraph_step: 1,
222
+ langgraph_task_idx: 0,
223
+ langgraph_triggers: ["__pregel_tasks"],
224
+ },
177
225
  },
178
226
  {
179
227
  event: "on_chat_model_start",
@@ -186,6 +234,10 @@ it("should pass config through if importing from the primary entrypoint", async
186
234
  tags: [],
187
235
  run_id: expect.any(String),
188
236
  metadata: {
237
+ langgraph_node: "testnode",
238
+ langgraph_step: 1,
239
+ langgraph_task_idx: 0,
240
+ langgraph_triggers: ["__pregel_tasks"],
189
241
  ls_model_type: "chat",
190
242
  ls_stop: undefined,
191
243
  },
@@ -202,6 +254,10 @@ it("should pass config through if importing from the primary entrypoint", async
202
254
  name: "conditional_edge_call",
203
255
  tags: [],
204
256
  metadata: {
257
+ langgraph_node: "testnode",
258
+ langgraph_step: 1,
259
+ langgraph_task_idx: 0,
260
+ langgraph_triggers: ["__pregel_tasks"],
205
261
  ls_model_type: "chat",
206
262
  ls_stop: undefined,
207
263
  },
@@ -219,7 +275,12 @@ it("should pass config through if importing from the primary entrypoint", async
219
275
  run_id: expect.any(String),
220
276
  name: "func",
221
277
  tags: ["seq:step:3"],
222
- metadata: {},
278
+ metadata: {
279
+ langgraph_node: "testnode",
280
+ langgraph_step: 1,
281
+ langgraph_task_idx: 0,
282
+ langgraph_triggers: ["__pregel_tasks"],
283
+ },
223
284
  },
224
285
  {
225
286
  event: "on_chain_end",
@@ -232,7 +293,12 @@ it("should pass config through if importing from the primary entrypoint", async
232
293
  run_id: expect.any(String),
233
294
  name: "testnode",
234
295
  tags: ["graph:step:1"],
235
- metadata: {},
296
+ metadata: {
297
+ langgraph_node: "testnode",
298
+ langgraph_step: 1,
299
+ langgraph_task_idx: 0,
300
+ langgraph_triggers: ["__pregel_tasks"],
301
+ },
236
302
  },
237
303
  {
238
304
  event: "on_chain_stream",
@@ -4,6 +4,7 @@ import { BaseMessage } from "@langchain/core/messages";
4
4
  import { ChatResult } from "@langchain/core/outputs";
5
5
  import { RunnableConfig } from "@langchain/core/runnables";
6
6
  import { Tool } from "@langchain/core/tools";
7
+ import { z } from "zod";
7
8
  import { MemorySaver } from "../checkpoint/memory.js";
8
9
  import { Checkpoint, CheckpointMetadata } from "../checkpoint/base.js";
9
10
  export interface FakeChatModelArgs extends BaseChatModelParams {
@@ -35,3 +36,18 @@ export declare class MemorySaverAssertImmutable extends MemorySaver {
35
36
  constructor();
36
37
  put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;
37
38
  }
39
+ export declare class FakeSearchTool extends Tool {
40
+ name: string;
41
+ description: string;
42
+ schema: z.ZodEffects<z.ZodObject<{
43
+ input: z.ZodOptional<z.ZodString>;
44
+ }, "strip", z.ZodTypeAny, {
45
+ input?: string | undefined;
46
+ }, {
47
+ input?: string | undefined;
48
+ }>, string | undefined, {
49
+ input?: string | undefined;
50
+ }>;
51
+ constructor();
52
+ _call(query: string): Promise<string>;
53
+ }
@@ -1,6 +1,8 @@
1
1
  import assert from "node:assert";
2
2
  import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
3
3
  import { AIMessage } from "@langchain/core/messages";
4
+ import { Tool } from "@langchain/core/tools";
5
+ import { z } from "zod";
4
6
  import { MemorySaver } from "../checkpoint/memory.js";
5
7
  export class FakeChatModel extends BaseChatModel {
6
8
  constructor(fields) {
@@ -133,3 +135,33 @@ export class MemorySaverAssertImmutable extends MemorySaver {
133
135
  return super.put(config, checkpoint, metadata);
134
136
  }
135
137
  }
138
+ export class FakeSearchTool extends Tool {
139
+ constructor() {
140
+ super();
141
+ Object.defineProperty(this, "name", {
142
+ enumerable: true,
143
+ configurable: true,
144
+ writable: true,
145
+ value: "search_api"
146
+ });
147
+ Object.defineProperty(this, "description", {
148
+ enumerable: true,
149
+ configurable: true,
150
+ writable: true,
151
+ value: "A simple API that returns the input string."
152
+ });
153
+ Object.defineProperty(this, "schema", {
154
+ enumerable: true,
155
+ configurable: true,
156
+ writable: true,
157
+ value: z
158
+ .object({
159
+ input: z.string().optional(),
160
+ })
161
+ .transform((data) => data.input)
162
+ });
163
+ }
164
+ async _call(query) {
165
+ return `result for ${query}`;
166
+ }
167
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "description": "LangGraph",
5
5
  "type": "module",
6
6
  "engines": {
@@ -37,12 +37,13 @@
37
37
  "author": "LangChain",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "@langchain/core": ">0.1.61 <0.3.0",
41
- "uuid": "^10.0.0"
40
+ "@langchain/core": ">=0.2.16 <0.3.0",
41
+ "uuid": "^10.0.0",
42
+ "zod": "^3.23.8"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@jest/globals": "^29.5.0",
45
- "@langchain/anthropic": "^0.1.21",
46
+ "@langchain/anthropic": "^0.2.3",
46
47
  "@langchain/community": "^0.2.12",
47
48
  "@langchain/openai": "^0.1.3",
48
49
  "@langchain/scripts": "^0.0.13",
@@ -73,7 +74,6 @@
73
74
  "ts-jest": "^29.1.0",
74
75
  "tsx": "^4.7.0",
75
76
  "typescript": "^4.9.5 || ^5.4.5",
76
- "zod": "^3.22.4",
77
77
  "zod-to-json-schema": "^3.22.4"
78
78
  },
79
79
  "peerDependencies": {