diff --git a/dashboard/src/views/ConversationDetailView.vue b/dashboard/src/views/ConversationDetailView.vue index 0c84f79..c1caa4b 100644 --- a/dashboard/src/views/ConversationDetailView.vue +++ b/dashboard/src/views/ConversationDetailView.vue @@ -136,7 +136,19 @@ const loadMessages = async () => { if (res.success) { messages.value = res.data.messages || [] if (messages.value.length > 0) { - conversationTitle.value = res.data.title || '' + // 优先使用服务器返回的标题,否则用用户的第一条提问 + if (res.data.title) { + conversationTitle.value = res.data.title + } else if (res.data.first_message) { + conversationTitle.value = res.data.first_message + } else { + // 查找用户的第一条消息作为标题 + const userMsg = messages.value.find(m => m.role === 'user') + if (userMsg) { + // 截取前30个字符作为标题 + conversationTitle.value = userMsg.content.slice(0, 30) + (userMsg.content.length > 30 ? '...' : '') + } + } } } } catch (e) { @@ -211,6 +223,17 @@ const sendMessage = async () => { ...streamingMessage.value, created_at: new Date().toISOString() }) + + // 如果标题为空,自动用第一条用户消息作为标题 + if (!conversationTitle.value || conversationTitle.value === '新对话') { + const userMsg = messages.value.find(m => m.role === 'user') + if (userMsg) { + conversationTitle.value = userMsg.content.slice(0, 30) + (userMsg.content.length > 30 ? '...' : '') + // 调用 API 更新标题 + conversationsAPI.update(conversationId.value, { title: conversationTitle.value }) + } + } + streamingMessage.value = null } sending.value = false diff --git a/dashboard/src/views/ConversationsView.vue b/dashboard/src/views/ConversationsView.vue index c246000..19ab49b 100644 --- a/dashboard/src/views/ConversationsView.vue +++ b/dashboard/src/views/ConversationsView.vue @@ -12,7 +12,7 @@
-

{{ c.title || '未命名会话' }}

+

{{ c.title || c.first_message || '未命名会话' }}

{{ formatDate(c.created_at) }} • {{ c.model || '默认模型' }}

diff --git a/luxx/routes/conversations.py b/luxx/routes/conversations.py index b0d3f1d..9722a05 100644 --- a/luxx/routes/conversations.py +++ b/luxx/routes/conversations.py @@ -5,7 +5,7 @@ from pydantic import BaseModel from sqlalchemy.orm import Session from luxx.database import get_db -from luxx.models import Conversation, User +from luxx.models import Conversation, Message, User from luxx.routes.auth import get_current_user from luxx.utils.helpers import generate_id, success_response, error_response, paginate @@ -45,8 +45,21 @@ def list_conversations( """Get conversation list""" query = db.query(Conversation).filter(Conversation.user_id == current_user.id) result = paginate(query.order_by(Conversation.updated_at.desc()), page, page_size) + + items = [] + for c in result["items"]: + conv_dict = c.to_dict() + # Get first user message for fallback title + first_msg = db.query(Message).filter( + Message.conversation_id == c.id, + Message.role == 'user' + ).order_by(Message.created_at).first() + if first_msg: + conv_dict['first_message'] = first_msg.content[:50] + ('...' if len(first_msg.content) > 50 else '') + items.append(conv_dict) + return success_response(data={ - "items": [c.to_dict() for c in result["items"]], + "items": items, "total": result["total"], "page": result["page"], "page_size": result["page_size"] diff --git a/luxx/routes/messages.py b/luxx/routes/messages.py index 29177c7..3048f17 100644 --- a/luxx/routes/messages.py +++ b/luxx/routes/messages.py @@ -51,7 +51,8 @@ def list_messages( return success_response(data={ "messages": [m.to_dict() for m in messages], - "title": conversation.title + "title": conversation.title, + "first_message": next((m.content[:50] + ('...' if len(m.content) > 50 else '') for m in messages if m.role == 'user'), None) })