feat(api): DeepSeek V4 Flash 默认、HTTP 错讯与多供应商分层
- 主链路默认 deepseek-v4-flash,DEEPSEEK_THINKING_ENABLED 对齐旧非思考 chat - 评测台评审装配迁入 adapters/llm(deepseek_eval_judge、zhipu_eval_judge)与 eval_judge_spec - 拆分 llm_http_openai_chat_errors 与 llm_errors(DeepSeek/智谱品牌与文档链),llm_call 支持 http_error_vendor - EvalJudgeService 按 spec.provider 传入 allm_json_call;评测台前端文案改为 V4 Flash - 更新 .env 示例与 staging/production 的 DEEPSEEK_MODEL;补充 openai/供应商错讯测试 Made-with: Cursor
This commit is contained in:
@@ -51,7 +51,8 @@ def test_build_eval_judge_deepseek_context_budget(
|
||||
spec = build_eval_judge_llm_spec("deepseek", None)
|
||||
assert spec is not None
|
||||
assert spec.provider == "deepseek"
|
||||
assert spec.resolved_model == "deepseek-reasoner"
|
||||
# 旧名 deepseek-reasoner 规范为 v4-flash 思考模式
|
||||
assert spec.resolved_model == "deepseek-v4-flash"
|
||||
assert spec.context_window_tokens == 64_000
|
||||
n = eval_judge_conversation_transcript_max_chars_for_context(64_000)
|
||||
glm_n = eval_judge_conversation_transcript_max_chars_for_context(200_000)
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from langchain_core.messages import AIMessage
|
||||
from openai import APIStatusError
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.core.langchain_llm import ensure_json_object_prompt_has_json_keyword
|
||||
@@ -128,6 +130,41 @@ def test_llm_json_call_no_fallback_raises() -> None:
|
||||
assert ei.value.kind == "validation"
|
||||
|
||||
|
||||
def _api_status_402() -> APIStatusError:
|
||||
req = httpx.Request("POST", "https://api.deepseek.com/v1/chat/completions")
|
||||
resp = httpx.Response(
|
||||
402, request=req, json={"error": {"message": "Insufficient balance"}}
|
||||
)
|
||||
return APIStatusError("Payment required", response=resp, body=resp.json())
|
||||
|
||||
|
||||
class _LlmInvokeRaises:
|
||||
def bind(self, **_kwargs: object):
|
||||
return self
|
||||
|
||||
def invoke(self, _prompt: str) -> object:
|
||||
raise _api_status_402()
|
||||
|
||||
async def ainvoke(self, _prompt: str) -> object:
|
||||
return self.invoke(_prompt)
|
||||
|
||||
|
||||
def test_llm_json_call_openai_status_maps_to_friendly_chinese() -> None:
|
||||
with pytest.raises(LLMCallError) as ei:
|
||||
llm_json_call(
|
||||
_LlmInvokeRaises(),
|
||||
"p",
|
||||
_SmallOut,
|
||||
max_tokens=8,
|
||||
agent="t",
|
||||
)
|
||||
assert ei.value.kind == "invoke"
|
||||
s = str(ei.value)
|
||||
assert "402" in s
|
||||
assert "余额" in s
|
||||
assert "DeepSeek" in s
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_allm_json_call_parity_with_sync() -> None:
|
||||
llm = _SyncFakeLlm(['{"answer": "async", "score": 7}'])
|
||||
|
||||
36
api/tests/test_llm_vendor_errors.py
Normal file
36
api/tests/test_llm_vendor_errors.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""供应商层 HTTP 错讯:DeepSeek / 智谱 品牌与文档链。"""
|
||||
|
||||
import httpx
|
||||
from openai import APIStatusError
|
||||
|
||||
from app.core.llm_errors import format_llm_http_error_message
|
||||
|
||||
|
||||
def _status_402() -> APIStatusError:
|
||||
req = httpx.Request("POST", "https://x/v1/chat/completions")
|
||||
resp = httpx.Response(402, request=req, json={"error": {"message": "x"}})
|
||||
return APIStatusError("u", response=resp, body=resp.json())
|
||||
|
||||
|
||||
def test_vendor_deepseek_includes_brand_and_doc() -> None:
|
||||
e = _status_402()
|
||||
m = format_llm_http_error_message(e, "deepseek")
|
||||
assert m is not None
|
||||
assert "DeepSeek" in m
|
||||
assert "api-docs.deepseek.com" in m
|
||||
|
||||
|
||||
def test_vendor_zhipu_includes_brand_and_doc() -> None:
|
||||
e = _status_402()
|
||||
m = format_llm_http_error_message(e, "zhipu")
|
||||
assert m is not None
|
||||
assert "智谱" in m
|
||||
assert "bigmodel" in m
|
||||
|
||||
|
||||
def test_vendor_none_is_transport_only() -> None:
|
||||
e = _status_402()
|
||||
m = format_llm_http_error_message(e, None)
|
||||
assert m is not None
|
||||
assert "【" not in m
|
||||
assert "402" in m
|
||||
68
api/tests/test_openai_compatible_errors.py
Normal file
68
api/tests/test_openai_compatible_errors.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""传输层 `llm_http_openai_chat_errors` 的中性错讯;兼容 re-export 仍经 openai_compatible_errors。"""
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
from openai import APIStatusError
|
||||
|
||||
from app.core.openai_compatible_errors import (
|
||||
extract_openai_http_status,
|
||||
format_openai_compatible_http_error_message,
|
||||
should_log_openai_error_as_warning,
|
||||
)
|
||||
|
||||
|
||||
def _status_error(status: int, *, body: object | None = None) -> APIStatusError:
|
||||
req = httpx.Request("POST", "https://api.deepseek.com/v1/chat/completions")
|
||||
resp = httpx.Response(status, request=req, json=body if body is not None else {})
|
||||
return APIStatusError("upstream", response=resp, body=body)
|
||||
|
||||
|
||||
def test_extract_status_from_api_status_error() -> None:
|
||||
e = _status_error(429)
|
||||
assert extract_openai_http_status(e) == 429
|
||||
|
||||
|
||||
def test_format_402_balance_chinese_message() -> None:
|
||||
e = _status_error(
|
||||
402,
|
||||
body={"error": {"message": "Insufficient balance", "type": "insufficient_quota"}},
|
||||
)
|
||||
msg = format_openai_compatible_http_error_message(e)
|
||||
assert msg is not None
|
||||
assert "402" in msg
|
||||
assert "余额" in msg
|
||||
|
||||
|
||||
def test_format_401_and_warning_flag() -> None:
|
||||
e = _status_error(401, body={"error": {"message": "invalid api key"}})
|
||||
assert should_log_openai_error_as_warning(e) is True
|
||||
m = format_openai_compatible_http_error_message(e)
|
||||
assert m is not None
|
||||
assert "401" in m
|
||||
assert "密钥" in m
|
||||
|
||||
|
||||
def test_format_503_server_busy() -> None:
|
||||
e = _status_error(503)
|
||||
m = format_openai_compatible_http_error_message(e)
|
||||
assert m is not None
|
||||
assert "503" in m
|
||||
assert should_log_openai_error_as_warning(e) is False
|
||||
|
||||
|
||||
def test_format_httpx_http_status_error() -> None:
|
||||
req = httpx.Request("GET", "https://api.deepseek.com/v1/models")
|
||||
resp = httpx.Response(429, request=req)
|
||||
try:
|
||||
resp.raise_for_status()
|
||||
except httpx.HTTPStatusError as e:
|
||||
m = format_openai_compatible_http_error_message(e)
|
||||
assert m is not None
|
||||
assert "429" in m
|
||||
|
||||
|
||||
def test_unknown_status_418() -> None:
|
||||
e = _status_error(418)
|
||||
m = format_openai_compatible_http_error_message(e)
|
||||
assert m is not None
|
||||
assert "418" in m
|
||||
Reference in New Issue
Block a user