From 8fe32d0b210db3632128e2c16a0f24a7af287836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=82=96=E6=B3=BD=E6=B6=9B?= Date: Fri, 20 Feb 2026 23:41:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=97=B6=E5=86=85=E5=AE=B9=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E6=A0=87=E9=A2=98=E9=95=BF=E5=BA=A6=E8=B6=85?= =?UTF-8?q?=E5=87=BA=E8=A1=A8=E5=AD=97=E6=AE=B5=E9=95=BF=E5=BA=A6=E9=99=90?= =?UTF-8?q?=E5=88=B6=E9=97=AE=E9=A2=98=EF=BC=8C=20titile=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E9=95=BF=E5=BA=A6=E9=99=90=E5=88=B6=E4=B8=BA255?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 MAX_CONVERSATION_TITLE_LENGTH 常量 (255 字符) - 新增 _normalize_title() 方法用于截断和校验标题 - 防止创建和更新对话时数据库字段溢出 - 前端 AgentChatComponent 同步添加标题标准化处理 - 添加标题标准化的单元测试 --- src/repositories/conversation_repository.py | 24 ++++++++++++++++++--- test/test_conversation_repository.py | 22 +++++++++++++++++++ web/src/components/AgentChatComponent.vue | 12 ++++++++--- 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 test/test_conversation_repository.py diff --git a/src/repositories/conversation_repository.py b/src/repositories/conversation_repository.py index 4b0cd3b76..018fb1806 100644 --- a/src/repositories/conversation_repository.py +++ b/src/repositories/conversation_repository.py @@ -12,11 +12,26 @@ from src.utils import logger from src.utils.datetime_utils import utc_now_naive +MAX_CONVERSATION_TITLE_LENGTH = 255 + class ConversationRepository: def __init__(self, db_session: AsyncSession): self.db = db_session + def _normalize_title(self, title: str | None) -> str | None: + if title is None: + return None + normalized = str(title).strip() + if not normalized: + return "" + if len(normalized) > MAX_CONVERSATION_TITLE_LENGTH: + logger.warning( + f"Conversation title too long ({len(normalized)}), truncate to {MAX_CONVERSATION_TITLE_LENGTH}" + ) + return normalized[:MAX_CONVERSATION_TITLE_LENGTH] + return normalized + async def create_conversation( self, user_id: str, @@ -31,11 +46,13 @@ async def create_conversation( metadata = (metadata or {}).copy() metadata.setdefault("attachments", []) + normalized_title = self._normalize_title(title) + conversation = Conversation( thread_id=thread_id, user_id=str(user_id), agent_id=agent_id, - title=title or "New Conversation", + title=normalized_title or "New Conversation", status="active", extra_metadata=metadata, ) @@ -203,8 +220,9 @@ async def update_conversation( if not conversation: return None - if title is not None: - conversation.title = title + normalized_title = self._normalize_title(title) + if normalized_title is not None: + conversation.title = normalized_title if status is not None: conversation.status = status diff --git a/test/test_conversation_repository.py b/test/test_conversation_repository.py new file mode 100644 index 000000000..8729223f1 --- /dev/null +++ b/test/test_conversation_repository.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from src.repositories.conversation_repository import ConversationRepository, MAX_CONVERSATION_TITLE_LENGTH + + +def test_normalize_title_truncates_when_too_long(): + repo = ConversationRepository(None) # type: ignore[arg-type] + raw = "a" * (MAX_CONVERSATION_TITLE_LENGTH + 50) + + normalized = repo._normalize_title(raw) + + assert normalized is not None + assert len(normalized) == MAX_CONVERSATION_TITLE_LENGTH + assert normalized == "a" * MAX_CONVERSATION_TITLE_LENGTH + + +def test_normalize_title_trims_spaces(): + repo = ConversationRepository(None) # type: ignore[arg-type] + + normalized = repo._normalize_title(" hello world ") + + assert normalized == "hello world" diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index d777dca22..b2468efaf 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -626,12 +626,15 @@ const deleteThread = async (threadId) => { const updateThread = async (threadId, title) => { if (!threadId || !title) return + const normalizedTitle = String(title).replace(/\s+/g, ' ').trim().slice(0, 255) + if (!normalizedTitle) return + chatState.isRenamingThread = true try { - await threadApi.updateThread(threadId, title) + await threadApi.updateThread(threadId, normalizedTitle) const thread = threads.value.find((t) => t.id === threadId) if (thread) { - thread.title = title + thread.title = normalizedTitle } } catch (error) { console.error('Failed to update thread:', error) @@ -750,7 +753,10 @@ const sendMessage = async ({ // 如果是新对话,用消息内容作为标题 if ((threadMessages.value[threadId] || []).length === 0) { - updateThread(threadId, text) + const autoTitle = text.replace(/\s+/g, ' ').trim().slice(0, 255) + if (autoTitle) { + void updateThread(threadId, autoTitle).catch(() => {}) + } } const requestData = {