feat(chat): 支持流式回复与用户节点导航

This commit is contained in:
2026-06-05 00:11:53 +08:00
parent 84e045f5ab
commit 7ab5aad938
8 changed files with 676 additions and 15 deletions

View File

@@ -53,6 +53,57 @@ def generate_reply(conversation, user_message: str) -> str:
raise LLMRequestError("模型接口返回格式不符合预期。") from exc
def stream_reply(conversation, user_message: str):
"""Streams incremental assistant text from the SiliconFlow chat endpoint."""
if not settings.LLM_API_KEY:
raise LLMConfigurationError("缺少 LLM_API_KEY 配置。")
if not settings.LLM_MODEL:
raise LLMConfigurationError("缺少 LLM_MODEL 配置。")
payload = {
"model": settings.LLM_MODEL,
"messages": build_messages(conversation, user_message),
"temperature": 0.3,
"stream": True,
}
body = json.dumps(payload).encode("utf-8")
endpoint = f"{settings.LLM_BASE_URL.rstrip('/')}/chat/completions"
http_request = request.Request(
endpoint,
data=body,
headers={
"Authorization": f"Bearer {settings.LLM_API_KEY}",
"Content-Type": "application/json",
},
method="POST",
)
try:
with request.urlopen(http_request, timeout=300) as response:
for raw_line in response:
line = raw_line.decode("utf-8", errors="ignore").strip()
if not line or not line.startswith("data:"):
continue
data = line[5:].strip()
if data == "[DONE]":
break
payload = json.loads(data)
delta = (
payload.get("choices", [{}])[0]
.get("delta", {})
.get("content", "")
)
if delta:
yield delta
except error.HTTPError as exc:
details = exc.read().decode("utf-8", errors="ignore")
raise LLMRequestError(f"模型接口调用失败HTTP {exc.code} {details}") from exc
except error.URLError as exc:
raise LLMRequestError(f"模型接口调用失败:{exc.reason}") from exc
def build_messages(conversation, latest_user_message: str) -> list[dict[str, str]]:
"""Builds system and conversation history messages for the provider call."""