Skip to content

feat: support display thought chain for deepseek r1 #597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions service/src/chatgpt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -159,6 +164,7 @@ async function chatReplyProcess(options: RequestOptions) {
// Final response object
const response = {
id: responseId || messageId,
reasoning: responseReasoning,
text: responseText,
role: 'assistant',
detail: {
Expand Down
1 change: 1 addition & 0 deletions service/src/chatgpt/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ChatMessage {
export interface ResponseChunk {
id: string
text: string
reasoning: string
role: string
finish_reason: string
}
Expand Down
4 changes: 4 additions & 0 deletions service/src/routes/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions service/src/storage/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export class ChatInfo {
dateTime: number
prompt: string
images?: string[]
reasoning?: string
response?: string
status: Status = Status.Normal
options: ChatOptions
Expand Down
3 changes: 2 additions & 1 deletion service/src/storage/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/typings/chat.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ declare namespace Chat {
interface Chat {
uuid?: number
dateTime: string
reasoning?: string
text: string
images?: string[]
inversion?: boolean
Expand Down
38 changes: 38 additions & 0 deletions src/views/chat/components/Message/Reasoning.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { useBasicLayout } from '@/hooks/useBasicLayout'

interface Props {
reasoning?: string
loading?: boolean
}

defineProps<Props>()

const { isMobile } = useBasicLayout()

const textRef = ref<HTMLElement>()

const wrapClass = computed(() => {
return [
'text-wrap',
'min-w-[20px]',
'rounded-md',
isMobile.value ? 'p-2' : 'px-3 py-2',
]
})
</script>

<template>
<div class="text-black" :class="wrapClass">
<div ref="textRef" class="leading-relaxed break-words">
<div class="flex items-end">
<div class="w-full dark:text-gray-50 text-xs" v-text="reasoning" />
</div>
</div>
</div>
</template>

<style lang="less">
@import url(./style.less);
</style>
3 changes: 3 additions & 0 deletions src/views/chat/components/Message/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -21,6 +22,7 @@ interface Props {
currentNavIndex: number
dateTime?: string
model?: string
reasoning?: string
text?: string
images?: string[]
isRecord?: boolean
Expand Down Expand Up @@ -223,6 +225,7 @@ function isEventTargetValid(event: any) {
</template>
</NSpace>
</p>
<Reasoning v-if="reasoning" :reasoning="reasoning" :loading="loading" />
<div
class="flex items-end gap-1 mt-2"
:class="[inversion ? 'flex-row-reverse' : 'flex-row']"
Expand Down
4 changes: 4 additions & 0 deletions src/views/chat/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ async function onConversation() {
dataSources.value.length - 1,
{
dateTime: new Date().toLocaleString(),
reasoning: data?.reasoning,
text: lastText + (data.text ?? ''),
inversion: false,
error: false,
Expand Down Expand Up @@ -318,6 +319,7 @@ async function onRegenerate(index: number) {
index,
{
dateTime: new Date().toLocaleString(),
reasoning: data?.reasoning,
text: lastText + (data.text ?? ''),
inversion: false,
responseCount,
Expand Down Expand Up @@ -386,6 +388,7 @@ async function onResponseHistory(index: number, historyIndex: number) {
index,
{
dateTime: chat.dateTime,
reasoning: chat?.reasoning,
text: chat.text,
inversion: false,
responseCount: chat.responseCount,
Expand Down Expand Up @@ -683,6 +686,7 @@ onUnmounted(() => {
:index="index"
:current-nav-index="currentNavIndexRef"
:date-time="item.dateTime"
:reasoning="item?.reasoning"
:text="item.text"
:images="item.images"
:inversion="item.inversion"
Expand Down