From 91142fc15ee81cdc3f80134e5e8faaff237f1e53 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期一, 04 十一月 2024 11:13:36 +0800 Subject: [PATCH] 简单流式展示 --- src/api/ai/chat.ts | 2 src/components/chat/Chat.vue | 103 +++++++++++++++++++++++++++++++--- src/utils/request.ts | 58 +++++++++++-------- 3 files changed, 128 insertions(+), 35 deletions(-) diff --git a/src/api/ai/chat.ts b/src/api/ai/chat.ts index 3dc8652..ec45975 100644 --- a/src/api/ai/chat.ts +++ b/src/api/ai/chat.ts @@ -259,7 +259,7 @@ * @description 娴佸紡澶фā鍨嬪璇� * @param {FormData} params **/ -export const questionStreamByPost = (params, callback: (value) => void) => +export const questionStreamByPost = (params, callback: (chunkRes) => void) => streamReq( { url: `/chat/question_stream`, diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue index 5a7e090..3f9d9aa 100644 --- a/src/components/chat/Chat.vue +++ b/src/components/chat/Chat.vue @@ -23,6 +23,21 @@ /> <div class="flex-auto flex" :class="{ 'justify-end': item.role === RoleEnum.user }"> <div class="inline-flex flex-col" :class="{ 'w-full': item.role === RoleEnum.assistant }"> + <div class="w-full" v-if="isTalking && index === computedMessageList.length - 1"> + <div class="text-sm rounded-[6px] p-4 leading-relaxed bg-white" + v-if="item.role === RoleEnum.assistant"> + <!-- 杩囩▼杈撳嚭 --> + <el-steps direction="vertical" :active="activeStep"> + <!-- <el-step title="process" status="process" /> + <el-step title="success" status="success" /> + <el-step title="wait" status="wait" /> + <el-step title="finish" status="finish" /> + <el-steps title="error" status="error" /> --> + + <el-step v-for="item in stepList" :title="item.title" :status="stepEnumMap[item.status]" /> + </el-steps> + </div> + </div> <div class="w-full" v-if="item.content?.values"> <div class="text-sm rounded-[6px] p-4 leading-relaxed" @@ -123,8 +138,6 @@ </div> </div> </div> - - <Loding v-if="isTalking && index === computedMessageList.length - 1" class="w-fit" :process="process" /> </div> </div> </div> @@ -173,7 +186,7 @@ import { useScrollToBottom } from './hooks/useScrollToBottom'; import type { ChatContent } from './model/types'; import { AnswerState, AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage } from './model/types'; -import { QuestionAi, extCallQuery } from '/@/api/ai/chat'; +import { QuestionAi, extCallQuery, questionStreamByPost } from '/@/api/ai/chat'; import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue'; import CustomDrawer from '/@/components/drawer/CustomDrawer.vue'; import router from '/@/router'; @@ -264,12 +277,38 @@ return content; }; const { clearQueryProcess, process, processId, queryProcess } = useQueryProcess(); + +//#region ====================== 姝ラ step ====================== +const enum StepEnum { + Loading, + Success, + Error, +} +const stepEnumMap = { + [StepEnum.Loading]: 'process', + [StepEnum.Success]: 'success', + [StepEnum.Error]: 'error', +}; + +type StepItem = { + title: string; + status: StepEnum; +}; +const activeStep = ref(-1); +const stepList = ref<StepItem[]>([]); + +const resetStep = () => { + activeStep.value = -1; + stepList.value = []; +}; +//#endregion + const DEFAULT_SECTION_A_ID = 'knowledge_base'; let questionRes = null; let finalCalcSectionAId = null; const questionAi = async (text) => { - processId.value = uuidv4(); + // processId.value = uuidv4(); let judgeParams = null; if (!preQuestion.value) { // const aiContent = computedMessageList.value.filter((item) => item.role === RoleEnum.assistant); @@ -300,7 +339,7 @@ finalCalcSectionAId = currentSectionAId; const params = { - process_id: processId.value, + // process_id: processId.value, question: text, // FIXME: 鏆傛椂杩欐牱 // section_a_id: currentSectionAId, @@ -321,10 +360,31 @@ // if (currentLLMId) { // params.llm_id = currentLLMId; // } - clearQueryProcess(); - queryProcess(); - const res = await QuestionAi(params).finally(() => { - clearQueryProcess(); + // clearQueryProcess(); + // queryProcess(); + resetStep(); + let res = null; + await questionStreamByPost(params, (chunkRes) => { + if (chunkRes.mode === 'result') { + res = chunkRes.value; + } else { + switch (chunkRes.mode) { + case 'begin': + break; + case 'end': + break; + } + + stepList.value.push({ + title: chunkRes.value, + status: StepEnum.Success, + }); + scrollToBottom(); + + // process.value = chunkRes.value; + } + }).finally(() => { + resetStep(); }); questionRes = res; const content = parseContent(res); @@ -515,4 +575,29 @@ } } } + +.el-step.is-horizontal.stepActive { + .el-step__head.is-finish { + .el-step__line { + // 褰撳墠姝ラ鏁版í绾挎牱寮忚缃� + .el-step__line-inner { + width: 50% !important; + border-width: 1px !important; + } + } + + // 褰撳墠姝ラ鏁板渾鍦堟牱寮忚缃� + .el-step__icon.is-text { + // background: #409eff; + color: #fff; + } + } +} + +:deep(.el-step__icon-inner) { + font-size: 16px !important; +} +:deep(.el-step__description) { + height: 20px; +} </style> diff --git a/src/utils/request.ts b/src/utils/request.ts index a79a4c5..6e9e5a9 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -148,35 +148,43 @@ }); const decoder = new TextDecoder(); const encoder = new TextEncoder(); -const readStream = async (stream: ReadableStream, cb: (value) => void) => { +const readStream = async (stream: ReadableStream, cb: (value) => void): Promise<any> => { const reader = stream.getReader(); - let lastValue = new Uint8Array(); - while (1) { - const { done, value } = await reader.read(); - if (done) { - break; - } - const txt = decoder.decode(Uint8Array.from([...lastValue, ...value])); - const txtArr = txt.split('\n'); - txtArr.forEach((value, index, array) => { - // 涓�鑸笉浼氬嚭鐜拌繛缁崲琛岋紝鍙彲鑳芥渶鍚庝竴涓槸鎹㈣ - if (index === array.length - 1) { - // 缂栫爜鍥炲幓锛屼腑鏂囧垏鍒嗕細涔辩爜 - lastValue = encoder.encode(value); - } else { - cb(value); + let lastValue = ''; + const p = new Promise(async (resolve, reject) => { + let fullValue = ''; + while (1) { + const { done, value } = await reader.read(); + if (done) { + break; } - }); - } + // const txt = decoder.decode(Uint8Array.from([...lastValue, ...value])); + const txt = decoder.decode(value); + const txtArr = txt.split('\n'); + txtArr[0] =lastValue+ txtArr[0] + txtArr.forEach((value, index, array) => { + // 涓�鑸笉浼氬嚭鐜拌繛缁崲琛岋紝鍙彲鑳芥渶鍚庝竴涓槸鎹㈣ + if (index === array.length - 1) { + lastValue =value; + + } else { + const decodeValue = decodeURIComponent(value); + fullValue += decodeValue; + cb(decodeValue); + } + }); + } + resolve(fullValue); + }); + return p; }; -export const streamReq = (config: AxiosRequestConfig<any>, callback: (value) => void) => { - streamInstance(config).then((response) => { - const stream = response as unknown as ReadableStream; - readStream(stream, (value) => { - const jsonValue = JSON.parse(value); - callback(jsonValue); - }); +export const streamReq = async (config: AxiosRequestConfig<any>, callback: (value) => void) => { + const response = await streamInstance(config); + const stream = response as unknown as ReadableStream; + return readStream(stream, (value) => { + const jsonValue = JSON.parse(value); + callback(jsonValue); }); }; //#endregion -- Gitblit v1.9.3