@langchain/langgraph 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +588 -0
- package/dist/channels/base.cjs +58 -0
- package/dist/channels/base.d.ts +46 -0
- package/dist/channels/base.js +50 -0
- package/dist/channels/binop.cjs +70 -0
- package/dist/channels/binop.d.ts +16 -0
- package/dist/channels/binop.js +66 -0
- package/dist/channels/index.cjs +9 -0
- package/dist/channels/index.d.ts +1 -0
- package/dist/channels/index.js +1 -0
- package/dist/channels/last_value.cjs +53 -0
- package/dist/channels/last_value.d.ts +12 -0
- package/dist/channels/last_value.js +49 -0
- package/dist/channels/topic.cjs +90 -0
- package/dist/channels/topic.d.ts +19 -0
- package/dist/channels/topic.js +86 -0
- package/dist/checkpoint/base.cjs +32 -0
- package/dist/checkpoint/base.d.ts +47 -0
- package/dist/checkpoint/base.js +27 -0
- package/dist/checkpoint/index.cjs +8 -0
- package/dist/checkpoint/index.d.ts +2 -0
- package/dist/checkpoint/index.js +2 -0
- package/dist/checkpoint/memory.cjs +35 -0
- package/dist/checkpoint/memory.d.ts +8 -0
- package/dist/checkpoint/memory.js +31 -0
- package/dist/constants.cjs +5 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/graph/graph.cjs +175 -0
- package/dist/graph/graph.d.ts +30 -0
- package/dist/graph/graph.js +171 -0
- package/dist/graph/index.cjs +9 -0
- package/dist/graph/index.d.ts +2 -0
- package/dist/graph/index.js +2 -0
- package/dist/graph/state.cjs +108 -0
- package/dist/graph/state.d.ts +17 -0
- package/dist/graph/state.js +104 -0
- package/dist/index.cjs +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/prebuilt/agent_executor.cjs +96 -0
- package/dist/prebuilt/agent_executor.d.ts +12 -0
- package/dist/prebuilt/agent_executor.js +92 -0
- package/dist/prebuilt/chat_agent_executor.cjs +130 -0
- package/dist/prebuilt/chat_agent_executor.d.ts +6 -0
- package/dist/prebuilt/chat_agent_executor.js +126 -0
- package/dist/prebuilt/index.cjs +9 -0
- package/dist/prebuilt/index.d.ts +3 -0
- package/dist/prebuilt/index.js +3 -0
- package/dist/prebuilt/tool_executor.cjs +63 -0
- package/dist/prebuilt/tool_executor.d.ts +27 -0
- package/dist/prebuilt/tool_executor.js +59 -0
- package/dist/pregel/debug.cjs +46 -0
- package/dist/pregel/debug.d.ts +4 -0
- package/dist/pregel/debug.js +41 -0
- package/dist/pregel/index.cjs +475 -0
- package/dist/pregel/index.d.ts +75 -0
- package/dist/pregel/index.js +469 -0
- package/dist/pregel/io.cjs +57 -0
- package/dist/pregel/io.d.ts +9 -0
- package/dist/pregel/io.js +52 -0
- package/dist/pregel/read.cjs +217 -0
- package/dist/pregel/read.d.ts +43 -0
- package/dist/pregel/read.js +211 -0
- package/dist/pregel/reserved.cjs +7 -0
- package/dist/pregel/reserved.d.ts +3 -0
- package/dist/pregel/reserved.js +4 -0
- package/dist/pregel/validate.cjs +90 -0
- package/dist/pregel/validate.d.ts +15 -0
- package/dist/pregel/validate.js +85 -0
- package/dist/pregel/write.cjs +54 -0
- package/dist/pregel/write.d.ts +13 -0
- package/dist/pregel/write.js +50 -0
- package/index.cjs +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +100 -0
- package/prebuilt.cjs +1 -0
- package/prebuilt.d.ts +1 -0
- package/prebuilt.js +1 -0
- package/pregel.cjs +1 -0
- package/pregel.d.ts +1 -0
- package/pregel.js +1 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { RunnableLambda } from "@langchain/core/runnables";
|
|
2
|
+
import { BinaryOperatorAggregate } from "../channels/binop.js";
|
|
3
|
+
import { END, Graph } from "./graph.js";
|
|
4
|
+
import { LastValue } from "../channels/last_value.js";
|
|
5
|
+
import { ChannelWrite } from "../pregel/write.js";
|
|
6
|
+
import { Pregel, Channel } from "../pregel/index.js";
|
|
7
|
+
import { ChannelRead } from "../pregel/read.js";
|
|
8
|
+
export const START = "__start__";
|
|
9
|
+
export class StateGraph extends Graph {
|
|
10
|
+
constructor(fields) {
|
|
11
|
+
super();
|
|
12
|
+
Object.defineProperty(this, "channels", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: void 0
|
|
17
|
+
});
|
|
18
|
+
this.channels = _getChannels(fields.channels);
|
|
19
|
+
}
|
|
20
|
+
compile(checkpointer) {
|
|
21
|
+
this.validate();
|
|
22
|
+
if (Object.keys(this.nodes).some((key) => key in this.channels)) {
|
|
23
|
+
throw new Error("Cannot use channel names as node names");
|
|
24
|
+
}
|
|
25
|
+
const stateKeys = Object.keys(this.channels);
|
|
26
|
+
const outgoingEdges = {};
|
|
27
|
+
for (const [start, end] of this.edges) {
|
|
28
|
+
if (!outgoingEdges[start]) {
|
|
29
|
+
outgoingEdges[start] = [];
|
|
30
|
+
}
|
|
31
|
+
outgoingEdges[start].push(end !== END ? `${end}:inbox` : END);
|
|
32
|
+
}
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
const nodes = {};
|
|
35
|
+
for (const [key, node] of Object.entries(this.nodes)) {
|
|
36
|
+
nodes[key] = Channel.subscribeTo(`${key}:inbox`)
|
|
37
|
+
.pipe(node)
|
|
38
|
+
.pipe(_updateState)
|
|
39
|
+
.pipe(Channel.writeTo(key));
|
|
40
|
+
}
|
|
41
|
+
for (const key of Object.keys(this.nodes)) {
|
|
42
|
+
const outgoing = outgoingEdges[key];
|
|
43
|
+
const edgesKey = `${key}:edges`;
|
|
44
|
+
if (outgoing || this.branches[key]) {
|
|
45
|
+
nodes[edgesKey] = Channel.subscribeTo(key, {
|
|
46
|
+
tags: ["langsmith:hidden"],
|
|
47
|
+
}).pipe(new ChannelRead(stateKeys));
|
|
48
|
+
}
|
|
49
|
+
if (outgoing) {
|
|
50
|
+
nodes[edgesKey] = nodes[edgesKey].pipe(Channel.writeTo(...outgoing));
|
|
51
|
+
}
|
|
52
|
+
if (this.branches[key]) {
|
|
53
|
+
for (const branch of this.branches[key]) {
|
|
54
|
+
nodes[edgesKey] = nodes[edgesKey].pipe(new RunnableLambda({
|
|
55
|
+
func: (i, c) => branch.runnable(i, c),
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
nodes[START] = Channel.subscribeTo(`${START}:inbox`, {
|
|
61
|
+
tags: ["langsmith:hidden"],
|
|
62
|
+
})
|
|
63
|
+
.pipe(_updateState)
|
|
64
|
+
.pipe(Channel.writeTo(START));
|
|
65
|
+
nodes[`${START}:edges`] = Channel.subscribeTo(START, {
|
|
66
|
+
tags: ["langsmith:hidden"],
|
|
67
|
+
})
|
|
68
|
+
.pipe(new ChannelRead(stateKeys))
|
|
69
|
+
.pipe(Channel.writeTo(`${this.entryPoint}:inbox`));
|
|
70
|
+
return new Pregel({
|
|
71
|
+
nodes,
|
|
72
|
+
channels: this.channels,
|
|
73
|
+
input: `${START}:inbox`,
|
|
74
|
+
output: END,
|
|
75
|
+
hidden: Object.keys(this.nodes)
|
|
76
|
+
.map((node) => `${node}:inbox`)
|
|
77
|
+
.concat(START, stateKeys),
|
|
78
|
+
checkpointer,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function _updateState(
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
input, options
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
) {
|
|
87
|
+
if (!options?.config) {
|
|
88
|
+
throw new Error("Config not found when updating state.");
|
|
89
|
+
}
|
|
90
|
+
ChannelWrite.doWrite(options.config, input);
|
|
91
|
+
return input;
|
|
92
|
+
}
|
|
93
|
+
function _getChannels(schema) {
|
|
94
|
+
const channels = {};
|
|
95
|
+
for (const [name, values] of Object.entries(schema)) {
|
|
96
|
+
if (values.value) {
|
|
97
|
+
channels[name] = new BinaryOperatorAggregate(values.value, values.default);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
channels[name] = new LastValue();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return channels;
|
|
104
|
+
}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StateGraph = exports.START = exports.Graph = exports.END = void 0;
|
|
4
|
+
var index_js_1 = require("./graph/index.cjs");
|
|
5
|
+
Object.defineProperty(exports, "END", { enumerable: true, get: function () { return index_js_1.END; } });
|
|
6
|
+
Object.defineProperty(exports, "Graph", { enumerable: true, get: function () { return index_js_1.Graph; } });
|
|
7
|
+
Object.defineProperty(exports, "START", { enumerable: true, get: function () { return index_js_1.START; } });
|
|
8
|
+
Object.defineProperty(exports, "StateGraph", { enumerable: true, get: function () { return index_js_1.StateGraph; } });
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { END, Graph, type StateGraphArgs, START, StateGraph, } from "./graph/index.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { END, Graph, START, StateGraph, } from "./graph/index.js";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAgentExecutor = void 0;
|
|
4
|
+
const runnables_1 = require("@langchain/core/runnables");
|
|
5
|
+
const tool_executor_js_1 = require("./tool_executor.cjs");
|
|
6
|
+
const state_js_1 = require("../graph/state.cjs");
|
|
7
|
+
const index_js_1 = require("../index.cjs");
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
function _getAgentState(inputSchema) {
|
|
10
|
+
if (!inputSchema) {
|
|
11
|
+
return {
|
|
12
|
+
input: {
|
|
13
|
+
value: null,
|
|
14
|
+
},
|
|
15
|
+
agentOutcome: {
|
|
16
|
+
value: null,
|
|
17
|
+
},
|
|
18
|
+
steps: {
|
|
19
|
+
value: (x, y) => x.concat(y),
|
|
20
|
+
default: () => [],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return inputSchema;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createAgentExecutor({ agentRunnable, tools, inputSchema, }) {
|
|
29
|
+
let toolExecutor;
|
|
30
|
+
if (!Array.isArray(tools)) {
|
|
31
|
+
toolExecutor = tools;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
toolExecutor = new tool_executor_js_1.ToolExecutor({
|
|
35
|
+
tools,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const state = _getAgentState(inputSchema);
|
|
39
|
+
// Define logic that will be used to determine which conditional edge to go down
|
|
40
|
+
const shouldContinue = (data) => {
|
|
41
|
+
if (data.agentOutcome && "returnValues" in data.agentOutcome) {
|
|
42
|
+
return "end";
|
|
43
|
+
}
|
|
44
|
+
return "continue";
|
|
45
|
+
};
|
|
46
|
+
const runAgent = async (data) => {
|
|
47
|
+
const agentOutcome = await agentRunnable.invoke(data);
|
|
48
|
+
return {
|
|
49
|
+
agentOutcome,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
const executeTools = async (data) => {
|
|
53
|
+
const agentAction = data.agentOutcome;
|
|
54
|
+
if (!agentAction || "returnValues" in agentAction) {
|
|
55
|
+
throw new Error("Agent has not been run yet");
|
|
56
|
+
}
|
|
57
|
+
const output = await toolExecutor.invoke(agentAction);
|
|
58
|
+
return {
|
|
59
|
+
steps: [[agentAction, output]],
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
// Define a new graph
|
|
63
|
+
const workflow = new state_js_1.StateGraph({
|
|
64
|
+
channels: state,
|
|
65
|
+
});
|
|
66
|
+
// Define the two nodes we will cycle between
|
|
67
|
+
workflow.addNode("agent", new runnables_1.RunnableLambda({ func: runAgent }));
|
|
68
|
+
workflow.addNode("action", new runnables_1.RunnableLambda({ func: executeTools }));
|
|
69
|
+
// Set the entrypoint as `agent`
|
|
70
|
+
// This means that this node is the first one called
|
|
71
|
+
workflow.setEntryPoint("agent");
|
|
72
|
+
// We now add a conditional edge
|
|
73
|
+
workflow.addConditionalEdges(
|
|
74
|
+
// First, we define the start node. We use `agent`.
|
|
75
|
+
// This means these are the edges taken after the `agent` node is called.
|
|
76
|
+
"agent",
|
|
77
|
+
// Next, we pass in the function that will determine which node is called next.
|
|
78
|
+
shouldContinue,
|
|
79
|
+
// Finally we pass in a mapping.
|
|
80
|
+
// The keys are strings, and the values are other nodes.
|
|
81
|
+
// END is a special node marking that the graph should finish.
|
|
82
|
+
// What will happen is we will call `should_continue`, and then the output of that
|
|
83
|
+
// will be matched against the keys in this mapping.
|
|
84
|
+
// Based on which one it matches, that node will then be called.
|
|
85
|
+
{
|
|
86
|
+
// If `tools`, then we call the tool node.
|
|
87
|
+
continue: "action",
|
|
88
|
+
// Otherwise we finish.
|
|
89
|
+
end: index_js_1.END,
|
|
90
|
+
});
|
|
91
|
+
// We now add a normal edge from `tools` to `agent`.
|
|
92
|
+
// This means that after `tools` is called, `agent` node is called next.
|
|
93
|
+
workflow.addEdge("action", "agent");
|
|
94
|
+
return workflow.compile();
|
|
95
|
+
}
|
|
96
|
+
exports.createAgentExecutor = createAgentExecutor;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Runnable } from "@langchain/core/runnables";
|
|
2
|
+
import { Tool } from "@langchain/core/tools";
|
|
3
|
+
import { ToolExecutor } from "./tool_executor.js";
|
|
4
|
+
import { StateGraphArgs } from "../graph/state.js";
|
|
5
|
+
import { Pregel } from "../pregel/index.js";
|
|
6
|
+
type AgentChannels<T> = StateGraphArgs<Array<any> | T>["channels"];
|
|
7
|
+
export declare function createAgentExecutor<T extends Array<any> = Array<any>>({ agentRunnable, tools, inputSchema, }: {
|
|
8
|
+
agentRunnable: Runnable;
|
|
9
|
+
tools: Array<Tool> | ToolExecutor;
|
|
10
|
+
inputSchema?: AgentChannels<T>;
|
|
11
|
+
}): Pregel;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { RunnableLambda } from "@langchain/core/runnables";
|
|
2
|
+
import { ToolExecutor } from "./tool_executor.js";
|
|
3
|
+
import { StateGraph } from "../graph/state.js";
|
|
4
|
+
import { END } from "../index.js";
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
function _getAgentState(inputSchema) {
|
|
7
|
+
if (!inputSchema) {
|
|
8
|
+
return {
|
|
9
|
+
input: {
|
|
10
|
+
value: null,
|
|
11
|
+
},
|
|
12
|
+
agentOutcome: {
|
|
13
|
+
value: null,
|
|
14
|
+
},
|
|
15
|
+
steps: {
|
|
16
|
+
value: (x, y) => x.concat(y),
|
|
17
|
+
default: () => [],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
return inputSchema;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function createAgentExecutor({ agentRunnable, tools, inputSchema, }) {
|
|
26
|
+
let toolExecutor;
|
|
27
|
+
if (!Array.isArray(tools)) {
|
|
28
|
+
toolExecutor = tools;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
toolExecutor = new ToolExecutor({
|
|
32
|
+
tools,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const state = _getAgentState(inputSchema);
|
|
36
|
+
// Define logic that will be used to determine which conditional edge to go down
|
|
37
|
+
const shouldContinue = (data) => {
|
|
38
|
+
if (data.agentOutcome && "returnValues" in data.agentOutcome) {
|
|
39
|
+
return "end";
|
|
40
|
+
}
|
|
41
|
+
return "continue";
|
|
42
|
+
};
|
|
43
|
+
const runAgent = async (data) => {
|
|
44
|
+
const agentOutcome = await agentRunnable.invoke(data);
|
|
45
|
+
return {
|
|
46
|
+
agentOutcome,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const executeTools = async (data) => {
|
|
50
|
+
const agentAction = data.agentOutcome;
|
|
51
|
+
if (!agentAction || "returnValues" in agentAction) {
|
|
52
|
+
throw new Error("Agent has not been run yet");
|
|
53
|
+
}
|
|
54
|
+
const output = await toolExecutor.invoke(agentAction);
|
|
55
|
+
return {
|
|
56
|
+
steps: [[agentAction, output]],
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
// Define a new graph
|
|
60
|
+
const workflow = new StateGraph({
|
|
61
|
+
channels: state,
|
|
62
|
+
});
|
|
63
|
+
// Define the two nodes we will cycle between
|
|
64
|
+
workflow.addNode("agent", new RunnableLambda({ func: runAgent }));
|
|
65
|
+
workflow.addNode("action", new RunnableLambda({ func: executeTools }));
|
|
66
|
+
// Set the entrypoint as `agent`
|
|
67
|
+
// This means that this node is the first one called
|
|
68
|
+
workflow.setEntryPoint("agent");
|
|
69
|
+
// We now add a conditional edge
|
|
70
|
+
workflow.addConditionalEdges(
|
|
71
|
+
// First, we define the start node. We use `agent`.
|
|
72
|
+
// This means these are the edges taken after the `agent` node is called.
|
|
73
|
+
"agent",
|
|
74
|
+
// Next, we pass in the function that will determine which node is called next.
|
|
75
|
+
shouldContinue,
|
|
76
|
+
// Finally we pass in a mapping.
|
|
77
|
+
// The keys are strings, and the values are other nodes.
|
|
78
|
+
// END is a special node marking that the graph should finish.
|
|
79
|
+
// What will happen is we will call `should_continue`, and then the output of that
|
|
80
|
+
// will be matched against the keys in this mapping.
|
|
81
|
+
// Based on which one it matches, that node will then be called.
|
|
82
|
+
{
|
|
83
|
+
// If `tools`, then we call the tool node.
|
|
84
|
+
continue: "action",
|
|
85
|
+
// Otherwise we finish.
|
|
86
|
+
end: END,
|
|
87
|
+
});
|
|
88
|
+
// We now add a normal edge from `tools` to `agent`.
|
|
89
|
+
// This means that after `tools` is called, `agent` node is called next.
|
|
90
|
+
workflow.addEdge("action", "agent");
|
|
91
|
+
return workflow.compile();
|
|
92
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFunctionCallingExecutor = void 0;
|
|
4
|
+
const function_calling_1 = require("@langchain/core/utils/function_calling");
|
|
5
|
+
const messages_1 = require("@langchain/core/messages");
|
|
6
|
+
const runnables_1 = require("@langchain/core/runnables");
|
|
7
|
+
const tool_executor_js_1 = require("./tool_executor.cjs");
|
|
8
|
+
const state_js_1 = require("../graph/state.cjs");
|
|
9
|
+
const index_js_1 = require("../index.cjs");
|
|
10
|
+
function createFunctionCallingExecutor({ model, tools, }) {
|
|
11
|
+
let toolExecutor;
|
|
12
|
+
let toolClasses;
|
|
13
|
+
if (!Array.isArray(tools)) {
|
|
14
|
+
toolExecutor = tools;
|
|
15
|
+
toolClasses = tools.tools;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
toolExecutor = new tool_executor_js_1.ToolExecutor({
|
|
19
|
+
tools,
|
|
20
|
+
});
|
|
21
|
+
toolClasses = tools;
|
|
22
|
+
}
|
|
23
|
+
if (!("bind" in model) || typeof model.bind !== "function") {
|
|
24
|
+
throw new Error("Model must be bindable");
|
|
25
|
+
}
|
|
26
|
+
const toolsAsOpenAIFunctions = toolClasses.map((tool) => (0, function_calling_1.convertToOpenAIFunction)(tool));
|
|
27
|
+
const newModel = model.bind({
|
|
28
|
+
functions: toolsAsOpenAIFunctions,
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
});
|
|
31
|
+
// Define the function that determines whether to continue or not
|
|
32
|
+
const shouldContinue = (state) => {
|
|
33
|
+
const { messages } = state;
|
|
34
|
+
const lastMessage = messages[messages.length - 1];
|
|
35
|
+
// If there is no function call, then we finish
|
|
36
|
+
if (!("function_call" in lastMessage.additional_kwargs) ||
|
|
37
|
+
!lastMessage.additional_kwargs.function_call) {
|
|
38
|
+
return "end";
|
|
39
|
+
}
|
|
40
|
+
// Otherwise if there is, we continue
|
|
41
|
+
return "continue";
|
|
42
|
+
};
|
|
43
|
+
// Define the function that calls the model
|
|
44
|
+
const callModel = async (state) => {
|
|
45
|
+
const { messages } = state;
|
|
46
|
+
const response = await newModel.invoke(messages);
|
|
47
|
+
// We return a list, because this will get added to the existing list
|
|
48
|
+
return {
|
|
49
|
+
messages: [response],
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
// Define the function to execute tools
|
|
53
|
+
const _getAction = (state) => {
|
|
54
|
+
const { messages } = state;
|
|
55
|
+
// Based on the continue condition
|
|
56
|
+
// we know the last message involves a function call
|
|
57
|
+
const lastMessage = messages[messages.length - 1];
|
|
58
|
+
if (!lastMessage) {
|
|
59
|
+
throw new Error("No messages found.");
|
|
60
|
+
}
|
|
61
|
+
if (!lastMessage.additional_kwargs.function_call) {
|
|
62
|
+
throw new Error("No function call found in message.");
|
|
63
|
+
}
|
|
64
|
+
// We construct an AgentAction from the function_call
|
|
65
|
+
return {
|
|
66
|
+
tool: lastMessage.additional_kwargs.function_call.name,
|
|
67
|
+
toolInput: JSON.stringify(lastMessage.additional_kwargs.function_call.arguments),
|
|
68
|
+
log: "",
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
const callTool = async (state) => {
|
|
72
|
+
const action = _getAction(state);
|
|
73
|
+
// We call the tool_executor and get back a response
|
|
74
|
+
const response = await toolExecutor.invoke(action);
|
|
75
|
+
// We use the response to create a FunctionMessage
|
|
76
|
+
const functionMessage = new messages_1.FunctionMessage({
|
|
77
|
+
content: response,
|
|
78
|
+
name: action.tool,
|
|
79
|
+
});
|
|
80
|
+
// We return a list, because this will get added to the existing list
|
|
81
|
+
return { messages: [functionMessage] };
|
|
82
|
+
};
|
|
83
|
+
// We create the AgentState that we will pass around
|
|
84
|
+
// This simply involves a list of messages
|
|
85
|
+
// We want steps to return messages to append to the list
|
|
86
|
+
// So we annotate the messages attribute with operator.add
|
|
87
|
+
const schema = {
|
|
88
|
+
messages: {
|
|
89
|
+
value: (x, y) => x.concat(y),
|
|
90
|
+
default: () => [],
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
// Define a new graph
|
|
94
|
+
const workflow = new state_js_1.StateGraph({
|
|
95
|
+
channels: schema,
|
|
96
|
+
});
|
|
97
|
+
// Define the two nodes we will cycle between
|
|
98
|
+
workflow.addNode("agent", new runnables_1.RunnableLambda({ func: callModel }));
|
|
99
|
+
workflow.addNode("action", new runnables_1.RunnableLambda({ func: callTool }));
|
|
100
|
+
// Set the entrypoint as `agent`
|
|
101
|
+
// This means that this node is the first one called
|
|
102
|
+
workflow.setEntryPoint("agent");
|
|
103
|
+
// We now add a conditional edge
|
|
104
|
+
workflow.addConditionalEdges(
|
|
105
|
+
// First, we define the start node. We use `agent`.
|
|
106
|
+
// This means these are the edges taken after the `agent` node is called.
|
|
107
|
+
"agent",
|
|
108
|
+
// Next, we pass in the function that will determine which node is called next.
|
|
109
|
+
shouldContinue,
|
|
110
|
+
// Finally we pass in a mapping.
|
|
111
|
+
// The keys are strings, and the values are other nodes.
|
|
112
|
+
// END is a special node marking that the graph should finish.
|
|
113
|
+
// What will happen is we will call `should_continue`, and then the output of that
|
|
114
|
+
// will be matched against the keys in this mapping.
|
|
115
|
+
// Based on which one it matches, that node will then be called.
|
|
116
|
+
{
|
|
117
|
+
// If `tools`, then we call the tool node.
|
|
118
|
+
continue: "action",
|
|
119
|
+
// Otherwise we finish.
|
|
120
|
+
end: index_js_1.END,
|
|
121
|
+
});
|
|
122
|
+
// We now add a normal edge from `tools` to `agent`.
|
|
123
|
+
// This means that after `tools` is called, `agent` node is called next.
|
|
124
|
+
workflow.addEdge("action", "agent");
|
|
125
|
+
// Finally, we compile it!
|
|
126
|
+
// This compiles it into a LangChain Runnable,
|
|
127
|
+
// meaning you can use it as you would any other runnable
|
|
128
|
+
return workflow.compile();
|
|
129
|
+
}
|
|
130
|
+
exports.createFunctionCallingExecutor = createFunctionCallingExecutor;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Tool } from "@langchain/core/tools";
|
|
2
|
+
import { ToolExecutor } from "./tool_executor.js";
|
|
3
|
+
export declare function createFunctionCallingExecutor<Model extends object>({ model, tools, }: {
|
|
4
|
+
model: Model;
|
|
5
|
+
tools: Array<Tool> | ToolExecutor;
|
|
6
|
+
}): import("../pregel/index.js").Pregel;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { convertToOpenAIFunction } from "@langchain/core/utils/function_calling";
|
|
2
|
+
import { FunctionMessage } from "@langchain/core/messages";
|
|
3
|
+
import { RunnableLambda } from "@langchain/core/runnables";
|
|
4
|
+
import { ToolExecutor } from "./tool_executor.js";
|
|
5
|
+
import { StateGraph } from "../graph/state.js";
|
|
6
|
+
import { END } from "../index.js";
|
|
7
|
+
export function createFunctionCallingExecutor({ model, tools, }) {
|
|
8
|
+
let toolExecutor;
|
|
9
|
+
let toolClasses;
|
|
10
|
+
if (!Array.isArray(tools)) {
|
|
11
|
+
toolExecutor = tools;
|
|
12
|
+
toolClasses = tools.tools;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
toolExecutor = new ToolExecutor({
|
|
16
|
+
tools,
|
|
17
|
+
});
|
|
18
|
+
toolClasses = tools;
|
|
19
|
+
}
|
|
20
|
+
if (!("bind" in model) || typeof model.bind !== "function") {
|
|
21
|
+
throw new Error("Model must be bindable");
|
|
22
|
+
}
|
|
23
|
+
const toolsAsOpenAIFunctions = toolClasses.map((tool) => convertToOpenAIFunction(tool));
|
|
24
|
+
const newModel = model.bind({
|
|
25
|
+
functions: toolsAsOpenAIFunctions,
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
});
|
|
28
|
+
// Define the function that determines whether to continue or not
|
|
29
|
+
const shouldContinue = (state) => {
|
|
30
|
+
const { messages } = state;
|
|
31
|
+
const lastMessage = messages[messages.length - 1];
|
|
32
|
+
// If there is no function call, then we finish
|
|
33
|
+
if (!("function_call" in lastMessage.additional_kwargs) ||
|
|
34
|
+
!lastMessage.additional_kwargs.function_call) {
|
|
35
|
+
return "end";
|
|
36
|
+
}
|
|
37
|
+
// Otherwise if there is, we continue
|
|
38
|
+
return "continue";
|
|
39
|
+
};
|
|
40
|
+
// Define the function that calls the model
|
|
41
|
+
const callModel = async (state) => {
|
|
42
|
+
const { messages } = state;
|
|
43
|
+
const response = await newModel.invoke(messages);
|
|
44
|
+
// We return a list, because this will get added to the existing list
|
|
45
|
+
return {
|
|
46
|
+
messages: [response],
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
// Define the function to execute tools
|
|
50
|
+
const _getAction = (state) => {
|
|
51
|
+
const { messages } = state;
|
|
52
|
+
// Based on the continue condition
|
|
53
|
+
// we know the last message involves a function call
|
|
54
|
+
const lastMessage = messages[messages.length - 1];
|
|
55
|
+
if (!lastMessage) {
|
|
56
|
+
throw new Error("No messages found.");
|
|
57
|
+
}
|
|
58
|
+
if (!lastMessage.additional_kwargs.function_call) {
|
|
59
|
+
throw new Error("No function call found in message.");
|
|
60
|
+
}
|
|
61
|
+
// We construct an AgentAction from the function_call
|
|
62
|
+
return {
|
|
63
|
+
tool: lastMessage.additional_kwargs.function_call.name,
|
|
64
|
+
toolInput: JSON.stringify(lastMessage.additional_kwargs.function_call.arguments),
|
|
65
|
+
log: "",
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const callTool = async (state) => {
|
|
69
|
+
const action = _getAction(state);
|
|
70
|
+
// We call the tool_executor and get back a response
|
|
71
|
+
const response = await toolExecutor.invoke(action);
|
|
72
|
+
// We use the response to create a FunctionMessage
|
|
73
|
+
const functionMessage = new FunctionMessage({
|
|
74
|
+
content: response,
|
|
75
|
+
name: action.tool,
|
|
76
|
+
});
|
|
77
|
+
// We return a list, because this will get added to the existing list
|
|
78
|
+
return { messages: [functionMessage] };
|
|
79
|
+
};
|
|
80
|
+
// We create the AgentState that we will pass around
|
|
81
|
+
// This simply involves a list of messages
|
|
82
|
+
// We want steps to return messages to append to the list
|
|
83
|
+
// So we annotate the messages attribute with operator.add
|
|
84
|
+
const schema = {
|
|
85
|
+
messages: {
|
|
86
|
+
value: (x, y) => x.concat(y),
|
|
87
|
+
default: () => [],
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
// Define a new graph
|
|
91
|
+
const workflow = new StateGraph({
|
|
92
|
+
channels: schema,
|
|
93
|
+
});
|
|
94
|
+
// Define the two nodes we will cycle between
|
|
95
|
+
workflow.addNode("agent", new RunnableLambda({ func: callModel }));
|
|
96
|
+
workflow.addNode("action", new RunnableLambda({ func: callTool }));
|
|
97
|
+
// Set the entrypoint as `agent`
|
|
98
|
+
// This means that this node is the first one called
|
|
99
|
+
workflow.setEntryPoint("agent");
|
|
100
|
+
// We now add a conditional edge
|
|
101
|
+
workflow.addConditionalEdges(
|
|
102
|
+
// First, we define the start node. We use `agent`.
|
|
103
|
+
// This means these are the edges taken after the `agent` node is called.
|
|
104
|
+
"agent",
|
|
105
|
+
// Next, we pass in the function that will determine which node is called next.
|
|
106
|
+
shouldContinue,
|
|
107
|
+
// Finally we pass in a mapping.
|
|
108
|
+
// The keys are strings, and the values are other nodes.
|
|
109
|
+
// END is a special node marking that the graph should finish.
|
|
110
|
+
// What will happen is we will call `should_continue`, and then the output of that
|
|
111
|
+
// will be matched against the keys in this mapping.
|
|
112
|
+
// Based on which one it matches, that node will then be called.
|
|
113
|
+
{
|
|
114
|
+
// If `tools`, then we call the tool node.
|
|
115
|
+
continue: "action",
|
|
116
|
+
// Otherwise we finish.
|
|
117
|
+
end: END,
|
|
118
|
+
});
|
|
119
|
+
// We now add a normal edge from `tools` to `agent`.
|
|
120
|
+
// This means that after `tools` is called, `agent` node is called next.
|
|
121
|
+
workflow.addEdge("action", "agent");
|
|
122
|
+
// Finally, we compile it!
|
|
123
|
+
// This compiles it into a LangChain Runnable,
|
|
124
|
+
// meaning you can use it as you would any other runnable
|
|
125
|
+
return workflow.compile();
|
|
126
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToolExecutor = exports.createFunctionCallingExecutor = exports.createAgentExecutor = void 0;
|
|
4
|
+
var agent_executor_js_1 = require("./agent_executor.cjs");
|
|
5
|
+
Object.defineProperty(exports, "createAgentExecutor", { enumerable: true, get: function () { return agent_executor_js_1.createAgentExecutor; } });
|
|
6
|
+
var chat_agent_executor_js_1 = require("./chat_agent_executor.cjs");
|
|
7
|
+
Object.defineProperty(exports, "createFunctionCallingExecutor", { enumerable: true, get: function () { return chat_agent_executor_js_1.createFunctionCallingExecutor; } });
|
|
8
|
+
var tool_executor_js_1 = require("./tool_executor.cjs");
|
|
9
|
+
Object.defineProperty(exports, "ToolExecutor", { enumerable: true, get: function () { return tool_executor_js_1.ToolExecutor; } });
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToolExecutor = void 0;
|
|
4
|
+
const runnables_1 = require("@langchain/core/runnables");
|
|
5
|
+
const INVALID_TOOL_MSG_TEMPLATE = `{requestedToolName} is not a valid tool, try one of [availableToolNamesString].`;
|
|
6
|
+
class ToolExecutor extends runnables_1.RunnableBinding {
|
|
7
|
+
constructor(fields) {
|
|
8
|
+
const fieldsWithDefaults = {
|
|
9
|
+
invalidToolMsgTemplate: INVALID_TOOL_MSG_TEMPLATE,
|
|
10
|
+
...fields,
|
|
11
|
+
};
|
|
12
|
+
const bound = new runnables_1.RunnableLambda({
|
|
13
|
+
func: async (input, options) => this._execute(input, options?.config),
|
|
14
|
+
});
|
|
15
|
+
super({
|
|
16
|
+
bound,
|
|
17
|
+
config: {},
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(this, "lc_graph_name", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: "ToolExecutor"
|
|
24
|
+
});
|
|
25
|
+
Object.defineProperty(this, "tools", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: void 0
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(this, "toolMap", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: void 0
|
|
36
|
+
});
|
|
37
|
+
Object.defineProperty(this, "invalidToolMsgTemplate", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: void 0
|
|
42
|
+
});
|
|
43
|
+
this.tools = fieldsWithDefaults.tools;
|
|
44
|
+
this.invalidToolMsgTemplate = fieldsWithDefaults.invalidToolMsgTemplate;
|
|
45
|
+
this.toolMap = this.tools.reduce((acc, tool) => {
|
|
46
|
+
acc[tool.name] = tool;
|
|
47
|
+
return acc;
|
|
48
|
+
}, {});
|
|
49
|
+
}
|
|
50
|
+
async _execute(toolInvocation, _config) {
|
|
51
|
+
if (!(toolInvocation.tool in this.toolMap)) {
|
|
52
|
+
return this.invalidToolMsgTemplate
|
|
53
|
+
.replace("{requestedToolName}", toolInvocation.tool)
|
|
54
|
+
.replace("{availableToolNamesString}", Object.keys(this.toolMap).join(", "));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const tool = this.toolMap[toolInvocation.tool];
|
|
58
|
+
const output = await tool.invoke(toolInvocation.toolInput);
|
|
59
|
+
return output;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.ToolExecutor = ToolExecutor;
|