Initial commit: 网页联机狼人杀(FastAPI+WebSocket 后端 / React+TS+Sass 前端)
This commit is contained in:
commit
7f5ea6bee8
55 files changed
+6999
No files matched your search
@@ -0,0 +1,75 @@
|
||||
"""OpenAI 兼容异步客户端:JSON 决策与自由文本发言,自动降级重试。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import re
|
||||
|
||||
from ...config import CONFIG, LLMConfig
|
||||
|
||||
|
||||
def parse_json_obj(text: str) -> dict | None:
|
||||
if not text:
|
||||
return None
|
||||
text = text.strip()
|
||||
text = re.sub(r"^```(?:json)?\s*|\s*```$", "", text, flags=re.S)
|
||||
start, end = text.find("{"), text.rfind("}")
|
||||
if start == -1 or end <= start:
|
||||
return None
|
||||
try:
|
||||
data = json.loads(text[start:end + 1])
|
||||
return data if isinstance(data, dict) else None
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
|
||||
class LLMClient:
|
||||
def __init__(self, cfg: LLMConfig) -> None:
|
||||
from openai import AsyncOpenAI
|
||||
self.model = cfg.model
|
||||
self.temperature = cfg.temperature
|
||||
self._client = AsyncOpenAI(
|
||||
base_url=cfg.base_url, api_key=cfg.api_key, timeout=30, max_retries=0)
|
||||
|
||||
async def _create(self, system: str, user: str, *, json_mode: bool, timeout: float) -> str:
|
||||
kwargs: dict = {
|
||||
"model": self.model,
|
||||
"temperature": self.temperature,
|
||||
"messages": [
|
||||
{"role": "system", "content": system},
|
||||
{"role": "user", "content": user},
|
||||
],
|
||||
"timeout": timeout,
|
||||
}
|
||||
if json_mode:
|
||||
kwargs["response_format"] = {"type": "json_object"}
|
||||
try:
|
||||
resp = await self._client.chat.completions.create(**kwargs)
|
||||
return resp.choices[0].message.content or ""
|
||||
except Exception:
|
||||
kwargs.pop("response_format", None) # 部分兼容端点不支持 response_format
|
||||
resp = await self._client.chat.completions.create(**kwargs)
|
||||
return resp.choices[0].message.content or ""
|
||||
|
||||
async def chat_json(self, system: str, user: str, *, timeout: float = 22.0) -> dict:
|
||||
last = ""
|
||||
for _ in range(2):
|
||||
text = await self._create(system, user, json_mode=True, timeout=timeout)
|
||||
data = parse_json_obj(text)
|
||||
if data is not None:
|
||||
return data
|
||||
last = text
|
||||
raise ValueError(f"LLM 未返回有效 JSON: {last[:120]}")
|
||||
|
||||
async def chat_text(self, system: str, user: str, *, timeout: float = 40.0) -> str:
|
||||
return (await self._create(system, user, json_mode=False, timeout=timeout)).strip()
|
||||
|
||||
|
||||
_client: LLMClient | None = None
|
||||
|
||||
|
||||
def get_llm_client() -> LLMClient:
|
||||
global _client
|
||||
if _client is None:
|
||||
_client = LLMClient(CONFIG.llm)
|
||||
return _client
|
||||
Reference in new issue
Block a user