@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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +588 -0
  3. package/dist/channels/base.cjs +58 -0
  4. package/dist/channels/base.d.ts +46 -0
  5. package/dist/channels/base.js +50 -0
  6. package/dist/channels/binop.cjs +70 -0
  7. package/dist/channels/binop.d.ts +16 -0
  8. package/dist/channels/binop.js +66 -0
  9. package/dist/channels/index.cjs +9 -0
  10. package/dist/channels/index.d.ts +1 -0
  11. package/dist/channels/index.js +1 -0
  12. package/dist/channels/last_value.cjs +53 -0
  13. package/dist/channels/last_value.d.ts +12 -0
  14. package/dist/channels/last_value.js +49 -0
  15. package/dist/channels/topic.cjs +90 -0
  16. package/dist/channels/topic.d.ts +19 -0
  17. package/dist/channels/topic.js +86 -0
  18. package/dist/checkpoint/base.cjs +32 -0
  19. package/dist/checkpoint/base.d.ts +47 -0
  20. package/dist/checkpoint/base.js +27 -0
  21. package/dist/checkpoint/index.cjs +8 -0
  22. package/dist/checkpoint/index.d.ts +2 -0
  23. package/dist/checkpoint/index.js +2 -0
  24. package/dist/checkpoint/memory.cjs +35 -0
  25. package/dist/checkpoint/memory.d.ts +8 -0
  26. package/dist/checkpoint/memory.js +31 -0
  27. package/dist/constants.cjs +5 -0
  28. package/dist/constants.d.ts +2 -0
  29. package/dist/constants.js +2 -0
  30. package/dist/graph/graph.cjs +175 -0
  31. package/dist/graph/graph.d.ts +30 -0
  32. package/dist/graph/graph.js +171 -0
  33. package/dist/graph/index.cjs +9 -0
  34. package/dist/graph/index.d.ts +2 -0
  35. package/dist/graph/index.js +2 -0
  36. package/dist/graph/state.cjs +108 -0
  37. package/dist/graph/state.d.ts +17 -0
  38. package/dist/graph/state.js +104 -0
  39. package/dist/index.cjs +8 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +1 -0
  42. package/dist/prebuilt/agent_executor.cjs +96 -0
  43. package/dist/prebuilt/agent_executor.d.ts +12 -0
  44. package/dist/prebuilt/agent_executor.js +92 -0
  45. package/dist/prebuilt/chat_agent_executor.cjs +130 -0
  46. package/dist/prebuilt/chat_agent_executor.d.ts +6 -0
  47. package/dist/prebuilt/chat_agent_executor.js +126 -0
  48. package/dist/prebuilt/index.cjs +9 -0
  49. package/dist/prebuilt/index.d.ts +3 -0
  50. package/dist/prebuilt/index.js +3 -0
  51. package/dist/prebuilt/tool_executor.cjs +63 -0
  52. package/dist/prebuilt/tool_executor.d.ts +27 -0
  53. package/dist/prebuilt/tool_executor.js +59 -0
  54. package/dist/pregel/debug.cjs +46 -0
  55. package/dist/pregel/debug.d.ts +4 -0
  56. package/dist/pregel/debug.js +41 -0
  57. package/dist/pregel/index.cjs +475 -0
  58. package/dist/pregel/index.d.ts +75 -0
  59. package/dist/pregel/index.js +469 -0
  60. package/dist/pregel/io.cjs +57 -0
  61. package/dist/pregel/io.d.ts +9 -0
  62. package/dist/pregel/io.js +52 -0
  63. package/dist/pregel/read.cjs +217 -0
  64. package/dist/pregel/read.d.ts +43 -0
  65. package/dist/pregel/read.js +211 -0
  66. package/dist/pregel/reserved.cjs +7 -0
  67. package/dist/pregel/reserved.d.ts +3 -0
  68. package/dist/pregel/reserved.js +4 -0
  69. package/dist/pregel/validate.cjs +90 -0
  70. package/dist/pregel/validate.d.ts +15 -0
  71. package/dist/pregel/validate.js +85 -0
  72. package/dist/pregel/write.cjs +54 -0
  73. package/dist/pregel/write.d.ts +13 -0
  74. package/dist/pregel/write.js +50 -0
  75. package/index.cjs +1 -0
  76. package/index.d.ts +1 -0
  77. package/index.js +1 -0
  78. package/package.json +100 -0
  79. package/prebuilt.cjs +1 -0
  80. package/prebuilt.d.ts +1 -0
  81. package/prebuilt.js +1 -0
  82. package/pregel.cjs +1 -0
  83. package/pregel.d.ts +1 -0
  84. 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; } });
@@ -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,3 @@
1
+ export { createAgentExecutor } from "./agent_executor.js";
2
+ export { createFunctionCallingExecutor } from "./chat_agent_executor.js";
3
+ export { type ToolExecutorArgs, type ToolInvocationInterface, ToolExecutor, } from "./tool_executor.js";
@@ -0,0 +1,3 @@
1
+ export { createAgentExecutor } from "./agent_executor.js";
2
+ export { createFunctionCallingExecutor } from "./chat_agent_executor.js";
3
+ export { ToolExecutor, } from "./tool_executor.js";
@@ -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;