feat: 故障推送 / 公需课自动重学 / 插件解耦,发版 v0.0.8
体检修复:启动日志不再打印 AUTH_TOKEN、密钥可清空、推送失败不占整段冷却、目录名与文案统一成 plugins/。
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
"""大模型调用(OpenAI 兼容 /chat/completions)。
|
||||
|
||||
后续所有大模型相关代码都放在这个目录里:新增供应商 / 提示词 / 重试策略时,
|
||||
在这里加模块,不要让路由或平台插件直接拼 HTTP 请求。
|
||||
"""
|
||||
import requests
|
||||
|
||||
|
||||
def llm_chat(messages, cfg, max_tokens=None, json_mode=True):
|
||||
"""OpenAI 兼容的 /chat/completions。不支持 response_format 的服务端会自动去掉重试一次。"""
|
||||
base = (cfg.get("base_url") or "").rstrip("/")
|
||||
if not base:
|
||||
raise RuntimeError("base_url 为空")
|
||||
url = base + "/chat/completions"
|
||||
body = {"model": cfg.get("model") or "gpt-4o-mini", "messages": messages,
|
||||
"temperature": float(cfg.get("temperature") or 0.2)}
|
||||
if max_tokens:
|
||||
body["max_tokens"] = int(max_tokens)
|
||||
if json_mode:
|
||||
body["response_format"] = {"type": "json_object"}
|
||||
headers = {"Authorization": "Bearer " + (cfg.get("api_key") or ""),
|
||||
"Content-Type": "application/json"}
|
||||
timeout = int(cfg.get("timeout") or 120)
|
||||
r = requests.post(url, json=body, headers=headers, timeout=timeout)
|
||||
# 有的网关不认 response_format 但回的是 400/422,而不是 400,去掉再发一次
|
||||
if json_mode and r.status_code in (400, 422):
|
||||
body.pop("response_format", None)
|
||||
r = requests.post(url, json=body, headers=headers, timeout=timeout)
|
||||
if r.status_code >= 400:
|
||||
msg = (r.text or "").strip()
|
||||
if msg.startswith("<"):
|
||||
msg = "网关返回 HTML 错误页(HTTP %s)" % r.status_code
|
||||
raise RuntimeError("LLM %s: %s" % (r.status_code, msg[:200]))
|
||||
try:
|
||||
j = r.json()
|
||||
except ValueError:
|
||||
raise RuntimeError("LLM 返回的不是 JSON(HTTP 200):%s" % (r.text or "")[:120])
|
||||
content = (((j.get("choices") or [{}])[0].get("message") or {}).get("content") or "").strip()
|
||||
if not content:
|
||||
raise RuntimeError("LLM 返回空内容(可能被 max_tokens 截断,或被网关过滤)")
|
||||
return content
|
||||
Reference in New Issue
Block a user