mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 23:47:54 +08:00
116 lines
4.3 KiB
Python
116 lines
4.3 KiB
Python
|
|
"""DeepSeek server-side search backend (Anthropic-compatible endpoint)."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import msgspec
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from plyngent.lmproto.anthropic.config import AnthropicConfig
|
||
|
|
from plyngent.lmproto.deepseek.anthropic.search import (
|
||
|
|
DeepseekSearchClient,
|
||
|
|
SearchHit,
|
||
|
|
hits_from_content,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_hits_from_content_dedupes_and_skips_noise() -> None:
|
||
|
|
content = [
|
||
|
|
{"type": "thinking", "thinking": "search"},
|
||
|
|
{
|
||
|
|
"type": "web_search_tool_result",
|
||
|
|
"tool_use_id": "call_1",
|
||
|
|
"content": [
|
||
|
|
{"type": "web_search_result", "title": "First", "url": "https://a.example/x"},
|
||
|
|
{"type": "web_search_result", "title": "First again", "url": "https://a.example/x"},
|
||
|
|
{"type": "web_search_result", "url": "https://b.example/y"},
|
||
|
|
{"type": "web_search_result", "title": "no url"},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "web_search_tool_result",
|
||
|
|
"tool_use_id": "call_2",
|
||
|
|
"content": [{"type": "web_search_tool_result_error", "error_code": "max_uses_exceeded"}],
|
||
|
|
},
|
||
|
|
]
|
||
|
|
hits = hits_from_content(content)
|
||
|
|
assert hits == [
|
||
|
|
SearchHit(title="First", url="https://a.example/x"),
|
||
|
|
SearchHit(title="", url="https://b.example/y"),
|
||
|
|
]
|
||
|
|
assert hits_from_content("not a list") == []
|
||
|
|
|
||
|
|
|
||
|
|
class _Resp:
|
||
|
|
def __init__(self, status_code: int, body: bytes) -> None:
|
||
|
|
self.status_code = status_code
|
||
|
|
self.content = body
|
||
|
|
|
||
|
|
|
||
|
|
def _search_body() -> bytes:
|
||
|
|
return msgspec.json.encode(
|
||
|
|
{
|
||
|
|
"id": "msg_1",
|
||
|
|
"content": [
|
||
|
|
{
|
||
|
|
"type": "server_tool_use",
|
||
|
|
"id": "call_00_1",
|
||
|
|
"name": "web_search",
|
||
|
|
"input": {"query": "DeepSeek-V4"},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"type": "web_search_tool_result",
|
||
|
|
"tool_use_id": "call_00_1",
|
||
|
|
"content": [
|
||
|
|
{
|
||
|
|
"type": "web_search_result",
|
||
|
|
"title": "DeepSeek-V4 preview",
|
||
|
|
"url": "https://api-docs.deepseek.com/news/news260424/",
|
||
|
|
"encrypted_content": "opaque",
|
||
|
|
}
|
||
|
|
],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
"usage": {"input_tokens": 9932, "output_tokens": 377},
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_search_sends_the_hosted_tool_and_reads_its_hits(monkeypatch: pytest.MonkeyPatch) -> None:
|
||
|
|
client = DeepseekSearchClient(AnthropicConfig(api_key="sk-test", base_url="https://api.deepseek.com/anthropic"))
|
||
|
|
sent: dict[str, object] = {}
|
||
|
|
|
||
|
|
async def fake_post(path: str, **kwargs: object) -> _Resp:
|
||
|
|
sent["path"] = path
|
||
|
|
sent.update(kwargs)
|
||
|
|
return _Resp(200, _search_body())
|
||
|
|
|
||
|
|
monkeypatch.setattr(client.session, "post", fake_post)
|
||
|
|
hits = await client.search("DeepSeek-V4", model="deepseek-flash")
|
||
|
|
|
||
|
|
assert hits == [SearchHit(title="DeepSeek-V4 preview", url="https://api-docs.deepseek.com/news/news260424/")]
|
||
|
|
assert sent["path"] == "/messages"
|
||
|
|
assert sent["stream"] is False
|
||
|
|
data = sent["data"]
|
||
|
|
assert isinstance(data, bytes)
|
||
|
|
payload = msgspec.json.decode(data, type=dict[str, object])
|
||
|
|
assert payload["tools"] == [{"type": "web_search_20250305", "name": "web_search", "max_uses": 3}]
|
||
|
|
# A search turn stops as soon as it has searched; the answer is not needed.
|
||
|
|
assert payload["max_tokens"] == 512
|
||
|
|
assert payload["messages"] == [{"role": "user", "content": "DeepSeek-V4"}]
|
||
|
|
headers = sent["headers"]
|
||
|
|
assert isinstance(headers, dict)
|
||
|
|
assert headers["x-api-key"] == "sk-test"
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_search_raises_with_the_endpoint_detail(monkeypatch: pytest.MonkeyPatch) -> None:
|
||
|
|
client = DeepseekSearchClient(AnthropicConfig(api_key="sk-test", base_url="https://api.deepseek.com/anthropic"))
|
||
|
|
|
||
|
|
async def fake_post(path: str, **kwargs: object) -> _Resp:
|
||
|
|
return _Resp(422, b'{"error": {"message": "tools[0]: unknown variant"}}')
|
||
|
|
|
||
|
|
monkeypatch.setattr(client.session, "post", fake_post)
|
||
|
|
with pytest.raises(RuntimeError, match="unknown variant"):
|
||
|
|
_ = await client.search("DeepSeek-V4", model="deepseek-flash")
|