fix(gui): 修复启动报错和对话列表不正确的两个关键Bug

Bug1: 'async for' requires __aiter__ method, got coroutine
- EventQueue.subscribe() 在 _closed=True 时直接 return,
  Python 将其视为协程而非异步生成器
- 修复: 添加不可达的 yield 语句,确保函数始终为异步生成器

Bug2: 启动时对话列表全显示"对话",无法识别之前的对话
- list_conversations() 不加载消息,_derive_conversation_title
  遍历空 messages 列表导致标题全为"对话"
- 修复: list_conversations 从 SQLite 加载首条用户消息用于标题推导

Bug2b: WebSocket 不响应前端对话切换
- conv 变量只在首条消息时设置,之后忽略 conversation_id
- 修复: 每条消息都检查 conversation_id,切换时更新 conv
This commit is contained in:
chiguyong 2026-06-18 16:26:02 +08:00
parent 771756814f
commit b4ba65b9ca
3 changed files with 27 additions and 9 deletions

View File

@ -276,13 +276,25 @@ class SqliteConversationStore:
if conv_id in self._cache:
result.append(self._cache[conv_id])
else:
result.append(
Conversation(
id=conv_id,
created_at=self._str_to_dt(row["created_at"]),
updated_at=self._str_to_dt(row["updated_at"]),
)
conv = Conversation(
id=conv_id,
created_at=self._str_to_dt(row["created_at"]),
updated_at=self._str_to_dt(row["updated_at"]),
)
# Load first user message from SQLite for title derivation
try:
title_cursor = await db.execute(
"SELECT content FROM messages "
"WHERE conversation_id = ? AND role = 'user' "
"ORDER BY timestamp ASC LIMIT 1",
(conv_id,),
)
title_row = await title_cursor.fetchone()
if title_row:
conv.messages.append(ChatMessage(role="user", content=title_row["content"]))
except Exception:
pass
result.append(conv)
return result
async def restore_from_store(

View File

@ -210,7 +210,9 @@ class EventQueue:
保证不会遗漏或重复事件
"""
if self._closed:
# Must yield to make this an async generator, not a coroutine
return
yield # noqa: unreachable — makes this an async generator
# P1 #13 fix: enforce subscriber cap to prevent resource exhaustion
# from malicious resume floods or runaway client loops.

View File

@ -1141,9 +1141,13 @@ async def portal_websocket(websocket: WebSocket):
if not message_text:
continue
# Create conversation on first message (not on connect)
if conv is None:
conv_id = msg.get("conversation_id")
# Create or switch conversation based on conversation_id from frontend
conv_id = msg.get("conversation_id")
if conv_id:
if conv is None or conv.id != conv_id:
conv = await _conversation_store.get_or_create(conv_id)
await websocket.send_json({"type": "connected", "conversation_id": conv.id})
elif conv is None:
conv = await _conversation_store.get_or_create(conv_id)
await websocket.send_json({"type": "connected", "conversation_id": conv.id})