Skip to content

Commit 1401dd0

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

File tree

7 files changed

+112
-53
lines changed

7 files changed

+112
-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: 43 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,19 @@ type Config struct {
5859
// MaxIteration limits the maximum number of reasoning iterations the agent can perform.
5960
MaxIteration int
6061

62+
// Backend provides filesystem operations used by tools and offloading.
63+
// If set, filesystem tools (read_file, write_file, edit_file, glob, grep) will be registered.
64+
// Optional.
65+
Backend filesystem.Backend
66+
// Shell provides shell command execution capability.
67+
// If set, an execute tool will be registered to support shell command execution.
68+
// Optional. Mutually exclusive with StreamingShell.
69+
Shell filesystem.Shell
70+
// StreamingShell provides streaming shell command execution capability.
71+
// If set, a streaming execute tool will be registered to support streaming shell command execution.
72+
// Optional. Mutually exclusive with Shell.
73+
StreamingShell filesystem.StreamingShell
74+
6175
// WithoutWriteTodos disables the built-in write_todos tool when set to true.
6276
WithoutWriteTodos bool
6377
// WithoutGeneralSubAgent disables the general-purpose subagent when set to true.
@@ -89,7 +103,7 @@ type Config struct {
89103
// This function initializes built-in tools, creates a task tool for subagent orchestration,
90104
// and returns a fully configured ChatModelAgent ready for execution.
91105
func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) {
92-
middlewares, err := buildBuiltinAgentMiddlewares(cfg.WithoutWriteTodos)
106+
handlers, err := buildBuiltinAgentMiddlewares(ctx, cfg)
93107
if err != nil {
94108
return nil, err
95109
}
@@ -116,12 +130,13 @@ func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) {
116130
instruction,
117131
cfg.ToolsConfig,
118132
cfg.MaxIteration,
119-
append(middlewares, cfg.Middlewares...),
133+
cfg.Middlewares,
134+
append(handlers, cfg.Handlers...),
120135
)
121136
if err != nil {
122137
return nil, fmt.Errorf("failed to new task tool: %w", err)
123138
}
124-
middlewares = append(middlewares, tt)
139+
handlers = append(handlers, tt)
125140
}
126141

127142
return adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
@@ -131,8 +146,8 @@ func New(ctx context.Context, cfg *Config) (adk.ResumableAgent, error) {
131146
Model: cfg.ChatModel,
132147
ToolsConfig: cfg.ToolsConfig,
133148
MaxIterations: cfg.MaxIteration,
134-
Middlewares: append(middlewares, cfg.Middlewares...),
135-
Handlers: cfg.Handlers,
149+
Middlewares: cfg.Middlewares,
150+
Handlers: append(handlers, cfg.Handlers...),
136151

137152
GenModelInput: genModelInput,
138153
ModelRetryConfig: cfg.ModelRetryConfig,
@@ -152,16 +167,29 @@ func genModelInput(ctx context.Context, instruction string, input *adk.AgentInpu
152167
return msgs, nil
153168
}
154169

155-
func buildBuiltinAgentMiddlewares(withoutWriteTodos bool) ([]adk.AgentMiddleware, error) {
156-
var ms []adk.AgentMiddleware
157-
if !withoutWriteTodos {
170+
func buildBuiltinAgentMiddlewares(ctx context.Context, cfg *Config) ([]adk.ChatModelAgentMiddleware, error) {
171+
var ms []adk.ChatModelAgentMiddleware
172+
if !cfg.WithoutWriteTodos {
158173
t, err := newWriteTodos()
159174
if err != nil {
160175
return nil, err
161176
}
162177
ms = append(ms, t)
163178
}
164179

180+
if cfg.Backend != nil || cfg.Shell != nil || cfg.StreamingShell != nil {
181+
fm, err := filesystem2.New(ctx, &filesystem2.Config{
182+
Backend: cfg.Backend,
183+
Shell: cfg.Shell,
184+
StreamingShell: cfg.StreamingShell,
185+
WithoutLargeToolResultOffloading: true,
186+
})
187+
if err != nil {
188+
return nil, err
189+
}
190+
ms = append(ms, fm)
191+
}
192+
165193
return ms, nil
166194
}
167195

@@ -175,27 +203,27 @@ type writeTodosArguments struct {
175203
Todos []TODO `json:"todos"`
176204
}
177205

178-
func newWriteTodos() (adk.AgentMiddleware, error) {
206+
func newWriteTodos() (adk.ChatModelAgentMiddleware, error) {
179207
toolDesc, err := internal.SelectPrompt(internal.I18nPrompts{
180208
English: writeTodosToolDescription,
181209
Chinese: writeTodosToolDescriptionChinese,
182210
})
183211
if err != nil {
184-
return adk.AgentMiddleware{}, err
212+
return nil, err
185213
}
186214
prompt, err := internal.SelectPrompt(internal.I18nPrompts{
187215
English: writeTodosPrompt,
188216
Chinese: writeTodosPromptChinese,
189217
})
190218
if err != nil {
191-
return adk.AgentMiddleware{}, err
219+
return nil, err
192220
}
193221
resultMsg, err := internal.SelectPrompt(internal.I18nPrompts{
194222
English: "Updated todo list to %s",
195223
Chinese: "已更新待办列表为 %s",
196224
})
197225
if err != nil {
198-
return adk.AgentMiddleware{}, err
226+
return nil, err
199227
}
200228

201229
t, err := utils.InferTool("write_todos", toolDesc, func(ctx context.Context, input writeTodosArguments) (output string, err error) {
@@ -207,11 +235,8 @@ func newWriteTodos() (adk.AgentMiddleware, error) {
207235
return fmt.Sprintf(resultMsg, todos), nil
208236
})
209237
if err != nil {
210-
return adk.AgentMiddleware{}, err
238+
return nil, err
211239
}
212240

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

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)