Skip to content

Commit a015ab4

Browse files
feat(adk): deepagents support default filesystem tools
1 parent 679b911 commit a015ab4

File tree

7 files changed

+103
-53
lines changed

7 files changed

+103
-53
lines changed

adk/middlewares/filesystem/filesystem.go

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,19 @@ import (
3737
// Config is the configuration for the filesystem middleware
3838
type Config struct {
3939
// Backend provides filesystem operations used by tools and offloading.
40-
// required
40+
// If set, filesystem tools (read_file, write_file, edit_file, glob, grep) will be registered.
41+
// At least one of Backend, Shell, or StreamingShell must be set.
4142
Backend filesystem.Backend
4243

4344
// Shell provides shell command execution capability.
4445
// If set, an execute tool will be registered to support shell command execution.
45-
// optional, mutually exclusive with StreamingShell
46+
// At least one of Backend, Shell, or StreamingShell must be set.
47+
// Mutually exclusive with StreamingShell.
4648
Shell filesystem.Shell
4749
// StreamingShell provides streaming shell command execution capability.
48-
// If set, a streaming execute tool will be registered for real-time output.
49-
// optional, mutually exclusive with Shell
50+
// If set, a streaming execute tool will be registered to support streaming shell command execution.
51+
// At least one of Backend, Shell, or StreamingShell must be set.
52+
// Mutually exclusive with Shell.
5053
StreamingShell filesystem.StreamingShell
5154

5255
// WithoutLargeToolResultOffloading disables automatic offloading of large tool result to Backend
@@ -90,8 +93,8 @@ func (c *Config) Validate() error {
9093
if c == nil {
9194
return errors.New("config should not be nil")
9295
}
93-
if c.Backend == nil {
94-
return errors.New("backend should not be nil")
96+
if c.Backend == nil && c.Shell == nil && c.StreamingShell == nil {
97+
return errors.New("at least one of Backend, Shell, or StreamingShell must be set")
9598
}
9699
if c.StreamingShell != nil && c.Shell != nil {
97100
return errors.New("shell and streaming shell should not be both set")
@@ -101,7 +104,7 @@ func (c *Config) Validate() error {
101104

102105
// NewMiddleware constructs and returns the filesystem middleware.
103106
//
104-
// Deprecated: Use NewChatModelAgentMiddleware instead. NewChatModelAgentMiddleware returns
107+
// Deprecated: Use New instead. New returns
105108
// a ChatModelAgentMiddleware which provides better context propagation through wrapper methods
106109
// and is the recommended approach for new code. See ChatModelAgentMiddleware documentation
107110
// for details on the benefits over AgentMiddleware.
@@ -294,6 +297,27 @@ func (m *filesystemMiddleware) WrapStreamableToolCall(ctx context.Context, endpo
294297

295298
func getFilesystemTools(_ context.Context, validatedConfig *Config) ([]tool.BaseTool, error) {
296299
var tools []tool.BaseTool
300+
var err error
301+
302+
if validatedConfig.StreamingShell != nil {
303+
var executeTool tool.BaseTool
304+
executeTool, err = newStreamingExecuteTool(validatedConfig.StreamingShell, validatedConfig.CustomExecuteToolDesc)
305+
if err != nil {
306+
return nil, err
307+
}
308+
tools = append(tools, executeTool)
309+
} else if validatedConfig.Shell != nil {
310+
var executeTool tool.BaseTool
311+
executeTool, err = newExecuteTool(validatedConfig.Shell, validatedConfig.CustomExecuteToolDesc)
312+
if err != nil {
313+
return nil, err
314+
}
315+
tools = append(tools, executeTool)
316+
}
317+
318+
if validatedConfig.Backend == nil {
319+
return tools, nil
320+
}
297321

298322
lsTool, err := newLsTool(validatedConfig.Backend, validatedConfig.CustomLsToolDesc)
299323
if err != nil {
@@ -331,22 +355,6 @@ func getFilesystemTools(_ context.Context, validatedConfig *Config) ([]tool.Base
331355
}
332356
tools = append(tools, grepTool)
333357

334-
if validatedConfig.StreamingShell != nil {
335-
var executeTool tool.BaseTool
336-
executeTool, err = newStreamingExecuteTool(validatedConfig.StreamingShell, validatedConfig.CustomExecuteToolDesc)
337-
if err != nil {
338-
return nil, err
339-
}
340-
tools = append(tools, executeTool)
341-
} else if validatedConfig.Shell != nil {
342-
var executeTool tool.BaseTool
343-
executeTool, err = newExecuteTool(validatedConfig.Shell, validatedConfig.CustomExecuteToolDesc)
344-
if err != nil {
345-
return nil, err
346-
}
347-
tools = append(tools, executeTool)
348-
}
349-
350358
return tools, nil
351359
}
352360

adk/middlewares/filesystem/filesystem_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ func TestNewMiddleware(t *testing.T) {
532532
t.Run("nil backend returns error", func(t *testing.T) {
533533
_, err := NewMiddleware(ctx, &Config{Backend: nil})
534534
assert.Error(t, err)
535-
assert.Contains(t, err.Error(), "backend should not be nil")
535+
assert.Contains(t, err.Error(), "at least one of Backend, Shell, or StreamingShell must be set")
536536
})
537537

538538
t.Run("valid config with default settings", func(t *testing.T) {
@@ -649,7 +649,7 @@ func TestNew(t *testing.T) {
649649
t.Run("nil backend returns error", func(t *testing.T) {
650650
_, err := New(ctx, &Config{Backend: nil})
651651
assert.Error(t, err)
652-
assert.Contains(t, err.Error(), "backend should not be nil")
652+
assert.Contains(t, err.Error(), "at least one of Backend, Shell, or StreamingShell must be set")
653653
})
654654

655655
t.Run("valid config with default settings", func(t *testing.T) {

adk/prebuilt/deep/deep.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ import (
2424
"github.com/bytedance/sonic"
2525

2626
"github.com/cloudwego/eino/adk"
27+
"github.com/cloudwego/eino/adk/filesystem"
2728
"github.com/cloudwego/eino/adk/internal"
29+
filesystem2 "github.com/cloudwego/eino/adk/middlewares/filesystem"
2830
"github.com/cloudwego/eino/components/model"
29-
"github.com/cloudwego/eino/components/tool"
3031
"github.com/cloudwego/eino/components/tool/utils"
3132
"github.com/cloudwego/eino/schema"
3233
)
@@ -58,6 +59,10 @@ type Config struct {
5859
// MaxIteration limits the maximum number of reasoning iterations the agent can perform.
5960
MaxIteration int
6061

62+
Backend filesystem.Backend
63+
Shell filesystem.Shell
64+
StreamingShell filesystem.StreamingShell
65+
6166
// WithoutWriteTodos disables the built-in write_todos tool when set to true.
6267
WithoutWriteTodos bool
6368
// WithoutGeneralSubAgent disables the general-purpose subagent when set to true.
@@ -89,7 +94,7 @@ type Config struct {
8994
// This function initializes built-in tools, creates a task tool for subagent orchestration,
9095
// and returns a fully configured ChatModelAgent ready for execution.
9196
func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) {
92-
middlewares, err := buildBuiltinAgentMiddlewares(cfg.WithoutWriteTodos)
97+
handlers, err := buildBuiltinAgentMiddlewares(ctx, cfg)
9398
if err != nil {
9499
return nil, err
95100
}
@@ -116,12 +121,13 @@ func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) {
116121
instruction,
117122
cfg.ToolsConfig,
118123
cfg.MaxIteration,
119-
append(middlewares, cfg.Middlewares...),
124+
cfg.Middlewares,
125+
append(handlers, cfg.Handlers...),
120126
)
121127
if err != nil {
122128
return nil, fmt.Errorf("failed to new task tool: %w", err)
123129
}
124-
middlewares = append(middlewares, tt)
130+
handlers = append(handlers, tt)
125131
}
126132

127133
return adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
@@ -131,8 +137,8 @@ func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) {
131137
Model: cfg.ChatModel,
132138
ToolsConfig: cfg.ToolsConfig,
133139
MaxIterations: cfg.MaxIteration,
134-
Middlewares: append(middlewares, cfg.Middlewares...),
135-
Handlers: cfg.Handlers,
140+
Middlewares: cfg.Middlewares,
141+
Handlers: append(handlers, cfg.Handlers...),
136142

137143
GenModelInput: genModelInput,
138144
ModelRetryConfig: cfg.ModelRetryConfig,
@@ -152,16 +158,29 @@ func genModelInput(ctx context.Context, instruction string, input *adk.AgentInpu
152158
return msgs, nil
153159
}
154160

155-
func buildBuiltinAgentMiddlewares(withoutWriteTodos bool) ([]adk.AgentMiddleware, error) {
156-
var ms []adk.AgentMiddleware
157-
if !withoutWriteTodos {
161+
func buildBuiltinAgentMiddlewares(ctx context.Context, cfg *Config) ([]adk.ChatModelAgentMiddleware, error) {
162+
var ms []adk.ChatModelAgentMiddleware
163+
if !cfg.WithoutWriteTodos {
158164
t, err := newWriteTodos()
159165
if err != nil {
160166
return nil, err
161167
}
162168
ms = append(ms, t)
163169
}
164170

171+
if cfg.Backend != nil || cfg.Shell != nil || cfg.StreamingShell != nil {
172+
fm, err := filesystem2.New(ctx, &filesystem2.Config{
173+
Backend: cfg.Backend,
174+
Shell: cfg.Shell,
175+
StreamingShell: cfg.StreamingShell,
176+
WithoutLargeToolResultOffloading: true,
177+
})
178+
if err != nil {
179+
return nil, err
180+
}
181+
ms = append(ms, fm)
182+
}
183+
165184
return ms, nil
166185
}
167186

@@ -175,27 +194,27 @@ type writeTodosArguments struct {
175194
Todos []TODO `json:"todos"`
176195
}
177196

178-
func newWriteTodos() (adk.AgentMiddleware, error) {
197+
func newWriteTodos() (adk.ChatModelAgentMiddleware, error) {
179198
toolDesc, err := internal.SelectPrompt(internal.I18nPrompts{
180199
English: writeTodosToolDescription,
181200
Chinese: writeTodosToolDescriptionChinese,
182201
})
183202
if err != nil {
184-
return adk.AgentMiddleware{}, err
203+
return nil, err
185204
}
186205
prompt, err := internal.SelectPrompt(internal.I18nPrompts{
187206
English: writeTodosPrompt,
188207
Chinese: writeTodosPromptChinese,
189208
})
190209
if err != nil {
191-
return adk.AgentMiddleware{}, err
210+
return nil, err
192211
}
193212
resultMsg, err := internal.SelectPrompt(internal.I18nPrompts{
194213
English: "Updated todo list to %s",
195214
Chinese: "已更新待办列表为 %s",
196215
})
197216
if err != nil {
198-
return adk.AgentMiddleware{}, err
217+
return nil, err
199218
}
200219

201220
t, err := utils.InferTool("write_todos", toolDesc, func(ctx context.Context, input writeTodosArguments) (output string, err error) {
@@ -207,11 +226,8 @@ func newWriteTodos() (adk.AgentMiddleware, error) {
207226
return fmt.Sprintf(resultMsg, todos), nil
208227
})
209228
if err != nil {
210-
return adk.AgentMiddleware{}, err
229+
return nil, err
211230
}
212231

213-
return adk.AgentMiddleware{
214-
AdditionalInstruction: prompt,
215-
AdditionalTools: []tool.BaseTool{t},
216-
}, nil
232+
return buildAppendPromptTool(prompt, t), nil
217233
}

adk/prebuilt/deep/deep_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ func TestGenModelInput(t *testing.T) {
6767
}
6868

6969
func TestWriteTodos(t *testing.T) {
70-
m, err := buildBuiltinAgentMiddlewares(false)
70+
m, err := buildBuiltinAgentMiddlewares(context.Background(), &Config{WithoutWriteTodos: false})
7171
assert.NoError(t, err)
7272

73-
wt := m[0].AdditionalTools[0].(tool.InvokableTool)
73+
wt := m[0].(*appendPromptTool).t.(tool.InvokableTool)
7474

7575
todos := `[{"content":"content1","activeForm":"","status":"pending"},{"content":"content2","activeForm":"","status":"pending"}]`
7676
args := fmt.Sprintf(`{"todos": %s}`, todos)

adk/prebuilt/deep/task_tool.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,21 @@ func newTaskToolMiddleware(
4444
toolsConfig adk.ToolsConfig,
4545
maxIteration int,
4646
middlewares []adk.AgentMiddleware,
47-
) (adk.AgentMiddleware, error) {
48-
t, err := newTaskTool(ctx, taskToolDescriptionGenerator, subAgents, withoutGeneralSubAgent, cm, instruction, toolsConfig, maxIteration, middlewares)
47+
handlers []adk.ChatModelAgentMiddleware,
48+
) (adk.ChatModelAgentMiddleware, error) {
49+
t, err := newTaskTool(ctx, taskToolDescriptionGenerator, subAgents, withoutGeneralSubAgent, cm, instruction, toolsConfig, maxIteration, middlewares, handlers)
4950
if err != nil {
50-
return adk.AgentMiddleware{}, err
51+
return nil, err
5152
}
5253
prompt, err := internal.SelectPrompt(internal.I18nPrompts{
5354
English: taskPrompt,
5455
Chinese: taskPromptChinese,
5556
})
5657
if err != nil {
57-
return adk.AgentMiddleware{}, err
58+
return nil, err
5859
}
5960

60-
return adk.AgentMiddleware{
61-
AdditionalInstruction: prompt,
62-
AdditionalTools: []tool.BaseTool{t},
63-
}, nil
61+
return buildAppendPromptTool(prompt, t), nil
6462
}
6563

6664
func newTaskTool(
@@ -75,6 +73,7 @@ func newTaskTool(
7573
ToolsConfig adk.ToolsConfig,
7674
MaxIteration int,
7775
middlewares []adk.AgentMiddleware,
76+
handlers []adk.ChatModelAgentMiddleware,
7877
) (tool.InvokableTool, error) {
7978
t := &taskTool{
8079
subAgents: map[string]tool.InvokableTool{},
@@ -102,6 +101,7 @@ func newTaskTool(
102101
ToolsConfig: ToolsConfig,
103102
MaxIterations: MaxIteration,
104103
Middlewares: middlewares,
104+
Handlers: handlers,
105105
GenModelInput: genModelInput,
106106
})
107107
if err != nil {

adk/prebuilt/deep/task_tool_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func TestTaskTool(t *testing.T) {
4040
adk.ToolsConfig{},
4141
10,
4242
nil,
43+
nil,
4344
)
4445
assert.NoError(t, err)
4546

adk/prebuilt/deep/types.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package deep
1818

1919
import (
20+
"context"
2021
"fmt"
2122

23+
"github.com/cloudwego/eino/adk"
2224
"github.com/cloudwego/eino/components/tool"
2325
)
2426

@@ -38,3 +40,26 @@ func assertAgentTool(t tool.BaseTool) (tool.InvokableTool, error) {
3840
}
3941
return it, nil
4042
}
43+
44+
func buildAppendPromptTool(prompt string, t tool.BaseTool) adk.ChatModelAgentMiddleware {
45+
return &appendPromptTool{
46+
BaseChatModelAgentMiddleware: &adk.BaseChatModelAgentMiddleware{},
47+
t: t,
48+
prompt: prompt,
49+
}
50+
}
51+
52+
type appendPromptTool struct {
53+
*adk.BaseChatModelAgentMiddleware
54+
t tool.BaseTool
55+
prompt string
56+
}
57+
58+
func (w *appendPromptTool) BeforeAgent(ctx context.Context, runCtx *adk.ChatModelAgentContext) (context.Context, *adk.ChatModelAgentContext, error) {
59+
nRunCtx := *runCtx
60+
nRunCtx.Instruction += w.prompt
61+
if w.t != nil {
62+
nRunCtx.Tools = append(nRunCtx.Tools, w.t)
63+
}
64+
return ctx, &nRunCtx, nil
65+
}

0 commit comments

Comments
 (0)