deepagents 0.0.0 → 0.0.2
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/README.md +3 -136
- package/dist/graph.d.ts +5 -4
- package/dist/graph.js +46 -4
- package/dist/interrupt.d.ts +2 -0
- package/dist/interrupt.js +103 -0
- package/dist/subAgent.d.ts +10 -10
- package/dist/subAgent.js +40 -32
- package/dist/tools.d.ts +7 -9
- package/dist/tools.js +2 -2
- package/dist/types.d.ts +11 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,145 +7,12 @@ Using an LLM to call tools in a loop is the simplest form of an agent. This arch
|
|
|
7
7
|
> ![TIP]
|
|
8
8
|
> Looking for the Python version of this package? See [here: hwchase17/deepagents](https://github.com/hwchase17/deepagents)
|
|
9
9
|
|
|
10
|
-
**Acknowledgements**: This project was primarily inspired by Claude Code, and initially was largely an attempt to see what made Claude Code general purpose, and make it even more so.
|
|
11
|
-
|
|
12
10
|
## Installation
|
|
13
11
|
|
|
14
12
|
```bash
|
|
15
|
-
yarn add
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Usage
|
|
19
|
-
|
|
20
|
-
(To run the example below, you will need to install tavily: `yarn add @langchain/tavily`)
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
import { TavilySearch } from "@langchain/tavily";
|
|
24
|
-
import { createDeepAgent } from "deepagentsjs";
|
|
25
|
-
|
|
26
|
-
// Search tool to use to do research
|
|
27
|
-
const internetSearch = new TavilySearch({
|
|
28
|
-
maxResults: 5,
|
|
29
|
-
tavilyApiKey: process.env.TAVILY_API_KEY,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Prompt prefix to steer the agent to be an expert researcher
|
|
33
|
-
const researchInstructions = `You are an expert researcher. Your job is to conduct thorough research, and then write a polished report.
|
|
34
|
-
|
|
35
|
-
You have access to a few tools.
|
|
36
|
-
|
|
37
|
-
## \`internet_search\`
|
|
38
|
-
|
|
39
|
-
Use this to run an internet search for a given query. You can specify the number of results, the topic, and whether raw content should be included.
|
|
40
|
-
`;
|
|
41
|
-
|
|
42
|
-
// Create the agent
|
|
43
|
-
const agent = createDeepAgent({
|
|
44
|
-
tools: [internetSearch],
|
|
45
|
-
instructions: researchInstructions,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Invoke the agent
|
|
49
|
-
const result = await agent.invoke({
|
|
50
|
-
messages: [{ role: "user", content: "what is langgraph?" }]
|
|
51
|
-
});
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
See `examples/research-agent.ts` for a more complex example.
|
|
55
|
-
|
|
56
|
-
The agent created with `createDeepAgent` is just a LangGraph graph - so you can interact with it (streaming, human-in-the-loop, memory, studio) in the same way you would any LangGraph agent.
|
|
57
|
-
|
|
58
|
-
## Creating a custom deep agent
|
|
59
|
-
|
|
60
|
-
There are three parameters you can pass to `createDeepAgent` to create your own custom deep agent.
|
|
61
|
-
|
|
62
|
-
### tools (Required)
|
|
63
|
-
|
|
64
|
-
The first argument to `createDeepAgent` is tools. This should be a list of LangChain tools. The agent (and any subagents) will have access to these tools.
|
|
65
|
-
|
|
66
|
-
### instructions (Required)
|
|
67
|
-
|
|
68
|
-
The second argument to `createDeepAgent` is instructions. This will serve as part of the prompt of the deep agent. Note that there is a built in system prompt as well, so this is not the entire prompt the agent will see.
|
|
69
|
-
|
|
70
|
-
### subagents (Optional)
|
|
71
|
-
|
|
72
|
-
A keyword-only argument to `createDeepAgent` is subagents. This can be used to specify any custom subagents this deep agent will have access to. You can read more about why you would want to use subagents [here](https://langchain-ai.github.io/deepagents/subagents/)
|
|
73
|
-
|
|
74
|
-
`subagents` should be a list of objects, where each object follows this schema:
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
interface SubAgent {
|
|
78
|
-
name: string;
|
|
79
|
-
description: string;
|
|
80
|
-
prompt: string;
|
|
81
|
-
tools?: string[];
|
|
82
|
-
}
|
|
13
|
+
yarn add deepagents
|
|
83
14
|
```
|
|
84
15
|
|
|
85
|
-
|
|
86
|
-
- **description**: This is the description of the subagent that is shown to the main agent
|
|
87
|
-
- **prompt**: This is the prompt used for the subagent
|
|
88
|
-
- **tools**: This is the list of tools that the subagent has access to. By default will have access to all tools passed in, as well as all built-in tools.
|
|
89
|
-
|
|
90
|
-
To use it looks like:
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
const researchSubAgent = {
|
|
94
|
-
name: "research-agent",
|
|
95
|
-
description: "Used to research more in depth questions",
|
|
96
|
-
prompt: subResearchPrompt,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const subagents = [researchSubAgent];
|
|
100
|
-
|
|
101
|
-
const agent = createDeepAgent({
|
|
102
|
-
tools,
|
|
103
|
-
instructions: prompt,
|
|
104
|
-
subagents
|
|
105
|
-
});
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### model (Optional)
|
|
109
|
-
|
|
110
|
-
By default, `deepagents` will use "claude-sonnet-4-20250514". If you want to use a different model, you can pass a LangChain model object.
|
|
111
|
-
|
|
112
|
-
## Deep Agent Details
|
|
113
|
-
|
|
114
|
-
The below components are built into `deepagents` and helps make it work for deep tasks off-the-shelf.
|
|
115
|
-
|
|
116
|
-
### System Prompt
|
|
117
|
-
|
|
118
|
-
`deepagents` comes with a built-in system prompt. This is relatively detailed prompt that is heavily based on and inspired by attempts to replicate Claude Code's system prompt. It was made more general purpose than Claude Code's system prompt. This contains detailed instructions for how to use the built-in planning tool, file system tools, and sub agents. Note that part of this system prompt can be customized
|
|
119
|
-
|
|
120
|
-
Without this default system prompt - the agent would not be nearly as successful at going as it is. The importance of prompting for creating a "deep" agent cannot be understated.
|
|
121
|
-
|
|
122
|
-
### Planning Tool
|
|
123
|
-
|
|
124
|
-
`deepagents` comes with a built-in planning tool. This planning tool is very simple and is based on ClaudeCode's TodoWrite tool. This tool doesn't actually do anything - it is just a way for the agent to come up with a plan, and then have that in the context to help keep it on track.
|
|
125
|
-
|
|
126
|
-
### File System Tools
|
|
127
|
-
|
|
128
|
-
`deepagents` comes with four built-in file system tools: `ls`, `edit_file`, `read_file`, `write_file`. These do not actually use a file system - rather, they mock out a file system using LangGraph's State object. This means you can easily run many of these agents on the same machine without worrying that they will edit the same underlying files.
|
|
129
|
-
|
|
130
|
-
Right now the "file system" will only be one level deep (no sub directories).
|
|
131
|
-
|
|
132
|
-
These files can be passed in (and also retrieved) by using the `files` key in the LangGraph State object.
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
const agent = createDeepAgent({ ... });
|
|
136
|
-
|
|
137
|
-
const result = await agent.invoke({
|
|
138
|
-
messages: [...],
|
|
139
|
-
// Pass in files to the agent using this key
|
|
140
|
-
// files: {"foo.txt": "foo", ...}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Access any files afterwards like this
|
|
144
|
-
result.files;
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### Sub Agents
|
|
148
|
-
|
|
149
|
-
`deepagents` comes with the built-in ability to call sub agents (based on Claude Code). It has access to a general-purpose subagent at all times - this is a subagent with the same instructions as the main agent and all the tools that is has access to. You can also specify custom sub agents with their own instructions and tools.
|
|
16
|
+
## Learn more
|
|
150
17
|
|
|
151
|
-
|
|
18
|
+
For more information, check out our docs: [https://docs.langchain.com/labs/deep-agents/overview](https://docs.langchain.com/labs/deep-agents/overview)
|
package/dist/graph.d.ts
CHANGED
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
* provided tools, creates task tool using createTaskTool(), and returns createReactAgent
|
|
7
7
|
* with proper configuration. Ensures exact parameter matching and behavior with Python version.
|
|
8
8
|
*/
|
|
9
|
-
import "@langchain/
|
|
10
|
-
import type { CreateDeepAgentParams } from "./types.js";
|
|
9
|
+
import { InteropZodObject } from "@langchain/core/utils/types";
|
|
10
|
+
import type { AnyAnnotationRoot, CreateDeepAgentParams } from "./types.js";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
/**
|
|
13
13
|
* Create a Deep Agent with TypeScript types for all parameters.
|
|
14
14
|
* Combines built-in tools with provided tools, creates task tool using createTaskTool(),
|
|
15
15
|
* and returns createReactAgent with proper configuration.
|
|
16
16
|
* Ensures exact parameter matching and behavior with Python version.
|
|
17
|
+
*
|
|
17
18
|
*/
|
|
18
|
-
export declare function createDeepAgent<StateSchema extends z.ZodObject<any, any, any, any, any
|
|
19
|
+
export declare function createDeepAgent<StateSchema extends z.ZodObject<any, any, any, any, any>, ContextSchema extends AnyAnnotationRoot | InteropZodObject = AnyAnnotationRoot>(params?: CreateDeepAgentParams<StateSchema, ContextSchema>): import("@langchain/langgraph").CompiledStateGraph<import("@langchain/langgraph").StateType<import("@langchain/langgraph/zod").InteropZodToStateDefinition<z.ZodObject<{
|
|
19
20
|
messages: import("@langchain/langgraph/zod").ReducedZodChannel<z.ZodType<import("@langchain/core/messages").BaseMessage[], z.ZodTypeDef, import("@langchain/core/messages").BaseMessage[]>, import("@langchain/core/utils/types").InteropZodType<import("@langchain/langgraph").Messages>>;
|
|
20
21
|
} & {
|
|
21
22
|
todos: import("@langchain/langgraph/zod").ReducedZodChannel<z.ZodType<import("./types.js").Todo[], z.ZodTypeDef, import("./types.js").Todo[]>, import("@langchain/core/utils/types").InteropZodType<import("./types.js").Todo[] | null | undefined>>;
|
|
@@ -124,4 +125,4 @@ export declare function createDeepAgent<StateSchema extends z.ZodObject<any, any
|
|
|
124
125
|
[x: string]: any;
|
|
125
126
|
}>, {
|
|
126
127
|
[x: string]: any;
|
|
127
|
-
}>), import("@langchain/langgraph").StateDefinition>;
|
|
128
|
+
}>), import("@langchain/langgraph").StateDefinition, unknown>;
|
package/dist/graph.js
CHANGED
|
@@ -6,12 +6,28 @@
|
|
|
6
6
|
* provided tools, creates task tool using createTaskTool(), and returns createReactAgent
|
|
7
7
|
* with proper configuration. Ensures exact parameter matching and behavior with Python version.
|
|
8
8
|
*/
|
|
9
|
-
import "@langchain/anthropic/zod";
|
|
9
|
+
// import "@langchain/anthropic/zod";
|
|
10
10
|
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
|
11
11
|
import { createTaskTool } from "./subAgent.js";
|
|
12
12
|
import { getDefaultModel } from "./model.js";
|
|
13
13
|
import { writeTodos, readFile, writeFile, editFile, ls } from "./tools.js";
|
|
14
14
|
import { DeepAgentState } from "./state.js";
|
|
15
|
+
import { createInterruptHook } from "./interrupt.js";
|
|
16
|
+
/**
|
|
17
|
+
* Base prompt that provides instructions about available tools
|
|
18
|
+
* Ported from Python implementation to ensure consistent behavior
|
|
19
|
+
*/
|
|
20
|
+
const BASE_PROMPT = `You have access to a number of standard tools
|
|
21
|
+
|
|
22
|
+
## \`write_todos\`
|
|
23
|
+
|
|
24
|
+
You have access to the \`write_todos\` tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress.
|
|
25
|
+
These tools are also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.
|
|
26
|
+
|
|
27
|
+
It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.
|
|
28
|
+
## \`task\`
|
|
29
|
+
|
|
30
|
+
- When doing web search, prefer to use the \`task\` tool in order to reduce context usage.`;
|
|
15
31
|
/**
|
|
16
32
|
* Built-in tools that are always available in Deep Agents
|
|
17
33
|
*/
|
|
@@ -27,14 +43,19 @@ const BUILTIN_TOOLS = [
|
|
|
27
43
|
* Combines built-in tools with provided tools, creates task tool using createTaskTool(),
|
|
28
44
|
* and returns createReactAgent with proper configuration.
|
|
29
45
|
* Ensures exact parameter matching and behavior with Python version.
|
|
46
|
+
*
|
|
30
47
|
*/
|
|
31
48
|
export function createDeepAgent(params = {}) {
|
|
32
|
-
const { tools = [], instructions, model = getDefaultModel(), subagents = [], } = params;
|
|
49
|
+
const { tools = [], instructions, model = getDefaultModel(), subagents = [], postModelHook, contextSchema, interruptConfig = {}, builtinTools, } = params;
|
|
33
50
|
const stateSchema = params.stateSchema
|
|
34
51
|
? DeepAgentState.extend(params.stateSchema.shape)
|
|
35
52
|
: DeepAgentState;
|
|
53
|
+
// Filter built-in tools if builtinTools parameter is provided
|
|
54
|
+
const selectedBuiltinTools = builtinTools
|
|
55
|
+
? BUILTIN_TOOLS.filter((tool) => builtinTools.some((bt) => bt === tool.name))
|
|
56
|
+
: BUILTIN_TOOLS;
|
|
36
57
|
// Combine built-in tools with provided tools
|
|
37
|
-
const allTools = [...
|
|
58
|
+
const allTools = [...selectedBuiltinTools, ...tools];
|
|
38
59
|
// Create task tool using createTaskTool() if subagents are provided
|
|
39
60
|
if (subagents.length > 0) {
|
|
40
61
|
// Create tools map for task tool creation
|
|
@@ -52,11 +73,32 @@ export function createDeepAgent(params = {}) {
|
|
|
52
73
|
});
|
|
53
74
|
allTools.push(taskTool);
|
|
54
75
|
}
|
|
76
|
+
// Combine instructions with base prompt like Python implementation
|
|
77
|
+
const finalInstructions = instructions
|
|
78
|
+
? instructions + BASE_PROMPT
|
|
79
|
+
: BASE_PROMPT;
|
|
80
|
+
// Should never be the case that both are specified
|
|
81
|
+
if (postModelHook && Object.keys(interruptConfig).length > 0) {
|
|
82
|
+
throw new Error("Cannot specify both postModelHook and interruptConfig together. " +
|
|
83
|
+
"Use either interruptConfig for tool interrupts or postModelHook for custom post-processing.");
|
|
84
|
+
}
|
|
85
|
+
let selectedPostModelHook;
|
|
86
|
+
if (postModelHook !== undefined) {
|
|
87
|
+
selectedPostModelHook = postModelHook;
|
|
88
|
+
}
|
|
89
|
+
else if (Object.keys(interruptConfig).length > 0) {
|
|
90
|
+
selectedPostModelHook = createInterruptHook(interruptConfig);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
selectedPostModelHook = undefined;
|
|
94
|
+
}
|
|
55
95
|
// Return createReactAgent with proper configuration
|
|
56
96
|
return createReactAgent({
|
|
57
97
|
llm: model,
|
|
58
98
|
tools: allTools,
|
|
59
99
|
stateSchema,
|
|
60
|
-
messageModifier:
|
|
100
|
+
messageModifier: finalInstructions,
|
|
101
|
+
contextSchema,
|
|
102
|
+
postModelHook: selectedPostModelHook,
|
|
61
103
|
});
|
|
62
104
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { interrupt } from "@langchain/langgraph";
|
|
2
|
+
import { isAIMessage, AIMessage, ToolMessage } from "@langchain/core/messages";
|
|
3
|
+
export function createInterruptHook(toolConfigs, messagePrefix = "Tool execution requires approval") {
|
|
4
|
+
/**
|
|
5
|
+
* Create a post model hook that handles interrupts using native LangGraph schemas.
|
|
6
|
+
*
|
|
7
|
+
* Args:
|
|
8
|
+
* toolConfigs: Record mapping tool names to HumanInterruptConfig objects
|
|
9
|
+
* messagePrefix: Optional message prefix for interrupt descriptions
|
|
10
|
+
*/
|
|
11
|
+
Object.entries(toolConfigs).forEach(([tool, interruptConfig]) => {
|
|
12
|
+
if (interruptConfig &&
|
|
13
|
+
typeof interruptConfig === "object" &&
|
|
14
|
+
interruptConfig.allow_ignore) {
|
|
15
|
+
throw new Error(`For ${tool} we get allow_ignore = true - we currently don't support ignore.`);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
return async function interruptHook(state) {
|
|
19
|
+
const messages = state.messages || [];
|
|
20
|
+
if (!messages.length) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const lastMessage = messages[messages.length - 1];
|
|
24
|
+
if (!isAIMessage(lastMessage) ||
|
|
25
|
+
!lastMessage.tool_calls ||
|
|
26
|
+
!lastMessage.tool_calls.length) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const interruptToolCalls = [];
|
|
30
|
+
const autoApprovedToolCalls = [];
|
|
31
|
+
for (const toolCall of lastMessage.tool_calls) {
|
|
32
|
+
const toolName = toolCall.name;
|
|
33
|
+
if (toolName in toolConfigs) {
|
|
34
|
+
interruptToolCalls.push(toolCall);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
autoApprovedToolCalls.push(toolCall);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!interruptToolCalls.length) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const approvedToolCalls = [...autoApprovedToolCalls];
|
|
44
|
+
if (interruptToolCalls.length > 1) {
|
|
45
|
+
throw new Error("Right now, interrupt hook only works when one tool requires interrupts");
|
|
46
|
+
}
|
|
47
|
+
const toolCall = interruptToolCalls[0];
|
|
48
|
+
const toolName = toolCall.name;
|
|
49
|
+
const toolArgs = toolCall.args;
|
|
50
|
+
const description = `${messagePrefix}\n\nTool: ${toolName}\nArgs: ${JSON.stringify(toolArgs, null, 2)}`;
|
|
51
|
+
const toolConfig = toolConfigs[toolName];
|
|
52
|
+
const defaultToolConfig = {
|
|
53
|
+
allow_accept: true,
|
|
54
|
+
allow_edit: true,
|
|
55
|
+
allow_respond: true,
|
|
56
|
+
allow_ignore: false,
|
|
57
|
+
};
|
|
58
|
+
const request = {
|
|
59
|
+
action_request: {
|
|
60
|
+
action: toolName,
|
|
61
|
+
args: toolArgs,
|
|
62
|
+
},
|
|
63
|
+
config: typeof toolConfig === "object" ? toolConfig : defaultToolConfig,
|
|
64
|
+
description: description,
|
|
65
|
+
};
|
|
66
|
+
const res = await interrupt([request]);
|
|
67
|
+
const responses = Array.isArray(res) ? res : [res];
|
|
68
|
+
if (responses.length !== 1) {
|
|
69
|
+
throw new Error(`Expected a list of one response, got ${responses}`);
|
|
70
|
+
}
|
|
71
|
+
const response = responses[0];
|
|
72
|
+
if (response.type === "accept") {
|
|
73
|
+
approvedToolCalls.push(toolCall);
|
|
74
|
+
}
|
|
75
|
+
else if (response.type === "edit") {
|
|
76
|
+
const edited = response.args;
|
|
77
|
+
const newToolCall = {
|
|
78
|
+
name: edited.action,
|
|
79
|
+
args: edited.args,
|
|
80
|
+
id: toolCall.id,
|
|
81
|
+
};
|
|
82
|
+
approvedToolCalls.push(newToolCall);
|
|
83
|
+
}
|
|
84
|
+
else if (response.type === "response") {
|
|
85
|
+
if (!toolCall.id) {
|
|
86
|
+
throw new Error("Tool call must have an ID for response type");
|
|
87
|
+
}
|
|
88
|
+
const responseMessage = new ToolMessage({
|
|
89
|
+
tool_call_id: toolCall.id,
|
|
90
|
+
content: response.args,
|
|
91
|
+
});
|
|
92
|
+
return { messages: [responseMessage] };
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
throw new Error(`Unknown response type: ${response.type}`);
|
|
96
|
+
}
|
|
97
|
+
const updatedLastMessage = new AIMessage({
|
|
98
|
+
...lastMessage,
|
|
99
|
+
tool_calls: approvedToolCalls,
|
|
100
|
+
});
|
|
101
|
+
return { messages: [updatedLastMessage] };
|
|
102
|
+
};
|
|
103
|
+
}
|
package/dist/subAgent.d.ts
CHANGED
|
@@ -22,20 +22,20 @@ export declare function createTaskTool<StateSchema extends z.ZodObject<any, any,
|
|
|
22
22
|
model: LanguageModelLike;
|
|
23
23
|
stateSchema: StateSchema;
|
|
24
24
|
}): import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
description: z.ZodString;
|
|
26
|
+
subagent_type: z.ZodString;
|
|
27
27
|
}, "strip", z.ZodTypeAny, {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
description: string;
|
|
29
|
+
subagent_type: string;
|
|
30
30
|
}, {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
description: string;
|
|
32
|
+
subagent_type: string;
|
|
33
33
|
}>, {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
description: string;
|
|
35
|
+
subagent_type: string;
|
|
36
36
|
}, {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
description: string;
|
|
38
|
+
subagent_type: string;
|
|
39
39
|
}, string | Command<unknown, {
|
|
40
40
|
messages: ToolMessage[];
|
|
41
41
|
}, string>>;
|
package/dist/subAgent.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { getDefaultModel } from "./model.js";
|
|
15
15
|
import { writeTodos, readFile, writeFile, editFile, ls } from "./tools.js";
|
|
16
|
+
import { TASK_DESCRIPTION_PREFIX, TASK_DESCRIPTION_SUFFIX } from "./prompts.js";
|
|
16
17
|
/**
|
|
17
18
|
* Built-in tools map for tool resolution by name
|
|
18
19
|
*/
|
|
@@ -30,20 +31,11 @@ const BUILTIN_TOOLS = {
|
|
|
30
31
|
*/
|
|
31
32
|
export function createTaskTool(inputs) {
|
|
32
33
|
const { subagents, tools = {}, model = getDefaultModel(), stateSchema, } = inputs;
|
|
33
|
-
// Create agents map from subagents array
|
|
34
|
-
const agentsMap = new Map();
|
|
35
|
-
for (const subagent of subagents) {
|
|
36
|
-
agentsMap.set(subagent.name, subagent);
|
|
37
|
-
}
|
|
38
34
|
// Combine built-in tools with provided tools for tool resolution
|
|
39
35
|
const allTools = { ...BUILTIN_TOOLS, ...tools };
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const subagent = agentsMap.get(agent_name);
|
|
44
|
-
if (!subagent) {
|
|
45
|
-
return `Error: Agent '${agent_name}' not found. Available agents: ${Array.from(agentsMap.keys()).join(", ")}`;
|
|
46
|
-
}
|
|
36
|
+
// Pre-create all agents like Python does
|
|
37
|
+
const agentsMap = new Map();
|
|
38
|
+
for (const subagent of subagents) {
|
|
47
39
|
// Resolve tools by name for this subagent
|
|
48
40
|
const subagentTools = [];
|
|
49
41
|
if (subagent.tools) {
|
|
@@ -54,37 +46,53 @@ export function createTaskTool(inputs) {
|
|
|
54
46
|
}
|
|
55
47
|
else {
|
|
56
48
|
// eslint-disable-next-line no-console
|
|
57
|
-
console.warn(`Warning: Tool '${toolName}' not found for agent '${
|
|
49
|
+
console.warn(`Warning: Tool '${toolName}' not found for agent '${subagent.name}'`);
|
|
58
50
|
}
|
|
59
51
|
}
|
|
60
52
|
}
|
|
53
|
+
else {
|
|
54
|
+
// If no tools specified, use all tools like Python does
|
|
55
|
+
subagentTools.push(...Object.values(allTools));
|
|
56
|
+
}
|
|
57
|
+
// Create react agent for the subagent (pre-create like Python)
|
|
58
|
+
const reactAgent = createReactAgent({
|
|
59
|
+
llm: model,
|
|
60
|
+
tools: subagentTools,
|
|
61
|
+
stateSchema,
|
|
62
|
+
messageModifier: subagent.prompt,
|
|
63
|
+
});
|
|
64
|
+
agentsMap.set(subagent.name, reactAgent);
|
|
65
|
+
}
|
|
66
|
+
return tool(async (input, config) => {
|
|
67
|
+
const { description, subagent_type } = input;
|
|
68
|
+
// Get the pre-created agent
|
|
69
|
+
const reactAgent = agentsMap.get(subagent_type);
|
|
70
|
+
if (!reactAgent) {
|
|
71
|
+
return `Error: Agent '${subagent_type}' not found. Available agents: ${Array.from(agentsMap.keys()).join(", ")}`;
|
|
72
|
+
}
|
|
61
73
|
try {
|
|
62
|
-
// Create react agent for the subagent
|
|
63
|
-
const reactAgent = createReactAgent({
|
|
64
|
-
llm: model,
|
|
65
|
-
tools: subagentTools,
|
|
66
|
-
stateSchema,
|
|
67
|
-
messageModifier: subagent.prompt,
|
|
68
|
-
});
|
|
69
74
|
// Get current state for context
|
|
70
75
|
const currentState = getCurrentTaskInput();
|
|
71
|
-
//
|
|
72
|
-
const
|
|
76
|
+
// Modify state messages like Python does
|
|
77
|
+
const modifiedState = {
|
|
73
78
|
...currentState,
|
|
74
79
|
messages: [
|
|
75
80
|
{
|
|
76
81
|
role: "user",
|
|
77
|
-
content:
|
|
82
|
+
content: description,
|
|
78
83
|
},
|
|
79
84
|
],
|
|
80
|
-
}
|
|
85
|
+
};
|
|
86
|
+
// Execute the subagent with the task
|
|
87
|
+
const result = await reactAgent.invoke(modifiedState, config);
|
|
81
88
|
// Use Command for state updates and navigation between agents
|
|
89
|
+
// Return the result using Command to properly handle subgraph state
|
|
82
90
|
return new Command({
|
|
83
91
|
update: {
|
|
84
|
-
|
|
92
|
+
files: result.files || {},
|
|
85
93
|
messages: [
|
|
86
94
|
new ToolMessage({
|
|
87
|
-
content:
|
|
95
|
+
content: result.messages?.slice(-1)[0]?.content || "Task completed",
|
|
88
96
|
tool_call_id: config.toolCall?.id,
|
|
89
97
|
}),
|
|
90
98
|
],
|
|
@@ -98,7 +106,7 @@ export function createTaskTool(inputs) {
|
|
|
98
106
|
update: {
|
|
99
107
|
messages: [
|
|
100
108
|
new ToolMessage({
|
|
101
|
-
content: `Error executing task '${
|
|
109
|
+
content: `Error executing task '${description}' with agent '${subagent_type}': ${errorMessage}`,
|
|
102
110
|
tool_call_id: config.toolCall?.id,
|
|
103
111
|
}),
|
|
104
112
|
],
|
|
@@ -107,14 +115,14 @@ export function createTaskTool(inputs) {
|
|
|
107
115
|
}
|
|
108
116
|
}, {
|
|
109
117
|
name: "task",
|
|
110
|
-
description:
|
|
118
|
+
description: TASK_DESCRIPTION_PREFIX.replace("{other_agents}", subagents.map((a) => `- ${a.name}: ${a.description}`).join("\n")) + TASK_DESCRIPTION_SUFFIX,
|
|
111
119
|
schema: z.object({
|
|
112
|
-
|
|
113
|
-
.string()
|
|
114
|
-
.describe(`Name of the agent to use. Available: ${subagents.map((a) => a.name).join(", ")}`),
|
|
115
|
-
task: z
|
|
120
|
+
description: z
|
|
116
121
|
.string()
|
|
117
122
|
.describe("The task to execute with the selected agent"),
|
|
123
|
+
subagent_type: z
|
|
124
|
+
.string()
|
|
125
|
+
.describe(`Name of the agent to use. Available: ${subagents.map((a) => a.name).join(", ")}`),
|
|
118
126
|
}),
|
|
119
127
|
});
|
|
120
128
|
}
|
package/dist/tools.d.ts
CHANGED
|
@@ -43,15 +43,13 @@ export declare const writeTodos: import("@langchain/core/tools").DynamicStructur
|
|
|
43
43
|
status: "pending" | "in_progress" | "completed";
|
|
44
44
|
content: string;
|
|
45
45
|
}[];
|
|
46
|
-
}, {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
};
|
|
54
|
-
}>;
|
|
46
|
+
}, Command<unknown, {
|
|
47
|
+
todos: {
|
|
48
|
+
status: "pending" | "in_progress" | "completed";
|
|
49
|
+
content: string;
|
|
50
|
+
}[];
|
|
51
|
+
messages: ToolMessage[];
|
|
52
|
+
}, string>>;
|
|
55
53
|
/**
|
|
56
54
|
* List files tool - returns list of files from state.files
|
|
57
55
|
* Equivalent to Python's ls function
|
package/dist/tools.js
CHANGED
|
@@ -15,7 +15,7 @@ import { WRITE_TODOS_DESCRIPTION, EDIT_DESCRIPTION, TOOL_DESCRIPTION, } from "./
|
|
|
15
15
|
* Uses getCurrentTaskInput() instead of Python's InjectedState
|
|
16
16
|
*/
|
|
17
17
|
export const writeTodos = tool((input, config) => {
|
|
18
|
-
return {
|
|
18
|
+
return new Command({
|
|
19
19
|
update: {
|
|
20
20
|
todos: input.todos,
|
|
21
21
|
messages: [
|
|
@@ -25,7 +25,7 @@ export const writeTodos = tool((input, config) => {
|
|
|
25
25
|
}),
|
|
26
26
|
],
|
|
27
27
|
},
|
|
28
|
-
};
|
|
28
|
+
});
|
|
29
29
|
}, {
|
|
30
30
|
name: "write_todos",
|
|
31
31
|
description: WRITE_TODOS_DESCRIPTION,
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ import type { StructuredTool } from "@langchain/core/tools";
|
|
|
11
11
|
import type { DeepAgentState } from "./state.js";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { Runnable } from "@langchain/core/runnables";
|
|
14
|
+
import { AnnotationRoot } from "@langchain/langgraph";
|
|
15
|
+
import { InteropZodObject } from "@langchain/core/utils/types";
|
|
16
|
+
import type { HumanInterruptConfig } from "@langchain/langgraph/prebuilt";
|
|
17
|
+
export type AnyAnnotationRoot = AnnotationRoot<any>;
|
|
14
18
|
export type InferZodObjectShape<T> = T extends z.ZodObject<infer Shape> ? Shape : never;
|
|
15
19
|
/**
|
|
16
20
|
* SubAgent interface matching Python's TypedDict structure
|
|
@@ -28,19 +32,19 @@ export interface Todo {
|
|
|
28
32
|
}
|
|
29
33
|
export type DeepAgentStateType = z.infer<typeof DeepAgentState>;
|
|
30
34
|
export type LanguageModelLike = Runnable<BaseLanguageModelInput, LanguageModelOutput>;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export interface CreateDeepAgentParams<StateSchema extends z.ZodObject<any, any, any, any, any>> {
|
|
35
|
+
export type PostModelHook = (state: DeepAgentStateType, model: LanguageModelLike) => Promise<Partial<DeepAgentStateType> | void>;
|
|
36
|
+
export type ToolInterruptConfig = Record<string, HumanInterruptConfig | boolean>;
|
|
37
|
+
export interface CreateDeepAgentParams<StateSchema extends z.ZodObject<any, any, any, any, any>, ContextSchema extends AnyAnnotationRoot | InteropZodObject = AnyAnnotationRoot> {
|
|
35
38
|
tools?: StructuredTool[];
|
|
36
39
|
instructions?: string;
|
|
37
40
|
model?: LanguageModelLike;
|
|
38
41
|
subagents?: SubAgent[];
|
|
39
42
|
stateSchema?: StateSchema;
|
|
43
|
+
contextSchema?: ContextSchema;
|
|
44
|
+
postModelHook?: PostModelHook;
|
|
45
|
+
interruptConfig?: ToolInterruptConfig;
|
|
46
|
+
builtinTools?: string[];
|
|
40
47
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Parameters for createTaskTool function
|
|
43
|
-
*/
|
|
44
48
|
export interface CreateTaskToolParams<StateSchema extends z.ZodObject<any, any, any, any, any>> {
|
|
45
49
|
subagents: SubAgent[];
|
|
46
50
|
tools?: Record<string, StructuredTool>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepagents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Deep Agents - a library for building controllable AI agents with LangGraph",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@langchain/anthropic": "^0.3.25",
|
|
43
43
|
"@langchain/core": "^0.3.66",
|
|
44
|
-
"@langchain/langgraph": "^0.4.
|
|
44
|
+
"@langchain/langgraph": "^0.4.6",
|
|
45
45
|
"zod": "^3.25.32"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|