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
7 changes: 2 additions & 5 deletions agent/agent_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,7 @@ def init_agent(
command: str = None,
args: list[str] | None = None,
model: str = "",
max_iterations: int = 500, # Default tool-calling iterations (shared with subagents)
tool_delay: float = 1.0,
max_iterations: int = 90, # Default tool-calling iterations (shared with subagents)
enabled_toolsets: List[str] = None,
disabled_toolsets: List[str] = None,
save_trajectories: bool = False,
Expand Down Expand Up @@ -529,8 +528,7 @@ def init_agent(
requested_provider (str): Original provider identity before runtime canonicalization
api_mode (str): API mode override: "chat_completions" or "codex_responses"
model (str): Model name to use (default: "anthropic/claude-opus-4.6")
max_iterations (int): Maximum number of tool calling iterations (default: 500)
tool_delay (float): Delay between tool calls in seconds (default: 1.0)
max_iterations (int): Maximum number of tool calling iterations (default: 90)
enabled_toolsets (List[str]): Only enable tools from these toolsets (optional)
disabled_toolsets (List[str]): Disable tools from these toolsets (optional)
save_trajectories (bool): Whether to save conversation trajectories to JSONL files (default: False)
Expand Down Expand Up @@ -576,7 +574,6 @@ def init_agent(
# Shared iteration budget — parent creates, children inherit.
# Consumed by every LLM turn across parent + all subagents.
agent.iteration_budget = iteration_budget or IterationBudget(max_iterations)
agent.tool_delay = tool_delay
agent.save_trajectories = save_trajectories
agent.verbose_logging = verbose_logging
agent.quiet_mode = quiet_mode
Expand Down
3 changes: 0 additions & 3 deletions agent/tool_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1980,9 +1980,6 @@ def _execute(next_args: dict) -> Any:
return
break

if agent.tool_delay > 0 and i < len(assistant_message.tool_calls):
time.sleep(agent.tool_delay)

# ── Per-turn aggregate budget enforcement ─────────────────────────
num_tools_seq = len(assistant_message.tool_calls)
if finalize and num_tools_seq > 0:
Expand Down
13 changes: 10 additions & 3 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import time
import threading
import uuid
import warnings
from typing import List, Dict, Any, Optional, Callable
# NOTE: `from openai import OpenAI` is deliberately NOT at module top — the
# SDK pulls ~240 ms of imports. We expose `OpenAI` as a thin proxy object
Expand Down Expand Up @@ -437,8 +438,8 @@ def __init__(
command: str = None,
args: list[str] | None = None,
model: str = "",
max_iterations: int = 500, # Default tool-calling iterations (shared with subagents)
tool_delay: float = 1.0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please retain this as an accepted deprecated no-op keyword for a release. AIAgent is a documented programmatic integration surface, so deleting the keyword turns existing AIAgent(tool_delay=0) callers into an upgrade-time TypeError.

max_iterations: int = 90, # Default tool-calling iterations (shared with subagents)
tool_delay: float = None, # Deprecated: accepted for compatibility, ignored
enabled_toolsets: List[str] = None,
disabled_toolsets: List[str] = None,
save_trajectories: bool = False,
Expand Down Expand Up @@ -502,6 +503,13 @@ def __init__(
requested_provider: str = None,
):
"""Forwarder — see ``agent.agent_init.init_agent``."""
if tool_delay is not None:
warnings.warn(
"tool_delay is deprecated and ignored; sequential tool calls "
"no longer sleep between executions.",
DeprecationWarning,
stacklevel=2,
)
from agent.agent_init import init_agent
init_agent(
self,
Expand All @@ -516,7 +524,6 @@ def __init__(
args=args,
model=model,
max_iterations=max_iterations,
tool_delay=tool_delay,
enabled_toolsets=enabled_toolsets,
disabled_toolsets=disabled_toolsets,
save_trajectories=save_trajectories,
Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_1630_context_overflow_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ def _make_agent(self):
a.client = MagicMock()
a._cached_system_prompt = "You are helpful."
a._use_prompt_caching = False
a.tool_delay = 0
a.compression_enabled = False
return a

Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_413_compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ def agent():
a.client = MagicMock()
a._cached_system_prompt = "You are helpful."
a._use_prompt_caching = False
a.tool_delay = 0
# Default matches production (`compression.enabled` defaults to True).
# Overflow-recovery tests below verify that 413 / context-overflow
# errors DO trigger compression; the disabled-path behavior is
Expand Down
2 changes: 0 additions & 2 deletions tests/run_agent/test_conversation_fallback_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ def test_substantive_tool_only_turn_invalidates_older_housekeeping_fallback():

agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False
agent.valid_tool_names = {"todo", "web_search"}
Expand Down Expand Up @@ -147,7 +146,6 @@ def test_housekeeping_only_turn_still_sets_fallback():

agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False
agent.valid_tool_names = {"memory"}
Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_malformed_tool_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def _make_agent() -> AIAgent:
skip_memory=True,
)
agent.client = MagicMock()
agent.tool_delay = 0
agent._flush_messages_to_session_db = MagicMock()
return agent

Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_nonretryable_error_html_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ def _make_agent() -> AIAgent:
a.client = MagicMock()
a._cached_system_prompt = "You are helpful."
a._use_prompt_caching = False
a.tool_delay = 0
a.compression_enabled = False
a.save_trajectories = False
return a
Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_partial_stream_finish_reason.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,6 @@ def loop_agent():
a.client = MagicMock()
a._cached_system_prompt = "You are helpful."
a._use_prompt_caching = False
a.tool_delay = 0
a.compression_enabled = False
a.save_trajectories = False
return a
Expand Down
38 changes: 35 additions & 3 deletions tests/run_agent/test_run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,25 @@ def test_anthropic_base_url_accepted(self):
assert agent.api_mode == "anthropic_messages"
mock_anthropic.Anthropic.assert_called_once()

def test_tool_delay_kwarg_is_deprecated_noop(self):
"""tool_delay stays accepted for compatibility but warns and is ignored."""
with (
patch("run_agent.get_tool_definitions", return_value=[]),
patch("run_agent.check_toolset_requirements", return_value={}),
patch("run_agent.OpenAI"),
):
with pytest.warns(DeprecationWarning, match="tool_delay"):
a = AIAgent(
api_key="test-key-1234567890",
base_url="https://openrouter.ai/api/v1",
tool_delay=0,
quiet_mode=True,
skip_context_files=True,
skip_memory=True,
)
# The value is discarded — nothing downstream reads it anymore.
assert not hasattr(a, "tool_delay")

def test_prompt_caching_claude_openrouter(self):
"""Claude model via OpenRouter should enable prompt caching."""
with (
Expand Down Expand Up @@ -2392,6 +2411,22 @@ def test_single_tool_executed(self, agent):
assert messages[0]["role"] == "tool"
assert "search result" in messages[0]["content"]

def test_sequential_tool_calls_run_without_delay(self, agent):
"""Two sequential tool calls execute back-to-back with no sleep between them."""
tc1 = _mock_tool_call(name="web_search", arguments="{}", call_id="c1")
tc2 = _mock_tool_call(name="web_search", arguments="{}", call_id="c2")
mock_msg = _mock_assistant_msg(content="", tool_calls=[tc1, tc2])
messages = []
with (
patch("run_agent.handle_function_call", return_value="ok") as mock_hfc,
patch("agent.tool_executor.time.sleep") as mock_sleep,
):
agent._execute_tool_calls_sequential(mock_msg, messages, "task-1")
assert mock_hfc.call_count == 2
mock_sleep.assert_not_called()
tool_results = [m for m in messages if m["role"] == "tool"]
assert [m["tool_call_id"] for m in tool_results] == ["c1", "c2"]

def test_sequential_memory_remove_notifies_provider_with_tool_result(self, agent):
old_text = "stale preference entry"
tc = _mock_tool_call(
Expand Down Expand Up @@ -4414,7 +4449,6 @@ def _setup_agent(self, agent):
"""Common setup for run_conversation tests."""
agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False

Expand Down Expand Up @@ -6697,7 +6731,6 @@ class TestRetryExhaustion:
def _setup_agent(self, agent):
agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False

Expand Down Expand Up @@ -8698,7 +8731,6 @@ class TestReasoningReplayForStrictProviders:
def _setup_agent(self, agent):
agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False

Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_tool_call_guardrail_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ def _make_agent(*tool_names: str, max_iterations: int = 10, config: dict | None
agent.client = MagicMock()
agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False
return agent
Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_tool_call_incremental_persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def _make_agent():
agent.client = MagicMock()
agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False
return agent
Expand Down
1 change: 0 additions & 1 deletion tests/run_agent/test_turn_completion_explainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ def _make_agent(max_iterations: int = 10, config: dict | None = None) -> AIAgent
agent.client = MagicMock()
agent._cached_system_prompt = "You are helpful."
agent._use_prompt_caching = False
agent.tool_delay = 0
agent.compression_enabled = False
agent.save_trajectories = False
# No fallback chain so empty responses exhaust deterministically.
Expand Down
Loading