@langchain/langgraph 0.0.27 → 0.0.29
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/graph/index.cjs +2 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/index.js +1 -1
- package/dist/graph/message.cjs +42 -4
- package/dist/graph/message.d.ts +3 -2
- package/dist/graph/message.js +40 -3
- package/dist/prebuilt/chat_agent_executor.d.ts +3 -2
- package/dist/prebuilt/chat_agent_executor.js +1 -1
- package/dist/prebuilt/react_agent_executor.d.ts +3 -3
- package/dist/prebuilt/tool_executor.d.ts +5 -5
- package/dist/prebuilt/tool_node.cjs +32 -11
- package/dist/prebuilt/tool_node.d.ts +10 -3
- package/dist/prebuilt/tool_node.js +32 -11
- package/dist/tests/prebuilt.int.test.js +28 -0
- package/dist/tests/prebuilt.test.js +119 -62
- package/dist/tests/pregel.test.js +3 -3
- package/dist/web.cjs +2 -1
- package/dist/web.d.ts +1 -1
- package/dist/web.js +1 -1
- package/package.json +5 -5
package/dist/graph/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MessageGraph = exports.StateGraph = exports.Graph = exports.START = exports.END = void 0;
|
|
3
|
+
exports.messagesStateReducer = exports.MessageGraph = exports.StateGraph = exports.Graph = exports.START = exports.END = void 0;
|
|
4
4
|
var graph_js_1 = require("./graph.cjs");
|
|
5
5
|
Object.defineProperty(exports, "END", { enumerable: true, get: function () { return graph_js_1.END; } });
|
|
6
6
|
Object.defineProperty(exports, "START", { enumerable: true, get: function () { return graph_js_1.START; } });
|
|
@@ -9,3 +9,4 @@ var state_js_1 = require("./state.cjs");
|
|
|
9
9
|
Object.defineProperty(exports, "StateGraph", { enumerable: true, get: function () { return state_js_1.StateGraph; } });
|
|
10
10
|
var message_js_1 = require("./message.cjs");
|
|
11
11
|
Object.defineProperty(exports, "MessageGraph", { enumerable: true, get: function () { return message_js_1.MessageGraph; } });
|
|
12
|
+
Object.defineProperty(exports, "messagesStateReducer", { enumerable: true, get: function () { return message_js_1.messagesStateReducer; } });
|
package/dist/graph/index.d.ts
CHANGED
package/dist/graph/index.js
CHANGED
package/dist/graph/message.cjs
CHANGED
|
@@ -1,18 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MessageGraph = void 0;
|
|
3
|
+
exports.MessageGraph = exports.messagesStateReducer = void 0;
|
|
4
|
+
const messages_1 = require("@langchain/core/messages");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
4
6
|
const state_js_1 = require("./state.cjs");
|
|
5
|
-
function
|
|
7
|
+
function messagesStateReducer(left, right) {
|
|
6
8
|
const leftArray = Array.isArray(left) ? left : [left];
|
|
7
9
|
const rightArray = Array.isArray(right) ? right : [right];
|
|
8
|
-
|
|
10
|
+
// coerce to message
|
|
11
|
+
const leftMessages = leftArray.map(messages_1.coerceMessageLikeToMessage);
|
|
12
|
+
const rightMessages = rightArray.map(messages_1.coerceMessageLikeToMessage);
|
|
13
|
+
// assign missing ids
|
|
14
|
+
for (const m of leftMessages) {
|
|
15
|
+
if (m.id === null || m.id === undefined) {
|
|
16
|
+
m.id = (0, uuid_1.v4)();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
for (const m of rightMessages) {
|
|
20
|
+
if (m.id === null || m.id === undefined) {
|
|
21
|
+
m.id = (0, uuid_1.v4)();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// merge
|
|
25
|
+
const leftIdxById = new Map(leftMessages.map((m, i) => [m.id, i]));
|
|
26
|
+
const merged = [...leftMessages];
|
|
27
|
+
const idsToRemove = new Set();
|
|
28
|
+
for (const m of rightMessages) {
|
|
29
|
+
const existingIdx = leftIdxById.get(m.id);
|
|
30
|
+
if (existingIdx !== undefined) {
|
|
31
|
+
if (m._getType() === "remove") {
|
|
32
|
+
idsToRemove.add(m.id);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
merged[existingIdx] = m;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
if (m._getType() === "remove") {
|
|
40
|
+
throw new Error(`Attempting to delete a message with an ID that doesn't exist ('${m.id}')`);
|
|
41
|
+
}
|
|
42
|
+
merged.push(m);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return merged.filter((m) => !idsToRemove.has(m.id));
|
|
9
46
|
}
|
|
47
|
+
exports.messagesStateReducer = messagesStateReducer;
|
|
10
48
|
class MessageGraph extends state_js_1.StateGraph {
|
|
11
49
|
constructor() {
|
|
12
50
|
super({
|
|
13
51
|
channels: {
|
|
14
52
|
__root__: {
|
|
15
|
-
reducer:
|
|
53
|
+
reducer: messagesStateReducer,
|
|
16
54
|
default: () => [],
|
|
17
55
|
},
|
|
18
56
|
},
|
package/dist/graph/message.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { BaseMessage } from "@langchain/core/messages";
|
|
1
|
+
import { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
|
|
2
2
|
import { StateGraph } from "./state.js";
|
|
3
|
-
type Messages = Array<BaseMessage> | BaseMessage;
|
|
3
|
+
type Messages = Array<BaseMessage | BaseMessageLike> | BaseMessage | BaseMessageLike;
|
|
4
|
+
export declare function messagesStateReducer(left: Messages, right: Messages): BaseMessage[];
|
|
4
5
|
export declare class MessageGraph extends StateGraph<BaseMessage[], Messages> {
|
|
5
6
|
constructor();
|
|
6
7
|
}
|
package/dist/graph/message.js
CHANGED
|
@@ -1,15 +1,52 @@
|
|
|
1
|
+
import { coerceMessageLikeToMessage, } from "@langchain/core/messages";
|
|
2
|
+
import { v4 } from "uuid";
|
|
1
3
|
import { StateGraph } from "./state.js";
|
|
2
|
-
function
|
|
4
|
+
export function messagesStateReducer(left, right) {
|
|
3
5
|
const leftArray = Array.isArray(left) ? left : [left];
|
|
4
6
|
const rightArray = Array.isArray(right) ? right : [right];
|
|
5
|
-
|
|
7
|
+
// coerce to message
|
|
8
|
+
const leftMessages = leftArray.map(coerceMessageLikeToMessage);
|
|
9
|
+
const rightMessages = rightArray.map(coerceMessageLikeToMessage);
|
|
10
|
+
// assign missing ids
|
|
11
|
+
for (const m of leftMessages) {
|
|
12
|
+
if (m.id === null || m.id === undefined) {
|
|
13
|
+
m.id = v4();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
for (const m of rightMessages) {
|
|
17
|
+
if (m.id === null || m.id === undefined) {
|
|
18
|
+
m.id = v4();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// merge
|
|
22
|
+
const leftIdxById = new Map(leftMessages.map((m, i) => [m.id, i]));
|
|
23
|
+
const merged = [...leftMessages];
|
|
24
|
+
const idsToRemove = new Set();
|
|
25
|
+
for (const m of rightMessages) {
|
|
26
|
+
const existingIdx = leftIdxById.get(m.id);
|
|
27
|
+
if (existingIdx !== undefined) {
|
|
28
|
+
if (m._getType() === "remove") {
|
|
29
|
+
idsToRemove.add(m.id);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
merged[existingIdx] = m;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
if (m._getType() === "remove") {
|
|
37
|
+
throw new Error(`Attempting to delete a message with an ID that doesn't exist ('${m.id}')`);
|
|
38
|
+
}
|
|
39
|
+
merged.push(m);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return merged.filter((m) => !idsToRemove.has(m.id));
|
|
6
43
|
}
|
|
7
44
|
export class MessageGraph extends StateGraph {
|
|
8
45
|
constructor() {
|
|
9
46
|
super({
|
|
10
47
|
channels: {
|
|
11
48
|
__root__: {
|
|
12
|
-
reducer:
|
|
49
|
+
reducer: messagesStateReducer,
|
|
13
50
|
default: () => [],
|
|
14
51
|
},
|
|
15
52
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StructuredToolInterface } from "@langchain/core/tools";
|
|
2
2
|
import { BaseMessage } from "@langchain/core/messages";
|
|
3
|
+
import { RunnableToolLike } from "@langchain/core/runnables";
|
|
3
4
|
import { ToolExecutor } from "./tool_executor.js";
|
|
4
5
|
import { CompiledStateGraph } from "../graph/state.js";
|
|
5
6
|
import { START } from "../graph/index.js";
|
|
@@ -8,5 +9,5 @@ export type FunctionCallingExecutorState = {
|
|
|
8
9
|
};
|
|
9
10
|
export declare function createFunctionCallingExecutor<Model extends object>({ model, tools, }: {
|
|
10
11
|
model: Model;
|
|
11
|
-
tools: Array<
|
|
12
|
+
tools: Array<StructuredToolInterface | RunnableToolLike> | ToolExecutor;
|
|
12
13
|
}): CompiledStateGraph<FunctionCallingExecutorState, Partial<FunctionCallingExecutorState>, typeof START | "agent" | "action">;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { convertToOpenAIFunction } from "@langchain/core/utils/function_calling";
|
|
2
2
|
import { FunctionMessage } from "@langchain/core/messages";
|
|
3
|
-
import { RunnableLambda } from "@langchain/core/runnables";
|
|
3
|
+
import { RunnableLambda, } from "@langchain/core/runnables";
|
|
4
4
|
import { ToolExecutor } from "./tool_executor.js";
|
|
5
5
|
import { StateGraph, } from "../graph/state.js";
|
|
6
6
|
import { END, START } from "../graph/index.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
2
2
|
import { BaseMessage, SystemMessage } from "@langchain/core/messages";
|
|
3
|
-
import { Runnable } from "@langchain/core/runnables";
|
|
4
|
-
import {
|
|
3
|
+
import { Runnable, RunnableToolLike } from "@langchain/core/runnables";
|
|
4
|
+
import { StructuredToolInterface } from "@langchain/core/tools";
|
|
5
5
|
import { BaseCheckpointSaver } from "../checkpoint/base.js";
|
|
6
6
|
import { START } from "../graph/index.js";
|
|
7
7
|
import { MessagesState } from "../graph/message.js";
|
|
@@ -14,7 +14,7 @@ export interface AgentState {
|
|
|
14
14
|
export type N = typeof START | "agent" | "tools";
|
|
15
15
|
export type CreateReactAgentParams = {
|
|
16
16
|
llm: BaseChatModel;
|
|
17
|
-
tools: ToolNode<MessagesState> |
|
|
17
|
+
tools: ToolNode<MessagesState> | (StructuredToolInterface | RunnableToolLike)[];
|
|
18
18
|
messageModifier?: SystemMessage | string | ((messages: BaseMessage[]) => BaseMessage[]) | ((messages: BaseMessage[]) => Promise<BaseMessage[]>) | Runnable;
|
|
19
19
|
checkpointSaver?: BaseCheckpointSaver;
|
|
20
20
|
interruptBefore?: N[] | All;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RunnableBinding, RunnableConfig } from "@langchain/core/runnables";
|
|
2
|
-
import {
|
|
1
|
+
import { RunnableBinding, RunnableConfig, RunnableToolLike } from "@langchain/core/runnables";
|
|
2
|
+
import { StructuredToolInterface } from "@langchain/core/tools";
|
|
3
3
|
export interface ToolExecutorArgs {
|
|
4
|
-
tools: Array<
|
|
4
|
+
tools: Array<StructuredToolInterface | RunnableToolLike>;
|
|
5
5
|
/**
|
|
6
6
|
* @default {INVALID_TOOL_MSG_TEMPLATE}
|
|
7
7
|
*/
|
|
@@ -18,8 +18,8 @@ type ToolExecutorInputType = any;
|
|
|
18
18
|
type ToolExecutorOutputType = any;
|
|
19
19
|
export declare class ToolExecutor extends RunnableBinding<ToolExecutorInputType, ToolExecutorOutputType> {
|
|
20
20
|
lc_graph_name: string;
|
|
21
|
-
tools: Array<
|
|
22
|
-
toolMap: Record<string,
|
|
21
|
+
tools: Array<StructuredToolInterface | RunnableToolLike>;
|
|
22
|
+
toolMap: Record<string, StructuredToolInterface | RunnableToolLike>;
|
|
23
23
|
invalidToolMsgTemplate: string;
|
|
24
24
|
constructor(fields: ToolExecutorArgs);
|
|
25
25
|
/**
|
|
@@ -5,7 +5,8 @@ const messages_1 = require("@langchain/core/messages");
|
|
|
5
5
|
const utils_js_1 = require("../utils.cjs");
|
|
6
6
|
const graph_js_1 = require("../graph/graph.cjs");
|
|
7
7
|
class ToolNode extends utils_js_1.RunnableCallable {
|
|
8
|
-
constructor(tools,
|
|
8
|
+
constructor(tools, options) {
|
|
9
|
+
const { name, tags, handleToolErrors } = options ?? {};
|
|
9
10
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
10
11
|
/**
|
|
11
12
|
A node that runs the tools requested in the last AIMessage. It can be used
|
|
@@ -19,7 +20,14 @@ class ToolNode extends utils_js_1.RunnableCallable {
|
|
|
19
20
|
writable: true,
|
|
20
21
|
value: void 0
|
|
21
22
|
});
|
|
23
|
+
Object.defineProperty(this, "handleToolErrors", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: true
|
|
28
|
+
});
|
|
22
29
|
this.tools = tools;
|
|
30
|
+
this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;
|
|
23
31
|
}
|
|
24
32
|
async run(input, config) {
|
|
25
33
|
const message = Array.isArray(input)
|
|
@@ -30,18 +38,31 @@ class ToolNode extends utils_js_1.RunnableCallable {
|
|
|
30
38
|
}
|
|
31
39
|
const outputs = await Promise.all(message.tool_calls?.map(async (call) => {
|
|
32
40
|
const tool = this.tools.find((tool) => tool.name === call.name);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
try {
|
|
42
|
+
if (tool === undefined) {
|
|
43
|
+
throw new Error(`Tool "${call.name}" not found.`);
|
|
44
|
+
}
|
|
45
|
+
const output = await tool.invoke({ ...call, type: "tool_call" }, config);
|
|
46
|
+
if ((0, messages_1.isBaseMessage)(output) && output._getType() === "tool") {
|
|
47
|
+
return output;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return new messages_1.ToolMessage({
|
|
51
|
+
name: tool.name,
|
|
52
|
+
content: typeof output === "string" ? output : JSON.stringify(output),
|
|
53
|
+
tool_call_id: call.id,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
57
|
}
|
|
40
|
-
|
|
58
|
+
catch (e) {
|
|
59
|
+
if (!this.handleToolErrors) {
|
|
60
|
+
throw e;
|
|
61
|
+
}
|
|
41
62
|
return new messages_1.ToolMessage({
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
tool_call_id: call.id,
|
|
63
|
+
content: `Error: ${e.message}\n Please fix your mistakes.`,
|
|
64
|
+
name: call.name,
|
|
65
|
+
tool_call_id: call.id ?? "",
|
|
45
66
|
});
|
|
46
67
|
}
|
|
47
68
|
}) ?? []);
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { BaseMessage } from "@langchain/core/messages";
|
|
2
|
-
import {
|
|
2
|
+
import { RunnableToolLike } from "@langchain/core/runnables";
|
|
3
|
+
import { StructuredToolInterface } from "@langchain/core/tools";
|
|
3
4
|
import { RunnableCallable } from "../utils.js";
|
|
4
5
|
import { END } from "../graph/graph.js";
|
|
5
6
|
import { MessagesState } from "../graph/message.js";
|
|
7
|
+
export type ToolNodeOptions = {
|
|
8
|
+
name?: string;
|
|
9
|
+
tags?: string[];
|
|
10
|
+
handleToolErrors?: boolean;
|
|
11
|
+
};
|
|
6
12
|
export declare class ToolNode<T extends BaseMessage[] | MessagesState> extends RunnableCallable<T, T> {
|
|
7
13
|
/**
|
|
8
14
|
A node that runs the tools requested in the last AIMessage. It can be used
|
|
@@ -10,8 +16,9 @@ export declare class ToolNode<T extends BaseMessage[] | MessagesState> extends R
|
|
|
10
16
|
tool calls are requested, they will be run in parallel. The output will be
|
|
11
17
|
a list of ToolMessages, one for each tool call.
|
|
12
18
|
*/
|
|
13
|
-
tools:
|
|
14
|
-
|
|
19
|
+
tools: (StructuredToolInterface | RunnableToolLike)[];
|
|
20
|
+
handleToolErrors: boolean;
|
|
21
|
+
constructor(tools: (StructuredToolInterface | RunnableToolLike)[], options?: ToolNodeOptions);
|
|
15
22
|
private run;
|
|
16
23
|
}
|
|
17
24
|
export declare function toolsCondition(state: BaseMessage[] | MessagesState): "tools" | typeof END;
|
|
@@ -2,7 +2,8 @@ import { ToolMessage, isBaseMessage, } from "@langchain/core/messages";
|
|
|
2
2
|
import { RunnableCallable } from "../utils.js";
|
|
3
3
|
import { END } from "../graph/graph.js";
|
|
4
4
|
export class ToolNode extends RunnableCallable {
|
|
5
|
-
constructor(tools,
|
|
5
|
+
constructor(tools, options) {
|
|
6
|
+
const { name, tags, handleToolErrors } = options ?? {};
|
|
6
7
|
super({ name, tags, func: (input, config) => this.run(input, config) });
|
|
7
8
|
/**
|
|
8
9
|
A node that runs the tools requested in the last AIMessage. It can be used
|
|
@@ -16,7 +17,14 @@ export class ToolNode extends RunnableCallable {
|
|
|
16
17
|
writable: true,
|
|
17
18
|
value: void 0
|
|
18
19
|
});
|
|
20
|
+
Object.defineProperty(this, "handleToolErrors", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
19
26
|
this.tools = tools;
|
|
27
|
+
this.handleToolErrors = handleToolErrors ?? this.handleToolErrors;
|
|
20
28
|
}
|
|
21
29
|
async run(input, config) {
|
|
22
30
|
const message = Array.isArray(input)
|
|
@@ -27,18 +35,31 @@ export class ToolNode extends RunnableCallable {
|
|
|
27
35
|
}
|
|
28
36
|
const outputs = await Promise.all(message.tool_calls?.map(async (call) => {
|
|
29
37
|
const tool = this.tools.find((tool) => tool.name === call.name);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
try {
|
|
39
|
+
if (tool === undefined) {
|
|
40
|
+
throw new Error(`Tool "${call.name}" not found.`);
|
|
41
|
+
}
|
|
42
|
+
const output = await tool.invoke({ ...call, type: "tool_call" }, config);
|
|
43
|
+
if (isBaseMessage(output) && output._getType() === "tool") {
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return new ToolMessage({
|
|
48
|
+
name: tool.name,
|
|
49
|
+
content: typeof output === "string" ? output : JSON.stringify(output),
|
|
50
|
+
tool_call_id: call.id,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
54
|
}
|
|
37
|
-
|
|
55
|
+
catch (e) {
|
|
56
|
+
if (!this.handleToolErrors) {
|
|
57
|
+
throw e;
|
|
58
|
+
}
|
|
38
59
|
return new ToolMessage({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
tool_call_id: call.id,
|
|
60
|
+
content: `Error: ${e.message}\n Please fix your mistakes.`,
|
|
61
|
+
name: call.name,
|
|
62
|
+
tool_call_id: call.id ?? "",
|
|
42
63
|
});
|
|
43
64
|
}
|
|
44
65
|
}) ?? []);
|
|
@@ -3,6 +3,8 @@ import { it, beforeAll, describe, expect } from "@jest/globals";
|
|
|
3
3
|
import { Tool } from "@langchain/core/tools";
|
|
4
4
|
import { ChatOpenAI } from "@langchain/openai";
|
|
5
5
|
import { HumanMessage } from "@langchain/core/messages";
|
|
6
|
+
import { RunnableLambda } from "@langchain/core/runnables";
|
|
7
|
+
import { z } from "zod";
|
|
6
8
|
import { createReactAgent, createFunctionCallingExecutor, } from "../prebuilt/index.js";
|
|
7
9
|
import { initializeAsyncLocalStorageSingleton } from "../setup/async_local_storage.js";
|
|
8
10
|
// Tracing slows down the tests
|
|
@@ -97,6 +99,32 @@ describe("createFunctionCallingExecutor", () => {
|
|
|
97
99
|
const functionCall = endState.messages.find((message) => message._getType() === "function");
|
|
98
100
|
expect(functionCall.content).toBe(weatherResponse);
|
|
99
101
|
});
|
|
102
|
+
it("can accept RunnableToolLike tools", async () => {
|
|
103
|
+
const weatherResponse = `Not too cold, not too hot 😎`;
|
|
104
|
+
const model = new ChatOpenAI();
|
|
105
|
+
const sfWeatherTool = RunnableLambda.from(async (_) => weatherResponse);
|
|
106
|
+
const tools = [
|
|
107
|
+
sfWeatherTool.asTool({
|
|
108
|
+
name: "current_weather",
|
|
109
|
+
description: "Get the current weather report for San Francisco, CA",
|
|
110
|
+
schema: z.object({
|
|
111
|
+
location: z.string(),
|
|
112
|
+
}),
|
|
113
|
+
}),
|
|
114
|
+
];
|
|
115
|
+
const functionsAgentExecutor = createFunctionCallingExecutor({
|
|
116
|
+
model,
|
|
117
|
+
tools,
|
|
118
|
+
});
|
|
119
|
+
const response = await functionsAgentExecutor.invoke({
|
|
120
|
+
messages: [new HumanMessage("What's the weather like in SF?")],
|
|
121
|
+
});
|
|
122
|
+
// It needs at least one human message, one AI and one function message.
|
|
123
|
+
expect(response.messages.length > 3).toBe(true);
|
|
124
|
+
const firstFunctionMessage = response.messages.find((message) => message._getType() === "function");
|
|
125
|
+
expect(firstFunctionMessage).toBeDefined();
|
|
126
|
+
expect(firstFunctionMessage?.content).toBe(weatherResponse);
|
|
127
|
+
});
|
|
100
128
|
});
|
|
101
129
|
describe("createReactAgent", () => {
|
|
102
130
|
it("can call a tool", async () => {
|
|
@@ -5,8 +5,9 @@ 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
7
|
import { z } from "zod";
|
|
8
|
+
import { RunnableLambda } from "@langchain/core/runnables";
|
|
8
9
|
import { FakeToolCallingChatModel } from "./utils.js";
|
|
9
|
-
import { createAgentExecutor, createReactAgent } from "../prebuilt/index.js";
|
|
10
|
+
import { ToolNode, createAgentExecutor, createReactAgent, } from "../prebuilt/index.js";
|
|
10
11
|
// Tracing slows down the tests
|
|
11
12
|
beforeAll(() => {
|
|
12
13
|
process.env.LANGCHAIN_TRACING_V2 = "false";
|
|
@@ -15,6 +16,67 @@ beforeAll(() => {
|
|
|
15
16
|
process.env.LANGCHAIN_API_KEY = "";
|
|
16
17
|
process.env.LANGCHAIN_PROJECT = "";
|
|
17
18
|
});
|
|
19
|
+
const searchSchema = z.object({
|
|
20
|
+
query: z.string().describe("The query to search for."),
|
|
21
|
+
});
|
|
22
|
+
class SearchAPI extends StructuredTool {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
Object.defineProperty(this, "name", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: "search_api"
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(this, "description", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: "A simple API that returns the input string."
|
|
36
|
+
});
|
|
37
|
+
Object.defineProperty(this, "schema", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: searchSchema
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async _call(input) {
|
|
45
|
+
return `result for ${input?.query}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class SearchAPIWithArtifact extends StructuredTool {
|
|
49
|
+
constructor() {
|
|
50
|
+
super(...arguments);
|
|
51
|
+
Object.defineProperty(this, "name", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
writable: true,
|
|
55
|
+
value: "search_api"
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(this, "description", {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
writable: true,
|
|
61
|
+
value: "A simple API that returns the input string."
|
|
62
|
+
});
|
|
63
|
+
Object.defineProperty(this, "schema", {
|
|
64
|
+
enumerable: true,
|
|
65
|
+
configurable: true,
|
|
66
|
+
writable: true,
|
|
67
|
+
value: searchSchema
|
|
68
|
+
});
|
|
69
|
+
Object.defineProperty(this, "responseFormat", {
|
|
70
|
+
enumerable: true,
|
|
71
|
+
configurable: true,
|
|
72
|
+
writable: true,
|
|
73
|
+
value: "content_and_artifact"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async _call(_) {
|
|
77
|
+
return ["some response format", Buffer.from("123")];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
18
80
|
describe("PreBuilt", () => {
|
|
19
81
|
class SearchAPI extends Tool {
|
|
20
82
|
constructor() {
|
|
@@ -197,67 +259,6 @@ describe("PreBuilt", () => {
|
|
|
197
259
|
});
|
|
198
260
|
});
|
|
199
261
|
describe("createReactAgent", () => {
|
|
200
|
-
const searchSchema = z.object({
|
|
201
|
-
query: z.string().describe("The query to search for."),
|
|
202
|
-
});
|
|
203
|
-
class SearchAPI extends StructuredTool {
|
|
204
|
-
constructor() {
|
|
205
|
-
super(...arguments);
|
|
206
|
-
Object.defineProperty(this, "name", {
|
|
207
|
-
enumerable: true,
|
|
208
|
-
configurable: true,
|
|
209
|
-
writable: true,
|
|
210
|
-
value: "search_api"
|
|
211
|
-
});
|
|
212
|
-
Object.defineProperty(this, "description", {
|
|
213
|
-
enumerable: true,
|
|
214
|
-
configurable: true,
|
|
215
|
-
writable: true,
|
|
216
|
-
value: "A simple API that returns the input string."
|
|
217
|
-
});
|
|
218
|
-
Object.defineProperty(this, "schema", {
|
|
219
|
-
enumerable: true,
|
|
220
|
-
configurable: true,
|
|
221
|
-
writable: true,
|
|
222
|
-
value: searchSchema
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
async _call(input) {
|
|
226
|
-
return `result for ${input?.query}`;
|
|
227
|
-
}
|
|
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
|
-
}
|
|
261
262
|
const tools = [new SearchAPI()];
|
|
262
263
|
it("Can use string message modifier", async () => {
|
|
263
264
|
const llm = new FakeToolCallingChatModel({
|
|
@@ -367,4 +368,60 @@ describe("createReactAgent", () => {
|
|
|
367
368
|
new AIMessage("result2"),
|
|
368
369
|
]);
|
|
369
370
|
});
|
|
371
|
+
it("Can accept RunnableToolLike", async () => {
|
|
372
|
+
const llm = new FakeToolCallingChatModel({
|
|
373
|
+
responses: [
|
|
374
|
+
new AIMessage({
|
|
375
|
+
content: "result1",
|
|
376
|
+
tool_calls: [
|
|
377
|
+
{ name: "search_api", id: "tool_abcd123", args: { query: "foo" } },
|
|
378
|
+
],
|
|
379
|
+
}),
|
|
380
|
+
new AIMessage("result2"),
|
|
381
|
+
],
|
|
382
|
+
});
|
|
383
|
+
// Instead of re-implementing the tool, wrap it in a RunnableLambda and
|
|
384
|
+
// call `asTool` to create a RunnableToolLike.
|
|
385
|
+
const searchApiTool = new SearchAPI();
|
|
386
|
+
const runnableToolLikeTool = RunnableLambda.from(async (input, config) => searchApiTool.invoke(input, config)).asTool({
|
|
387
|
+
name: searchApiTool.name,
|
|
388
|
+
description: searchApiTool.description,
|
|
389
|
+
schema: searchApiTool.schema,
|
|
390
|
+
});
|
|
391
|
+
const agent = createReactAgent({
|
|
392
|
+
llm,
|
|
393
|
+
tools: [runnableToolLikeTool],
|
|
394
|
+
messageModifier: "You are a helpful assistant",
|
|
395
|
+
});
|
|
396
|
+
const result = await agent.invoke({
|
|
397
|
+
messages: [new HumanMessage("Hello Input!")],
|
|
398
|
+
});
|
|
399
|
+
expect(result.messages).toEqual([
|
|
400
|
+
new HumanMessage("Hello Input!"),
|
|
401
|
+
new AIMessage({
|
|
402
|
+
content: "result1",
|
|
403
|
+
tool_calls: [
|
|
404
|
+
{ name: "search_api", id: "tool_abcd123", args: { query: "foo" } },
|
|
405
|
+
],
|
|
406
|
+
}),
|
|
407
|
+
new ToolMessage({
|
|
408
|
+
name: "search_api",
|
|
409
|
+
content: "result for foo",
|
|
410
|
+
tool_call_id: "tool_abcd123",
|
|
411
|
+
}),
|
|
412
|
+
new AIMessage("result2"),
|
|
413
|
+
]);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
describe("ToolNode", () => {
|
|
417
|
+
it("Should support graceful error handling", async () => {
|
|
418
|
+
const toolNode = new ToolNode([new SearchAPI()]);
|
|
419
|
+
const res = await toolNode.invoke([
|
|
420
|
+
new AIMessage({
|
|
421
|
+
content: "",
|
|
422
|
+
tool_calls: [{ name: "badtool", args: {}, id: "testid" }],
|
|
423
|
+
}),
|
|
424
|
+
]);
|
|
425
|
+
expect(res[0].content).toEqual(`Error: Tool "badtool" not found.\n Please fix your mistakes.`);
|
|
426
|
+
});
|
|
370
427
|
});
|
|
@@ -1652,7 +1652,7 @@ describe("MessageGraph", () => {
|
|
|
1652
1652
|
.compile();
|
|
1653
1653
|
const result = await app.invoke(new HumanMessage("what is the weather in sf?"));
|
|
1654
1654
|
expect(result).toHaveLength(6);
|
|
1655
|
-
expect(result).
|
|
1655
|
+
expect(JSON.stringify(result)).toEqual(JSON.stringify([
|
|
1656
1656
|
new HumanMessage("what is the weather in sf?"),
|
|
1657
1657
|
new AIMessage({
|
|
1658
1658
|
content: "",
|
|
@@ -1681,7 +1681,7 @@ describe("MessageGraph", () => {
|
|
|
1681
1681
|
name: "search_api",
|
|
1682
1682
|
}),
|
|
1683
1683
|
new AIMessage("answer"),
|
|
1684
|
-
]);
|
|
1684
|
+
]));
|
|
1685
1685
|
});
|
|
1686
1686
|
it("can stream a list of messages", async () => {
|
|
1687
1687
|
const model = new FakeChatModel({
|
|
@@ -1752,7 +1752,7 @@ describe("MessageGraph", () => {
|
|
|
1752
1752
|
}
|
|
1753
1753
|
const lastItem = streamItems[streamItems.length - 1];
|
|
1754
1754
|
expect(Object.keys(lastItem)).toEqual(["agent"]);
|
|
1755
|
-
expect(Object.values(lastItem)[0]).toEqual(new AIMessage("answer"));
|
|
1755
|
+
expect(JSON.stringify(Object.values(lastItem)[0])).toEqual(JSON.stringify(new AIMessage("answer")));
|
|
1756
1756
|
});
|
|
1757
1757
|
});
|
|
1758
1758
|
it("StateGraph start branch then end", async () => {
|
package/dist/web.cjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EmptyChannelError = exports.InvalidUpdateError = exports.GraphValueError = exports.GraphRecursionError = exports.BaseCheckpointSaver = exports.emptyCheckpoint = exports.copyCheckpoint = exports.MemorySaver = exports.MessageGraph = exports.StateGraph = exports.START = exports.Graph = exports.END = void 0;
|
|
3
|
+
exports.EmptyChannelError = exports.InvalidUpdateError = exports.GraphValueError = exports.GraphRecursionError = exports.BaseCheckpointSaver = exports.emptyCheckpoint = exports.copyCheckpoint = exports.MemorySaver = exports.messagesStateReducer = exports.MessageGraph = exports.StateGraph = exports.START = exports.Graph = exports.END = void 0;
|
|
4
4
|
var index_js_1 = require("./graph/index.cjs");
|
|
5
5
|
Object.defineProperty(exports, "END", { enumerable: true, get: function () { return index_js_1.END; } });
|
|
6
6
|
Object.defineProperty(exports, "Graph", { enumerable: true, get: function () { return index_js_1.Graph; } });
|
|
7
7
|
Object.defineProperty(exports, "START", { enumerable: true, get: function () { return index_js_1.START; } });
|
|
8
8
|
Object.defineProperty(exports, "StateGraph", { enumerable: true, get: function () { return index_js_1.StateGraph; } });
|
|
9
9
|
Object.defineProperty(exports, "MessageGraph", { enumerable: true, get: function () { return index_js_1.MessageGraph; } });
|
|
10
|
+
Object.defineProperty(exports, "messagesStateReducer", { enumerable: true, get: function () { return index_js_1.messagesStateReducer; } });
|
|
10
11
|
var memory_js_1 = require("./checkpoint/memory.cjs");
|
|
11
12
|
Object.defineProperty(exports, "MemorySaver", { enumerable: true, get: function () { return memory_js_1.MemorySaver; } });
|
|
12
13
|
var base_js_1 = require("./checkpoint/base.cjs");
|
package/dist/web.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { END, Graph, type StateGraphArgs, START, StateGraph, type CompiledStateGraph, MessageGraph, } from "./graph/index.js";
|
|
1
|
+
export { END, Graph, type StateGraphArgs, START, StateGraph, type CompiledStateGraph, MessageGraph, messagesStateReducer, } from "./graph/index.js";
|
|
2
2
|
export { MemorySaver } from "./checkpoint/memory.js";
|
|
3
3
|
export { type Checkpoint, type CheckpointMetadata, type CheckpointTuple, copyCheckpoint, emptyCheckpoint, BaseCheckpointSaver, } from "./checkpoint/base.js";
|
|
4
4
|
export { GraphRecursionError, GraphValueError, InvalidUpdateError, EmptyChannelError, } from "./errors.js";
|
package/dist/web.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { END, Graph, START, StateGraph, MessageGraph, } from "./graph/index.js";
|
|
1
|
+
export { END, Graph, START, StateGraph, MessageGraph, messagesStateReducer, } from "./graph/index.js";
|
|
2
2
|
export { MemorySaver } from "./checkpoint/memory.js";
|
|
3
3
|
export { copyCheckpoint, emptyCheckpoint, BaseCheckpointSaver, } from "./checkpoint/base.js";
|
|
4
4
|
export { GraphRecursionError, GraphValueError, InvalidUpdateError, EmptyChannelError, } from "./errors.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/langgraph",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"description": "LangGraph",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@jest/globals": "^29.5.0",
|
|
46
|
-
"@langchain/anthropic": "^0.2.
|
|
47
|
-
"@langchain/community": "^0.2.
|
|
48
|
-
"@langchain/openai": "^0.
|
|
46
|
+
"@langchain/anthropic": "^0.2.6",
|
|
47
|
+
"@langchain/community": "^0.2.19",
|
|
48
|
+
"@langchain/openai": "^0.2.4",
|
|
49
49
|
"@langchain/scripts": "^0.0.13",
|
|
50
50
|
"@swc/core": "^1.3.90",
|
|
51
51
|
"@swc/jest": "^0.2.29",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"eslint-plugin-prettier": "^4.2.1",
|
|
68
68
|
"jest": "^29.5.0",
|
|
69
69
|
"jest-environment-node": "^29.6.4",
|
|
70
|
-
"langchain": "^0.2.
|
|
70
|
+
"langchain": "^0.2.10",
|
|
71
71
|
"prettier": "^2.8.3",
|
|
72
72
|
"release-it": "^15.10.1",
|
|
73
73
|
"rollup": "^4.5.2",
|