Skip to content

Commit 0721f9f

Browse files
kovtcharov-amdOvtcharov
andauthored
fix(agents): raise step limit and centralize on one global default (#1389)
## Why this matters Agents were running out of steps mid-task. The browser agent (and others) stopped at **10 steps** and told users to re-run with `--max-steps 60`, even on routine multi-step work. The cap was hardcoded low almost everywhere — `10` for most agents, `5`–`6` for a couple — and the Agent UI pinned `10` in four more places, while the CLI used `100` and background ticks `20`. So the limit was both too low *and* inconsistent depending on how you launched the agent. After this change there's **one knob**: `default_max_steps()` in `base/agent.py` (default **50**, overridable at runtime with `GAIA_AGENT_MAX_STEPS=<n>`). Every agent config inherits it, as do the base `Agent`, the UI chat paths, and the autonomous-tick loop — so retuning the whole fleet is a one-line change or a single env var, no per-agent edits. Agents that genuinely need more keep their explicit override (CodeAgent=100, EMR=50), and a typo'd env value now fails loudly instead of silently capping. ## Test plan - [x] `pytest tests/unit/agents/test_default_max_steps.py` — new helper tests (default, env override, empty, loud-failure on non-int/non-positive, configs inherit override) - [x] `pytest tests/unit/agents/` — existing config-default tests updated to assert against the helper; all green (pre-existing context-overflow + Windows-encoding failures are unrelated, confirmed failing on `main`) - [x] `python util/lint.py --black --isort` clean; `pyflakes` clean on all touched files - [ ] Manual: launch the browser agent in the Agent UI and confirm it no longer stops at 10 steps; set `GAIA_AGENT_MAX_STEPS=5` and confirm every agent caps at 5 Co-authored-by: Ovtcharov <kovtchar@amd.com>
1 parent 020960f commit 0721f9f

27 files changed

Lines changed: 195 additions & 63 deletions

File tree

docs/reference/cli.mdx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,21 @@ gaia --ui --base-url https://your-server:13305/api/v1
9696
`gaia chat --ui` continues to work as an alias. The Agent UI requires an AMD Ryzen AI Max (Strix Halo) or an AMD Radeon GPU with ≥ 24 GB VRAM. If your device is not supported, a dismissible warning banner will appear in the UI.
9797
</Note>
9898

99+
### Agent step limit
100+
101+
Agents stop after a maximum number of reasoning/tool steps. The default is **50**, applied consistently across the CLI, the Agent UI, and background runs.
102+
103+
- **Per-invocation:** pass `--max-steps <n>` to any agent command (e.g. `gaia browse --max-steps 80`). `gaia blender` uses `--steps <n>`.
104+
- **Fleet-wide:** set the `GAIA_AGENT_MAX_STEPS` environment variable to change the default for every agent at once, without per-command flags.
105+
106+
```bash
107+
# Raise the limit for every agent in this shell
108+
export GAIA_AGENT_MAX_STEPS=80
109+
gaia browse "research the latest AMD Ryzen AI news"
110+
```
111+
112+
A few agents intentionally override the default (e.g. the Code agent uses 100 for multi-file generation). An invalid `GAIA_AGENT_MAX_STEPS` value (non-integer or ≤ 0) fails fast with an actionable error rather than silently capping agents.
113+
99114
---
100115

101116
## Initialization

hub/agents/python/analyst/gaia_agent_analyst/agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
# SPDX-License-Identifier: MIT
33
"""Structured-data analysis GAIA agent."""
44

5-
from dataclasses import dataclass
5+
from dataclasses import dataclass, field
66
from typing import List, Optional
77

8-
from gaia.agents.base.agent import Agent
8+
from gaia.agents.base.agent import Agent, default_max_steps
99
from gaia.agents.base.tools import _TOOL_REGISTRY
1010
from gaia.agents.tools import ScratchpadToolsMixin
1111
from gaia.mcp.mixin import MCPClientMixin
@@ -20,7 +20,7 @@ class AnalystAgentConfig:
2020
claude_model: str = "claude-sonnet-4-20250514"
2121
base_url: Optional[str] = None
2222
model_id: Optional[str] = None
23-
max_steps: int = 10
23+
max_steps: int = field(default_factory=default_max_steps)
2424
streaming: bool = False
2525
debug: bool = False
2626
debug_prompts: bool = False

hub/agents/python/analyst/tests/test_analyst_agent.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ class TestAnalystAgentConfig(unittest.TestCase):
2626
def test_defaults(self):
2727
from gaia_agent_analyst import AnalystAgentConfig
2828

29+
from gaia.agents.base.agent import default_max_steps
30+
2931
cfg = AnalystAgentConfig()
3032
self.assertFalse(cfg.use_claude)
3133
self.assertIsNone(cfg.model_id)
32-
self.assertEqual(cfg.max_steps, 10)
34+
self.assertEqual(cfg.max_steps, default_max_steps())
3335
self.assertTrue(cfg.scratchpad_db_path)
3436

3537

hub/agents/python/blender/gaia_agent_blender/agent.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __init__(
3030
mcp: Optional[MCPClient] = None,
3131
model_id: str = None,
3232
base_url: str = "http://localhost:13305/api/v1",
33-
max_steps: int = 5,
33+
max_steps: Optional[int] = None,
3434
debug_prompts: bool = False,
3535
output_dir: str = None,
3636
streaming: bool = False,
@@ -526,10 +526,11 @@ def create_interactive_scene(
526526
Returns:
527527
Dict containing the scene creation result
528528
"""
529-
# Same process as process_query but with more steps allowed if specified
529+
# When the caller doesn't override, fall back to the agent's configured
530+
# max_steps (the global default unless explicitly set at construction).
530531
return self.process_query(
531532
f"Create a complete 3D scene with the following description: {scene_description}",
532-
max_steps=max_steps if max_steps is not None else self.max_steps * 2,
533+
max_steps=max_steps,
533534
trace=trace,
534535
filename=filename,
535536
)

hub/agents/python/browser/gaia_agent_browser/agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
# SPDX-License-Identifier: MIT
33
"""Browser-focused GAIA agent."""
44

5-
from dataclasses import dataclass
5+
from dataclasses import dataclass, field
66
from typing import List, Optional
77

8-
from gaia.agents.base.agent import Agent
8+
from gaia.agents.base.agent import Agent, default_max_steps
99
from gaia.agents.base.tools import _TOOL_REGISTRY
1010
from gaia.agents.tools import BrowserToolsMixin
1111
from gaia.mcp.mixin import MCPClientMixin
@@ -20,7 +20,7 @@ class BrowserAgentConfig:
2020
claude_model: str = "claude-sonnet-4-20250514"
2121
base_url: Optional[str] = None
2222
model_id: Optional[str] = None
23-
max_steps: int = 10
23+
max_steps: int = field(default_factory=default_max_steps)
2424
streaming: bool = False
2525
debug: bool = False
2626
debug_prompts: bool = False

hub/agents/python/code/gaia_agent_code/agent.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,10 @@ def __init__(
115115
self.language = language
116116
self.project_type = project_type
117117

118-
# Default to more steps for complex workflows
119-
if "max_steps" not in kwargs:
118+
# Default to more steps for complex workflows. Treat an explicit None
119+
# (the CLI's "use the default" sentinel) the same as omitted, so this
120+
# override isn't silently dropped to the global default.
121+
if kwargs.get("max_steps") is None:
120122
kwargs["max_steps"] = 100 # Increased for complex project generation
121123
# Use the coding model for better code understanding
122124
if "model_id" not in kwargs:

hub/agents/python/code/tests/test_code_agent.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ def test_agent_initialization(self):
6868
self.assertTrue(agent2.debug)
6969
self.assertTrue(agent2.show_prompts)
7070

71+
# Explicit max_steps=None is the CLI "use the default" sentinel and must
72+
# NOT drop CodeAgent's 100-step override to the global default.
73+
agent3 = CodeAgent(max_steps=None)
74+
self.assertEqual(agent3.max_steps, 100)
75+
7176
def test_system_prompt(self):
7277
"""Test that system prompt is properly generated."""
7378
prompt = self.agent._get_system_prompt()

hub/agents/python/connectors-demo/gaia_agent_connectors_demo/agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@
3636

3737
import json
3838
import os
39-
from dataclasses import dataclass
39+
from dataclasses import dataclass, field
4040
from datetime import datetime, time
4141
from typing import Any, ClassVar, Dict, List, Optional
4242

4343
import httpx
4444

45-
from gaia.agents.base.agent import Agent
45+
from gaia.agents.base.agent import Agent, default_max_steps
4646
from gaia.agents.base.console import AgentConsole
4747
from gaia.agents.base.tools import tool
4848
from gaia.connectors.errors import ConnectorsError
@@ -295,7 +295,7 @@ class ConnectorsDemoAgentConfig:
295295

296296
base_url: Optional[str] = None
297297
model_id: Optional[str] = None
298-
max_steps: int = 6
298+
max_steps: int = field(default_factory=default_max_steps)
299299
streaming: bool = False
300300
debug: bool = False
301301
show_stats: bool = False

hub/agents/python/docker/gaia_agent_docker/agent.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from pathlib import Path
1515
from typing import Any, Dict
1616

17+
from gaia.agents.base.agent import default_max_steps
1718
from gaia.agents.base.console import AgentConsole, SilentConsole
1819
from gaia.agents.base.mcp_agent import MCPAgent
1920
from gaia.agents.base.tools import tool
@@ -22,7 +23,6 @@
2223
logger = logging.getLogger(__name__)
2324

2425
DEFAULT_MODEL = "Qwen3.5-35B-A3B-GGUF"
25-
DEFAULT_MAX_STEPS = 10
2626
DEFAULT_PORT = 8080
2727

2828

@@ -45,7 +45,7 @@ def __init__(self, **kwargs):
4545
4646
Args:
4747
**kwargs: Agent initialization parameters:
48-
- max_steps: Maximum conversation steps (default: 10)
48+
- max_steps: Maximum conversation steps (default: global default_max_steps())
4949
- model_id: LLM model to use (default: Qwen3.5-35B-A3B-GGUF)
5050
- silent_mode: Suppress console output (default: False)
5151
- debug: Enable debug logging (default: False)
@@ -55,8 +55,10 @@ def __init__(self, **kwargs):
5555
if "model_id" not in kwargs:
5656
kwargs["model_id"] = DEFAULT_MODEL
5757

58-
if "max_steps" not in kwargs:
59-
kwargs["max_steps"] = DEFAULT_MAX_STEPS
58+
# An explicit None (the CLI's "use the default" sentinel) counts as no
59+
# override and falls through to the global default.
60+
if kwargs.get("max_steps") is None:
61+
kwargs["max_steps"] = default_max_steps()
6062

6163
# Security: Configure allowed paths for file operations
6264
# If None, allow current directory and subdirectories

hub/agents/python/emr/gaia_agent_emr/agent.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,11 @@ def __init__(
130130
# Signature: callback(filename, step_num, total_steps, step_name, status)
131131
self._progress_callback: Optional[callable] = None
132132

133-
# Set reasonable defaults for agent - higher max_steps for interactive use
134-
kwargs.setdefault("max_steps", 50)
133+
# Set reasonable defaults for agent - higher max_steps for interactive
134+
# use. Treat an explicit None (the CLI "use the default" sentinel) the
135+
# same as omitted so this override isn't dropped to the global default.
136+
if kwargs.get("max_steps") is None:
137+
kwargs["max_steps"] = 50
135138

136139
super().__init__(**kwargs)
137140

0 commit comments

Comments
 (0)