Skip to content

Commit 1a39272

Browse files
author
Kerwin
committed
feat: 允许查看历史响应结果
1 parent c01eac0 commit 1a39272

File tree

6 files changed

+157
-31
lines changed

6 files changed

+157
-31
lines changed

service/src/index.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ router.post('/room-delete', auth, async (req, res) => {
135135
}
136136
})
137137

138-
router.get('/chat-hisroty', auth, async (req, res) => {
138+
router.get('/chat-history', auth, async (req, res) => {
139139
try {
140140
const userId = req.headers.userId as string
141141
const roomId = +req.query.roomId
@@ -179,6 +179,7 @@ router.get('/chat-hisroty', auth, async (req, res) => {
179179
inversion: false,
180180
error: false,
181181
loading: false,
182+
responseCount: (c.previousResponse?.length ?? 0) + 1,
182183
conversationOptions: {
183184
parentMessageId: c.options.messageId,
184185
conversationId: c.options.conversationId,
@@ -204,6 +205,66 @@ router.get('/chat-hisroty', auth, async (req, res) => {
204205
}
205206
})
206207

208+
router.get('/chat-response-history', auth, async (req, res) => {
209+
try {
210+
const userId = req.headers.userId as string
211+
const roomId = +req.query.roomId
212+
const uuid = +req.query.uuid
213+
const index = +req.query.index
214+
if (!roomId || !await existsChatRoom(userId, roomId)) {
215+
res.send({ status: 'Success', message: null, data: [] })
216+
// res.send({ status: 'Fail', message: 'Unknow room', data: null })
217+
return
218+
}
219+
const chat = await getChat(roomId, uuid)
220+
if (chat.previousResponse === undefined || chat.previousResponse.length < index) {
221+
res.send({ status: 'Fail', message: 'Error', data: [] })
222+
return
223+
}
224+
const response = index >= chat.previousResponse.length
225+
? chat
226+
: chat.previousResponse[index]
227+
const usage = response.options.completion_tokens
228+
? {
229+
completion_tokens: response.options.completion_tokens || null,
230+
prompt_tokens: response.options.prompt_tokens || null,
231+
total_tokens: response.options.total_tokens || null,
232+
estimated: response.options.estimated || null,
233+
}
234+
: undefined
235+
res.send({
236+
status: 'Success',
237+
message: null,
238+
data: {
239+
uuid: chat.uuid,
240+
dateTime: new Date(chat.dateTime).toLocaleString(),
241+
text: response.response,
242+
inversion: false,
243+
error: false,
244+
loading: false,
245+
responseCount: (chat.previousResponse?.length ?? 0) + 1,
246+
conversationOptions: {
247+
parentMessageId: response.options.messageId,
248+
conversationId: response.options.conversationId,
249+
},
250+
requestOptions: {
251+
prompt: chat.prompt,
252+
parentMessageId: response.options.parentMessageId,
253+
options: {
254+
parentMessageId: response.options.messageId,
255+
conversationId: response.options.conversationId,
256+
},
257+
},
258+
usage,
259+
},
260+
})
261+
}
262+
catch (error) {
263+
console.error(error)
264+
res.send({ status: 'Fail', message: 'Load error', data: null })
265+
}
266+
})
267+
207268
router.post('/chat-delete', auth, async (req, res) => {
208269
try {
209270
const userId = req.headers.userId as string
@@ -296,9 +357,10 @@ router.post('/chat-process', [auth, limiter], async (req, res) => {
296357
let { roomId, uuid, regenerate, prompt, options = {}, systemMessage, temperature, top_p } = req.body as RequestProps
297358
const userId = req.headers.userId as string
298359
const room = await getChatRoom(userId, roomId)
360+
if (room == null)
361+
global.console.error(`Unable to get chat room \t ${userId}\t ${roomId}`)
299362
if (room != null && isNotEmptyString(room.prompt))
300363
systemMessage = room.prompt
301-
302364
let lastResponse
303365
let result
304366
let message: ChatInfo

service/src/storage/model.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ export class ChatOptions {
5151
parentMessageId?: string
5252
messageId?: string
5353
conversationId?: string
54-
promptTokens?: number
55-
completionTokens?: number
56-
totalTokens?: number
54+
prompt_tokens?: number
55+
completion_tokens?: number
56+
total_tokens?: number
5757
estimated?: boolean
5858
constructor(parentMessageId?: string, messageId?: string, conversationId?: string) {
5959
this.parentMessageId = parentMessageId

src/api/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ export function fetchChatAPIProcess<T = any>(
5959
})
6060
}
6161

62+
export function fetchChatResponseoHistory<T = any>(roomId: number, uuid: number, index: number) {
63+
return get<T>({
64+
url: '/chat-response-history',
65+
data: { roomId, uuid, index },
66+
})
67+
}
68+
6269
export function fetchSession<T>() {
6370
return post<T>({
6471
url: '/session',
@@ -150,7 +157,7 @@ export function fetchDeleteChatRoom<T = any>(roomId: number) {
150157

151158
export function fetchGetChatHistory<T = any>(roomId: number, lastId?: number) {
152159
return get<T>({
153-
url: `/chat-hisroty?roomId=${roomId}&lastId=${lastId}`,
160+
url: `/chat-history?roomId=${roomId}&lastId=${lastId}`,
154161
})
155162
}
156163

src/typings/chat.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ declare namespace Chat {
55
dateTime: string
66
text: string
77
inversion?: boolean
8+
responseCount?: number
89
error?: boolean
910
loading?: boolean
1011
conversationOptions?: ConversationRequest | null

src/views/chat/components/Message/index.vue

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang='ts'>
22
import { computed, ref } from 'vue'
3-
import { NDropdown, NPopover, useMessage } from 'naive-ui'
3+
import { NButtonGroup, NDropdown, NPopover, NSpace, useMessage } from 'naive-ui'
44
import AvatarComponent from './Avatar.vue'
55
import TextComponent from './Text.vue'
66
import { SvgIcon } from '@/components/common'
@@ -15,23 +15,24 @@ interface Props {
1515
inversion?: boolean
1616
error?: boolean
1717
loading?: boolean
18+
responseCount?: number
1819
usage?: {
1920
completion_tokens: number
2021
prompt_tokens: number
2122
total_tokens: number
2223
estimated: boolean
2324
}
2425
}
26+
const props = defineProps<Props>()
27+
28+
const emit = defineEmits<Emit>()
2529
2630
interface Emit {
2731
(ev: 'regenerate'): void
2832
(ev: 'delete'): void
33+
(ev: 'responseHistory', historyIndex: number): void
2934
}
3035
31-
const props = defineProps<Props>()
32-
33-
const emit = defineEmits<Emit>()
34-
3536
const { isMobile } = useBasicLayout()
3637
3738
const { iconRender } = useIconRender()
@@ -44,6 +45,9 @@ const asRawText = ref(props.inversion)
4445
4546
const messageRef = ref<HTMLElement>()
4647
48+
const indexRef = ref<number>(0)
49+
indexRef.value = props.responseCount ?? 0
50+
4751
const url_openai_token = 'https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them'
4852
4953
const options = computed(() => {
@@ -98,6 +102,13 @@ async function handleCopy() {
98102
message.error('复制失败')
99103
}
100104
}
105+
106+
async function handlePreviousResponse(next: number) {
107+
if (indexRef.value + next < 1 || indexRef.value + next > props.responseCount!)
108+
return
109+
indexRef.value += next
110+
emit('responseHistory', indexRef.value - 1)
111+
}
101112
</script>
102113

103114
<template>
@@ -114,26 +125,45 @@ async function handleCopy() {
114125
</div>
115126
<div class="overflow-hidden text-sm " :class="[inversion ? 'items-end' : 'items-start']">
116127
<p class="text-xs text-[#b4bbc4]" :class="[inversion ? 'text-right' : 'text-left']">
117-
{{ new Date(dateTime as string).toLocaleString() }}
118-
<template v-if="usage">
119-
<NPopover trigger="hover">
120-
<template #trigger>
121-
<span>
122-
<span>[</span>
123-
<span>{{ usage.estimated ? '~' : '' }}</span>
124-
<span>{{ usage.prompt_tokens }}+{{ usage.completion_tokens }}={{ usage.total_tokens }}</span>
125-
<span>]</span>
128+
<NSpace>
129+
{{ new Date(dateTime as string).toLocaleString() }}
130+
<NButtonGroup v-if="!inversion && responseCount && responseCount > 1">
131+
<NButton
132+
style="cursor: pointer;"
133+
:disabled="indexRef === 1"
134+
@click="handlePreviousResponse(-1)"
135+
>
136+
<svg stroke="currentColor" fill="none" stroke-width="1.5" viewBox="5 -5 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-3 w-3" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="15 18 9 12 15 6" /></svg>
137+
</NButton>
138+
<span class="text-xs text-[#b4bbc4]"> {{ indexRef }} / {{ responseCount }}</span>
139+
<NButton
140+
style="cursor: pointer;"
141+
:disabled="indexRef === responseCount"
142+
@click="handlePreviousResponse(1)"
143+
>
144+
<svg stroke="currentColor" fill="none" stroke-width="1.5" viewBox="-5 -5 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-3 w-3" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="9 18 15 12 9 6" /></svg>
145+
</NButton>
146+
</NButtonGroup>
147+
<template v-if="usage">
148+
<NPopover trigger="hover">
149+
<template #trigger>
150+
<span>
151+
<span>[</span>
152+
<span>{{ usage.estimated ? '~' : '' }}</span>
153+
<span>{{ usage.prompt_tokens }}+{{ usage.completion_tokens }}={{ usage.total_tokens }}</span>
154+
<span>]</span>
155+
</span>
156+
</template>
157+
<span class="text-xs">
158+
{{ usage.estimated ? t('chat.usageEstimate') : '' }}
159+
{{ t('chat.usagePrompt') }} {{ usage.prompt_tokens }}
160+
+ {{ t('chat.usageResponse') }} {{ usage.completion_tokens }}
161+
= {{ t('chat.usageTotal') }}<a :href="url_openai_token" target="_blank">(?)</a>
162+
{{ usage.total_tokens }}
126163
</span>
127-
</template>
128-
<span class="text-xs">
129-
{{ usage.estimated ? t('chat.usageEstimate') : '' }}
130-
{{ t('chat.usagePrompt') }} {{ usage.prompt_tokens }}
131-
+ {{ t('chat.usageResponse') }} {{ usage.completion_tokens }}
132-
= {{ t('chat.usageTotal') }}<a :href="url_openai_token" target="_blank">(?)</a>
133-
{{ usage.total_tokens }}
134-
</span>
135-
</NPopover>
136-
</template>
164+
</NPopover>
165+
</template>
166+
</NSpace>
137167
</p>
138168
<div
139169
class="flex items-end gap-1 mt-2"

src/views/chat/index.vue

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import HeaderComponent from './components/Header/index.vue'
1414
import { HoverButton, SvgIcon } from '@/components/common'
1515
import { useBasicLayout } from '@/hooks/useBasicLayout'
1616
import { useAuthStore, useChatStore, usePromptStore } from '@/store'
17-
import { fetchChatAPIProcess } from '@/api'
17+
import { fetchChatAPIProcess, fetchChatResponseoHistory } from '@/api'
1818
import { t } from '@/locales'
1919
import { debounce } from '@/utils/functions/debounce'
2020
import IconPrompt from '@/icons/Prompt.vue'
@@ -235,6 +235,8 @@ async function onRegenerate(index: number) {
235235
controller = new AbortController()
236236
237237
const { requestOptions } = dataSources.value[index]
238+
let responseCount = dataSources.value[index].responseCount || 1
239+
responseCount++
238240
239241
let message = requestOptions?.prompt ?? ''
240242
@@ -252,6 +254,7 @@ async function onRegenerate(index: number) {
252254
dateTime: new Date().toLocaleString(),
253255
text: '',
254256
inversion: false,
257+
responseCount,
255258
error: false,
256259
loading: true,
257260
conversationOptions: null,
@@ -294,6 +297,7 @@ async function onRegenerate(index: number) {
294297
dateTime: new Date().toLocaleString(),
295298
text: lastText + (data.text ?? ''),
296299
inversion: false,
300+
responseCount,
297301
error: false,
298302
loading: true,
299303
conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
@@ -339,6 +343,7 @@ async function onRegenerate(index: number) {
339343
dateTime: new Date().toLocaleString(),
340344
text: errorMessage,
341345
inversion: false,
346+
responseCount,
342347
error: true,
343348
loading: false,
344349
conversationOptions: null,
@@ -351,6 +356,25 @@ async function onRegenerate(index: number) {
351356
}
352357
}
353358
359+
async function onResponseHistory(index: number, historyIndex: number) {
360+
const chat = (await fetchChatResponseoHistory(+uuid, dataSources.value[index].uuid || Date.now(), historyIndex)).data
361+
updateChat(
362+
+uuid,
363+
index,
364+
{
365+
dateTime: chat.dateTime,
366+
text: chat.text,
367+
inversion: false,
368+
responseCount: chat.responseCount,
369+
error: true,
370+
loading: false,
371+
conversationOptions: chat.conversationOptions,
372+
requestOptions: { prompt: chat.requestOptions.prompt, options: { ...chat.requestOptions.options } },
373+
usage: chat.usage,
374+
},
375+
)
376+
}
377+
354378
function handleExport() {
355379
if (loading.value)
356380
return
@@ -578,11 +602,13 @@ onUnmounted(() => {
578602
:date-time="item.dateTime"
579603
:text="item.text"
580604
:inversion="item.inversion"
605+
:response-count="item.responseCount"
581606
:usage="item && item.usage || undefined"
582607
:error="item.error"
583608
:loading="item.loading"
584609
@regenerate="onRegenerate(index)"
585610
@delete="handleDelete(index)"
611+
@response-history="(ev) => onResponseHistory(index, ev)"
586612
/>
587613
<div class="sticky bottom-0 left-0 flex justify-center">
588614
<NButton v-if="loading" type="warning" @click="handleStop">

0 commit comments

Comments
 (0)