102 lines
3.1 KiB
Python
102 lines
3.1 KiB
Python
import pytest
|
|
import httpx
|
|
|
|
from genesis.inference.client import HttpLLMClient, LLMClient
|
|
from genesis.inference.exceptions import LLMNetworkError, LLMNotConfiguredError, LLMTimeoutError
|
|
from genesis.inference.types import ChatMessage
|
|
|
|
|
|
def make_client(handler, *, api_key="sk-test", retry_backoff=(0.0, 0.0)):
|
|
return HttpLLMClient(
|
|
base_url="https://api.test.local",
|
|
api_key=api_key,
|
|
timeout_sec=0.1,
|
|
retry_backoff=retry_backoff,
|
|
transport=httpx.MockTransport(handler),
|
|
)
|
|
|
|
|
|
def _ok_handler(request):
|
|
return httpx.Response(200, json={
|
|
"choices": [{"message": {"content": "Hello"}}],
|
|
"usage": {"prompt_tokens": 10, "completion_tokens": 5},
|
|
})
|
|
|
|
|
|
def test_client_implements_protocol():
|
|
# 结构性断言:HttpLLMClient.chat 的关键字参数签名与 LLMClient Protocol 一致
|
|
import inspect
|
|
|
|
proto_params = set(inspect.signature(LLMClient.chat).parameters)
|
|
impl_params = set(inspect.signature(HttpLLMClient.chat).parameters)
|
|
assert proto_params.issubset(impl_params)
|
|
|
|
|
|
def test_chat_success():
|
|
client = make_client(_ok_handler)
|
|
text, usage = client.chat(
|
|
model="deepseek-chat",
|
|
messages=[ChatMessage(role="user", content="hi")],
|
|
temperature=0.2,
|
|
max_tokens=100,
|
|
)
|
|
assert text == "Hello"
|
|
assert usage.input_tokens == 10 and usage.output_tokens == 5
|
|
|
|
|
|
def test_chat_requires_api_key():
|
|
with pytest.raises(LLMNotConfiguredError):
|
|
HttpLLMClient(base_url="https://x", api_key="")
|
|
|
|
|
|
def test_chat_timeout():
|
|
def slow(request):
|
|
raise httpx.ReadTimeout("slow")
|
|
|
|
with pytest.raises(LLMTimeoutError):
|
|
make_client(slow, retry_backoff=(0, 0)).chat(
|
|
model="m", messages=[ChatMessage(role="user", content="x")],
|
|
temperature=0.2, max_tokens=100,
|
|
)
|
|
|
|
|
|
def test_chat_5xx_retry_then_network_error():
|
|
calls = {"n": 0}
|
|
|
|
def handler(request):
|
|
calls["n"] += 1
|
|
return httpx.Response(500, text="boom")
|
|
|
|
with pytest.raises(LLMNetworkError):
|
|
make_client(handler, retry_backoff=(0, 0)).chat(
|
|
model="m", messages=[ChatMessage(role="user", content="x")],
|
|
temperature=0.2, max_tokens=100,
|
|
)
|
|
assert calls["n"] == 3 # 初始 + 2 次退避重试(间隔 0/0.01)
|
|
|
|
|
|
def test_chat_network_error_retry_then_network_error():
|
|
# 非超时的网络错误走 httpx.HTTPError 分支,重试耗尽后抛 LLMNetworkError
|
|
def handler(request):
|
|
raise httpx.ConnectError("connection refused")
|
|
|
|
with pytest.raises(LLMNetworkError):
|
|
make_client(handler, retry_backoff=(0, 0)).chat(
|
|
model="m", messages=[ChatMessage(role="user", content="x")],
|
|
temperature=0.2, max_tokens=100,
|
|
)
|
|
|
|
|
|
def test_chat_4xx_no_retry():
|
|
calls = {"n": 0}
|
|
|
|
def handler(request):
|
|
calls["n"] += 1
|
|
return httpx.Response(429, text="rate limit")
|
|
|
|
with pytest.raises(LLMNetworkError):
|
|
make_client(handler).chat(
|
|
model="m", messages=[ChatMessage(role="user", content="x")],
|
|
temperature=0.2, max_tokens=100,
|
|
)
|
|
assert calls["n"] == 1 # 4xx 不重试 |