@langchain/langgraph 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channels/any_value.cjs +57 -0
- package/dist/channels/any_value.d.ts +16 -0
- package/dist/channels/any_value.js +53 -0
- package/dist/channels/base.cjs +19 -28
- package/dist/channels/base.d.ts +13 -19
- package/dist/channels/base.js +17 -24
- package/dist/channels/binop.cjs +4 -3
- package/dist/channels/binop.d.ts +1 -1
- package/dist/channels/binop.js +3 -2
- package/dist/channels/dynamic_barrier_value.cjs +88 -0
- package/dist/channels/dynamic_barrier_value.d.ts +26 -0
- package/dist/channels/dynamic_barrier_value.js +84 -0
- package/dist/channels/ephemeral_value.cjs +64 -0
- package/dist/channels/ephemeral_value.d.ts +14 -0
- package/dist/channels/ephemeral_value.js +60 -0
- package/dist/channels/index.cjs +1 -3
- package/dist/channels/index.d.ts +1 -1
- package/dist/channels/index.js +1 -1
- package/dist/channels/last_value.cjs +11 -5
- package/dist/channels/last_value.d.ts +5 -1
- package/dist/channels/last_value.js +9 -3
- package/dist/channels/named_barrier_value.cjs +71 -0
- package/dist/channels/named_barrier_value.d.ts +18 -0
- package/dist/channels/named_barrier_value.js +66 -0
- package/dist/channels/topic.cjs +5 -3
- package/dist/channels/topic.d.ts +3 -3
- package/dist/channels/topic.js +5 -3
- package/dist/checkpoint/base.cjs +30 -12
- package/dist/checkpoint/base.d.ts +39 -22
- package/dist/checkpoint/base.js +28 -11
- package/dist/checkpoint/id.cjs +40 -0
- package/dist/checkpoint/id.d.ts +2 -0
- package/dist/checkpoint/id.js +35 -0
- package/dist/checkpoint/index.cjs +2 -2
- package/dist/checkpoint/index.d.ts +2 -2
- package/dist/checkpoint/index.js +2 -2
- package/dist/checkpoint/memory.cjs +63 -49
- package/dist/checkpoint/memory.d.ts +7 -10
- package/dist/checkpoint/memory.js +62 -47
- package/dist/checkpoint/sqlite.cjs +170 -0
- package/dist/checkpoint/sqlite.d.ts +14 -0
- package/dist/checkpoint/sqlite.js +163 -0
- package/dist/constants.cjs +3 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/errors.cjs +31 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +24 -0
- package/dist/graph/graph.cjs +234 -96
- package/dist/graph/graph.d.ts +52 -23
- package/dist/graph/graph.js +233 -97
- package/dist/graph/index.cjs +2 -2
- package/dist/graph/index.d.ts +2 -2
- package/dist/graph/index.js +2 -2
- package/dist/graph/message.cjs +4 -3
- package/dist/graph/message.d.ts +4 -1
- package/dist/graph/message.js +4 -3
- package/dist/graph/state.cjs +237 -102
- package/dist/graph/state.d.ts +41 -18
- package/dist/graph/state.js +238 -104
- package/dist/index.cjs +6 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/prebuilt/agent_executor.cjs +22 -36
- package/dist/prebuilt/agent_executor.d.ts +7 -10
- package/dist/prebuilt/agent_executor.js +23 -37
- package/dist/prebuilt/chat_agent_executor.cjs +13 -13
- package/dist/prebuilt/chat_agent_executor.d.ts +3 -1
- package/dist/prebuilt/chat_agent_executor.js +15 -15
- package/dist/prebuilt/index.cjs +4 -1
- package/dist/prebuilt/index.d.ts +1 -0
- package/dist/prebuilt/index.js +1 -0
- package/dist/prebuilt/tool_node.cjs +59 -0
- package/dist/prebuilt/tool_node.d.ts +17 -0
- package/dist/prebuilt/tool_node.js +54 -0
- package/dist/pregel/debug.cjs +6 -8
- package/dist/pregel/debug.d.ts +2 -2
- package/dist/pregel/debug.js +5 -7
- package/dist/pregel/index.cjs +406 -236
- package/dist/pregel/index.d.ts +77 -41
- package/dist/pregel/index.js +408 -241
- package/dist/pregel/io.cjs +117 -30
- package/dist/pregel/io.d.ts +11 -3
- package/dist/pregel/io.js +111 -28
- package/dist/pregel/read.cjs +126 -46
- package/dist/pregel/read.d.ts +27 -18
- package/dist/pregel/read.js +125 -45
- package/dist/pregel/types.cjs +2 -0
- package/dist/pregel/types.d.ts +32 -0
- package/dist/pregel/types.js +1 -0
- package/dist/pregel/validate.cjs +58 -51
- package/dist/pregel/validate.d.ts +14 -13
- package/dist/pregel/validate.js +56 -50
- package/dist/pregel/write.cjs +46 -30
- package/dist/pregel/write.d.ts +18 -8
- package/dist/pregel/write.js +45 -29
- package/dist/serde/base.cjs +2 -0
- package/dist/serde/base.d.ts +4 -0
- package/dist/serde/base.js +1 -0
- package/dist/setup/async_local_storage.cjs +2 -2
- package/dist/setup/async_local_storage.js +1 -1
- package/dist/tests/channels.test.d.ts +1 -0
- package/dist/tests/channels.test.js +151 -0
- package/dist/tests/chatbot.int.test.d.ts +1 -0
- package/dist/tests/chatbot.int.test.js +61 -0
- package/dist/tests/checkpoints.test.d.ts +1 -0
- package/dist/tests/checkpoints.test.js +190 -0
- package/dist/tests/graph.test.d.ts +1 -0
- package/dist/tests/graph.test.js +15 -0
- package/dist/tests/prebuilt.int.test.d.ts +1 -0
- package/dist/tests/prebuilt.int.test.js +101 -0
- package/dist/tests/prebuilt.test.d.ts +1 -0
- package/dist/tests/prebuilt.test.js +195 -0
- package/dist/tests/pregel.io.test.d.ts +1 -0
- package/dist/tests/pregel.io.test.js +332 -0
- package/dist/tests/pregel.read.test.d.ts +1 -0
- package/dist/tests/pregel.read.test.js +109 -0
- package/dist/tests/pregel.test.d.ts +1 -0
- package/dist/tests/pregel.test.js +1879 -0
- package/dist/tests/pregel.validate.test.d.ts +1 -0
- package/dist/tests/pregel.validate.test.js +198 -0
- package/dist/tests/pregel.write.test.d.ts +1 -0
- package/dist/tests/pregel.write.test.js +44 -0
- package/dist/tests/tracing.int.test.d.ts +1 -0
- package/dist/tests/tracing.int.test.js +449 -0
- package/dist/tests/utils.d.ts +22 -0
- package/dist/tests/utils.js +76 -0
- package/dist/utils.cjs +74 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.js +70 -0
- package/package.json +12 -8
- package/dist/pregel/reserved.cjs +0 -6
- package/dist/pregel/reserved.d.ts +0 -3
- package/dist/pregel/reserved.js +0 -3
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { deepCopy } from "../checkpoint/base.js";
|
|
3
|
+
import { MemorySaver } from "../checkpoint/memory.js";
|
|
4
|
+
import { SqliteSaver } from "../checkpoint/sqlite.js";
|
|
5
|
+
import { convert1to6, uuid6 } from "../checkpoint/id.js";
|
|
6
|
+
const checkpoint1 = {
|
|
7
|
+
v: 1,
|
|
8
|
+
id: uuid6(-1),
|
|
9
|
+
ts: "2024-04-19T17:19:07.952Z",
|
|
10
|
+
channel_values: {
|
|
11
|
+
someKey1: "someValue1",
|
|
12
|
+
},
|
|
13
|
+
channel_versions: {
|
|
14
|
+
someKey2: 1,
|
|
15
|
+
},
|
|
16
|
+
versions_seen: {
|
|
17
|
+
someKey3: {
|
|
18
|
+
someKey4: 1,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const checkpoint2 = {
|
|
23
|
+
v: 1,
|
|
24
|
+
id: uuid6(1),
|
|
25
|
+
ts: "2024-04-20T17:19:07.952Z",
|
|
26
|
+
channel_values: {
|
|
27
|
+
someKey1: "someValue2",
|
|
28
|
+
},
|
|
29
|
+
channel_versions: {
|
|
30
|
+
someKey2: 2,
|
|
31
|
+
},
|
|
32
|
+
versions_seen: {
|
|
33
|
+
someKey3: {
|
|
34
|
+
someKey4: 2,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
describe("Base", () => {
|
|
39
|
+
it("should deep copy a simple object", () => {
|
|
40
|
+
const obj = { a: 1, b: { c: 2 } };
|
|
41
|
+
const copiedObj = deepCopy(obj);
|
|
42
|
+
// Check if the copied object is equal to the original object
|
|
43
|
+
expect(copiedObj).toEqual(obj);
|
|
44
|
+
// Check if the copied object is not the same object reference as the original object
|
|
45
|
+
expect(copiedObj).not.toBe(obj);
|
|
46
|
+
// Check if the nested object is also deep copied
|
|
47
|
+
expect(copiedObj.b).toEqual(obj.b);
|
|
48
|
+
expect(copiedObj.b).not.toBe(obj.b);
|
|
49
|
+
});
|
|
50
|
+
it("should deep copy an array", () => {
|
|
51
|
+
const arr = [1, 2, 3];
|
|
52
|
+
const copiedArr = deepCopy(arr);
|
|
53
|
+
// Check if the copied array is equal to the original array
|
|
54
|
+
expect(copiedArr).toEqual(arr);
|
|
55
|
+
});
|
|
56
|
+
it("should deep copy an array of objects", () => {
|
|
57
|
+
const arr = [{ a: 1 }, { b: 2 }];
|
|
58
|
+
const copiedArr = deepCopy(arr);
|
|
59
|
+
// Check if the copied array is equal to the original array
|
|
60
|
+
expect(copiedArr).toEqual(arr);
|
|
61
|
+
// Check if the copied array is not the same array reference as the original array
|
|
62
|
+
expect(copiedArr).not.toBe(arr);
|
|
63
|
+
// Check if the nested objects in the array are also deep copied
|
|
64
|
+
expect(copiedArr[0]).toEqual(arr[0]);
|
|
65
|
+
expect(copiedArr[0]).not.toBe(arr[0]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("MemorySaver", () => {
|
|
69
|
+
it("should save and retrieve checkpoints correctly", async () => {
|
|
70
|
+
const memorySaver = new MemorySaver();
|
|
71
|
+
// save checkpoint
|
|
72
|
+
const runnableConfig = await memorySaver.put({ configurable: { thread_id: "1" } }, checkpoint1, { source: "update", step: -1 });
|
|
73
|
+
expect(runnableConfig).toEqual({
|
|
74
|
+
configurable: {
|
|
75
|
+
thread_id: "1",
|
|
76
|
+
checkpoint_id: checkpoint1.id,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
// get checkpoint tuple
|
|
80
|
+
const checkpointTuple = await memorySaver.getTuple({
|
|
81
|
+
configurable: { thread_id: "1" },
|
|
82
|
+
});
|
|
83
|
+
expect(checkpointTuple?.config).toEqual({
|
|
84
|
+
configurable: {
|
|
85
|
+
thread_id: "1",
|
|
86
|
+
checkpoint_id: checkpoint1.id,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
expect(checkpointTuple?.checkpoint).toEqual(checkpoint1);
|
|
90
|
+
// save another checkpoint
|
|
91
|
+
await memorySaver.put({ configurable: { thread_id: "1" } }, checkpoint2, {
|
|
92
|
+
source: "update",
|
|
93
|
+
step: -1,
|
|
94
|
+
});
|
|
95
|
+
// list checkpoints
|
|
96
|
+
const checkpointTupleGenerator = await memorySaver.list({
|
|
97
|
+
configurable: { thread_id: "1" },
|
|
98
|
+
});
|
|
99
|
+
const checkpointTuples = [];
|
|
100
|
+
for await (const checkpoint of checkpointTupleGenerator) {
|
|
101
|
+
checkpointTuples.push(checkpoint);
|
|
102
|
+
}
|
|
103
|
+
expect(checkpointTuples.length).toBe(2);
|
|
104
|
+
const checkpointTuple1 = checkpointTuples[0];
|
|
105
|
+
const checkpointTuple2 = checkpointTuples[1];
|
|
106
|
+
expect(checkpointTuple1.checkpoint.ts).toBe("2024-04-20T17:19:07.952Z");
|
|
107
|
+
expect(checkpointTuple2.checkpoint.ts).toBe("2024-04-19T17:19:07.952Z");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe("SqliteSaver", () => {
|
|
111
|
+
it("should save and retrieve checkpoints correctly", async () => {
|
|
112
|
+
const sqliteSaver = SqliteSaver.fromConnString(":memory:");
|
|
113
|
+
// get undefined checkpoint
|
|
114
|
+
const undefinedCheckpoint = await sqliteSaver.getTuple({
|
|
115
|
+
configurable: { thread_id: "1" },
|
|
116
|
+
});
|
|
117
|
+
expect(undefinedCheckpoint).toBeUndefined();
|
|
118
|
+
// save first checkpoint
|
|
119
|
+
const runnableConfig = await sqliteSaver.put({ configurable: { thread_id: "1" } }, checkpoint1, { source: "update", step: -1 });
|
|
120
|
+
expect(runnableConfig).toEqual({
|
|
121
|
+
configurable: {
|
|
122
|
+
thread_id: "1",
|
|
123
|
+
checkpoint_id: checkpoint1.id,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
// get first checkpoint tuple
|
|
127
|
+
const firstCheckpointTuple = await sqliteSaver.getTuple({
|
|
128
|
+
configurable: { thread_id: "1" },
|
|
129
|
+
});
|
|
130
|
+
expect(firstCheckpointTuple?.config).toEqual({
|
|
131
|
+
configurable: {
|
|
132
|
+
thread_id: "1",
|
|
133
|
+
checkpoint_id: checkpoint1.id,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
expect(firstCheckpointTuple?.checkpoint).toEqual(checkpoint1);
|
|
137
|
+
expect(firstCheckpointTuple?.parentConfig).toBeUndefined();
|
|
138
|
+
// save second checkpoint
|
|
139
|
+
await sqliteSaver.put({
|
|
140
|
+
configurable: {
|
|
141
|
+
thread_id: "1",
|
|
142
|
+
checkpoint_id: "2024-04-18T17:19:07.952Z",
|
|
143
|
+
},
|
|
144
|
+
}, checkpoint2, { source: "update", step: -1 });
|
|
145
|
+
// verify that parentTs is set and retrieved correctly for second checkpoint
|
|
146
|
+
const secondCheckpointTuple = await sqliteSaver.getTuple({
|
|
147
|
+
configurable: { thread_id: "1" },
|
|
148
|
+
});
|
|
149
|
+
expect(secondCheckpointTuple?.parentConfig).toEqual({
|
|
150
|
+
configurable: {
|
|
151
|
+
thread_id: "1",
|
|
152
|
+
checkpoint_id: "2024-04-18T17:19:07.952Z",
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
// list checkpoints
|
|
156
|
+
const checkpointTupleGenerator = await sqliteSaver.list({
|
|
157
|
+
configurable: { thread_id: "1" },
|
|
158
|
+
});
|
|
159
|
+
const checkpointTuples = [];
|
|
160
|
+
for await (const checkpoint of checkpointTupleGenerator) {
|
|
161
|
+
checkpointTuples.push(checkpoint);
|
|
162
|
+
}
|
|
163
|
+
expect(checkpointTuples.length).toBe(2);
|
|
164
|
+
const checkpointTuple1 = checkpointTuples[0];
|
|
165
|
+
const checkpointTuple2 = checkpointTuples[1];
|
|
166
|
+
expect(checkpointTuple1.checkpoint.ts).toBe("2024-04-20T17:19:07.952Z");
|
|
167
|
+
expect(checkpointTuple2.checkpoint.ts).toBe("2024-04-19T17:19:07.952Z");
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
describe("id", () => {
|
|
171
|
+
it("should convert uuid1 to uuid6", () => {
|
|
172
|
+
const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-6[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
173
|
+
// [UUIDv1, UUIDv6]
|
|
174
|
+
const cases = [
|
|
175
|
+
[
|
|
176
|
+
"5714f720-1268-11e7-a24b-96d95aa38c32",
|
|
177
|
+
"1e712685-714f-6720-a24b-96d95aa38c32",
|
|
178
|
+
],
|
|
179
|
+
[
|
|
180
|
+
"68f820c0-1268-11e7-a24b-671acd892c6a",
|
|
181
|
+
"1e712686-8f82-60c0-a24b-671acd892c6a",
|
|
182
|
+
],
|
|
183
|
+
];
|
|
184
|
+
cases.forEach(([v1, v6]) => {
|
|
185
|
+
const converted = convert1to6(v1);
|
|
186
|
+
expect(converted).toBe(v6);
|
|
187
|
+
expect(converted).toMatch(regex);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, it, expect } from "@jest/globals";
|
|
2
|
+
import { StateGraph } from "../graph/state.js";
|
|
3
|
+
describe("State", () => {
|
|
4
|
+
it("should validate a new node key correctly ", () => {
|
|
5
|
+
const stateGraph = new StateGraph({
|
|
6
|
+
channels: { existingStateAttributeKey: null },
|
|
7
|
+
});
|
|
8
|
+
expect(() => {
|
|
9
|
+
stateGraph.addNode("existingStateAttributeKey", (_) => ({}));
|
|
10
|
+
}).toThrow("existingStateAttributeKey");
|
|
11
|
+
expect(() => {
|
|
12
|
+
stateGraph.addNode("newNodeKey", (_) => ({}));
|
|
13
|
+
}).not.toThrow();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* eslint-disable no-process-env */
|
|
2
|
+
import { it, beforeAll, describe, expect } from "@jest/globals";
|
|
3
|
+
import { Tool } from "@langchain/core/tools";
|
|
4
|
+
import { ChatOpenAI } from "@langchain/openai";
|
|
5
|
+
import { HumanMessage } from "@langchain/core/messages";
|
|
6
|
+
import { END } from "../index.js";
|
|
7
|
+
import { createFunctionCallingExecutor } from "../prebuilt/index.js";
|
|
8
|
+
// Tracing slows down the tests
|
|
9
|
+
beforeAll(() => {
|
|
10
|
+
process.env.LANGCHAIN_TRACING_V2 = "false";
|
|
11
|
+
process.env.LANGCHAIN_ENDPOINT = "";
|
|
12
|
+
process.env.LANGCHAIN_API_KEY = "";
|
|
13
|
+
process.env.LANGCHAIN_PROJECT = "";
|
|
14
|
+
});
|
|
15
|
+
describe("createFunctionCallingExecutor", () => {
|
|
16
|
+
it("can call a function", async () => {
|
|
17
|
+
const weatherResponse = `Not too cold, not too hot 😎`;
|
|
18
|
+
const model = new ChatOpenAI();
|
|
19
|
+
class SanFranciscoWeatherTool extends Tool {
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
Object.defineProperty(this, "name", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: "current_weather"
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "description", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: "Get the current weather report for San Francisco, CA"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async _call(_) {
|
|
36
|
+
return weatherResponse;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const tools = [new SanFranciscoWeatherTool()];
|
|
40
|
+
const functionsAgentExecutor = createFunctionCallingExecutor({
|
|
41
|
+
model,
|
|
42
|
+
tools,
|
|
43
|
+
});
|
|
44
|
+
const response = await functionsAgentExecutor.invoke({
|
|
45
|
+
messages: [new HumanMessage("What's the weather like in SF?")],
|
|
46
|
+
});
|
|
47
|
+
console.log(response);
|
|
48
|
+
// It needs at least one human message, one AI and one function message.
|
|
49
|
+
expect(response.messages.length > 3).toBe(true);
|
|
50
|
+
const firstFunctionMessage = response.messages.find((message) => message._getType() === "function");
|
|
51
|
+
expect(firstFunctionMessage).toBeDefined();
|
|
52
|
+
expect(firstFunctionMessage?.content).toBe(weatherResponse);
|
|
53
|
+
});
|
|
54
|
+
it("can stream a function", async () => {
|
|
55
|
+
const weatherResponse = `Not too cold, not too hot 😎`;
|
|
56
|
+
const model = new ChatOpenAI({
|
|
57
|
+
streaming: true,
|
|
58
|
+
});
|
|
59
|
+
class SanFranciscoWeatherTool extends Tool {
|
|
60
|
+
constructor() {
|
|
61
|
+
super();
|
|
62
|
+
Object.defineProperty(this, "name", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: "current_weather"
|
|
67
|
+
});
|
|
68
|
+
Object.defineProperty(this, "description", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: "Get the current weather report for San Francisco, CA"
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async _call(_) {
|
|
76
|
+
return weatherResponse;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const tools = [new SanFranciscoWeatherTool()];
|
|
80
|
+
const functionsAgentExecutor = createFunctionCallingExecutor({
|
|
81
|
+
model,
|
|
82
|
+
tools,
|
|
83
|
+
});
|
|
84
|
+
const stream = await functionsAgentExecutor.stream({
|
|
85
|
+
messages: [new HumanMessage("What's the weather like in SF?")],
|
|
86
|
+
});
|
|
87
|
+
const fullResponse = [];
|
|
88
|
+
for await (const item of stream) {
|
|
89
|
+
console.log(item);
|
|
90
|
+
console.log("-----\n");
|
|
91
|
+
fullResponse.push(item);
|
|
92
|
+
}
|
|
93
|
+
// Needs at least 3 llm calls, plus one `__end__` call.
|
|
94
|
+
expect(fullResponse.length >= 4).toBe(true);
|
|
95
|
+
const endMessage = fullResponse[fullResponse.length - 1];
|
|
96
|
+
expect(END in endMessage).toBe(true);
|
|
97
|
+
expect(endMessage[END].messages.length > 0).toBe(true);
|
|
98
|
+
const functionCall = endMessage[END].messages.find((message) => message._getType() === "function");
|
|
99
|
+
expect(functionCall.content).toBe(weatherResponse);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/* eslint-disable no-process-env */
|
|
2
|
+
import { it, expect, beforeAll, describe } from "@jest/globals";
|
|
3
|
+
import { PromptTemplate } from "@langchain/core/prompts";
|
|
4
|
+
import { FakeStreamingLLM } from "@langchain/core/utils/testing";
|
|
5
|
+
import { Tool } from "@langchain/core/tools";
|
|
6
|
+
import { createAgentExecutor } from "../prebuilt/index.js";
|
|
7
|
+
// Tracing slows down the tests
|
|
8
|
+
beforeAll(() => {
|
|
9
|
+
process.env.LANGCHAIN_TRACING_V2 = "false";
|
|
10
|
+
process.env.LANGCHAIN_ENDPOINT = "";
|
|
11
|
+
process.env.LANGCHAIN_ENDPOINT = "";
|
|
12
|
+
process.env.LANGCHAIN_API_KEY = "";
|
|
13
|
+
process.env.LANGCHAIN_PROJECT = "";
|
|
14
|
+
});
|
|
15
|
+
describe("PreBuilt", () => {
|
|
16
|
+
class SearchAPI extends Tool {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
Object.defineProperty(this, "name", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: "search_api"
|
|
24
|
+
});
|
|
25
|
+
Object.defineProperty(this, "description", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: "A simple API that returns the input string."
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async _call(query) {
|
|
33
|
+
return `result for ${query}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const tools = [new SearchAPI()];
|
|
37
|
+
it("Can invoke createAgentExecutor", async () => {
|
|
38
|
+
const prompt = PromptTemplate.fromTemplate("Hello!");
|
|
39
|
+
const llm = new FakeStreamingLLM({
|
|
40
|
+
responses: [
|
|
41
|
+
"tool:search_api:query",
|
|
42
|
+
"tool:search_api:another",
|
|
43
|
+
"finish:answer",
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
const agentParser = (input) => {
|
|
47
|
+
if (input.startsWith("finish")) {
|
|
48
|
+
const answer = input.split(":")[1];
|
|
49
|
+
return {
|
|
50
|
+
returnValues: { answer },
|
|
51
|
+
log: input,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const [, toolName, toolInput] = input.split(":");
|
|
55
|
+
return {
|
|
56
|
+
tool: toolName,
|
|
57
|
+
toolInput,
|
|
58
|
+
log: input,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
const agent = prompt.pipe(llm).pipe(agentParser);
|
|
62
|
+
const agentExecutor = createAgentExecutor({
|
|
63
|
+
agentRunnable: agent,
|
|
64
|
+
tools,
|
|
65
|
+
});
|
|
66
|
+
const result = await agentExecutor.invoke({
|
|
67
|
+
input: "what is the weather in sf?",
|
|
68
|
+
});
|
|
69
|
+
expect(result).toEqual({
|
|
70
|
+
input: "what is the weather in sf?",
|
|
71
|
+
agentOutcome: {
|
|
72
|
+
returnValues: {
|
|
73
|
+
answer: "answer",
|
|
74
|
+
},
|
|
75
|
+
log: "finish:answer",
|
|
76
|
+
},
|
|
77
|
+
steps: [
|
|
78
|
+
{
|
|
79
|
+
action: {
|
|
80
|
+
tool: "search_api",
|
|
81
|
+
toolInput: "query",
|
|
82
|
+
log: "tool:search_api:query",
|
|
83
|
+
},
|
|
84
|
+
observation: "result for query",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
action: {
|
|
88
|
+
tool: "search_api",
|
|
89
|
+
toolInput: "another",
|
|
90
|
+
log: "tool:search_api:another",
|
|
91
|
+
},
|
|
92
|
+
observation: "result for another",
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
it("Can stream createAgentExecutor", async () => {
|
|
98
|
+
const prompt = PromptTemplate.fromTemplate("Hello!");
|
|
99
|
+
const llm = new FakeStreamingLLM({
|
|
100
|
+
responses: [
|
|
101
|
+
"tool:search_api:query",
|
|
102
|
+
"tool:search_api:another",
|
|
103
|
+
"finish:answer",
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
const agentParser = (input) => {
|
|
107
|
+
if (input.startsWith("finish")) {
|
|
108
|
+
const answer = input.split(":")[1];
|
|
109
|
+
return {
|
|
110
|
+
returnValues: { answer },
|
|
111
|
+
log: input,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const [, toolName, toolInput] = input.split(":");
|
|
115
|
+
return {
|
|
116
|
+
tool: toolName,
|
|
117
|
+
toolInput,
|
|
118
|
+
log: input,
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
const agent = prompt.pipe(llm).pipe(agentParser);
|
|
122
|
+
const agentExecutor = createAgentExecutor({
|
|
123
|
+
agentRunnable: agent,
|
|
124
|
+
tools,
|
|
125
|
+
});
|
|
126
|
+
const stream = agentExecutor.stream({
|
|
127
|
+
input: "what is the weather in sf?",
|
|
128
|
+
});
|
|
129
|
+
const fullResponse = [];
|
|
130
|
+
for await (const item of await stream) {
|
|
131
|
+
fullResponse.push(item);
|
|
132
|
+
}
|
|
133
|
+
expect(fullResponse.length > 3).toBe(true);
|
|
134
|
+
const allAgentMessages = fullResponse.filter((res) => "agent" in res);
|
|
135
|
+
expect(allAgentMessages.length >= 3).toBe(true);
|
|
136
|
+
expect(fullResponse).toEqual([
|
|
137
|
+
{
|
|
138
|
+
agent: {
|
|
139
|
+
agentOutcome: {
|
|
140
|
+
log: "tool:search_api:query",
|
|
141
|
+
tool: "search_api",
|
|
142
|
+
toolInput: "query",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
action: {
|
|
148
|
+
steps: [
|
|
149
|
+
{
|
|
150
|
+
action: {
|
|
151
|
+
log: "tool:search_api:query",
|
|
152
|
+
tool: "search_api",
|
|
153
|
+
toolInput: "query",
|
|
154
|
+
},
|
|
155
|
+
observation: "result for query",
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
agent: {
|
|
162
|
+
agentOutcome: {
|
|
163
|
+
log: "tool:search_api:another",
|
|
164
|
+
tool: "search_api",
|
|
165
|
+
toolInput: "another",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
action: {
|
|
171
|
+
steps: [
|
|
172
|
+
{
|
|
173
|
+
action: {
|
|
174
|
+
log: "tool:search_api:another",
|
|
175
|
+
tool: "search_api",
|
|
176
|
+
toolInput: "another",
|
|
177
|
+
},
|
|
178
|
+
observation: "result for another",
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
agent: {
|
|
185
|
+
agentOutcome: {
|
|
186
|
+
log: "finish:answer",
|
|
187
|
+
returnValues: {
|
|
188
|
+
answer: "answer",
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
]);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|