前端页面展示内容修改,带totaltoken
This commit is contained in:
@@ -48,6 +48,11 @@ export default {
|
||||
clearHistoryConfirm: 'Are you sure to clear chat history?',
|
||||
preview: 'Preview',
|
||||
showRawText: 'Show as raw text',
|
||||
sourceSemantic: 'Semantic Match',
|
||||
sourceLlm: 'LLM Output',
|
||||
promptTokens: 'Prompt {count} tokens',
|
||||
completionTokens: 'Completion {count} tokens',
|
||||
sessionTokens: 'Session {count} tokens',
|
||||
},
|
||||
setting: {
|
||||
setting: 'Setting',
|
||||
|
||||
@@ -48,6 +48,11 @@ export default {
|
||||
clearHistoryConfirm: '确定清空聊天记录?',
|
||||
preview: '预览',
|
||||
showRawText: '显示原文',
|
||||
sourceSemantic: '语义匹配',
|
||||
sourceLlm: '大模型输出',
|
||||
promptTokens: '问题 {count} tokens',
|
||||
completionTokens: '回答 {count} tokens',
|
||||
sessionTokens: '本轮消耗 {count} tokens',
|
||||
},
|
||||
setting: {
|
||||
setting: '设置',
|
||||
|
||||
@@ -48,6 +48,11 @@ export default {
|
||||
clearHistoryConfirm: '確定清除紀錄?',
|
||||
preview: '預覽',
|
||||
showRawText: '顯示原文',
|
||||
sourceSemantic: '語義匹配',
|
||||
sourceLlm: '大模型輸出',
|
||||
promptTokens: '問題 {count} tokens',
|
||||
completionTokens: '回答 {count} tokens',
|
||||
sessionTokens: '本輪消耗 {count} tokens',
|
||||
},
|
||||
setting: {
|
||||
setting: '設定',
|
||||
|
||||
@@ -2,19 +2,36 @@ import { ss } from '@/utils/storage'
|
||||
|
||||
const LOCAL_NAME = 'chatStorage'
|
||||
|
||||
export function emptyUsage(): Chat.TokenUsage {
|
||||
return {
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
}
|
||||
}
|
||||
|
||||
export function defaultState(): Chat.ChatState {
|
||||
const uuid = 1002
|
||||
return {
|
||||
active: uuid,
|
||||
usingContext: true,
|
||||
history: [{ uuid, title: 'New Chat', isEdit: false }],
|
||||
chat: [{ uuid, data: [] }],
|
||||
chat: [{ uuid, data: [], sessionUsage: emptyUsage() }],
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocalState(): Chat.ChatState {
|
||||
const localState = ss.get(LOCAL_NAME)
|
||||
return { ...defaultState(), ...localState }
|
||||
const state = { ...defaultState(), ...localState }
|
||||
|
||||
return {
|
||||
...state,
|
||||
chat: (state.chat || []).map((item: Partial<Chat.Session>) => ({
|
||||
uuid: item.uuid || Date.now(),
|
||||
data: item.data || [],
|
||||
sessionUsage: { ...emptyUsage(), ...(item.sessionUsage || {}) },
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
export function setLocalState(state: Chat.ChatState) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { getLocalState, setLocalState } from './helper'
|
||||
import { emptyUsage, getLocalState, setLocalState } from './helper'
|
||||
import { router } from '@/router'
|
||||
|
||||
export const useChatStore = defineStore('chat-store', {
|
||||
@@ -20,6 +20,14 @@ export const useChatStore = defineStore('chat-store', {
|
||||
return state.chat.find(item => item.uuid === state.active)?.data ?? []
|
||||
}
|
||||
},
|
||||
|
||||
getSessionUsageByUuid(state: Chat.ChatState) {
|
||||
return (uuid?: number) => {
|
||||
if (uuid)
|
||||
return state.chat.find(item => item.uuid === uuid)?.sessionUsage ?? emptyUsage()
|
||||
return state.chat.find(item => item.uuid === state.active)?.sessionUsage ?? emptyUsage()
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
@@ -30,7 +38,7 @@ export const useChatStore = defineStore('chat-store', {
|
||||
|
||||
addHistory(history: Chat.History, chatData: Chat.Chat[] = []) {
|
||||
this.history.unshift(history)
|
||||
this.chat.unshift({ uuid: history.uuid, data: chatData })
|
||||
this.chat.unshift({ uuid: history.uuid, data: chatData, sessionUsage: emptyUsage() })
|
||||
this.active = history.uuid
|
||||
this.reloadRoute(history.uuid)
|
||||
},
|
||||
@@ -97,7 +105,7 @@ export const useChatStore = defineStore('chat-store', {
|
||||
if (this.history.length === 0) {
|
||||
const uuid = Date.now()
|
||||
this.history.push({ uuid, title: chat.text, isEdit: false })
|
||||
this.chat.push({ uuid, data: [chat] })
|
||||
this.chat.push({ uuid, data: [chat], sessionUsage: emptyUsage() })
|
||||
this.active = uuid
|
||||
this.recordState()
|
||||
}
|
||||
@@ -182,6 +190,32 @@ export const useChatStore = defineStore('chat-store', {
|
||||
}
|
||||
},
|
||||
|
||||
addSessionUsageByUuid(uuid: number, usage: Partial<Chat.TokenUsage>) {
|
||||
const sessionUsage = { ...emptyUsage(), ...usage }
|
||||
|
||||
if (!uuid || uuid === 0) {
|
||||
if (this.chat.length) {
|
||||
this.chat[0].sessionUsage = {
|
||||
prompt_tokens: this.chat[0].sessionUsage.prompt_tokens + sessionUsage.prompt_tokens,
|
||||
completion_tokens: this.chat[0].sessionUsage.completion_tokens + sessionUsage.completion_tokens,
|
||||
total_tokens: this.chat[0].sessionUsage.total_tokens + sessionUsage.total_tokens,
|
||||
}
|
||||
this.recordState()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const index = this.chat.findIndex(item => item.uuid === uuid)
|
||||
if (index !== -1) {
|
||||
this.chat[index].sessionUsage = {
|
||||
prompt_tokens: this.chat[index].sessionUsage.prompt_tokens + sessionUsage.prompt_tokens,
|
||||
completion_tokens: this.chat[index].sessionUsage.completion_tokens + sessionUsage.completion_tokens,
|
||||
total_tokens: this.chat[index].sessionUsage.total_tokens + sessionUsage.total_tokens,
|
||||
}
|
||||
this.recordState()
|
||||
}
|
||||
},
|
||||
|
||||
async reloadRoute(uuid?: number) {
|
||||
this.recordState()
|
||||
await router.push({ name: 'Chat', params: { uuid } })
|
||||
|
||||
38
ai-chat-web/src/typings/chat.d.ts
vendored
38
ai-chat-web/src/typings/chat.d.ts
vendored
@@ -1,4 +1,17 @@
|
||||
declare namespace Chat {
|
||||
interface TokenUsage {
|
||||
prompt_tokens: number
|
||||
completion_tokens: number
|
||||
total_tokens: number
|
||||
}
|
||||
|
||||
type ReplySource = 'semantic_match' | 'llm'
|
||||
|
||||
interface MessageMeta {
|
||||
source?: ReplySource | null
|
||||
tokenUsed?: boolean
|
||||
usage?: TokenUsage | null
|
||||
}
|
||||
|
||||
interface Chat {
|
||||
dateTime: string
|
||||
@@ -8,6 +21,7 @@ declare namespace Chat {
|
||||
loading?: boolean
|
||||
conversationOptions?: ConversationRequest | null
|
||||
requestOptions: { prompt: string; options?: ConversationRequest | null }
|
||||
messageMeta?: MessageMeta | null
|
||||
}
|
||||
|
||||
interface History {
|
||||
@@ -16,11 +30,17 @@ declare namespace Chat {
|
||||
uuid: number
|
||||
}
|
||||
|
||||
interface Session {
|
||||
uuid: number
|
||||
data: Chat[]
|
||||
sessionUsage: TokenUsage
|
||||
}
|
||||
|
||||
interface ChatState {
|
||||
active: number | null
|
||||
usingContext: boolean;
|
||||
history: History[]
|
||||
chat: { uuid: number; data: Chat[] }[]
|
||||
chat: Session[]
|
||||
}
|
||||
|
||||
interface ConversationRequest {
|
||||
@@ -29,18 +49,22 @@ declare namespace Chat {
|
||||
}
|
||||
|
||||
interface ConversationResponse {
|
||||
conversationId: string
|
||||
detail: {
|
||||
conversationId?: string
|
||||
detail?: {
|
||||
choices: { finish_reason: string; index: number; logprobs: any; text: string }[]
|
||||
created: number
|
||||
id: string
|
||||
model: string
|
||||
object: string
|
||||
usage: { completion_tokens: number; prompt_tokens: number; total_tokens: number }
|
||||
usage: TokenUsage
|
||||
}
|
||||
id: string
|
||||
parentMessageId: string
|
||||
role: string
|
||||
id?: string
|
||||
parentMessageId?: string
|
||||
role?: string
|
||||
text: string
|
||||
usage?: TokenUsage
|
||||
source?: ReplySource
|
||||
tokenUsed?: boolean
|
||||
meta?: MessageMeta | null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ interface Props {
|
||||
inversion?: boolean
|
||||
error?: boolean
|
||||
loading?: boolean
|
||||
messageMeta?: Chat.MessageMeta | null
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
@@ -36,6 +37,42 @@ const asRawText = ref(props.inversion)
|
||||
|
||||
const messageRef = ref<HTMLElement>()
|
||||
|
||||
const sourceLabel = computed(() => {
|
||||
if (props.inversion)
|
||||
return ''
|
||||
|
||||
switch (props.messageMeta?.source) {
|
||||
case 'semantic_match':
|
||||
return t('chat.sourceSemantic')
|
||||
case 'llm':
|
||||
return t('chat.sourceLlm')
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
const sourceClass = computed(() => {
|
||||
switch (props.messageMeta?.source) {
|
||||
case 'semantic_match':
|
||||
return 'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900/60 dark:bg-sky-950/40 dark:text-sky-300'
|
||||
case 'llm':
|
||||
return 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/40 dark:text-amber-300'
|
||||
default:
|
||||
return 'border-[#d0d7de] bg-white text-[#57606a] dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-300'
|
||||
}
|
||||
})
|
||||
|
||||
const usageLabel = computed(() => {
|
||||
const usage = props.messageMeta?.usage
|
||||
if (!usage)
|
||||
return ''
|
||||
|
||||
if (props.inversion)
|
||||
return t('chat.promptTokens', { count: usage.prompt_tokens })
|
||||
|
||||
return t('chat.completionTokens', { count: usage.completion_tokens })
|
||||
})
|
||||
|
||||
const options = computed(() => {
|
||||
const common = [
|
||||
{
|
||||
@@ -93,9 +130,22 @@ function handleRegenerate() {
|
||||
<AvatarComponent :image="inversion" />
|
||||
</div>
|
||||
<div class="overflow-hidden text-sm " :class="[inversion ? 'items-end' : 'items-start']">
|
||||
<p class="text-xs text-[#b4bbc4]" :class="[inversion ? 'text-right' : 'text-left']">
|
||||
{{ dateTime }}
|
||||
</p>
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs" :class="[inversion ? 'justify-end' : 'justify-start']">
|
||||
<span class="font-medium text-[#9aa4af] dark:text-neutral-500">{{ dateTime }}</span>
|
||||
<span
|
||||
v-if="sourceLabel"
|
||||
class="rounded-full border px-2.5 py-1 text-[11px] font-medium leading-none"
|
||||
:class="sourceClass"
|
||||
>
|
||||
{{ sourceLabel }}
|
||||
</span>
|
||||
<span
|
||||
v-if="usageLabel"
|
||||
class="rounded-full border border-violet-200 bg-violet-50 px-2.5 py-1 text-[11px] font-medium leading-none text-violet-700 dark:border-violet-900/60 dark:bg-violet-950/40 dark:text-violet-300"
|
||||
>
|
||||
{{ usageLabel }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-end gap-1 mt-2"
|
||||
:class="[inversion ? 'flex-row-reverse' : 'flex-row']"
|
||||
|
||||
@@ -37,6 +37,8 @@ const { usingContext, toggleUsingContext } = useUsingContext()
|
||||
const { uuid } = route.params as { uuid: string }
|
||||
|
||||
const dataSources = computed(() => chatStore.getChatByUuid(+uuid))
|
||||
const sessionUsage = computed(() => chatStore.getSessionUsageByUuid(+uuid))
|
||||
const sessionTokenText = computed(() => t('chat.sessionTokens', { count: sessionUsage.value.total_tokens }))
|
||||
const conversationList = computed(() => dataSources.value.filter(item => (!item.inversion && !item.error)))
|
||||
|
||||
const prompt = ref<string>('')
|
||||
@@ -49,6 +51,102 @@ const promptStore = usePromptStore()
|
||||
// 使用storeToRefs,保证store修改后,联想部分能够重新渲染
|
||||
const { promptList: promptTemplate } = storeToRefs<any>(promptStore)
|
||||
|
||||
function createEmptyUsage(): Chat.TokenUsage {
|
||||
return {
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: 0,
|
||||
total_tokens: 0,
|
||||
}
|
||||
}
|
||||
|
||||
function mergeUsage(current: Chat.TokenUsage, next?: Partial<Chat.TokenUsage> | null): Chat.TokenUsage {
|
||||
return {
|
||||
prompt_tokens: current.prompt_tokens + Number(next?.prompt_tokens || 0),
|
||||
completion_tokens: current.completion_tokens + Number(next?.completion_tokens || 0),
|
||||
total_tokens: current.total_tokens + Number(next?.total_tokens || 0),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeUsage(usage?: Partial<Chat.TokenUsage> | null): Chat.TokenUsage | null {
|
||||
if (!usage)
|
||||
return null
|
||||
|
||||
return {
|
||||
prompt_tokens: Number(usage.prompt_tokens || 0),
|
||||
completion_tokens: Number(usage.completion_tokens || 0),
|
||||
total_tokens: Number(usage.total_tokens || 0),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeResponseMeta(data: Chat.ConversationResponse): {
|
||||
source: Chat.ReplySource | null
|
||||
tokenUsed?: boolean
|
||||
usage: Chat.TokenUsage | null
|
||||
} {
|
||||
const usage = normalizeUsage(data.meta?.usage || data.usage || data.detail?.usage || null)
|
||||
const rawSource = data.meta?.source ?? data.source ?? null
|
||||
const source = rawSource === 'llm' ? 'llm' : rawSource ? 'semantic_match' : null
|
||||
const tokenUsed = data.meta?.tokenUsed ?? data.tokenUsed ?? (usage ? usage.total_tokens > 0 : undefined)
|
||||
|
||||
return {
|
||||
source,
|
||||
tokenUsed,
|
||||
usage,
|
||||
}
|
||||
}
|
||||
|
||||
function buildPromptUsage(usage: Chat.TokenUsage): Chat.TokenUsage {
|
||||
return {
|
||||
prompt_tokens: usage.prompt_tokens,
|
||||
completion_tokens: 0,
|
||||
total_tokens: usage.prompt_tokens,
|
||||
}
|
||||
}
|
||||
|
||||
function buildCompletionUsage(usage: Chat.TokenUsage): Chat.TokenUsage {
|
||||
return {
|
||||
prompt_tokens: 0,
|
||||
completion_tokens: usage.completion_tokens,
|
||||
total_tokens: usage.completion_tokens,
|
||||
}
|
||||
}
|
||||
|
||||
function findQuestionIndex(answerIndex: number) {
|
||||
for (let current = answerIndex - 1; current >= 0; current -= 1) {
|
||||
if (dataSources.value[current]?.inversion)
|
||||
return current
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
function updateMessageMeta(questionIndex: number, answerIndex: number, usage: Chat.TokenUsage, source: Chat.ReplySource | null, tokenUsed?: boolean) {
|
||||
if (questionIndex >= 0) {
|
||||
updateChatSome(
|
||||
+uuid,
|
||||
questionIndex,
|
||||
{
|
||||
messageMeta: {
|
||||
tokenUsed,
|
||||
usage: buildPromptUsage(usage),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
updateChatSome(
|
||||
+uuid,
|
||||
answerIndex,
|
||||
{
|
||||
messageMeta: {
|
||||
source,
|
||||
tokenUsed,
|
||||
usage: buildCompletionUsage(usage),
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 未知原因刷新页面,loading 状态不会重置,手动重置
|
||||
dataSources.value.forEach((item, index) => {
|
||||
if (item.loading)
|
||||
@@ -79,10 +177,13 @@ async function onConversation() {
|
||||
error: false,
|
||||
conversationOptions: null,
|
||||
requestOptions: { prompt: message, options: null },
|
||||
messageMeta: null,
|
||||
},
|
||||
)
|
||||
scrollToBottom()
|
||||
|
||||
const questionIndex = dataSources.value.length - 1
|
||||
|
||||
loading.value = true
|
||||
prompt.value = ''
|
||||
|
||||
@@ -102,13 +203,18 @@ async function onConversation() {
|
||||
error: false,
|
||||
conversationOptions: null,
|
||||
requestOptions: { prompt: message, options: { ...options } },
|
||||
messageMeta: null,
|
||||
},
|
||||
)
|
||||
scrollToBottom()
|
||||
|
||||
const answerIndex = dataSources.value.length - 1
|
||||
|
||||
try {
|
||||
let lastText = ''
|
||||
let accumulatedUsage = createEmptyUsage()
|
||||
const fetchChatAPIOnce = async () => {
|
||||
let usageApplied = false
|
||||
await fetchChatAPIProcess<Chat.ConversationResponse>({
|
||||
prompt: message,
|
||||
options,
|
||||
@@ -122,22 +228,37 @@ async function onConversation() {
|
||||
if (lastIndex !== -1)
|
||||
chunk = responseText.substring(lastIndex)
|
||||
try {
|
||||
const data = JSON.parse(chunk)
|
||||
const data = JSON.parse(chunk.trim()) as Chat.ConversationResponse
|
||||
const responseMeta = normalizeResponseMeta(data)
|
||||
const nextUsage = responseMeta.usage && !usageApplied
|
||||
? mergeUsage(accumulatedUsage, responseMeta.usage)
|
||||
: accumulatedUsage
|
||||
|
||||
updateChat(
|
||||
+uuid,
|
||||
dataSources.value.length - 1,
|
||||
answerIndex,
|
||||
{
|
||||
dateTime: new Date().toLocaleString(),
|
||||
text: lastText + data.text ?? '',
|
||||
inversion: false,
|
||||
error: false,
|
||||
loading: false,
|
||||
conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
||||
conversationOptions: data.conversationId && data.id ? { conversationId: data.conversationId, parentMessageId: data.id } : null,
|
||||
requestOptions: { prompt: message, options: { ...options } },
|
||||
messageMeta: getChatByUuidAndIndex(+uuid, answerIndex)?.messageMeta ?? null,
|
||||
},
|
||||
)
|
||||
|
||||
if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
|
||||
if (responseMeta.source || responseMeta.usage || responseMeta.tokenUsed !== undefined)
|
||||
updateMessageMeta(questionIndex, answerIndex, nextUsage, responseMeta.source, responseMeta.tokenUsed)
|
||||
|
||||
if (responseMeta.usage && !usageApplied) {
|
||||
accumulatedUsage = nextUsage
|
||||
usageApplied = true
|
||||
chatStore.addSessionUsageByUuid(+uuid, responseMeta.usage)
|
||||
}
|
||||
|
||||
if (openLongReply && data.detail?.choices?.[0]?.finish_reason === 'length' && data.id) {
|
||||
options.parentMessageId = data.id
|
||||
lastText = data.text
|
||||
message = ''
|
||||
@@ -161,7 +282,7 @@ async function onConversation() {
|
||||
if (error.message === 'canceled') {
|
||||
updateChatSome(
|
||||
+uuid,
|
||||
dataSources.value.length - 1,
|
||||
answerIndex,
|
||||
{
|
||||
loading: false,
|
||||
},
|
||||
@@ -170,12 +291,12 @@ async function onConversation() {
|
||||
return
|
||||
}
|
||||
|
||||
const currentChat = getChatByUuidAndIndex(+uuid, dataSources.value.length - 1)
|
||||
const currentChat = getChatByUuidAndIndex(+uuid, answerIndex)
|
||||
|
||||
if (currentChat?.text && currentChat.text !== '') {
|
||||
updateChatSome(
|
||||
+uuid,
|
||||
dataSources.value.length - 1,
|
||||
answerIndex,
|
||||
{
|
||||
text: `${currentChat.text}\n[${errorMessage}]`,
|
||||
error: false,
|
||||
@@ -187,7 +308,7 @@ async function onConversation() {
|
||||
|
||||
updateChat(
|
||||
+uuid,
|
||||
dataSources.value.length - 1,
|
||||
answerIndex,
|
||||
{
|
||||
dateTime: new Date().toLocaleString(),
|
||||
text: errorMessage,
|
||||
@@ -196,6 +317,7 @@ async function onConversation() {
|
||||
loading: false,
|
||||
conversationOptions: null,
|
||||
requestOptions: { prompt: message, options: { ...options } },
|
||||
messageMeta: getChatByUuidAndIndex(+uuid, answerIndex)?.messageMeta ?? null,
|
||||
},
|
||||
)
|
||||
scrollToBottomIfAtBottom()
|
||||
@@ -222,6 +344,9 @@ async function onRegenerate(index: number) {
|
||||
|
||||
loading.value = true
|
||||
|
||||
let accumulatedUsage = createEmptyUsage()
|
||||
const questionIndex = findQuestionIndex(index)
|
||||
|
||||
updateChat(
|
||||
+uuid,
|
||||
index,
|
||||
@@ -232,13 +357,15 @@ async function onRegenerate(index: number) {
|
||||
error: false,
|
||||
loading: true,
|
||||
conversationOptions: null,
|
||||
requestOptions: { prompt: message, ...options },
|
||||
requestOptions: { prompt: message, options: { ...options } },
|
||||
messageMeta: null,
|
||||
},
|
||||
)
|
||||
|
||||
try {
|
||||
let lastText = ''
|
||||
const fetchChatAPIOnce = async () => {
|
||||
let usageApplied = false
|
||||
await fetchChatAPIProcess<Chat.ConversationResponse>({
|
||||
prompt: message,
|
||||
options,
|
||||
@@ -252,7 +379,12 @@ async function onRegenerate(index: number) {
|
||||
if (lastIndex !== -1)
|
||||
chunk = responseText.substring(lastIndex)
|
||||
try {
|
||||
const data = JSON.parse(chunk)
|
||||
const data = JSON.parse(chunk.trim()) as Chat.ConversationResponse
|
||||
const responseMeta = normalizeResponseMeta(data)
|
||||
const nextUsage = responseMeta.usage && !usageApplied
|
||||
? mergeUsage(accumulatedUsage, responseMeta.usage)
|
||||
: accumulatedUsage
|
||||
|
||||
updateChat(
|
||||
+uuid,
|
||||
index,
|
||||
@@ -262,12 +394,22 @@ async function onRegenerate(index: number) {
|
||||
inversion: false,
|
||||
error: false,
|
||||
loading: false,
|
||||
conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
||||
requestOptions: { prompt: message, ...options },
|
||||
conversationOptions: data.conversationId && data.id ? { conversationId: data.conversationId, parentMessageId: data.id } : null,
|
||||
requestOptions: { prompt: message, options: { ...options } },
|
||||
messageMeta: getChatByUuidAndIndex(+uuid, index)?.messageMeta ?? null,
|
||||
},
|
||||
)
|
||||
|
||||
if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
|
||||
if (responseMeta.source || responseMeta.usage || responseMeta.tokenUsed !== undefined)
|
||||
updateMessageMeta(questionIndex, index, nextUsage, responseMeta.source, responseMeta.tokenUsed)
|
||||
|
||||
if (responseMeta.usage && !usageApplied) {
|
||||
accumulatedUsage = nextUsage
|
||||
usageApplied = true
|
||||
chatStore.addSessionUsageByUuid(+uuid, responseMeta.usage)
|
||||
}
|
||||
|
||||
if (openLongReply && data.detail?.choices?.[0]?.finish_reason === 'length' && data.id) {
|
||||
options.parentMessageId = data.id
|
||||
lastText = data.text
|
||||
message = ''
|
||||
@@ -306,7 +448,8 @@ async function onRegenerate(index: number) {
|
||||
error: true,
|
||||
loading: false,
|
||||
conversationOptions: null,
|
||||
requestOptions: { prompt: message, ...options },
|
||||
requestOptions: { prompt: message, options: { ...options } },
|
||||
messageMeta: getChatByUuidAndIndex(+uuid, index)?.messageMeta ?? null,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -483,6 +626,12 @@ onUnmounted(() => {
|
||||
class="w-full max-w-screen-xl m-auto dark:bg-[#101014]"
|
||||
:class="[isMobile ? 'p-2' : 'p-4']"
|
||||
>
|
||||
<div class="sticky top-0 z-10 flex justify-center px-2 pb-2 pt-2">
|
||||
<div class="flex items-center gap-2 rounded-full border border-[#d9e2ec] bg-white/88 px-3 py-2 text-xs text-[#52606d] shadow-[0_8px_30px_rgba(15,23,42,0.08)] backdrop-blur-md dark:border-neutral-700 dark:bg-[#121316]/82 dark:text-neutral-200">
|
||||
<span class="inline-flex h-2 w-2 rounded-full bg-emerald-500" />
|
||||
<span class="font-medium tracking-[0.02em]">{{ sessionTokenText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="!dataSources.length">
|
||||
<div class="flex items-center justify-center mt-4 text-center text-neutral-300">
|
||||
<SvgIcon icon="ri:bubble-chart-fill" class="mr-2 text-3xl" />
|
||||
@@ -499,6 +648,7 @@ onUnmounted(() => {
|
||||
:inversion="item.inversion"
|
||||
:error="item.error"
|
||||
:loading="item.loading"
|
||||
:message-meta="item.messageMeta"
|
||||
@regenerate="onRegenerate(index)"
|
||||
@delete="handleDelete(index)"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user