2026-10-06 23:45:35 +08:00
|
|
|
"""The routing decisions around a provider's API surface."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from dataclasses import replace
|
|
|
|
|
|
|
|
|
|
from plyngent.config.models import DeepseekProvider
|
|
|
|
|
from plyngent.config.routing import (
|
|
|
|
|
EffectiveProvider,
|
2026-10-17 16:52:31 +08:00
|
|
|
deepseek_api_base_url,
|
2026-10-06 23:45:35 +08:00
|
|
|
local_web_search,
|
|
|
|
|
resolve_effective_provider,
|
|
|
|
|
wants_web_search,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
SEARCH_TOOLS: list[dict[str, object]] = [{"type": "web_search"}]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _effective(
|
|
|
|
|
*,
|
|
|
|
|
convention: str = "",
|
|
|
|
|
url: str = "",
|
|
|
|
|
tools: list[dict[str, object]] | None = None,
|
2026-10-07 00:09:46 +08:00
|
|
|
sources: list[str] | None = None,
|
2026-10-06 23:45:35 +08:00
|
|
|
) -> EffectiveProvider:
|
|
|
|
|
provider = DeepseekProvider(access_key_or_token="sk-test", convention=convention, url=url)
|
|
|
|
|
effective = resolve_effective_provider(provider, model="deepseek-flash")
|
2026-10-07 00:09:46 +08:00
|
|
|
if tools is not None:
|
|
|
|
|
effective = replace(effective, provider_tools=tools)
|
|
|
|
|
if sources is not None:
|
|
|
|
|
effective = replace(effective, search_sources=sources)
|
|
|
|
|
return effective
|
2026-10-06 23:45:35 +08:00
|
|
|
|
|
|
|
|
|
2026-10-06 23:59:01 +08:00
|
|
|
def test_a_deepseek_provider_asks_for_a_search_tool_like_openai_does() -> None:
|
|
|
|
|
# ``provider_tools`` defaults to web_search for both presets; ``[]`` disables.
|
|
|
|
|
assert wants_web_search(_effective()) is True
|
|
|
|
|
assert wants_web_search(_effective(tools=[])) is False
|
2026-10-06 23:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_chat_and_responses_conventions_use_the_local_tool() -> None:
|
|
|
|
|
# Only the Anthropic surface hosts a search; the others ignore a hosted tool,
|
2026-10-06 23:59:01 +08:00
|
|
|
# so the default web_search becomes plyngent's own tool there.
|
|
|
|
|
assert local_web_search(_effective(convention="")) is True
|
|
|
|
|
assert local_web_search(_effective(convention="responses")) is True
|
|
|
|
|
assert local_web_search(_effective(convention="anthropic")) is False
|
2026-10-06 23:45:35 +08:00
|
|
|
|
|
|
|
|
|
2026-10-17 16:52:31 +08:00
|
|
|
def test_provider_tools_only_gate_the_hosted_search() -> None:
|
|
|
|
|
# ``search_sources`` owns the local tool; ``provider_tools`` owns the hosted
|
|
|
|
|
# one. Turning hosted tools off must not take local search with it.
|
|
|
|
|
assert local_web_search(_effective(convention="responses", tools=[])) is True
|
|
|
|
|
assert local_web_search(_effective(convention="anthropic", tools=[])) is True
|
|
|
|
|
# With hosted tools on, the Anthropic surface keeps the hosted search and
|
|
|
|
|
# the local tool stays out of the way.
|
|
|
|
|
assert local_web_search(_effective(convention="anthropic")) is False
|
2026-10-06 23:45:35 +08:00
|
|
|
|
|
|
|
|
|
2026-10-07 00:09:46 +08:00
|
|
|
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
|
2026-10-17 17:06:56 +08:00
|
|
|
assert _effective(convention="responses").search_sources == ["deepseek"]
|
2026-10-07 00:09:46 +08:00
|
|
|
|
|
|
|
|
|
2026-10-17 16:52:31 +08:00
|
|
|
def test_api_base_url_drops_the_convention_and_version_paths() -> None:
|
|
|
|
|
# The Search API appends ``/v1/tools/search`` itself, so neither the
|
|
|
|
|
# Anthropic convention's ``/anthropic`` nor an OpenAI-compatible ``/v1``
|
|
|
|
|
# may survive in the base.
|
|
|
|
|
assert deepseek_api_base_url(_effective(convention="responses")) == "https://api.deepseek.com"
|
|
|
|
|
assert deepseek_api_base_url(_effective(convention="anthropic")) == "https://api.deepseek.com"
|
|
|
|
|
assert (
|
|
|
|
|
deepseek_api_base_url(_effective(convention="anthropic", url="https://gateway.example/api/anthropic"))
|
|
|
|
|
== "https://gateway.example/api"
|
|
|
|
|
)
|
2026-10-06 23:45:35 +08:00
|
|
|
assert (
|
2026-10-17 16:52:31 +08:00
|
|
|
deepseek_api_base_url(_effective(convention="responses", url="https://gateway.example/v1"))
|
|
|
|
|
== "https://gateway.example"
|
2026-10-06 23:45:35 +08:00
|
|
|
)
|