fix: 修复新增对话时内容直接作为标题长度超出表字段长度限制问题, titile 字段长度限制为255
- 新增 MAX_CONVERSATION_TITLE_LENGTH 常量 (255 字符) - 新增 _normalize_title() 方法用于截断和校验标题 - 防止创建和更新对话时数据库字段溢出 - 前端 AgentChatComponent 同步添加标题标准化处理 - 添加标题标准化的单元测试
This commit is contained in:
parent
1f5636520e
commit
80cf6937ed
@ -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
|
||||||
|
|
||||||
|
|||||||
22
test/test_conversation_repository.py
Normal file
22
test/test_conversation_repository.py
Normal 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"
|
||||||
@ -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 = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user