Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/specs/code_mode_token_comparison_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func TestCodeModeTokenComparison(t *testing.T) {
codeModeSvc := gai.NewAnthropicServiceWrapper(&codeModeClient.Messages)
codeModeGen := gai.NewAnthropicGenerator(codeModeSvc, "claude-sonnet-4-20250514", "")

executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{getCityTool, getWeatherTool})
executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{getCityTool, getWeatherTool}, 300)
if err != nil {
t.Fatalf("failed to generate execute_go_code tool: %v", err)
}
Expand Down Expand Up @@ -244,7 +244,7 @@ func Run(ctx context.Context) error {
codeModeSvc := gai.NewAnthropicServiceWrapper(&codeModeClient.Messages)
codeModeGen := gai.NewAnthropicGenerator(codeModeSvc, "claude-sonnet-4-20250514", "")

executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{getWeatherTool})
executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{getWeatherTool}, 300)
if err != nil {
t.Fatalf("failed to generate execute_go_code tool: %v", err)
}
Expand Down Expand Up @@ -363,7 +363,7 @@ func Run(ctx context.Context) error {
codeModeSvc := gai.NewAnthropicServiceWrapper(&codeModeClient.Messages)
codeModeGen := gai.NewAnthropicGenerator(codeModeSvc, "claude-sonnet-4-20250514", "")

executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{getCityTool, getWeatherTool})
executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{getCityTool, getWeatherTool}, 300)
if err != nil {
t.Fatalf("failed to generate execute_go_code tool: %v", err)
}
Expand Down Expand Up @@ -462,7 +462,7 @@ func Run(ctx context.Context) error {
codeModeSvc := gai.NewAnthropicServiceWrapper(&codeModeClient.Messages)
codeModeGen := gai.NewAnthropicGenerator(codeModeSvc, "claude-sonnet-4-20250514", "")

executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{})
executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool([]*mcp.Tool{}, 300)
if err != nil {
t.Fatalf("failed to generate execute_go_code tool: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func CreateToolCapableGenerator(

// Always register execute_go_code when code mode is enabled, even without MCP tools.
// The tool provides access to the Go standard library for file I/O, etc.
executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool(allCodeModeTools)
executeGoCodeTool, err := codemode.GenerateExecuteGoCodeTool(allCodeModeTools, codeModeConfig.MaxTimeout)
if err != nil {
return nil, fmt.Errorf("failed to generate execute_go_code tool: %w", err)
}
Expand Down
14 changes: 9 additions & 5 deletions internal/codemode/tooldesc.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,21 @@ func main() {

// GenerateExecuteGoCodeTool generates the complete gai.Tool definition for the
// execute_go_code tool, including its description and input schema.
func GenerateExecuteGoCodeTool(tools []*mcp.Tool) (gai.Tool, error) {
func GenerateExecuteGoCodeTool(tools []*mcp.Tool, maxTimeout int) (gai.Tool, error) {
description, err := GenerateExecuteGoCodeDescription(tools)
if err != nil {
return gai.Tool{}, fmt.Errorf("generating description: %w", err)
}

if maxTimeout <= 0 {
maxTimeout = 300
}

// Build input schema per spec:
// - code: string (required) - Complete Go source file contents
// - executionTimeout: integer (required, min 1, max 300) - Maximum execution time in seconds
// - executionTimeout: integer (required, min 1, max maxTimeout) - Maximum execution time in seconds
minTimeout := 1.0
maxTimeout := 300.0
maxTimeoutF := float64(maxTimeout)

inputSchema := &jsonschema.Schema{
Type: "object",
Expand All @@ -125,9 +129,9 @@ func GenerateExecuteGoCodeTool(tools []*mcp.Tool) (gai.Tool, error) {
},
"executionTimeout": {
Type: "integer",
Description: "Maximum execution time in seconds (1-300). Estimate based on expected runtime of the generated code.",
Description: fmt.Sprintf("Maximum execution time in seconds (1-%d). Estimate based on expected runtime of the generated code.", maxTimeout),
Minimum: &minTimeout,
Maximum: &maxTimeout,
Maximum: &maxTimeoutF,
},
},
Required: []string{"code", "executionTimeout"},
Expand Down
24 changes: 15 additions & 9 deletions internal/codemode/tooldesc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,16 +268,20 @@ IMPORTANT: Generate the complete file contents including package declaration and

func TestGenerateExecuteGoCodeTool(t *testing.T) {
tests := []struct {
name string
tools []*mcp.Tool
wantErr bool
name string
tools []*mcp.Tool
maxTimeout int
wantMax float64
wantErr bool
}{
{
name: "empty tools",
tools: []*mcp.Tool{},
name: "empty tools, default timeout",
tools: []*mcp.Tool{},
maxTimeout: 0,
wantMax: 300,
},
{
name: "with tools",
name: "with tools, custom timeout",
tools: []*mcp.Tool{
{
Name: "test_tool",
Expand All @@ -291,12 +295,14 @@ func TestGenerateExecuteGoCodeTool(t *testing.T) {
OutputSchema: nil,
},
},
maxTimeout: 600,
wantMax: 600,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tool, err := GenerateExecuteGoCodeTool(tt.tools)
tool, err := GenerateExecuteGoCodeTool(tt.tools, tt.maxTimeout)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateExecuteGoCodeTool() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down Expand Up @@ -347,8 +353,8 @@ func TestGenerateExecuteGoCodeTool(t *testing.T) {
if timeoutProp.Minimum == nil || *timeoutProp.Minimum != 1 {
t.Error("executionTimeout.Minimum should be 1")
}
if timeoutProp.Maximum == nil || *timeoutProp.Maximum != 300 {
t.Error("executionTimeout.Maximum should be 300")
if timeoutProp.Maximum == nil || *timeoutProp.Maximum != tt.wantMax {
t.Errorf("executionTimeout.Maximum = %v, want %v", *timeoutProp.Maximum, tt.wantMax)
}
}
})
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type PatchRequestConfig struct {
type CodeModeConfig struct {
Enabled bool `yaml:"enabled" json:"enabled"`
ExcludedTools []string `yaml:"excludedTools,omitempty" json:"excludedTools,omitempty"`
MaxTimeout int `yaml:"maxTimeout,omitempty" json:"maxTimeout,omitempty" validate:"omitempty,gte=0"`
}

// RawConfig represents the configuration file structure
Expand Down