Skip to content

Commit 5fa4a62

Browse files
adam-elementorHapshanko
authored andcommitted
Feat: Add configurable command tool timeout
Signed-off-by: Hapshanko <adamgaming391100@gmail.com>
1 parent c0e9c7f commit 5fa4a62

5 files changed

Lines changed: 67 additions & 12 deletions

File tree

‎go/adk/pkg/skills/shell.go‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ import (
88
"os"
99
"os/exec"
1010
"path/filepath"
11+
"strconv"
1112
"strings"
1213
"time"
1314
)
1415

16+
const commandTimeoutEnv = "KAGENT_COMMAND_TIMEOUT"
17+
1518
const srtSettingsPathEnv = "KAGENT_SRT_SETTINGS_PATH"
1619

1720
type CommandExecutor struct {
@@ -124,12 +127,21 @@ func NewCommandExecutorFromEnv() (*CommandExecutor, error) {
124127
return &CommandExecutor{srtArgs: srtArgs}, nil
125128
}
126129

127-
// ExecuteCommand executes a shell command.
128-
func (e *CommandExecutor) ExecuteCommand(ctx context.Context, command string, workingDir string) (string, error) {
129-
timeout := 30 * time.Second
130+
func commandTimeout(command string) time.Duration {
131+
if v := strings.TrimSpace(os.Getenv(commandTimeoutEnv)); v != "" {
132+
if secs, err := strconv.Atoi(v); err == nil && secs > 0 {
133+
return time.Duration(secs) * time.Second
134+
}
135+
}
130136
if strings.Contains(command, "python") {
131-
timeout = 60 * time.Second
137+
return 60 * time.Second
132138
}
139+
return 30 * time.Second
140+
}
141+
142+
// ExecuteCommand executes a shell command.
143+
func (e *CommandExecutor) ExecuteCommand(ctx context.Context, command string, workingDir string) (string, error) {
144+
timeout := commandTimeout(command)
133145

134146
ctx, cancel := context.WithTimeout(ctx, timeout)
135147
defer cancel()

‎go/adk/pkg/skills/shell_test.go‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,33 @@ func TestExecuteCommand_RequiresMountedSRTSettings(t *testing.T) {
425425
}
426426
}
427427

428+
func TestCommandTimeout(t *testing.T) {
429+
tests := []struct {
430+
name string
431+
envVal string
432+
command string
433+
want time.Duration
434+
}{
435+
{name: "default for generic command", envVal: "", command: "ls -la", want: 30 * time.Second},
436+
{name: "default for python command", envVal: "", command: "python script.py", want: 60 * time.Second},
437+
{name: "env override applies to generic", envVal: "300", command: "ls -la", want: 300 * time.Second},
438+
{name: "env override applies to python too", envVal: "300", command: "python script.py", want: 300 * time.Second},
439+
{name: "invalid env value falls back", envVal: "notanumber", command: "ls -la", want: 30 * time.Second},
440+
{name: "zero env value falls back", envVal: "0", command: "ls -la", want: 30 * time.Second},
441+
{name: "negative env value falls back", envVal: "-5", command: "ls -la", want: 30 * time.Second},
442+
}
443+
444+
for _, tt := range tests {
445+
t.Run(tt.name, func(t *testing.T) {
446+
t.Setenv(commandTimeoutEnv, tt.envVal)
447+
got := commandTimeout(tt.command)
448+
if got != tt.want {
449+
t.Errorf("commandTimeout(%q) with env=%q = %v, want %v", tt.command, tt.envVal, got, tt.want)
450+
}
451+
})
452+
}
453+
}
454+
428455
func TestExecuteCommand_Timeout(t *testing.T) {
429456
// Skip this test if running in CI or if test timeout is too short
430457
// This test requires at least 35 seconds to run properly

‎go/adk/pkg/tools/skills.go‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ For file operations:
6565
- Use read_file, write_file, and edit_file for interacting with the filesystem.
6666
6767
Timeouts:
68-
- python scripts: 60s
69-
- other commands: 30s`
68+
- python scripts: 60s (default)
69+
- other commands: 30s (default)
70+
- Override with KAGENT_COMMAND_TIMEOUT env var (value in seconds)`
7071
)
7172

7273
type skillsInput struct {

‎python/packages/kagent-skills/src/kagent/skills/prompts.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def get_bash_description() -> str:
111111
- Use read_file, write_file, and edit_file for interacting with the filesystem.
112112
113113
Timeouts:
114-
- python scripts: 60s
115-
- other commands: 30s
114+
- python scripts: 60s (default)
115+
- other commands: 30s (default)
116+
- Override with KAGENT_COMMAND_TIMEOUT env var (value in seconds)
116117
"""

‎python/packages/kagent-skills/src/kagent/skills/shell.py‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,26 @@ def _get_srt_settings_args() -> list[str]:
167167
return ["--settings", settings_path_env]
168168

169169

170+
_COMMAND_TIMEOUT_ENV = "KAGENT_COMMAND_TIMEOUT"
171+
172+
170173
def _get_command_timeout_seconds(command: str) -> float:
171-
"""Determine appropriate timeout for a command."""
174+
"""Determine appropriate timeout for a command.
175+
176+
Respects the KAGENT_COMMAND_TIMEOUT environment variable (value in seconds)
177+
which, when set to a positive number, overrides the built-in defaults.
178+
"""
179+
override = os.environ.get(_COMMAND_TIMEOUT_ENV, "").strip()
180+
if override:
181+
try:
182+
val = float(override)
183+
if val > 0:
184+
return val
185+
except ValueError:
186+
pass
172187
if "python " in command or "python3 " in command:
173-
return 60.0 # 1 minute for python scripts
174-
else:
175-
return 30.0 # 30 seconds for other commands
188+
return 60.0
189+
return 30.0
176190

177191

178192
async def execute_command(

0 commit comments

Comments
 (0)