fix:优化界面

This commit is contained in:
丹尼尔
2026-03-11 00:22:41 +08:00
parent 0655410134
commit 0e8639fde1
4268 changed files with 1224126 additions and 92 deletions

123
backend/llm_client.py Normal file
View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
"""多模型统一调用:从 store 读取当前模型配置API Key、base_url、model_name按 OpenAI 兼容或豆包 Responses API 请求。"""
import logging
from typing import List, Optional
logger = logging.getLogger("wechat-backend.llm")
try:
from backend import store
except ImportError:
import store
def _doubao_input_from_messages(messages: List[dict]) -> list:
"""将 [{"role":"user","content":"..."}] 转为豆包 Responses API 的 input 格式。"""
result = []
for m in messages:
role = (m.get("role") or "user").lower()
if role == "system":
role = "user"
content = m.get("content")
if isinstance(content, str):
content = [{"type": "input_text", "text": content}]
elif isinstance(content, list):
# 已是多模态格式,或需转为 input_text
out = []
for item in content:
if isinstance(item, dict):
if item.get("type") == "input_text":
out.append(item)
elif "text" in item:
out.append({"type": "input_text", "text": item["text"]})
else:
out.append({"type": "input_text", "text": str(item)})
content = out if out else [{"type": "input_text", "text": ""}]
else:
content = [{"type": "input_text", "text": str(content or "")}]
result.append({"role": role, "content": content})
return result
async def _chat_doubao(api_key: str, base_url: str, model_name: str, messages: List[dict]) -> Optional[str]:
"""豆包Volcengine ARKResponses APIPOST /responsesinput 格式见示例。"""
import httpx
url = base_url.rstrip("/") + "/responses"
payload = {"model": model_name, "input": _doubao_input_from_messages(messages)}
try:
async with httpx.AsyncClient(timeout=60.0) as client:
r = await client.post(
url,
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json=payload,
)
if r.status_code != 200:
logger.warning("doubao API status=%s body=%s", r.status_code, r.text[:500])
return None
data = r.json()
# 常见返回结构output.output_text / output.text / choices[0].message.content
output = data.get("output") or data
if isinstance(output, dict):
text = output.get("output_text") or output.get("text")
if isinstance(text, str):
return text
choices = output.get("choices")
if choices and len(choices) > 0:
msg = choices[0].get("message") or choices[0]
if isinstance(msg, dict) and msg.get("content"):
return msg["content"]
if "choices" in data and data["choices"]:
c = data["choices"][0]
msg = c.get("message", c)
if isinstance(msg, dict) and msg.get("content"):
return msg["content"]
logger.warning("doubao unknown response shape: %s", list(data.keys()))
return None
except Exception as e:
logger.exception("doubao chat error: %s", e)
return None
async def chat(messages: List[dict], model_id: Optional[str] = None) -> Optional[str]:
"""
使用已配置的模型进行对话。
model_id: 若指定则用该模型,否则用当前选中的模型。
返回 assistant 回复文本。
"""
if model_id:
model_config = store.get_model(model_id)
else:
model_config = store.get_current_model()
if not model_config or not model_config.get("api_key"):
logger.warning("No model configured or no api_key")
return None
api_key = model_config["api_key"]
base_url = (model_config.get("base_url") or "").strip()
model_name = (model_config.get("model_name") or "qwen-turbo").strip()
provider = (model_config.get("provider") or "openai").lower()
if not base_url and provider == "qwen":
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
if not base_url and provider == "doubao":
base_url = "https://ark.cn-beijing.volces.com/api/v3"
if not base_url:
base_url = "https://api.openai.com/v1"
# 豆包:火山方舟支持 chat/completions 兼容接口,优先走统一 OpenAI 客户端;若需 Responses API 可走 _chat_doubao
if provider == "doubao":
try:
from openai import AsyncOpenAI
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
r = await client.chat.completions.create(model=model_name, messages=messages)
if r.choices and r.choices[0].message.content:
return r.choices[0].message.content
except Exception as e:
logger.info("doubao chat/completions failed, try responses API: %s", e)
return await _chat_doubao(api_key, base_url, model_name, messages)
try:
from openai import AsyncOpenAI
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
r = await client.chat.completions.create(model=model_name, messages=messages)
text = r.choices[0].message.content if r.choices else None
return text
except Exception as e:
logger.exception("llm chat error: %s", e)
return None