Skip to content
Open
3 changes: 1 addition & 2 deletions benchmarks/clawbench/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def __init__(
model=self.model,
max_iterations=max_iterations,
context_window_tokens=self.context_window,
brave_api_key=self.config.tools.web.search.api_key or None,
web_search_config=self.config.tools.web.search,
jina_api_key=self.config.tools.web.jina_api_key or None,
web_proxy=self.config.tools.web.proxy or None,
exec_config=self.config.tools.exec,
Expand All @@ -152,7 +152,6 @@ def __init__(
mcp_servers={},
sandbox_config=self.config.tools.sandbox,
channels_config=self.config.channels,
everos_config=self.config.agents.defaults.everos,
context_config=context_config,
# Benchmarks are non-interactive batch runs — opt out of Bug2's
# per-turn shadow-git checkpoint (no recovery channel to inject
Expand Down
33 changes: 27 additions & 6 deletions raven/agent/loop/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@
RuntimeConfig,
SkillForgeRouterConfig,
)
from raven.config.schema import AskUserToolConfig, ChannelsConfig, DeepResearchToolConfig, ExecToolConfig
from raven.config.schema import (
AskUserToolConfig,
ChannelsConfig,
DeepResearchToolConfig,
ExecToolConfig,
WebSearchConfig,
)
from raven.context_engine import ContextEngine
from raven.memory_engine.backend import MemoryBackend
from raven.proactive_engine.schedulers.cron.service import CronService
Expand Down Expand Up @@ -266,7 +272,7 @@ def __init__(
model: str | None = None,
max_iterations: int = 40,
context_window_tokens: int | None = None,
brave_api_key: str | None = None,
web_search_config: "WebSearchConfig | None" = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This rename misses a caller. benchmarks/clawbench/stream.py:146 still passes brave_api_key=self.config.tools.web.search.api_key or None, and AgentLoop.__init__ takes no **kwargs, so every clawbench run now dies before the first task:

$ uv run python -c "from raven.agent.loop import AgentLoop; AgentLoop(provider=None, workspace='.', brave_api_key='k')"
TypeError: AgentLoop.__init__() got an unexpected keyword argument 'brave_api_key'

Ruff will not catch it (wrong kwarg, not a lint), and no test constructs that harness, so CI stays green while the benchmark entry point is broken. The one-line fix mirrors the three CLI sites:

web_search_config=self.config.tools.web.search,

Worth grepping for brave_api_key once more before merge -- that is the only remaining hit today.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in cc715dc, and better than I asked for. benchmarks/clawbench/stream.py:146 now passes web_search_config=self.config.tools.web.search, and you also dropped the everos_config= line next to it -- which I had missed. That one is the more interesting half: everos_config is not in AgentLoop.__init__ either, so clawbench was already dead on the target branch and my "this PR breaks it" framing was only half right.

The static guard is the right shape for this. I checked it is not vacuous -- reverting the kwarg makes both tests fail with the offending file and line named:

$ uv run pytest tests/test_benchmark_entry_points.py -q   # with brave_api_key= put back
FAILED test_the_benchmarks_still_construct_agentloop_with_parameters_it_has
FAILED test_a_retired_parameter_is_gone_from_the_benchmarks_too[brave_api_key]
2 failed, 1 passed

and on the tree as it stands, 3 passed -- it finds all four AgentLoop( call sites under benchmarks/, so pinchbench and appworld are covered too, not just clawbench. The assert seen guard is what keeps it honest if the tree moves.

One thing this does not reach: it matches ast.Name only, so a module.AgentLoop(...) call would slip past. Nothing in benchmarks/ calls it that way today, so it is a note, not a request.

web_proxy: str | None = None,
exec_config: ExecToolConfig | None = None,
ask_user_config: AskUserToolConfig | None = None,
Expand Down Expand Up @@ -372,11 +378,11 @@ def __init__(
self.max_iterations = max_iterations
# Empty-response recovery budgets. None → enabled defaults.
self._recovery_limits = empty_recovery if empty_recovery is not None else RecoveryLimits()
self.brave_api_key = brave_api_key
self.jina_api_key = jina_api_key
self.web_proxy = web_proxy
from raven.config.schema import DeepResearchToolConfig, MediaGenConfig
from raven.config.schema import DeepResearchToolConfig, MediaGenConfig, WebSearchConfig

self.web_search_config = web_search_config or WebSearchConfig()
self.media_config = media_config or MediaGenConfig()
self.deep_research_config = deep_research_config or DeepResearchToolConfig()
self.exec_config = exec_config or ExecToolConfig()
Expand Down Expand Up @@ -522,7 +528,7 @@ def __init__(
provider=provider,
workspace=workspace,
model=self._default_binding.model,
brave_api_key=brave_api_key,
web_search_config=web_search_config,
jina_api_key=jina_api_key,
web_proxy=web_proxy,
exec_config=self.exec_config,
Expand Down Expand Up @@ -848,7 +854,22 @@ def _register_default_tools(self) -> None:
extra_deny_patterns=self.exec_config.extra_deny_patterns,
)
)
self.tools.register(WebSearchTool(api_key=self.brave_api_key, proxy=self.web_proxy))
# web_search needs a Serper key it does not have by default, and offering
# it anyway is worse than withholding it: the model reaches for it, the
# call fails, and the error text -- naming a config file and an env var --
# gets relayed to whoever is on the other end of the channel. Ask the tool
# rather than the config, because it resolves the key at call time from
# either source; gating on the configured key alone would withdraw the
# tool from a deploy that only exports SERPER_API_KEY.
web_search = WebSearchTool(
api_key=self.web_search_config.api_key or None,
max_results=self.web_search_config.max_results,
proxy=self.web_proxy,
)
if web_search.api_key:
self.tools.register(web_search)
# web_fetch is unconditional by contrast: it works without a key, and the
# Jina one only upgrades the extraction.
self.tools.register(WebFetchTool(api_key=self.jina_api_key, proxy=self.web_proxy))
# Media tools (image/speech/video) are opt-in: a tool is registered only
# when the user configured it (a model or apiKey under tools.media.<tool>),
Expand Down
17 changes: 13 additions & 4 deletions raven/agent/subagent/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from raven.agent.tools.registry import ToolRegistry
from raven.agent.tools.shell import ExecTool
from raven.agent.tools.web import WebFetchTool, WebSearchTool
from raven.config.schema import ExecToolConfig
from raven.config.schema import ExecToolConfig, WebSearchConfig
from raven.providers.base import LLMProvider
from raven.providers.binding import ModelBinding, resolve
from raven.sandbox import SandboxConfig, build_executor
Expand All @@ -39,7 +39,7 @@ def __init__(
provider: LLMProvider,
workspace: Path,
model: str | None = None,
brave_api_key: str | None = None,
web_search_config: "WebSearchConfig | None" = None,
web_proxy: str | None = None,
exec_config: "ExecToolConfig | None" = None,
restrict_to_workspace: bool = False,
Expand All @@ -59,7 +59,7 @@ def __init__(
# SUBAGENT-origin turn.
self._submit = None
self._fallback = ModelBinding(provider, model or provider.get_default_model())
self.brave_api_key = brave_api_key
self.web_search_config = web_search_config or WebSearchConfig()
self.jina_api_key = jina_api_key
self.web_proxy = web_proxy
self.exec_config = exec_config or ExecToolConfig()
Expand Down Expand Up @@ -202,7 +202,16 @@ async def _run_subagent_inner(
extra_deny_patterns=self.exec_config.extra_deny_patterns,
)
)
tools.register(WebSearchTool(api_key=self.brave_api_key, proxy=self.web_proxy))
# Withheld without a key, same as the main loop: a sub-agent that
# reaches for a search it cannot run reports the failure to its
# caller, and that text ends up in the parent turn.
web_search = WebSearchTool(
api_key=self.web_search_config.api_key or None,
max_results=self.web_search_config.max_results,
proxy=self.web_proxy,
)
if web_search.api_key:
tools.register(web_search)
tools.register(WebFetchTool(api_key=self.jina_api_key, proxy=self.web_proxy))

system_prompt = self._build_subagent_prompt()
Expand Down
Loading
Loading