From c375d8978ba8d22abe392fce5d2efe1d1d2114f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jack=E9=AD=8F?= Date: Wed, 10 Sep 2025 17:05:09 +0800 Subject: [PATCH] =?UTF-8?q?agent=E8=B0=83=E8=AF=95=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4=E6=98=BE=E7=A4=BA=E9=87=8D=E6=9E=84=EF=BC=8C?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=89=A7=E8=A1=8C=E5=88=87=E6=8D=A2=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/dialoguePanel/DialogueFlow.vue | 2 +- src/store/account.ts | 1 + src/store/conversation.ts | 11 +- src/store/historySession.ts | 3 +- src/views/createapp/components/DebugApp.vue | 469 ++++++------------ .../dialogue/components/DialogueSession.vue | 8 +- 6 files changed, 170 insertions(+), 324 deletions(-) diff --git a/src/components/dialoguePanel/DialogueFlow.vue b/src/components/dialoguePanel/DialogueFlow.vue index 66816eb..e986a15 100644 --- a/src/components/dialoguePanel/DialogueFlow.vue +++ b/src/components/dialoguePanel/DialogueFlow.vue @@ -289,7 +289,7 @@ watch( -
+
{ revsionNumber: null, organization: '', user_sub: '', // 用户唯一标识 + auto_execute: false, }); /** diff --git a/src/store/conversation.ts b/src/store/conversation.ts index a462446..ed830a9 100644 --- a/src/store/conversation.ts +++ b/src/store/conversation.ts @@ -431,6 +431,7 @@ export const useSessionStore = defineStore('conversation', () => { params: Record, innerParams: Record, fetchParams: Record, + isDebug: boolean, ) => { await fetchEventSource(url, { ...fetchParams, @@ -441,6 +442,7 @@ export const useSessionStore = defineStore('conversation', () => { flowId: '', params: innerParams || {}, }, + ...(isDebug && { debug: isDebug }), conversationId: params.conversationId, features: features, language: langStore.language, @@ -520,6 +522,7 @@ export const useSessionStore = defineStore('conversation', () => { }, ind?: number, waitType?: string, + isDebug?: boolean, ): Promise => { const { currentSelectedSession } = useHistorySessionStore(); params.conversationId = currentSelectedSession; @@ -565,7 +568,9 @@ export const useSessionStore = defineStore('conversation', () => { handleMsgDataShow(params, ev, conversationItem); }, }; - if (params.user_selected_flow) { + if(isDebug){ + await funcFetch.fetchAppNew(streamUrl, params, pp, fetchParams, isDebug); + } else if (params.user_selected_flow) { // 之前的对话历史记录 await funcFetch.fetchHistory(streamUrl, params, pp, fetchParams); } else if (params.user_selected_app) { @@ -627,6 +632,7 @@ export const useSessionStore = defineStore('conversation', () => { * @param params * @param type * @param waitType + * @param isDebug */ const sendQuestion = async ( groupId: string | undefined, @@ -638,6 +644,7 @@ export const useSessionStore = defineStore('conversation', () => { params?: any, type?: any, waitType?: string, + isDebug?: boolean, ): Promise => { const { updateSessionTitle, currentSelectedSession } = useHistorySessionStore(); @@ -713,7 +720,7 @@ export const useSessionStore = defineStore('conversation', () => { if (waitType) { getStreamParams = params; } - await getStream(getStreamParams, regenerateInd ?? undefined, waitType); + await getStream(getStreamParams, regenerateInd ?? undefined, waitType, isDebug); }; /** diff --git a/src/store/historySession.ts b/src/store/historySession.ts index 9b702d5..6a0c5ea 100644 --- a/src/store/historySession.ts +++ b/src/store/historySession.ts @@ -217,7 +217,8 @@ export const useHistorySessionStore = defineStore( * 创建一个新的会话 */ const generateSession = async (): Promise => { - const [_, res] = await api.createSession(user_selected_app.value); + const appId = user_selected_app.value ?? ''; + const [_, res] = await api.createSession(appId); if (!_ && res) { currentSelectedSession.value = res.result.conversationId; await getHistorySession(); diff --git a/src/views/createapp/components/DebugApp.vue b/src/views/createapp/components/DebugApp.vue index 80e3895..9110ef5 100644 --- a/src/views/createapp/components/DebugApp.vue +++ b/src/views/createapp/components/DebugApp.vue @@ -20,7 +20,14 @@ import { fetchStream } from '@/utils/fetchStream'; import { useScrollBottom } from '@/hooks/useScrollBottom'; import dayjs from 'dayjs'; import { useRoute } from 'vue-router'; - +import i18n from 'src/i18n'; +import DialoguePanel from 'src/components/dialoguePanel/DialoguePanel.vue'; +import type { + ConversationItem, + RobotConversationItem, +} from 'src/views/dialogue/types'; + +const { t } = i18n.global; let isDebugSuccess = false; interface DebugConfig { @@ -59,17 +66,20 @@ const { currentSelectedSession, historySession } = storeToRefs( const { generateSession, getHistorySession } = useHistorySessionStore(); async function initDebugSession() { - const { conversationList } = useSessionStore(); - if (conversationList.length === 0) return; await generateSession(); await getHistorySession(); currentSelectedSession.value = historySession.value[0].conversationId; } -async function deleteSession(id: string) { +/** + * 删除会话 + */ +async function toDeleteSession(id: string) { + // 先停止生成 + stopStream(); const [, res] = await api.deleteSession({ conversationList: [id] }); if (res) { - conversations.value = []; + currentSelectedSession.value = ''; } } @@ -90,207 +100,105 @@ const chatContainerRef = ref(null); const { scrollToBottom } = useScrollBottom(chatContainerRef, { threshold: 15, }); +const { pausedStream } = useSessionStore(); +const isStreaming = ref(false); +const stopStream = async () => { + pausedStream(Number(conversationList.value.length)); +}; +// 对话列表 +const { sendQuestion } = useSessionStore(); +const { conversationList, isAnswerGenerating, dialogueRef } = storeToRefs( + useSessionStore(), +); -const { conversations, setConversations } = useConversations(); - -const { isStreaming, queryStream, stopStream } = useStream(); - -function useStream() { - const isStreaming = ref(false); - - let controller: AbortController; - - const queryStream = async ( - q: string, - sessionId: string, - lang: 'zh' | 'en' = 'zh', - cId?: string, - ) => { - isStreaming.value = true; - - const headers = {}; - headers['Content-Type'] = 'application/json; charset=UTF-8'; - const token = localStorage.getItem('ECSESSION'); - if (token) headers['Authorization'] = `Bearer ${token}`; - - controller = new AbortController(); +/** + * 获取指定字段值 + * @param item + */ +const getItem = (item: ConversationItem, field: string): T | undefined => { + if (field in item) { + return (item as RobotConversationItem)[field] as T; + } + return undefined; +}; - const body = { - question: q, - conversationId: sessionId, - app: { - appId: route.query.appId as string, - flowId: '', - params: {}, - }, - language: lang, - features: { - context_num: 2, - max_tokens: 2048, - }, - }; - try { - const resp = await fetch('/api/chat', { - signal: controller.signal, - method: 'POST', - body: JSON.stringify(body), - headers, +/** + * @description 处理并过滤文件列表,将文件列表中的字段名统一为指定格式 + * @param {ConversationItem} ConversationItem - 对话项对象 + * @param {string} str - 字段名 + * @returns {Array} 格式化后的文件列表 + */ +const getFormatFileList = (ConversationItem, str) => { + let fileList: any = getItem(ConversationItem, str); + if (!fileList || fileList?.length === 0) return; + let newFileList: any = []; + fileList?.forEach((file) => { + if (file.associated === 'answer') { + newFileList.push({ + documentId: file._id, + documentName: file.name, + documentAbstract: file.abstract, + documentType: file.type, + documentSize: file.size, + sourceUrl: file.sourceUrl, + documentOrder: file.order, + createdAt: file.created_at, + documentAuthor: file.author, }); - if (!resp.ok) { - isStreaming.value = false; - conversations.value.push({ - id: '', - question: q, - answer: [ - { - content: '系统错误,请稍后再试', - }, - ], - answerIndex: 0, - role: 'assistant', - }); - return; - } - - for await (const chunk of fetchStream({ - readableStream: resp.body!, - })) { - if (!chunk.data) { - break; - } - if (chunk.data.trim() === '[ERROR]') { - isStreaming.value = false; - const conversation = - conversations.value[conversations.value.length - 1]; - conversation.answer[conversation.answerIndex].content = - '系统错误,请稍后再试'; - break; - } - if (chunk.data.trim() === '[DONE]') { - isStreaming.value = false; - setTimeout(() => { - scrollToBottom(true); - }, 100); - break; - } - - let conversation = conversations.value.find((item) => item.id === cId); - - setConversations(chunk.data, q, conversation); - } - } catch (error) { - console.log(error); } - }; - - const stopStream = async () => { - const [, res] = await api.stopGeneration(); - if (res) { - isStreaming.value = false; - controller.abort(); - const conversation = conversations.value[conversations.value.length - 1]; - if (conversation.answer[conversation.answerIndex].content === '') { - conversation.answer[conversation.answerIndex].content = '对话已终止'; - return; - } - scrollToBottom(true); - } - }; - - return { isStreaming, queryStream, stopStream }; -} + }); + return newFileList; +}; -function useConversations() { - interface Conversation { - id: string; - question: string; - answer: { - content: string; - metadata?: StreamMetadata; - }[]; - answerIndex: number; - role: 'user' | 'assistant'; - createdAt?: Date | number; +const clearSuggestion = (index: number): void => { + if ('search_suggestions' in conversationList.value[index]) { + conversationList.value[index].search_suggestions = undefined; } +}; - type StreamEvent = 'text.add' | 'init' | 'input'; - interface StreamMetadata { - inputTokens: number; - - outputTokens: number; - - timeCost: number; - } +const showFileSource = ref(false); +const curFileList = ref>([]); +const closeShowFileSource = () => { + showFileSource.value = false; +}; +const openShowFileSource = (fileList: Array) => { + showFileSource.value = true; + curFileList.value = fileList; +}; - interface StreamChunk { - content: { - text: string; - }; - conversationId: string; - event: StreamEvent; - groupId: string; - id: string; - metadata: StreamMetadata; - taskId: string; +/** + * 发送消息 + */ +const handleSendMessage = async ( + groupId: string | undefined, + question: string, + user_selected_flow?: string, +) => { + if (isAnswerGenerating.value) return; + const len = conversationList.value.length; + if ( + len > 0 && + !(conversationList.value[len - 1] as RobotConversationItem).isFinish + ) + return; + dialogueInput.value = ''; + if (!currentSelectedSession.value) { + await generateSession(); } - const conversations = ref([]); - - const setConversations = ( - data: string, - question: string, - conversation?: Conversation, - ) => { - const { id, event, content, metadata } = JSON.parse(data) as StreamChunk; - - if (event === 'init') { - if (conversation) { - conversation.answer.push({ - content: '', - }); - conversation.answerIndex = conversation.answer.length - 1; - } else { - conversations.value.push({ - id: id, - question, - answer: [ - { - content: '', - }, - ], - answerIndex: 0, - role: 'assistant', - }); - } - } - if (event === 'text.add') { - if (!isDebugSuccess) { - isDebugSuccess = true; - emits('success', true); - } - const c = conversations.value[conversations.value.length - 1]; - c.answer[c.answerIndex].content += content.text; - c.answer[c.answerIndex].metadata = metadata; - } - scrollToBottom(); - }; - - return { conversations, setConversations }; -} - -function onSend(q: string) { - if (isStreaming.value) return; - conversations.value.push({ - id: `user-${(conversations.value.length % 2) + 1}`, - question: q, - answer: [], - answerIndex: 0, - role: 'user', - }); - scrollToBottom(true); - queryStream(q, currentSelectedSession.value, language.value as 'zh' | 'en'); - dialogueInput.value = ''; -} + await sendQuestion( + undefined, + question, + route.query.appId as string, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + true, + ); +}; /** * 处理鼠标事件 @@ -300,7 +208,7 @@ const handleKeydown = (event: KeyboardEvent) => { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); if (dialogueInput.value !== '') { - onSend(dialogueInput.value); + handleSendMessage(undefined, dialogueInput.value); } } }; @@ -335,8 +243,10 @@ watch( () => props.visible, () => { if (!props.visible) { - deleteSession(currentSelectedSession.value); + toDeleteSession(currentSelectedSession.value); return; + } else { + conversationList.value = []; } initDebugSession(); }, @@ -348,7 +258,7 @@ watch( class="mcp-debug-dialog" :visible="visible" :model-value="visible" - title="调试" + :title="i18n.global.t('flow.debug')" @close="emits('update:visible', false)" align-center destroy-on-close @@ -361,7 +271,7 @@ watch(
- MCP 服务 + {{ $t('semantic.mcp_service') }}
-
+
- 你好,我是 + {{ $t('main.describe1') }}
{{ config.name }}
- ,很高兴为您服务 + {{ $t('main.describe2') }}
- - - + :messageArray="item.belong === 'robot' ? item.messageList : ''" + :is-finish="getItem(item, 'isFinish')" + :test="getItem(item, 'test')" + :metadata="getItem(item, 'metadata')" + :flowdata="getItem(item, 'flowdata')" + :created-at="item.createdAt" + :current-selected="item.currentInd" + :need-regernerate="item.cid === conversationList.slice(-1)[0].cid" + :user-selected-app="user_selected_app" + :search_suggestions="getItem(item, 'search_suggestions')" + :paramsList="getItem(item, 'paramsList')" + :fileList="item.files ?? getFormatFileList(item, 'document')" + @handleReport="handleReport" + @handleSendMessage="handleSendMessage" + @clearSuggestion="clearSuggestion(index)" + @openShowFileSource="openShowFileSource" + />
@@ -501,7 +348,7 @@ watch( {{ $t('feedback.stop') }}
- +