85d14a1e56
根因(VPS 实测): glm_cn 默认 base_url=paas/v4 是按量计费端点,coding plan 的 key 打它=HTTP 429 code 1113 余额不足;同一 key 在 coding plan 接入点 /api/anthropic/v1/messages 实测 200(glm-5.2 与 glm-5.3-flash 均通)。 修复: ProviderSpec 增 protocol 字段(openai|anthropic,env SANGUO_LLM_PROTOCOL 可覆盖), glm_cn 改指 anthropic 端点; client/probe 共用 chat_endpoint() 按协议 选 URL+鉴权头; anthropic 差异面=system 提顶层/无 response_format/content 块 只取 text(glm-5.x thinking 块跳过)/usage input→prompt 归一(漏斗计量不漂移)。 草稿/分解/测试连接/漏斗 lane 四消费方同 client 一处修复全生效。 venv310 实测 82 绿(含 anthropic 协议 8 新测); 档随码走=总设计 §13 P5.1 补 端点勘误节。
67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
# tests/api/test_llm_probe.py
|
|
"""probe(P5.1):单发最小 chat 探测,无重试无退避;MockTransport 零真调用."""
|
|
import asyncio
|
|
|
|
import httpx
|
|
|
|
from sanguo_api.llm.probe import probe_connection
|
|
from sanguo_api.llm.provider import LLMConfig
|
|
|
|
CFG = LLMConfig("glm_cn", "http://x/v4/", "m", "sk-test", timeout=5.0)
|
|
|
|
|
|
def _run(transport):
|
|
return asyncio.run(probe_connection(CFG, transport=transport))
|
|
|
|
|
|
def test_ok_with_latency():
|
|
t = httpx.MockTransport(
|
|
lambda req: httpx.Response(200, json={"choices": []}))
|
|
r = _run(t)
|
|
assert r["ok"] is True and r["error"] == ""
|
|
assert 0 <= r["latency_ms"] < 5000
|
|
|
|
|
|
def test_http_error_carries_status():
|
|
t = httpx.MockTransport(lambda req: httpx.Response(401, text="bad key"))
|
|
r = _run(t)
|
|
assert r["ok"] is False and "401" in r["error"]
|
|
|
|
|
|
def test_transport_error():
|
|
def boom(req):
|
|
raise httpx.ConnectError("refused")
|
|
r = _run(httpx.MockTransport(boom))
|
|
assert r["ok"] is False and "refused" in r["error"]
|
|
|
|
|
|
def test_malformed_base_url_no_raise():
|
|
"""畸形 base_url 也不 raise——设置页填错 URL 点测试连接是核心 UX.
|
|
'://bad'=UnsupportedProtocol(TransportError 系,本就被捕);
|
|
'http://[::1'=InvalidURL(Exception 直系,不在 TransportError 树内,
|
|
曾击穿「任何失败都不 raise」承诺 500,放宽 HTTPError 后归拢)."""
|
|
for url in ("://bad", "http://[::1"):
|
|
bad = LLMConfig("glm_cn", url, "m", "sk-test", timeout=5.0)
|
|
r = asyncio.run(probe_connection(bad))
|
|
assert r["ok"] is False and r["error"]
|
|
|
|
|
|
def test_anthropic_protocol_probes_messages_url():
|
|
"""10-08 coding plan:protocol=anthropic 探针打 /v1/messages+x-api-key."""
|
|
import json as j
|
|
seen = {}
|
|
|
|
def h(req):
|
|
seen["url"] = str(req.url)
|
|
seen["key"] = req.headers.get("x-api-key")
|
|
seen["body"] = j.loads(req.content)
|
|
return httpx.Response(200, json={"content": [{"type": "text",
|
|
"text": "pong"}]})
|
|
cfg = LLMConfig("glm_cn", "http://x/api/anthropic", "m", "sk-test",
|
|
timeout=5.0, protocol="anthropic")
|
|
r = asyncio.run(probe_connection(cfg, transport=httpx.MockTransport(h)))
|
|
assert r["ok"] is True
|
|
assert seen["url"] == "http://x/api/anthropic/v1/messages"
|
|
assert seen["key"] == "sk-test"
|
|
assert "response_format" not in seen["body"]
|