fix: 修复新增对话时内容直接作为标题长度超出表字段长度限制问题, titile 字段长度限制为255

- 新增 MAX_CONVERSATION_TITLE_LENGTH 常量 (255 字符)
- 新增 _normalize_title() 方法用于截断和校验标题
- 防止创建和更新对话时数据库字段溢出
- 前端 AgentChatComponent 同步添加标题标准化处理
- 添加标题标准化的单元测试
This commit is contained in:
肖泽涛 2026-02-20 23:41:44 +08:00 committed by Wenjie Zhang
parent 1f5636520e
commit 80cf6937ed
3 changed files with 52 additions and 6 deletions

View File

@ -12,11 +12,26 @@ from src.storage.postgres.models_business import Conversation, ConversationStats
from src.utils import logger from src.utils import logger
from src.utils.datetime_utils import utc_now_naive from src.utils.datetime_utils import utc_now_naive
MAX_CONVERSATION_TITLE_LENGTH = 255
class ConversationRepository: class ConversationRepository:
def __init__(self, db_session: AsyncSession): def __init__(self, db_session: AsyncSession):
self.db = db_session 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( async def create_conversation(
self, self,
user_id: str, user_id: str,
@ -31,11 +46,13 @@ class ConversationRepository:
metadata = (metadata or {}).copy() metadata = (metadata or {}).copy()
metadata.setdefault("attachments", []) metadata.setdefault("attachments", [])
normalized_title = self._normalize_title(title)
conversation = Conversation( conversation = Conversation(
thread_id=thread_id, thread_id=thread_id,
user_id=str(user_id), user_id=str(user_id),
agent_id=agent_id, agent_id=agent_id,
title=title or "New Conversation", title=normalized_title or "New Conversation",
status="active", status="active",
extra_metadata=metadata, extra_metadata=metadata,
) )
@ -203,8 +220,9 @@ class ConversationRepository:
if not conversation: if not conversation:
return None return None
if title is not None: normalized_title = self._normalize_title(title)
conversation.title = title if normalized_title is not None:
conversation.title = normalized_title
if status is not None: if status is not None:
conversation.status = status conversation.status = status

View File

@ -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"

View File

@ -626,12 +626,15 @@ const deleteThread = async (threadId) => {
const updateThread = async (threadId, title) => { const updateThread = async (threadId, title) => {
if (!threadId || !title) return if (!threadId || !title) return
const normalizedTitle = String(title).replace(/\s+/g, ' ').trim().slice(0, 255)
if (!normalizedTitle) return
chatState.isRenamingThread = true chatState.isRenamingThread = true
try { try {
await threadApi.updateThread(threadId, title) await threadApi.updateThread(threadId, normalizedTitle)
const thread = threads.value.find((t) => t.id === threadId) const thread = threads.value.find((t) => t.id === threadId)
if (thread) { if (thread) {
thread.title = title thread.title = normalizedTitle
} }
} catch (error) { } catch (error) {
console.error('Failed to update thread:', error) console.error('Failed to update thread:', error)
@ -750,7 +753,10 @@ const sendMessage = async ({
// //
if ((threadMessages.value[threadId] || []).length === 0) { 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 = { const requestData = {