core/tools: search a list of sources, DeepSeek's index then Bing

``search_sources`` (a provider list, ``["deepseek", "bing"]`` by default,
``[]`` to not offer the tool at all) names what the local ``web_search`` tool
searches with, in fallback order: ``search_in_order`` asks each source in turn
and keeps the first answer, so a source that errors — a rate limit, a rejected
key — or finds nothing hands over to the next instead of failing the tool. The
handler reports the failures only when every source came up empty.

The Bing leg answers with its RSS results (``?format=rss``), which need no key
and carry a description — the snippet DeepSeek's own index does not hand out —
and it goes through the ``fetch`` client, so the SSRF and host-grant policy
applies to it too (a host that binds tool instance state, as the CLI does).
This commit is contained in:
2026-10-07 00:09:46 +08:00
parent 19ab86f590
commit 2bb6e06171
9 changed files with 348 additions and 56 deletions
+14 -1
View File
@@ -21,10 +21,15 @@ def _effective(
convention: str = "",
url: str = "",
tools: list[dict[str, object]] | None = None,
sources: list[str] | None = None,
) -> EffectiveProvider:
provider = DeepseekProvider(access_key_or_token="sk-test", convention=convention, url=url)
effective = resolve_effective_provider(provider, model="deepseek-flash")
return effective if tools is None else replace(effective, provider_tools=tools)
if tools is not None:
effective = replace(effective, provider_tools=tools)
if sources is not None:
effective = replace(effective, search_sources=sources)
return effective
def test_a_deepseek_provider_asks_for_a_search_tool_like_openai_does() -> None:
@@ -45,6 +50,14 @@ def test_without_provider_tools_there_is_no_search() -> None:
assert local_web_search(_effective(convention="responses", tools=[])) is False
def test_without_search_sources_there_is_no_local_tool() -> None:
# ``search_sources = []`` leaves the tool out even though provider_tools ask
# for a search (the hosted path on the Anthropic convention is unaffected).
assert _effective(convention="responses", sources=[]).search_sources == []
assert local_web_search(_effective(convention="responses", sources=[])) is False
assert _effective(convention="responses").search_sources == ["deepseek", "bing"]
def test_search_base_url_prefers_an_anthropic_endpoint() -> None:
assert deepseek_search_base_url(_effective(convention="responses")) == "https://api.deepseek.com/anthropic"
assert (