mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-04-25 11:18:22 +00:00
chroe(config):Increase subagent max-turn limits (#1852)
This commit is contained in:
parent
d3b59a7931
commit
0ffe5a73c1
@ -15,6 +15,11 @@ class SubagentOverrideConfig(BaseModel):
|
|||||||
ge=1,
|
ge=1,
|
||||||
description="Timeout in seconds for this subagent (None = use global default)",
|
description="Timeout in seconds for this subagent (None = use global default)",
|
||||||
)
|
)
|
||||||
|
max_turns: int | None = Field(
|
||||||
|
default=None,
|
||||||
|
ge=1,
|
||||||
|
description="Maximum turns for this subagent (None = use global or builtin default)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SubagentsAppConfig(BaseModel):
|
class SubagentsAppConfig(BaseModel):
|
||||||
@ -25,6 +30,11 @@ class SubagentsAppConfig(BaseModel):
|
|||||||
ge=1,
|
ge=1,
|
||||||
description="Default timeout in seconds for all subagents (default: 900 = 15 minutes)",
|
description="Default timeout in seconds for all subagents (default: 900 = 15 minutes)",
|
||||||
)
|
)
|
||||||
|
max_turns: int | None = Field(
|
||||||
|
default=None,
|
||||||
|
ge=1,
|
||||||
|
description="Optional default max-turn override for all subagents (None = keep builtin defaults)",
|
||||||
|
)
|
||||||
agents: dict[str, SubagentOverrideConfig] = Field(
|
agents: dict[str, SubagentOverrideConfig] = Field(
|
||||||
default_factory=dict,
|
default_factory=dict,
|
||||||
description="Per-agent configuration overrides keyed by agent name",
|
description="Per-agent configuration overrides keyed by agent name",
|
||||||
@ -44,6 +54,15 @@ class SubagentsAppConfig(BaseModel):
|
|||||||
return override.timeout_seconds
|
return override.timeout_seconds
|
||||||
return self.timeout_seconds
|
return self.timeout_seconds
|
||||||
|
|
||||||
|
def get_max_turns_for(self, agent_name: str, builtin_default: int) -> int:
|
||||||
|
"""Get the effective max_turns for a specific agent."""
|
||||||
|
override = self.agents.get(agent_name)
|
||||||
|
if override is not None and override.max_turns is not None:
|
||||||
|
return override.max_turns
|
||||||
|
if self.max_turns is not None:
|
||||||
|
return self.max_turns
|
||||||
|
return builtin_default
|
||||||
|
|
||||||
|
|
||||||
_subagents_config: SubagentsAppConfig = SubagentsAppConfig()
|
_subagents_config: SubagentsAppConfig = SubagentsAppConfig()
|
||||||
|
|
||||||
@ -58,8 +77,26 @@ def load_subagents_config_from_dict(config_dict: dict) -> None:
|
|||||||
global _subagents_config
|
global _subagents_config
|
||||||
_subagents_config = SubagentsAppConfig(**config_dict)
|
_subagents_config = SubagentsAppConfig(**config_dict)
|
||||||
|
|
||||||
overrides_summary = {name: f"{override.timeout_seconds}s" for name, override in _subagents_config.agents.items() if override.timeout_seconds is not None}
|
overrides_summary = {}
|
||||||
|
for name, override in _subagents_config.agents.items():
|
||||||
|
parts = []
|
||||||
|
if override.timeout_seconds is not None:
|
||||||
|
parts.append(f"timeout={override.timeout_seconds}s")
|
||||||
|
if override.max_turns is not None:
|
||||||
|
parts.append(f"max_turns={override.max_turns}")
|
||||||
|
if parts:
|
||||||
|
overrides_summary[name] = ", ".join(parts)
|
||||||
|
|
||||||
if overrides_summary:
|
if overrides_summary:
|
||||||
logger.info(f"Subagents config loaded: default timeout={_subagents_config.timeout_seconds}s, per-agent overrides={overrides_summary}")
|
logger.info(
|
||||||
|
"Subagents config loaded: default timeout=%ss, default max_turns=%s, per-agent overrides=%s",
|
||||||
|
_subagents_config.timeout_seconds,
|
||||||
|
_subagents_config.max_turns,
|
||||||
|
overrides_summary,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.info(f"Subagents config loaded: default timeout={_subagents_config.timeout_seconds}s, no per-agent overrides")
|
logger.info(
|
||||||
|
"Subagents config loaded: default timeout=%ss, default max_turns=%s, no per-agent overrides",
|
||||||
|
_subagents_config.timeout_seconds,
|
||||||
|
_subagents_config.max_turns,
|
||||||
|
)
|
||||||
|
|||||||
@ -43,5 +43,5 @@ You have access to the sandbox environment:
|
|||||||
tools=["bash", "ls", "read_file", "write_file", "str_replace"], # Sandbox tools only
|
tools=["bash", "ls", "read_file", "write_file", "str_replace"], # Sandbox tools only
|
||||||
disallowed_tools=["task", "ask_clarification", "present_files"],
|
disallowed_tools=["task", "ask_clarification", "present_files"],
|
||||||
model="inherit",
|
model="inherit",
|
||||||
max_turns=30,
|
max_turns=60,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -44,5 +44,5 @@ You have access to the same sandbox environment as the parent agent:
|
|||||||
tools=None, # Inherit all tools from parent
|
tools=None, # Inherit all tools from parent
|
||||||
disallowed_tools=["task", "ask_clarification", "present_files"], # Prevent nesting and clarification
|
disallowed_tools=["task", "ask_clarification", "present_files"], # Prevent nesting and clarification
|
||||||
model="inherit",
|
model="inherit",
|
||||||
max_turns=50,
|
max_turns=100,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -28,9 +28,27 @@ def get_subagent_config(name: str) -> SubagentConfig | None:
|
|||||||
|
|
||||||
app_config = get_subagents_app_config()
|
app_config = get_subagents_app_config()
|
||||||
effective_timeout = app_config.get_timeout_for(name)
|
effective_timeout = app_config.get_timeout_for(name)
|
||||||
|
effective_max_turns = app_config.get_max_turns_for(name, config.max_turns)
|
||||||
|
|
||||||
|
overrides = {}
|
||||||
if effective_timeout != config.timeout_seconds:
|
if effective_timeout != config.timeout_seconds:
|
||||||
logger.debug(f"Subagent '{name}': timeout overridden by config.yaml ({config.timeout_seconds}s -> {effective_timeout}s)")
|
logger.debug(
|
||||||
config = replace(config, timeout_seconds=effective_timeout)
|
"Subagent '%s': timeout overridden by config.yaml (%ss -> %ss)",
|
||||||
|
name,
|
||||||
|
config.timeout_seconds,
|
||||||
|
effective_timeout,
|
||||||
|
)
|
||||||
|
overrides["timeout_seconds"] = effective_timeout
|
||||||
|
if effective_max_turns != config.max_turns:
|
||||||
|
logger.debug(
|
||||||
|
"Subagent '%s': max_turns overridden by config.yaml (%s -> %s)",
|
||||||
|
name,
|
||||||
|
config.max_turns,
|
||||||
|
effective_max_turns,
|
||||||
|
)
|
||||||
|
overrides["max_turns"] = effective_max_turns
|
||||||
|
if overrides:
|
||||||
|
config = replace(config, **overrides)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
"""Tests for subagent timeout configuration.
|
"""Tests for subagent runtime configuration.
|
||||||
|
|
||||||
Covers:
|
Covers:
|
||||||
- SubagentsAppConfig / SubagentOverrideConfig model validation and defaults
|
- SubagentsAppConfig / SubagentOverrideConfig model validation and defaults
|
||||||
- get_timeout_for() resolution logic (global vs per-agent)
|
- get_timeout_for() / get_max_turns_for() resolution logic
|
||||||
- load_subagents_config_from_dict() and get_subagents_app_config() singleton
|
- load_subagents_config_from_dict() and get_subagents_app_config() singleton
|
||||||
- registry.get_subagent_config() applies config overrides
|
- registry.get_subagent_config() applies config overrides
|
||||||
- registry.list_subagents() applies overrides for all agents
|
- registry.list_subagents() applies overrides for all agents
|
||||||
@ -24,9 +24,20 @@ from deerflow.subagents.config import SubagentConfig
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def _reset_subagents_config(timeout_seconds: int = 900, agents: dict | None = None) -> None:
|
def _reset_subagents_config(
|
||||||
|
timeout_seconds: int = 900,
|
||||||
|
*,
|
||||||
|
max_turns: int | None = None,
|
||||||
|
agents: dict | None = None,
|
||||||
|
) -> None:
|
||||||
"""Reset global subagents config to a known state."""
|
"""Reset global subagents config to a known state."""
|
||||||
load_subagents_config_from_dict({"timeout_seconds": timeout_seconds, "agents": agents or {}})
|
load_subagents_config_from_dict(
|
||||||
|
{
|
||||||
|
"timeout_seconds": timeout_seconds,
|
||||||
|
"max_turns": max_turns,
|
||||||
|
"agents": agents or {},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -38,22 +49,29 @@ class TestSubagentOverrideConfig:
|
|||||||
def test_default_is_none(self):
|
def test_default_is_none(self):
|
||||||
override = SubagentOverrideConfig()
|
override = SubagentOverrideConfig()
|
||||||
assert override.timeout_seconds is None
|
assert override.timeout_seconds is None
|
||||||
|
assert override.max_turns is None
|
||||||
|
|
||||||
def test_explicit_value(self):
|
def test_explicit_value(self):
|
||||||
override = SubagentOverrideConfig(timeout_seconds=300)
|
override = SubagentOverrideConfig(timeout_seconds=300, max_turns=42)
|
||||||
assert override.timeout_seconds == 300
|
assert override.timeout_seconds == 300
|
||||||
|
assert override.max_turns == 42
|
||||||
|
|
||||||
def test_rejects_zero(self):
|
def test_rejects_zero(self):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
SubagentOverrideConfig(timeout_seconds=0)
|
SubagentOverrideConfig(timeout_seconds=0)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
SubagentOverrideConfig(max_turns=0)
|
||||||
|
|
||||||
def test_rejects_negative(self):
|
def test_rejects_negative(self):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
SubagentOverrideConfig(timeout_seconds=-1)
|
SubagentOverrideConfig(timeout_seconds=-1)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
SubagentOverrideConfig(max_turns=-1)
|
||||||
|
|
||||||
def test_minimum_valid_value(self):
|
def test_minimum_valid_value(self):
|
||||||
override = SubagentOverrideConfig(timeout_seconds=1)
|
override = SubagentOverrideConfig(timeout_seconds=1, max_turns=1)
|
||||||
assert override.timeout_seconds == 1
|
assert override.timeout_seconds == 1
|
||||||
|
assert override.max_turns == 1
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -66,66 +84,86 @@ class TestSubagentsAppConfigDefaults:
|
|||||||
config = SubagentsAppConfig()
|
config = SubagentsAppConfig()
|
||||||
assert config.timeout_seconds == 900
|
assert config.timeout_seconds == 900
|
||||||
|
|
||||||
|
def test_default_max_turns_override_is_none(self):
|
||||||
|
config = SubagentsAppConfig()
|
||||||
|
assert config.max_turns is None
|
||||||
|
|
||||||
def test_default_agents_empty(self):
|
def test_default_agents_empty(self):
|
||||||
config = SubagentsAppConfig()
|
config = SubagentsAppConfig()
|
||||||
assert config.agents == {}
|
assert config.agents == {}
|
||||||
|
|
||||||
def test_custom_global_timeout(self):
|
def test_custom_global_runtime_overrides(self):
|
||||||
config = SubagentsAppConfig(timeout_seconds=1800)
|
config = SubagentsAppConfig(timeout_seconds=1800, max_turns=120)
|
||||||
assert config.timeout_seconds == 1800
|
assert config.timeout_seconds == 1800
|
||||||
|
assert config.max_turns == 120
|
||||||
|
|
||||||
def test_rejects_zero_timeout(self):
|
def test_rejects_zero_timeout(self):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
SubagentsAppConfig(timeout_seconds=0)
|
SubagentsAppConfig(timeout_seconds=0)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
SubagentsAppConfig(max_turns=0)
|
||||||
|
|
||||||
def test_rejects_negative_timeout(self):
|
def test_rejects_negative_timeout(self):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
SubagentsAppConfig(timeout_seconds=-60)
|
SubagentsAppConfig(timeout_seconds=-60)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
SubagentsAppConfig(max_turns=-60)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# SubagentsAppConfig.get_timeout_for()
|
# SubagentsAppConfig resolution helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class TestGetTimeoutFor:
|
class TestRuntimeResolution:
|
||||||
def test_returns_global_default_when_no_override(self):
|
def test_returns_global_default_when_no_override(self):
|
||||||
config = SubagentsAppConfig(timeout_seconds=600)
|
config = SubagentsAppConfig(timeout_seconds=600)
|
||||||
assert config.get_timeout_for("general-purpose") == 600
|
assert config.get_timeout_for("general-purpose") == 600
|
||||||
assert config.get_timeout_for("bash") == 600
|
assert config.get_timeout_for("bash") == 600
|
||||||
assert config.get_timeout_for("unknown-agent") == 600
|
assert config.get_timeout_for("unknown-agent") == 600
|
||||||
|
assert config.get_max_turns_for("general-purpose", 100) == 100
|
||||||
|
assert config.get_max_turns_for("bash", 60) == 60
|
||||||
|
|
||||||
def test_returns_per_agent_override_when_set(self):
|
def test_returns_per_agent_override_when_set(self):
|
||||||
config = SubagentsAppConfig(
|
config = SubagentsAppConfig(
|
||||||
timeout_seconds=900,
|
timeout_seconds=900,
|
||||||
agents={"bash": SubagentOverrideConfig(timeout_seconds=300)},
|
max_turns=120,
|
||||||
|
agents={"bash": SubagentOverrideConfig(timeout_seconds=300, max_turns=80)},
|
||||||
)
|
)
|
||||||
assert config.get_timeout_for("bash") == 300
|
assert config.get_timeout_for("bash") == 300
|
||||||
|
assert config.get_max_turns_for("bash", 60) == 80
|
||||||
|
|
||||||
def test_other_agents_still_use_global_default(self):
|
def test_other_agents_still_use_global_default(self):
|
||||||
config = SubagentsAppConfig(
|
config = SubagentsAppConfig(
|
||||||
timeout_seconds=900,
|
timeout_seconds=900,
|
||||||
agents={"bash": SubagentOverrideConfig(timeout_seconds=300)},
|
max_turns=140,
|
||||||
|
agents={"bash": SubagentOverrideConfig(timeout_seconds=300, max_turns=80)},
|
||||||
)
|
)
|
||||||
assert config.get_timeout_for("general-purpose") == 900
|
assert config.get_timeout_for("general-purpose") == 900
|
||||||
|
assert config.get_max_turns_for("general-purpose", 100) == 140
|
||||||
|
|
||||||
def test_agent_with_none_override_falls_back_to_global(self):
|
def test_agent_with_none_override_falls_back_to_global(self):
|
||||||
config = SubagentsAppConfig(
|
config = SubagentsAppConfig(
|
||||||
timeout_seconds=900,
|
timeout_seconds=900,
|
||||||
agents={"general-purpose": SubagentOverrideConfig(timeout_seconds=None)},
|
max_turns=150,
|
||||||
|
agents={"general-purpose": SubagentOverrideConfig(timeout_seconds=None, max_turns=None)},
|
||||||
)
|
)
|
||||||
assert config.get_timeout_for("general-purpose") == 900
|
assert config.get_timeout_for("general-purpose") == 900
|
||||||
|
assert config.get_max_turns_for("general-purpose", 100) == 150
|
||||||
|
|
||||||
def test_multiple_per_agent_overrides(self):
|
def test_multiple_per_agent_overrides(self):
|
||||||
config = SubagentsAppConfig(
|
config = SubagentsAppConfig(
|
||||||
timeout_seconds=900,
|
timeout_seconds=900,
|
||||||
|
max_turns=120,
|
||||||
agents={
|
agents={
|
||||||
"general-purpose": SubagentOverrideConfig(timeout_seconds=1800),
|
"general-purpose": SubagentOverrideConfig(timeout_seconds=1800, max_turns=200),
|
||||||
"bash": SubagentOverrideConfig(timeout_seconds=120),
|
"bash": SubagentOverrideConfig(timeout_seconds=120, max_turns=80),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert config.get_timeout_for("general-purpose") == 1800
|
assert config.get_timeout_for("general-purpose") == 1800
|
||||||
assert config.get_timeout_for("bash") == 120
|
assert config.get_timeout_for("bash") == 120
|
||||||
|
assert config.get_max_turns_for("general-purpose", 100) == 200
|
||||||
|
assert config.get_max_turns_for("bash", 60) == 80
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -139,54 +177,63 @@ class TestLoadSubagentsConfig:
|
|||||||
_reset_subagents_config()
|
_reset_subagents_config()
|
||||||
|
|
||||||
def test_load_global_timeout(self):
|
def test_load_global_timeout(self):
|
||||||
load_subagents_config_from_dict({"timeout_seconds": 300})
|
load_subagents_config_from_dict({"timeout_seconds": 300, "max_turns": 120})
|
||||||
assert get_subagents_app_config().timeout_seconds == 300
|
assert get_subagents_app_config().timeout_seconds == 300
|
||||||
|
assert get_subagents_app_config().max_turns == 120
|
||||||
|
|
||||||
def test_load_with_per_agent_overrides(self):
|
def test_load_with_per_agent_overrides(self):
|
||||||
load_subagents_config_from_dict(
|
load_subagents_config_from_dict(
|
||||||
{
|
{
|
||||||
"timeout_seconds": 900,
|
"timeout_seconds": 900,
|
||||||
|
"max_turns": 120,
|
||||||
"agents": {
|
"agents": {
|
||||||
"general-purpose": {"timeout_seconds": 1800},
|
"general-purpose": {"timeout_seconds": 1800, "max_turns": 200},
|
||||||
"bash": {"timeout_seconds": 60},
|
"bash": {"timeout_seconds": 60, "max_turns": 80},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
cfg = get_subagents_app_config()
|
cfg = get_subagents_app_config()
|
||||||
assert cfg.get_timeout_for("general-purpose") == 1800
|
assert cfg.get_timeout_for("general-purpose") == 1800
|
||||||
assert cfg.get_timeout_for("bash") == 60
|
assert cfg.get_timeout_for("bash") == 60
|
||||||
|
assert cfg.get_max_turns_for("general-purpose", 100) == 200
|
||||||
|
assert cfg.get_max_turns_for("bash", 60) == 80
|
||||||
|
|
||||||
def test_load_partial_override(self):
|
def test_load_partial_override(self):
|
||||||
load_subagents_config_from_dict(
|
load_subagents_config_from_dict(
|
||||||
{
|
{
|
||||||
"timeout_seconds": 600,
|
"timeout_seconds": 600,
|
||||||
"agents": {"bash": {"timeout_seconds": 120}},
|
"agents": {"bash": {"timeout_seconds": 120, "max_turns": 70}},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
cfg = get_subagents_app_config()
|
cfg = get_subagents_app_config()
|
||||||
assert cfg.get_timeout_for("general-purpose") == 600
|
assert cfg.get_timeout_for("general-purpose") == 600
|
||||||
assert cfg.get_timeout_for("bash") == 120
|
assert cfg.get_timeout_for("bash") == 120
|
||||||
|
assert cfg.get_max_turns_for("general-purpose", 100) == 100
|
||||||
|
assert cfg.get_max_turns_for("bash", 60) == 70
|
||||||
|
|
||||||
def test_load_empty_dict_uses_defaults(self):
|
def test_load_empty_dict_uses_defaults(self):
|
||||||
load_subagents_config_from_dict({})
|
load_subagents_config_from_dict({})
|
||||||
cfg = get_subagents_app_config()
|
cfg = get_subagents_app_config()
|
||||||
assert cfg.timeout_seconds == 900
|
assert cfg.timeout_seconds == 900
|
||||||
|
assert cfg.max_turns is None
|
||||||
assert cfg.agents == {}
|
assert cfg.agents == {}
|
||||||
|
|
||||||
def test_load_replaces_previous_config(self):
|
def test_load_replaces_previous_config(self):
|
||||||
load_subagents_config_from_dict({"timeout_seconds": 100})
|
load_subagents_config_from_dict({"timeout_seconds": 100, "max_turns": 90})
|
||||||
assert get_subagents_app_config().timeout_seconds == 100
|
assert get_subagents_app_config().timeout_seconds == 100
|
||||||
|
assert get_subagents_app_config().max_turns == 90
|
||||||
|
|
||||||
load_subagents_config_from_dict({"timeout_seconds": 200})
|
load_subagents_config_from_dict({"timeout_seconds": 200, "max_turns": 110})
|
||||||
assert get_subagents_app_config().timeout_seconds == 200
|
assert get_subagents_app_config().timeout_seconds == 200
|
||||||
|
assert get_subagents_app_config().max_turns == 110
|
||||||
|
|
||||||
def test_singleton_returns_same_instance_between_calls(self):
|
def test_singleton_returns_same_instance_between_calls(self):
|
||||||
load_subagents_config_from_dict({"timeout_seconds": 777})
|
load_subagents_config_from_dict({"timeout_seconds": 777, "max_turns": 123})
|
||||||
assert get_subagents_app_config() is get_subagents_app_config()
|
assert get_subagents_app_config() is get_subagents_app_config()
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# registry.get_subagent_config – timeout override applied
|
# registry.get_subagent_config – runtime overrides applied
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@ -211,25 +258,29 @@ class TestRegistryGetSubagentConfig:
|
|||||||
_reset_subagents_config(timeout_seconds=900)
|
_reset_subagents_config(timeout_seconds=900)
|
||||||
config = get_subagent_config("general-purpose")
|
config = get_subagent_config("general-purpose")
|
||||||
assert config.timeout_seconds == 900
|
assert config.timeout_seconds == 900
|
||||||
|
assert config.max_turns == 100
|
||||||
|
|
||||||
def test_global_timeout_override_applied(self):
|
def test_global_timeout_override_applied(self):
|
||||||
from deerflow.subagents.registry import get_subagent_config
|
from deerflow.subagents.registry import get_subagent_config
|
||||||
|
|
||||||
_reset_subagents_config(timeout_seconds=1800)
|
_reset_subagents_config(timeout_seconds=1800, max_turns=140)
|
||||||
config = get_subagent_config("general-purpose")
|
config = get_subagent_config("general-purpose")
|
||||||
assert config.timeout_seconds == 1800
|
assert config.timeout_seconds == 1800
|
||||||
|
assert config.max_turns == 140
|
||||||
|
|
||||||
def test_per_agent_timeout_override_applied(self):
|
def test_per_agent_runtime_override_applied(self):
|
||||||
from deerflow.subagents.registry import get_subagent_config
|
from deerflow.subagents.registry import get_subagent_config
|
||||||
|
|
||||||
load_subagents_config_from_dict(
|
load_subagents_config_from_dict(
|
||||||
{
|
{
|
||||||
"timeout_seconds": 900,
|
"timeout_seconds": 900,
|
||||||
"agents": {"bash": {"timeout_seconds": 120}},
|
"max_turns": 120,
|
||||||
|
"agents": {"bash": {"timeout_seconds": 120, "max_turns": 80}},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
bash_config = get_subagent_config("bash")
|
bash_config = get_subagent_config("bash")
|
||||||
assert bash_config.timeout_seconds == 120
|
assert bash_config.timeout_seconds == 120
|
||||||
|
assert bash_config.max_turns == 80
|
||||||
|
|
||||||
def test_per_agent_override_does_not_affect_other_agents(self):
|
def test_per_agent_override_does_not_affect_other_agents(self):
|
||||||
from deerflow.subagents.registry import get_subagent_config
|
from deerflow.subagents.registry import get_subagent_config
|
||||||
@ -237,11 +288,13 @@ class TestRegistryGetSubagentConfig:
|
|||||||
load_subagents_config_from_dict(
|
load_subagents_config_from_dict(
|
||||||
{
|
{
|
||||||
"timeout_seconds": 900,
|
"timeout_seconds": 900,
|
||||||
"agents": {"bash": {"timeout_seconds": 120}},
|
"max_turns": 120,
|
||||||
|
"agents": {"bash": {"timeout_seconds": 120, "max_turns": 80}},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
gp_config = get_subagent_config("general-purpose")
|
gp_config = get_subagent_config("general-purpose")
|
||||||
assert gp_config.timeout_seconds == 900
|
assert gp_config.timeout_seconds == 900
|
||||||
|
assert gp_config.max_turns == 120
|
||||||
|
|
||||||
def test_builtin_config_object_is_not_mutated(self):
|
def test_builtin_config_object_is_not_mutated(self):
|
||||||
"""Registry must return a new object, leaving the builtin default intact."""
|
"""Registry must return a new object, leaving the builtin default intact."""
|
||||||
@ -249,24 +302,27 @@ class TestRegistryGetSubagentConfig:
|
|||||||
from deerflow.subagents.registry import get_subagent_config
|
from deerflow.subagents.registry import get_subagent_config
|
||||||
|
|
||||||
original_timeout = BUILTIN_SUBAGENTS["bash"].timeout_seconds
|
original_timeout = BUILTIN_SUBAGENTS["bash"].timeout_seconds
|
||||||
load_subagents_config_from_dict({"timeout_seconds": 42})
|
original_max_turns = BUILTIN_SUBAGENTS["bash"].max_turns
|
||||||
|
load_subagents_config_from_dict({"timeout_seconds": 42, "max_turns": 88})
|
||||||
|
|
||||||
returned = get_subagent_config("bash")
|
returned = get_subagent_config("bash")
|
||||||
assert returned.timeout_seconds == 42
|
assert returned.timeout_seconds == 42
|
||||||
|
assert returned.max_turns == 88
|
||||||
assert BUILTIN_SUBAGENTS["bash"].timeout_seconds == original_timeout
|
assert BUILTIN_SUBAGENTS["bash"].timeout_seconds == original_timeout
|
||||||
|
assert BUILTIN_SUBAGENTS["bash"].max_turns == original_max_turns
|
||||||
|
|
||||||
def test_config_preserves_other_fields(self):
|
def test_config_preserves_other_fields(self):
|
||||||
"""Applying timeout override must not change other SubagentConfig fields."""
|
"""Applying runtime overrides must not change other SubagentConfig fields."""
|
||||||
from deerflow.subagents.builtins import BUILTIN_SUBAGENTS
|
from deerflow.subagents.builtins import BUILTIN_SUBAGENTS
|
||||||
from deerflow.subagents.registry import get_subagent_config
|
from deerflow.subagents.registry import get_subagent_config
|
||||||
|
|
||||||
_reset_subagents_config(timeout_seconds=300)
|
_reset_subagents_config(timeout_seconds=300, max_turns=140)
|
||||||
original = BUILTIN_SUBAGENTS["general-purpose"]
|
original = BUILTIN_SUBAGENTS["general-purpose"]
|
||||||
overridden = get_subagent_config("general-purpose")
|
overridden = get_subagent_config("general-purpose")
|
||||||
|
|
||||||
assert overridden.name == original.name
|
assert overridden.name == original.name
|
||||||
assert overridden.description == original.description
|
assert overridden.description == original.description
|
||||||
assert overridden.max_turns == original.max_turns
|
assert overridden.max_turns == 140
|
||||||
assert overridden.model == original.model
|
assert overridden.model == original.model
|
||||||
assert overridden.tools == original.tools
|
assert overridden.tools == original.tools
|
||||||
assert overridden.disallowed_tools == original.disallowed_tools
|
assert overridden.disallowed_tools == original.disallowed_tools
|
||||||
@ -291,9 +347,10 @@ class TestRegistryListSubagents:
|
|||||||
def test_all_returned_configs_get_global_override(self):
|
def test_all_returned_configs_get_global_override(self):
|
||||||
from deerflow.subagents.registry import list_subagents
|
from deerflow.subagents.registry import list_subagents
|
||||||
|
|
||||||
_reset_subagents_config(timeout_seconds=123)
|
_reset_subagents_config(timeout_seconds=123, max_turns=77)
|
||||||
for cfg in list_subagents():
|
for cfg in list_subagents():
|
||||||
assert cfg.timeout_seconds == 123, f"{cfg.name} has wrong timeout"
|
assert cfg.timeout_seconds == 123, f"{cfg.name} has wrong timeout"
|
||||||
|
assert cfg.max_turns == 77, f"{cfg.name} has wrong max_turns"
|
||||||
|
|
||||||
def test_per_agent_overrides_reflected_in_list(self):
|
def test_per_agent_overrides_reflected_in_list(self):
|
||||||
from deerflow.subagents.registry import list_subagents
|
from deerflow.subagents.registry import list_subagents
|
||||||
@ -301,15 +358,18 @@ class TestRegistryListSubagents:
|
|||||||
load_subagents_config_from_dict(
|
load_subagents_config_from_dict(
|
||||||
{
|
{
|
||||||
"timeout_seconds": 900,
|
"timeout_seconds": 900,
|
||||||
|
"max_turns": 120,
|
||||||
"agents": {
|
"agents": {
|
||||||
"general-purpose": {"timeout_seconds": 1800},
|
"general-purpose": {"timeout_seconds": 1800, "max_turns": 200},
|
||||||
"bash": {"timeout_seconds": 60},
|
"bash": {"timeout_seconds": 60, "max_turns": 80},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
by_name = {cfg.name: cfg for cfg in list_subagents()}
|
by_name = {cfg.name: cfg for cfg in list_subagents()}
|
||||||
assert by_name["general-purpose"].timeout_seconds == 1800
|
assert by_name["general-purpose"].timeout_seconds == 1800
|
||||||
assert by_name["bash"].timeout_seconds == 60
|
assert by_name["bash"].timeout_seconds == 60
|
||||||
|
assert by_name["general-purpose"].max_turns == 200
|
||||||
|
assert by_name["bash"].max_turns == 80
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -456,13 +456,17 @@ sandbox:
|
|||||||
# subagents:
|
# subagents:
|
||||||
# # Default timeout in seconds for all subagents (default: 900 = 15 minutes)
|
# # Default timeout in seconds for all subagents (default: 900 = 15 minutes)
|
||||||
# timeout_seconds: 900
|
# timeout_seconds: 900
|
||||||
|
# # Optional global max-turn override for all subagents
|
||||||
|
# # max_turns: 120
|
||||||
#
|
#
|
||||||
# # Optional per-agent timeout overrides
|
# # Optional per-agent overrides
|
||||||
# agents:
|
# agents:
|
||||||
# general-purpose:
|
# general-purpose:
|
||||||
# timeout_seconds: 1800 # 30 minutes for complex multi-step tasks
|
# timeout_seconds: 1800 # 30 minutes for complex multi-step tasks
|
||||||
|
# max_turns: 160
|
||||||
# bash:
|
# bash:
|
||||||
# timeout_seconds: 300 # 5 minutes for quick command execution
|
# timeout_seconds: 300 # 5 minutes for quick command execution
|
||||||
|
# max_turns: 80
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# ACP Agents Configuration
|
# ACP Agents Configuration
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user