feat: 添加生成对话标题功能,支持根据内容自动生成简短标题,使用 fast_model

This commit is contained in:
Wenjie Zhang 2026-03-23 09:43:04 +08:00
parent 74229c05a4
commit 53e0ed37f6
3 changed files with 67 additions and 8 deletions

View File

@ -47,11 +47,11 @@ class Config(BaseModel):
# 模型配置
# ============================================================
default_model: str = Field(
default="siliconflow/Pro/deepseek-ai/DeepSeek-V3.2",
default="siliconflow/Pro/MiniMaxAI/MiniMax-M2.5",
description="默认对话模型",
)
fast_model: str = Field(
default="siliconflow/Qwen/Qwen3.5-9B",
default="siliconflow/Pro/MiniMaxAI/MiniMax-M2.5",
description="快速响应模型",
)
embed_model: str = Field(
@ -63,7 +63,7 @@ class Config(BaseModel):
description="默认 Re-Ranker 模型",
)
content_guard_llm_model: str = Field(
default="siliconflow/Qwen/Qwen3.5-9B",
default="siliconflow/Pro/MiniMaxAI/MiniMax-M2.5",
description="内容审查LLM模型",
)

View File

@ -52,6 +52,20 @@ export const agentApi = {
*/
simpleCall: (query) => apiPost('/api/chat/call', { query }),
/**
* 生成对话标题
* @param {string} query - 查询内容
* @param {Object} modelSpec - 模型配置
* @returns {Promise<string>} - 生成的标题
*/
generateTitle: async (query, modelSpec) => {
const response = await apiPost('/api/chat/call', {
query: `根据以下对话内容生成一个简短的标题最多30个字符中英文均可不要包含 markdown 标记:\n\n${query.slice(0, 2000)}`,
meta: { model_spec: modelSpec }
})
return response.response
},
/**
* 获取默认智能体
* @returns {Promise} - 默认智能体信息

View File

@ -63,6 +63,12 @@
<MessageCirclePlus v-else class="nav-btn-icon" size="16" />
<span class="text">新对话</span>
</div>
<div
v-if="currentThread?.title && currentThread.title !== '新的对话'"
class="conversation-title"
>
{{ currentThread.title }}
</div>
</div>
<div class="header__right">
<UserInfoComponent v-if="!userStore.isAdmin" />
@ -227,6 +233,7 @@ import { useAgentStore } from '@/stores/agent'
import { useChatUIStore } from '@/stores/chatUI'
import { useInfoStore } from '@/stores/info'
import { useUserStore } from '@/stores/user'
import { useConfigStore } from '@/stores/config'
import { storeToRefs } from 'pinia'
import { MessageProcessor } from '@/utils/messageProcessor'
import { agentApi, threadApi } from '@/apis'
@ -250,6 +257,7 @@ const agentStore = useAgentStore()
const chatUIStore = useChatUIStore()
const infoStore = useInfoStore()
const userStore = useUserStore()
const configStore = useConfigStore()
const {
agents,
selectedAgentId,
@ -798,11 +806,24 @@ const sendMessage = async ({
return Promise.reject(error)
}
//
// fast-model
if ((threadMessages.value[threadId] || []).length === 0) {
const autoTitle = text.replace(/\s+/g, ' ').trim().slice(0, 255)
const autoTitle = text.replace(/\s+/g, ' ').trim().slice(0, 2000)
if (autoTitle) {
void updateThread(threadId, autoTitle).catch(() => {})
void (async () => {
try {
const generatedTitle = await agentApi.generateTitle(autoTitle, configStore.config?.fast_model)
if (generatedTitle) {
const finalTitle = generatedTitle.slice(0, 30).replace(/\s+/g, ' ').trim()
if (finalTitle) {
void updateThread(threadId, finalTitle).catch(() => {})
}
}
} catch (e) {
// 使
void updateThread(threadId, autoTitle.slice(0, 30)).catch(() => {})
}
})()
}
}
@ -1017,9 +1038,22 @@ const handleSendMessage = async ({ image } = {}) => {
if (useRunsApi) {
if ((threadMessages.value[threadId] || []).length === 0) {
const autoTitle = text.replace(/\s+/g, ' ').trim().slice(0, 255)
const autoTitle = text.replace(/\s+/g, ' ').trim().slice(0, 2000)
if (autoTitle) {
void updateThread(threadId, autoTitle).catch(() => {})
void (async () => {
try {
const generatedTitle = await agentApi.generateTitle(autoTitle, configStore.config?.fast_model)
if (generatedTitle) {
const finalTitle = generatedTitle.slice(0, 30).replace(/\s+/g, ' ').trim()
if (finalTitle) {
void updateThread(threadId, finalTitle).catch(() => {})
}
}
} catch (e) {
// 使
void updateThread(threadId, autoTitle.slice(0, 30)).catch(() => {})
}
})()
}
}
@ -1459,6 +1493,17 @@ watch(
.sidebar-logo-toggle:hover .sidebar-expand-overlay {
opacity: 1;
}
.conversation-title {
font-size: 14px;
font-weight: 500;
color: var(--text-primary);
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 8px;
}
}
}