diff --git a/package.json b/package.json index 834fe569e4f56b8657518097baef2b9f41efc309..b2a8904fb8c47bd3cd34427d2ff30eb3b20d1915 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@computing/opendesign2": "file:lib\\opendesign2-2.0.23.tgz", "@dagrejs/dagre": "1.1.2", "@element-plus/icons-vue": "^2.3.1", + "@microsoft/fetch-event-source": "^2.0.1", "axios": "1.7.9", "babel-loader": "^9.2.1", "codemirror": "^6.0.1", @@ -36,6 +37,8 @@ "highlight.js": "11.10.0", "js-yaml": "^4.1.0", "marked": "4.3", + "monaco-editor": "^0.52.2", + "monaco-yaml": "^5.3.1", "pinia": "2.1.6", "sass": "1.62.0", "typescript": "4.9.5", diff --git a/src/components/Upload/index.vue b/src/components/Upload/index.vue index 40a39052088a453ccef9b8863e220465723c8982..9193b61115854c0ff4344755ade154e4fc5f1b26 100644 --- a/src/components/Upload/index.vue +++ b/src/components/Upload/index.vue @@ -20,6 +20,7 @@ import { yaml } from '@codemirror/lang-yaml'; import { oneDark } from '@codemirror/theme-one-dark'; import { useChangeThemeStore } from 'src/store/conversation'; import CustomLoading from 'src/views/customLoading/index.vue'; +import MonacoEditor from 'src/components/monaco/MonacoEditor.vue'; const loading = ref(false); const themeStore = useChangeThemeStore(); @@ -34,7 +35,9 @@ const handleCreateapi = async () => { if (res.code === 200) { getServiceJson.value = res?.result?.apis; getServiceName.value = res?.result?.name; - activeServiceNameList.value = getServiceJson.value.map((item) => item.name); + activeServiceNameList.value = getServiceJson.value.map( + (item) => item.name, + ); uploadtype.value = 'get'; successMsg('创建成功'); } else { @@ -188,9 +191,6 @@ const getServiceYamlFun = async (id: string) => { }; const handleChange = (payload) => { yamlToJsonContent.value = jsYaml.load(payload); - setTimeout(() => { - payload.view.scrollDOM.scrollTop = 0; - }, 100); }; watch( () => props, @@ -199,7 +199,9 @@ watch( getServiceYaml.value = props.getServiceYaml; getServiceName.value = props.getServiceName; if (getServiceJson.value?.length) { - activeServiceNameList.value = getServiceJson.value.map((item) => item.name); + activeServiceNameList.value = getServiceJson.value.map( + (item) => item.name, + ); } if (props.type === 'edit' && props) { getServiceYamlFun(props.serviceId); @@ -293,7 +295,7 @@ onMounted(() => {
{{ getServiceName }} - { @ready="handleReady" @update="updateFunc" @change="handleChange" + /> --> +
diff --git a/src/components/dialoguePanel/FlowCode.vue b/src/components/dialoguePanel/FlowCode.vue index 183a74d68d15d5b247ca1215dcedcc54a04dce25..3fb306b71d563e2ac3ddb00f825a76352eca9017 100644 --- a/src/components/dialoguePanel/FlowCode.vue +++ b/src/components/dialoguePanel/FlowCode.vue @@ -41,9 +41,6 @@ const codeMirrorView = shallowRef(); const extensions = ref([json()]); const handleReady = (payload) => { codeMirrorView.value = payload.view; - setTimeout(() => { - payload.view.scrollDOM.scrollTop = 0; - }, 100); }; const handleChange = (payload) => { params.value = payload; diff --git a/src/components/monaco/MonacoEditor.vue b/src/components/monaco/MonacoEditor.vue new file mode 100644 index 0000000000000000000000000000000000000000..4ad8e47f3770bd7b01aff68a3f381ec3c93fa369 --- /dev/null +++ b/src/components/monaco/MonacoEditor.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/src/components/monaco/yaml.worker.js b/src/components/monaco/yaml.worker.js new file mode 100644 index 0000000000000000000000000000000000000000..36d88a410ce4016749ef52ff48b36b2576427207 --- /dev/null +++ b/src/components/monaco/yaml.worker.js @@ -0,0 +1 @@ +import 'monaco-yaml/yaml.worker.js' \ No newline at end of file diff --git a/src/store/conversation.ts b/src/store/conversation.ts index 9bee998b77d29f143781218ddeef9efbd1e1e4e2..9df2653b255e46085af86206492d92d9b05a211b 100644 --- a/src/store/conversation.ts +++ b/src/store/conversation.ts @@ -25,6 +25,7 @@ import { ElMessageBox } from 'element-plus'; import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'; import { Application } from 'src/apis/paths/type'; import $bus from 'src/bus/index'; +import { fetchEventSource } from '@microsoft/fetch-event-source'; const STREAM_URL = '/api/chat'; const newStreamUrl = 'api/chat'; let controller = new AbortController(); @@ -32,6 +33,7 @@ export var txt2imgPath = ref(''); export var echartsObj = ref({}); export var echartsHas = ref(false); const excelPath = ref(''); +const resp = ref(); const features = { max_tokens: 2048, context_num: 2, @@ -131,7 +133,6 @@ export const useSessionStore = defineStore('conversation', () => { headers['Content-Type'] = 'application/json; charset=UTF-8'; headers['X-CSRF-Token'] = getCookie('_csrf_tk'); try { - let resp; let pp = {}; if (params.params && typeof params.params === 'object') { pp = params.params; @@ -140,13 +141,191 @@ export const useSessionStore = defineStore('conversation', () => { } else { pp = {}; } + isPaused.value = false; + excelPath.value = ''; + echartsObj.value = {}; + txt2imgPath.value = ''; + const handelMsgDataShow = (msgData) => { + if (isPaused.value) { + // 手动暂停输出 + isAnswerGenerating.value = false; + return; + } + if (msgData.data === '[DONE]') { + if (excelPath.value.length > 0) { + conversationItem.message[conversationItem.currentInd] += + `

下载地址:${excelPath.value}`; + } + conversationItem.isFinish = true; + isAnswerGenerating.value = false; + // 如果是工作流的调试功能-调试对话结束时-发送调试对话结束 + if (params.type) { + $bus.emit('debugChatEnd'); + } + return; + } + + // 同一时间戳传来的decodeValue是含有三条信息的合并,so需要分割 + // 这里json解析 + const message = JSON.parse(msgData.data || '{}'); + const eventType = message['event']; + if ('metadata' in message) { + conversationItem.metadata = message.metadata; + } + if ('event' in message) { + switch (eventType) { + case 'text.add': + { + scrollBottom(); + conversationItem.message[conversationItem.currentInd] += + message.content.text; + } + break; + case 'heartbeat': + break; + case 'graph': + { + conversationItem.echartsObj = message.content.option; + } + break; + case 'ducument.add': + { + conversationItem.message[conversationItem.currentInd] += + message.content; + conversationItem.files = [ + ...conversationItem.files, + message.content, + ]; + } + break; + case 'Suggestion': + { + if (conversationItem.search_suggestions) { + conversationItem.search_suggestions.push( + Object(message.content), + ); + } else { + conversationItem.search_suggestions = [ + Object(message.content), + ]; + } + } + break; + case 'init': + { + //初始化获取 metadata + conversationItem.metadata = message.metadata; + conversationItem.createdAt = message.content.created_at; + conversationItem.groupId = message.groupId; + } + break; + case 'flow.start': + { + //事件流开始--后续验证对话无下拉连接后则完全替换 + const flow = message.flow; + conversationItem.flowdata = { + id: flow?.stepId || '', + title: i18n.global.t('flow.flow_start'), + // 工作流这里stepName代替step_progresss,为不影响首页对话暂且用|| + progress: flow?.stepProgress || '', + status: 'running', + display: true, + flowId: flow?.flowId || '', + data: [[]], + }; + } + break; + case 'step.input': + { + conversationItem.flowdata?.data[0].push({ + id: message.flow?.stepId, + title: message.flow?.stepName, + status: message.flow?.stepStatus, + data: { + input: message.content, + }, + }); + if (conversationItem.flowdata) { + conversationItem.flowdata.progress = + message.flow?.stepProgress; + conversationItem.flowdata.status = message.flow?.stepStatus; + } + } + break; + case 'step.output': + { + const target = conversationItem.flowdata?.data[0].find( + (item) => item.id === message.flow?.stepId, + ); + if (target) { + target.data.output = message.content; + target.status = message.flow?.stepStatus; + // 工作流添加每阶段的时间耗时 + target['costTime'] = message.metadata?.timeCost; + if ( + message.flow.step_status === 'error' && + conversationItem.flowdata + ) { + conversationItem.flowdata.status = message.flow?.stepStatus; + } + } + } + break; + case 'flow.stop': + //时间流结束 + { + const flow = message.content.flow; + if (params.type) { + // 如果是工作流的调试功能-添加status/data + conversationItem.flowdata = { + id: flow?.stepId, + title: i18n.global.t('flow.flow_end'), + progress: flow?.stepProgress, + status: message.flow?.stepStatus, + display: true, + data: conversationItem?.flowdata?.data, + }; + } else if ( + message.content.type !== 'schema' && + conversationItem.flowdata + ) { + // 删除 end 逻辑 + conversationItem.flowdata = { + id: flow?.stepId, + title: i18n.global.t('flow.flow_end'), + progress: flow?.stepProgress, + status: 'success', + display: true, + data: conversationItem.flowdata.data, + }; + } else { + conversationItem.paramsList = message.content.data; + if (conversationItem.flowdata) { + conversationItem.flowdata.title = i18n.global.t( + 'flow.flow_params_error', + ); + conversationItem.flowdata.status = 'error'; + conversationItem.paramsList = message.content.data; + } + } + } + break; + default: + break; + } + } + // 将lines传递给workflow-以更新工作流节点的状态 + if (params.user_selected_flow && params.user_selected_app) { + $bus.emit('getNodesStatue', { data: message }); + } + }; if (params.user_selected_flow) { // 之前的对话历史记录 if (!params.type) { - resp = await fetch(STREAM_URL, { + await fetchEventSource(STREAM_URL, { signal: controller.signal, - method: 'POST', keepalive: true, + method: 'POST', headers: headers, body: JSON.stringify({ question: params.question, @@ -162,13 +341,19 @@ export const useSessionStore = defineStore('conversation', () => { }, features: features, }), + async onopen(response) { + resp.value = response; + }, + async onmessage(ev) { + handelMsgDataShow(ev); + }, }); } else { // 新的工作流调试记录 - resp = await fetch(newStreamUrl, { + await fetchEventSource(newStreamUrl, { signal: controller.signal, - method: 'POST', keepalive: true, + method: 'POST', headers: headers, body: JSON.stringify({ app: { @@ -180,13 +365,20 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: params.conversationId, debug: true, }), + async onopen(response) { + resp.value = response; + }, + async onmessage(ev) { + handelMsgDataShow(ev); + }, }); } } else if (params.user_selected_app) { - resp = await fetch(STREAM_URL, { + // 新的工作流调试记录 + await fetchEventSource(STREAM_URL, { signal: controller.signal, - method: 'POST', keepalive: true, + method: 'POST', headers: headers, body: JSON.stringify({ question: params.question, @@ -203,11 +395,17 @@ export const useSessionStore = defineStore('conversation', () => { }, features: features, }), + async onopen(response) { + resp.value = response; + }, + async onmessage(ev) { + handelMsgDataShow(ev); + }, }); } else if (false) { //写传参数情况 } else { - resp = await fetch(STREAM_URL, { + await fetchEventSource(STREAM_URL, { signal: controller.signal, keepalive: true, method: 'POST', @@ -227,181 +425,28 @@ export const useSessionStore = defineStore('conversation', () => { }, features: features, }), + async onopen(response) { + resp.value = response; + }, + async onmessage(ev) { + handelMsgDataShow(ev); + }, }); } - - const isServiceOk = await handleServiceStatus(resp.status, params, ind); + const isServiceOk = await handleServiceStatus( + resp.value.status, + params, + ind, + ); if (!isServiceOk) { return; } - if (!resp.ok) { - throw new Error(`HTTP error! status: ${resp.status}`); + if (!resp.value.ok) { + throw new Error(`HTTP error! status: ${resp.value.status}`); } - if (!resp.body) { + if (!resp.value.body) { throw new Error(`HTTP error, body not exits`); } - const reader = resp.body.getReader(); - const decoder = new TextDecoder('utf-8'); - let isEnd = true; - isPaused.value = false; - excelPath.value = ''; - echartsObj.value = {}; - txt2imgPath.value = ''; - let addItem = ''; - while (isEnd) { - if (isPaused.value) { - // 手动暂停输出 - isAnswerGenerating.value = false; - break; - } - const { done, value } = await reader.read(); - const decodedValue = addItem + decoder.decode(value, { stream: true }); - if (done) { - if (excelPath.value.length > 0) { - conversationItem.message[conversationItem.currentInd] += - `

下载地址:${excelPath.value}`; - } - conversationItem.isFinish = true; - isEnd = false; - isAnswerGenerating.value = false; - // 如果是工作流的调试功能-调试对话结束时-发送调试对话结束 - if (params.type) { - $bus.emit('debugChatEnd'); - } - break; - } - if (decodedValue.includes('data: [DONE]')) { - isEnd = true; - continue; - } - // 同一时间戳传来的decodeValue是含有三条信息的合并,so需要分割 - const lines = splitDataString(decodedValue); - lines.forEach((line) => { - // 这里json解析 - const message = Object( - JSON.parse(line.replace(/^data:\s*/, '').trim()), - ); - if ('metadata' in message) { - conversationItem.metadata = message.metadata; - } - if ('event' in message) { - if (message['event'] === 'text.add') { - // conversationItem.message[conversationItem.currentInd] += message.content; - scrollBottom(); - conversationItem.message[conversationItem.currentInd] += - message.content.text; - } else if (message['event'] === 'heartbeat') { - // conversationItem.files = [...conversationItem.files, message.content]; - // 不处理 - } else if (message['event'] === 'graph') { - //echarts处理 待验证 - conversationItem.echartsObj = message.content.option; - } else if (message['event'] == 'ducument.add') { - conversationItem.message[conversationItem.currentInd] += - message.content; - conversationItem.files = [ - ...conversationItem.files, - message.content, - ]; - } else if (message['event'] === 'Suggestion') { - if (conversationItem.search_suggestions) { - conversationItem.search_suggestions.push( - Object(message.content), - ); - } else { - conversationItem.search_suggestions = [Object(message.content)]; - } - } else if (message['event'] === 'init') { - //初始化获取 metadata - conversationItem.metadata = message.metadata; - conversationItem.createdAt = message.content.created_at; - conversationItem.groupId = message.groupId; - } else if (message['event'] === 'flow.start') { - //事件流开始--后续验证对话无下拉连接后则完全替换 - const flow = message.flow; - conversationItem.flowdata = { - id: flow?.stepId || '', - title: i18n.global.t('flow.flow_start'), - // 工作流这里stepName代替step_progresss,为不影响首页对话暂且用|| - progress: flow?.stepProgress || '', - status: 'running', - display: true, - flowId: flow?.flowId || '', - data: [[]], - }; - } else if (message['event'] === 'step.input') { - conversationItem.flowdata?.data[0].push({ - id: message.flow?.stepId, - title: message.flow?.stepName, - status: message.flow?.stepStatus, - data: { - input: message.content, - }, - }); - if (conversationItem.flowdata) { - conversationItem.flowdata.progress = message.flow?.stepProgress; - conversationItem.flowdata.status = message.flow?.stepStatus; - } - } else if (message['event'] === 'step.output') { - const target = conversationItem.flowdata?.data[0].find( - (item) => item.id === message.flow?.stepId, - ); - if (target) { - target.data.output = message.content; - target.status = message.flow?.stepStatus; - // 工作流添加每阶段的时间耗时 - target['costTime'] = message.metadata?.timeCost; - if ( - message.flow.step_status === 'error' && - conversationItem.flowdata - ) { - conversationItem.flowdata.status = message.flow?.stepStatus; - } - } - } else if (message['event'] === 'flow.stop') { - //时间流结束 - const flow = message.content.flow; - if (params.type) { - // 如果是工作流的调试功能-添加status/data - conversationItem.flowdata = { - id: flow?.stepId, - title: i18n.global.t('flow.flow_end'), - progress: flow?.stepProgress, - status: message.flow?.stepStatus, - display: true, - data: conversationItem?.flowdata?.data, - }; - } else if ( - message.content.type !== 'schema' && - conversationItem.flowdata - ) { - // 删除 end 逻辑 - conversationItem.flowdata = { - id: flow?.stepId, - title: i18n.global.t('flow.flow_end'), - progress: flow?.stepProgress, - status: 'success', - display: true, - data: conversationItem.flowdata.data, - }; - } else { - conversationItem.paramsList = message.content.data; - if (conversationItem.flowdata) { - conversationItem.flowdata.title = i18n.global.t( - 'flow.flow_params_error', - ); - conversationItem.flowdata.status = 'error'; - conversationItem.paramsList = message.content.data; - } - } - } - } - }); - // 将lines传递给workflow-以更新工作流节点的状态 - if (params.user_selected_flow && params.user_selected_app) { - $bus.emit('getNodesStatue', lines); - } - } } catch (err: any) { isPaused.value = true; isAnswerGenerating.value = false; @@ -643,7 +688,10 @@ export const useSessionStore = defineStore('conversation', () => { (conversationList.value[answerIndex] as RobotConversationItem).isFinish = true; cancel(); - await api.stopGeneration(); + const resp = await api.stopGeneration(); + if (resp?.[1]?.code === 200) { + isAnswerGenerating.value = false; + } }; /** * 重新生成回答 @@ -836,7 +884,10 @@ export const useSessionStore = defineStore('conversation', () => { ] as RobotConversationItem ).isFinish = true; cancel(); - await api.stopGeneration(); + const resp = await api.stopGeneration(); + if (resp?.[1]?.code === 200) { + isAnswerGenerating.value = false; + } }; // 判断string是否是json对象 diff --git a/src/views/api/index.vue b/src/views/api/index.vue index 0ccfb6b0ba2124307a2bdff529d1c9a7aedb2246..a104024de6238c4aec882dee90ff7df61cf3779d 100644 --- a/src/views/api/index.vue +++ b/src/views/api/index.vue @@ -133,14 +133,14 @@ :before-close="handleClose" >

-
+
-
+
-
+
{ margin-bottom: 0px !important; } .drawerContent { - overflow-y: auto; height: calc(100% - 32px); } +.monacoEditorBox{ + height: 100%; +} .el-drawer { margin: 0px; padding: 0px; @@ -441,6 +443,7 @@ img { } } :deep(.el-drawer__body) { + overflow: unset !important; padding: 8px 24px 16px !important; .drawerBody { height: 100%; diff --git a/src/views/createapp/components/workFlow.vue b/src/views/createapp/components/workFlow.vue index 8eb9a89db35c2f154d41c4607fe2e4296b06e943..44b8999e77d7a92d2d8ddf7893f78d872fac4d75 100644 --- a/src/views/createapp/components/workFlow.vue +++ b/src/views/createapp/components/workFlow.vue @@ -466,62 +466,60 @@ const redrageFlow = (nodesList, edgesList) => { }; // 接受工作流调试时获取的相应的数据 -$bus.on('getNodesStatue', (lines) => { +$bus.on('getNodesStatue', (item:any) => { // 对相应节点修改状态--此处需要分为开始/结束,分支,普通三种节点修改 try { - lines?.forEach((item) => { - const newLines = yaml.load(item); - // 工作流开始时更新debugResult - if (newLines?.data?.event === 'flow.start') { - totalTime.value = 0; - debugTime.value = ''; - debugStatus.value = newLines.data.flow?.stepStatus; - updateNodeFunc('start', 'success', ''); - } + const newLines = item; + // 工作流开始时更新debugResult + if (newLines?.data?.event === 'flow.start') { + totalTime.value = 0; + debugTime.value = ''; + debugStatus.value = newLines.data.flow?.stepStatus; + updateNodeFunc('start', 'success', ''); + } - // 这里判断是否有调试状态的值,无值不处理 - if (!debugStatus.value) { - return; - } - // step.input和step.output对应的节点状态需要修改 - if ( - newLines?.data?.event === 'step.input' || - newLines?.data?.event === 'step.output' - ) { - // output-节点运行结束时,获取节点运行的耗时 - let constTime = ''; - if (newLines.data.event === 'step.output') { - totalTime.value += newLines.data?.metadata?.timeCost; - constTime = `${newLines.data?.metadata?.timeCost?.toFixed(3)}s`; - // 此处获取output的数据,并将此数据传给节点显示 - updateNodeFunc( - newLines.data.flow.stepId, - newLines.data.flow?.stepStatus, - constTime, - { - params: newLines.data?.content, - type: 'output', - }, - ); - } else { - updateNodeFunc( - newLines.data.flow.stepId, - newLines.data.flow?.stepStatus, - constTime, - { - params: newLines.data?.content, - type: 'input', - }, - ); - } - } else if (newLines?.data?.event === 'flow.stop') { - debugStatus.value = newLines.data.flow?.stepStatus; - debugTime.value = `${totalTime.value.toFixed(3)}s`; - // 最后更新-调用一下接口 + // 这里判断是否有调试状态的值,无值不处理 + if (!debugStatus.value) { + return; + } + // step.input和step.output对应的节点状态需要修改 + if ( + newLines?.data?.event === 'step.input' || + newLines?.data?.event === 'step.output' + ) { + // output-节点运行结束时,获取节点运行的耗时 + let constTime = ''; + if (newLines.data.event === 'step.output') { + totalTime.value += newLines.data?.metadata?.timeCost; + constTime = `${newLines.data?.metadata?.timeCost?.toFixed(3)}s`; + // 此处获取output的数据,并将此数据传给节点显示 + updateNodeFunc( + newLines.data.flow.stepId, + newLines.data.flow?.stepStatus, + constTime, + { + params: newLines.data?.content, + type: 'output', + }, + ); } else { - // do nothing + updateNodeFunc( + newLines.data.flow.stepId, + newLines.data.flow?.stepStatus, + constTime, + { + params: newLines.data?.content, + type: 'input', + }, + ); } - }); + } else if (newLines?.data?.event === 'flow.stop') { + debugStatus.value = newLines.data.flow?.stepStatus; + debugTime.value = `${totalTime.value.toFixed(3)}s`; + // 最后更新-调用一下接口 + } else { + // do nothing + } } catch (error) { ElMessage.error('请检查格式是否正确'); } @@ -656,9 +654,9 @@ const saveFlow = (updateNodeParameter?) => { nodes: updateNodes, edges: updateEdges, focusPoint: { - x: 800, - y: 800, - }, + x: 800, + y: 800, + }, }, }, ) @@ -821,11 +819,17 @@ defineExpose({ @@ -899,9 +903,7 @@ defineExpose({ content="节点连接完成才能进行调试" placement="top" > -
+