config: default DeepSeek to web_search and honour its provider_tools

``provider_tools`` reached the effective provider only for the OpenAI preset,
so a DeepSeek ``provider_tools`` in TOML was dropped on the way — and the preset
defaulted to none, leaving search unavailable for every convention.

DeepSeek now defaults to ``[{type="web_search"}]`` like OpenAI does (``[]``
turns it off) and its list reaches the agent: the Anthropic convention runs the
hosted search inside the response, the Responses and chat conventions get the
local ``web_search`` tool (see ``config.routing.local_web_search``).
This commit is contained in:
2026-10-06 23:59:01 +08:00
parent 27a901083a
commit e877df9d94
3 changed files with 22 additions and 13 deletions
+9 -3
View File
@@ -332,8 +332,8 @@ def _default_openai_models() -> dict[str, ModelConfig]:
}
def _default_openai_provider_tools() -> list[dict[str, Any]]:
"""Hosted tools when TOML omits ``provider_tools``."""
def _default_provider_tools() -> list[dict[str, Any]]:
"""Hosted tools when TOML omits ``provider_tools`` (OpenAI and DeepSeek)."""
return [{"type": "web_search"}]
@@ -352,7 +352,7 @@ class OpenAIProvider(ProviderConfig, tag="openai"):
"""
models: dict[str, ModelConfig] = field(default_factory=_default_openai_models)
provider_tools: list[dict[str, Any]] = field(default_factory=_default_openai_provider_tools)
provider_tools: list[dict[str, Any]] = field(default_factory=_default_provider_tools)
class OpenAICompatibleProvider(ProviderConfig, tag="openai-compatible"):
@@ -380,12 +380,18 @@ class DeepseekProvider(ProviderConfig, tag="deepseek"):
- ``"anthropic"`` → Anthropic Messages API (``POST /messages`` on
``https://api.deepseek.com/anthropic``)
``provider_tools`` asks for a search tool like OpenAI's does (``[]``
disables it). DeepSeek runs a hosted search only on the Anthropic surface, so
the other conventions get a local ``web_search`` tool instead (see
``config.routing.local_web_search``).
``extras`` keeps arbitrary provider keys; the legacy ``extras.convention``
key is parsed but ignored (prefer the typed ``convention`` field).
"""
models: dict[str, ModelConfig] = field(default_factory=_default_deepseek_models)
convention: DeepSeekConvention = ""
provider_tools: list[dict[str, Any]] = field(default_factory=_default_provider_tools)
extras: dict[str, str] = field(default_factory=dict)
+5 -1
View File
@@ -147,8 +147,12 @@ def resolve_effective_provider(provider: Provider, *, model: str | None = None)
else:
url = default_url_for_preset(preset)
# Hosted tools belong to the preset that declares them: OpenAI's Responses
# surface, and DeepSeek's (whose Anthropic surface runs the search inside the
# response while its other surfaces get a local tool — see
# :func:`local_web_search`).
provider_tools: list[dict[str, object]] = []
if preset == "openai" and isinstance(provider, OpenAIProvider) and parent_preset == "openai":
if parent_preset == preset and isinstance(provider, OpenAIProvider | DeepseekProvider):
provider_tools = [dict(item) for item in provider.provider_tools]
return EffectiveProvider(