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:
@@ -18,8 +18,10 @@ class DeepSeekLLMProvider:
|
||||
self,
|
||||
api_key: str,
|
||||
base_url: str = "https://api.deepseek.com",
|
||||
model: str = "deepseek-chat",
|
||||
model: str = "deepseek-v4-flash",
|
||||
temperature: float = 0.7,
|
||||
*,
|
||||
extra_body: dict | None = None,
|
||||
):
|
||||
self._default_model = model
|
||||
self._default_temperature = temperature
|
||||
@@ -28,6 +30,8 @@ class DeepSeekLLMProvider:
|
||||
"model": model,
|
||||
"api_key": api_key,
|
||||
}
|
||||
if extra_body:
|
||||
kwargs["extra_body"] = extra_body
|
||||
if base_url:
|
||||
cleaned = base_url.rstrip("/")
|
||||
for suffix in ("/v1/chat/completions", "/v1"):
|
||||
|
||||
85
api/app/adapters/llm/deepseek_eval_judge.py
Normal file
85
api/app/adapters/llm/deepseek_eval_judge.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""DeepSeek 评测台评审:模型别名解析 + ChatOpenAI 装配。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
from app.adapters.llm.openai_base_url import normalize_openai_compatible_base_url
|
||||
from app.core.config import settings
|
||||
from app.core.eval_judge_spec import EvalJudgeLlmSpec
|
||||
|
||||
|
||||
def resolve_deepseek_eval_judge_model(
|
||||
requested: str,
|
||||
) -> tuple[str, dict | None, str | None]:
|
||||
"""将模型名(含旧别名)规范为 V4 的 model id、extra_body 与 reasoning_effort。
|
||||
|
||||
官方:deepseek-chat / deepseek-reasoner 将弃用,分别对应 v4-flash 非思考 / 思考。
|
||||
"""
|
||||
m = (requested or "").strip()
|
||||
if m == "deepseek-chat":
|
||||
return (
|
||||
"deepseek-v4-flash",
|
||||
{"thinking": {"type": "disabled"}},
|
||||
None,
|
||||
)
|
||||
if m in (
|
||||
"deepseek-reasoner",
|
||||
"deepseek-r1",
|
||||
):
|
||||
return (
|
||||
"deepseek-v4-flash",
|
||||
{"thinking": {"type": "enabled"}},
|
||||
"high",
|
||||
)
|
||||
if m == "deepseek-v4-pro":
|
||||
return ("deepseek-v4-pro", None, "high")
|
||||
if m in ("", "deepseek-v4-flash"):
|
||||
if settings.eval_judge_deepseek_thinking_enabled:
|
||||
return (
|
||||
"deepseek-v4-flash",
|
||||
{"thinking": {"type": "enabled"}},
|
||||
"high",
|
||||
)
|
||||
return (
|
||||
"deepseek-v4-flash",
|
||||
{"thinking": {"type": "disabled"}},
|
||||
None,
|
||||
)
|
||||
if "flash" in m.lower() or m.startswith("deepseek-v4"):
|
||||
return (m, None, None)
|
||||
return (m, None, None)
|
||||
|
||||
|
||||
def build_deepseek_eval_judge_spec(
|
||||
judge_model: str | None,
|
||||
) -> EvalJudgeLlmSpec | None:
|
||||
"""密钥缺失时返回 None。"""
|
||||
api_key = (settings.deepseek_api_key or settings.llm_api_key or "").strip()
|
||||
if not api_key:
|
||||
return None
|
||||
want = (judge_model or "").strip()
|
||||
base = normalize_openai_compatible_base_url(
|
||||
settings.deepseek_base_url,
|
||||
fallback="https://api.deepseek.com",
|
||||
)
|
||||
default_m = (settings.eval_judge_deepseek_model or "deepseek-v4-flash").strip()
|
||||
combined = want or default_m
|
||||
model, extra, effort = resolve_deepseek_eval_judge_model(combined)
|
||||
ctx = int(settings.eval_judge_deepseek_context_window_tokens)
|
||||
llm_kw: dict = {
|
||||
"api_key": api_key,
|
||||
"base_url": base,
|
||||
"model": model,
|
||||
"temperature": settings.eval_judge_temperature,
|
||||
}
|
||||
if extra is not None:
|
||||
llm_kw["extra_body"] = extra
|
||||
if effort is not None:
|
||||
llm_kw["reasoning_effort"] = effort
|
||||
return EvalJudgeLlmSpec(
|
||||
llm=ChatOpenAI(**llm_kw),
|
||||
provider="deepseek",
|
||||
resolved_model=model,
|
||||
context_window_tokens=ctx,
|
||||
)
|
||||
9
api/app/adapters/llm/openai_base_url.py
Normal file
9
api/app/adapters/llm/openai_base_url.py
Normal file
@@ -0,0 +1,9 @@
|
||||
"""OpenAI/Chat Completions 兼容基址规范化(多供应商共用)。"""
|
||||
|
||||
|
||||
def normalize_openai_compatible_base_url(raw: str, *, fallback: str) -> str:
|
||||
base = (raw or "").strip().rstrip("/") or fallback
|
||||
for suffix in ("/v1/chat/completions", "/v1"):
|
||||
if base.endswith(suffix):
|
||||
base = base[: -len(suffix)]
|
||||
return base
|
||||
37
api/app/adapters/llm/zhipu_eval_judge.py
Normal file
37
api/app/adapters/llm/zhipu_eval_judge.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""智谱 GLM 评测台评审:OpenAI 兼容端点 + ChatOpenAI 装配。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
from app.adapters.llm.openai_base_url import normalize_openai_compatible_base_url
|
||||
from app.core.config import settings
|
||||
from app.core.eval_judge_spec import EvalJudgeLlmSpec
|
||||
|
||||
|
||||
def build_zhipu_eval_judge_spec(
|
||||
judge_model: str | None,
|
||||
) -> EvalJudgeLlmSpec | None:
|
||||
"""密钥缺失时返回 None。"""
|
||||
api_key = (settings.eval_judge_api_key or settings.zhipu_api_key or "").strip()
|
||||
if not api_key:
|
||||
return None
|
||||
want = (judge_model or "").strip()
|
||||
base = normalize_openai_compatible_base_url(
|
||||
settings.eval_judge_base_url,
|
||||
fallback="https://open.bigmodel.cn/api/paas/v4",
|
||||
)
|
||||
model = want or (settings.eval_judge_model or "glm-5")
|
||||
ctx = int(settings.eval_judge_context_window_tokens)
|
||||
llm_kw: dict = {
|
||||
"api_key": api_key,
|
||||
"base_url": base,
|
||||
"model": model,
|
||||
"temperature": settings.eval_judge_temperature,
|
||||
}
|
||||
return EvalJudgeLlmSpec(
|
||||
llm=ChatOpenAI(**llm_kw),
|
||||
provider="zhipu",
|
||||
resolved_model=model,
|
||||
context_window_tokens=ctx,
|
||||
)
|
||||
Reference in New Issue
Block a user