From b7f7d3947f9ccb56d5692ae81b813baf52af9249 Mon Sep 17 00:00:00 2001 From: Bob Du Date: Tue, 6 May 2025 18:36:41 +0800 Subject: [PATCH] feat: support display thought chain for deepseek r1 Signed-off-by: Bob Du --- service/src/chatgpt/index.ts | 6 +++ service/src/chatgpt/types.ts | 1 + service/src/routes/chat.ts | 4 ++ service/src/storage/model.ts | 1 + service/src/storage/mongo.ts | 3 +- src/typings/chat.d.ts | 1 + .../chat/components/Message/Reasoning.vue | 38 +++++++++++++++++++ src/views/chat/components/Message/index.vue | 3 ++ src/views/chat/index.vue | 4 ++ 9 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/views/chat/components/Message/Reasoning.vue diff --git a/service/src/chatgpt/index.ts b/service/src/chatgpt/index.ts index 333cb5ce..e8d8a7b7 100644 --- a/service/src/chatgpt/index.ts +++ b/service/src/chatgpt/index.ts @@ -126,12 +126,16 @@ async function chatReplyProcess(options: RequestOptions) { }) // Process the stream + let responseReasoning = '' let responseText = '' let responseId = '' const usage = new UsageResponse() for await (const chunk of stream) { // Extract the content from the chunk + // @ts-expect-error For deepseek-reasoner model only. The reasoning contents of the assistant message, before the final answer. + const reasoningContent = chunk.choices[0]?.delta?.reasoning_content || '' + responseReasoning += reasoningContent const content = chunk.choices[0]?.delta?.content || '' responseText += content responseId = chunk.id @@ -141,6 +145,7 @@ async function chatReplyProcess(options: RequestOptions) { // Build response object similar to the original implementation const responseChunk = { id: chunk.id, + reasoning: responseReasoning, text: responseText, role: 'assistant', finish_reason, @@ -159,6 +164,7 @@ async function chatReplyProcess(options: RequestOptions) { // Final response object const response = { id: responseId || messageId, + reasoning: responseReasoning, text: responseText, role: 'assistant', detail: { diff --git a/service/src/chatgpt/types.ts b/service/src/chatgpt/types.ts index 4efc3ffb..353e0116 100644 --- a/service/src/chatgpt/types.ts +++ b/service/src/chatgpt/types.ts @@ -14,6 +14,7 @@ export interface ChatMessage { export interface ResponseChunk { id: string text: string + reasoning: string role: string finish_reason: string } diff --git a/service/src/routes/chat.ts b/service/src/routes/chat.ts index 5ec764d8..98d9ae04 100644 --- a/service/src/routes/chat.ts +++ b/service/src/routes/chat.ts @@ -83,6 +83,7 @@ router.get('/chat-history', auth, async (req, res) => { result.push({ uuid: c.uuid, dateTime: new Date(c.dateTime).toLocaleString(), + reasoning: c.reasoning, text: c.response, inversion: false, error: false, @@ -143,6 +144,7 @@ router.get('/chat-response-history', auth, async (req, res) => { data: { uuid: chat.uuid, dateTime: new Date(chat.dateTime).toLocaleString(), + reasoning: chat.reasoning, text: response.response, inversion: false, error: false, @@ -295,6 +297,7 @@ router.post('/chat-process', [auth, limiter], async (req, res) => { const previousResponse = message.previousResponse || [] previousResponse.push({ response: message.response, options: message.options }) await updateChat(message._id as unknown as string, + result.data.reasoning, result.data.text, result.data.id, model, @@ -303,6 +306,7 @@ router.post('/chat-process', [auth, limiter], async (req, res) => { } else { await updateChat(message._id as unknown as string, + result.data.reasoning, result.data.text, result.data.id, model, diff --git a/service/src/storage/model.ts b/service/src/storage/model.ts index d4b1cd7e..794110b1 100644 --- a/service/src/storage/model.ts +++ b/service/src/storage/model.ts @@ -121,6 +121,7 @@ export class ChatInfo { dateTime: number prompt: string images?: string[] + reasoning?: string response?: string status: Status = Status.Normal options: ChatOptions diff --git a/service/src/storage/mongo.ts b/service/src/storage/mongo.ts index a48693e4..92e8e0aa 100644 --- a/service/src/storage/mongo.ts +++ b/service/src/storage/mongo.ts @@ -93,10 +93,11 @@ export async function getChatByMessageId(messageId: string) { return await chatCol.findOne({ 'options.messageId': messageId }) } -export async function updateChat(chatId: string, response: string, messageId: string, model: string, usage: UsageResponse, previousResponse?: []) { +export async function updateChat(chatId: string, reasoning: string, response: string, messageId: string, model: string, usage: UsageResponse, previousResponse?: []) { const query = { _id: new ObjectId(chatId) } const update = { $set: { + 'reasoning': reasoning, 'response': response, 'model': model || '', 'options.messageId': messageId, diff --git a/src/typings/chat.d.ts b/src/typings/chat.d.ts index 616af229..7fa5f846 100644 --- a/src/typings/chat.d.ts +++ b/src/typings/chat.d.ts @@ -2,6 +2,7 @@ declare namespace Chat { interface Chat { uuid?: number dateTime: string + reasoning?: string text: string images?: string[] inversion?: boolean diff --git a/src/views/chat/components/Message/Reasoning.vue b/src/views/chat/components/Message/Reasoning.vue new file mode 100644 index 00000000..24ea88b4 --- /dev/null +++ b/src/views/chat/components/Message/Reasoning.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/views/chat/components/Message/index.vue b/src/views/chat/components/Message/index.vue index bf2f4ecb..53f148a2 100644 --- a/src/views/chat/components/Message/index.vue +++ b/src/views/chat/components/Message/index.vue @@ -3,6 +3,7 @@ import { computed, ref } from 'vue' import { NButton, NButtonGroup, NDropdown, NPopover, NSpace, useMessage } from 'naive-ui' import AvatarComponent from './Avatar.vue' import TextComponent from './Text.vue' +import Reasoning from './Reasoning.vue' import { SvgIcon } from '@/components/common' import { useIconRender } from '@/hooks/useIconRender' import { t } from '@/locales' @@ -21,6 +22,7 @@ interface Props { currentNavIndex: number dateTime?: string model?: string + reasoning?: string text?: string images?: string[] isRecord?: boolean @@ -223,6 +225,7 @@ function isEventTargetValid(event: any) {

+
{ :index="index" :current-nav-index="currentNavIndexRef" :date-time="item.dateTime" + :reasoning="item?.reasoning" :text="item.text" :images="item.images" :inversion="item.inversion"