| | |
| | | "@vue/compiler-sfc": "^3.4.15", |
| | | "autoprefixer": "^10.4.19", |
| | | "code-inspector-plugin": "^0.20.3", |
| | | "dotenv": "^16.4.7", |
| | | "eslint": "^8.35.0", |
| | | "eslint-plugin-vue": "^9.9.0", |
| | | "fs-extra": "^11.2.0", |
| | |
| | | const os = require('os'); |
| | | const path = require('path'); |
| | | const chalk = require('chalk'); |
| | | require('dotenv').config({ path: '.env.local' }); |
| | | |
| | | // è·åå½åèæ¬æå¨çç®å½ |
| | | const scriptDir = __dirname; |
| | |
| | | const argv2 = process.argv[2]; |
| | | const customerList = argv2?.split(' ') ?? ''; |
| | | const publicDir = path.join(rootDir, 'public'); |
| | | const distDir = path.join(rootDir, 'dist'); |
| | | // process.env.VITE_OUTPUT_DIR || |
| | | const distDir = path.join(rootDir, 'dist'); |
| | | const customerListDir = path.join(rootDir, 'customer_list'); |
| | | const customerProjectListDir = path.join(rootDir, 'src', 'views', 'project'); |
| | | const firstCustomerName = customerList[0]?.split(':')[0]; |
| | | /** å
Œ
±æä»¶å¤¹ï¼ææå®¢æ·æä»¶å¤¹å
±äº«æä»¶ */ |
| | | const commonDir = path.join(customerListDir, 'common'); |
| | | |
| | | const item = customerList[0]; |
| | | const item = customerList[0]; |
| | | const customerSplit = item?.split(':'); |
| | | const deployEnv = customerSplit?.[1]; |
| | | // æ¯å¦ä¸ºç产ç¯å¢ |
| | | const isPro = deployEnv==='pro'; |
| | | const isPro = deployEnv === 'pro'; |
| | | // const deployEnv = process.argv[3]; |
| | | |
| | | const logColor = (text, color) => { |
| | |
| | | */ |
| | | const checkCustomer = (command, customer = firstCustomerName) => { |
| | | if (!customer) { |
| | | console.error(chalk.red(`请æ£ç¡®ä½¿ç¨å½ä»¤ â${command} [customer]â `)); |
| | | console.error(chalk.red(`请æ£ç¡®ä½¿ç¨å½ä»¤ "${command} [customer]"`)); |
| | | |
| | | exit(); // éåºèæ¬ |
| | | } |
| | |
| | | checkFileExist(deployJSON, 'é
ç½®æä»¶'); |
| | | // 读å JSON æä»¶ |
| | | const config = await fs.readJson(deployJSON).catch((err) => { |
| | | console.error(`读åé
ç½®æä»¶â${deployJSON}âåºé:`, err); |
| | | console.error(`读åé
ç½®æä»¶"${deployJSON}"åºé:`, err); |
| | | exit(); |
| | | }); |
| | | |
| | |
| | | } |
| | | } |
| | | } catch (error) { |
| | | console.error(`读åâ${customerFolder}â失败`, error); |
| | | console.error(`读å"${customerFolder}"失败`, error); |
| | | } |
| | | |
| | | try { |
| | |
| | | /** |
| | | * 忢忝 |
| | | */ |
| | | const changeBranch = () =>{ |
| | | const changeBranch = () => { |
| | | return; |
| | | if (isPro) { |
| | | try { |
| | |
| | | execSync('git checkout test', { stdio: 'inherit' }); |
| | | } catch (error) {} |
| | | } |
| | | } |
| | | }; |
| | | |
| | | module.exports = { |
| | | isPro, |
| | |
| | | updateImportGlob, |
| | | restoreImportGlob, |
| | | deployEnv, |
| | | changeBranch |
| | | changeBranch, |
| | | }; |
| | |
| | | <template> |
| | | <ChatContainer :loading="chatListLoading" :more-is-loading="moreIsLoading" :is-share-page="isSharePage" ref="containerRef"> |
| | | <ChatContainer |
| | | :loading="chatListLoading" |
| | | :more-is-loading="moreIsLoading" |
| | | :is-share-page="isSharePage" |
| | | ref="containerRef" |
| | | @autoSendMessage="autoSendMessage" |
| | | > |
| | | <!-- æ¶æ¯å表 --> |
| | | <template #message-list> |
| | | <MessageList |
| | |
| | | import { computed, nextTick, onActivated, onMounted, ref } from 'vue'; |
| | | import { loadAmisSource } from '../amis/load'; |
| | | import ChatContainer from './components/ChatContainer.vue'; |
| | | import { getKnowledgePlainText } from './components/playBar/hook/useDigitalHuman'; |
| | | import ShareLinkDlg from './components/shareLink/index.vue'; |
| | | import type { SendMsg } from './hooks/types'; |
| | | import { useLoadData } from './hooks/useLoadData'; |
| | |
| | | import MessageList from './messageList/index.vue'; |
| | | import type { ChatContent } from './model/types'; |
| | | import { AnswerState, AnswerType, RoleEnum, type ChatMessage } from './model/types'; |
| | | import type { QuestionLifecycle } from './types'; |
| | | import { getShareChatJsonByPost, questionStreamByPost } from '/@/api/ai/chat'; |
| | | import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue'; |
| | | import CustomDrawer from '/@/components/drawer/CustomDrawer.vue'; |
| | |
| | | |
| | | let streamOutputIsStart = false; |
| | | let position: Position = null; |
| | | const questionAi = async (text) => { |
| | | |
| | | const questionAi = async (text: string, lifecycleCall?: QuestionLifecycle) => { |
| | | let judgeParams = null; |
| | | if (!preQuestion.value) { |
| | | judgeParams = {}; |
| | |
| | | if (chunkRes.mode === 'result') { |
| | | lastIsResult = true; |
| | | const res = chunkRes.value; |
| | | if (chunkRes.value?.answer_type === 'knowledge') { |
| | | lifecycleCall?.receiveText?.(getKnowledgePlainText(chunkRes.value)); |
| | | } |
| | | if (checkReportEmpty()) { |
| | | const resReport = getResReport(); |
| | | resReport.reports.push(res); |
| | |
| | | stepList.at(-1).ms = ms; |
| | | isTalking.value = false; |
| | | streamOutputIsStart = false; |
| | | lifecycleCall?.finish?.(); |
| | | return; |
| | | } |
| | | |
| | |
| | | scrollToBottom(); |
| | | }, 300); |
| | | }; |
| | | const sendChatMessage = async (content: ChatContent = messageContent.value) => { |
| | | const sendChatMessage = async (content: ChatContent = messageContent.value, lifecycleCall?: QuestionLifecycle) => { |
| | | if (!checkCanSend(content)) { |
| | | return; |
| | | } |
| | |
| | | |
| | | try { |
| | | const [userItem, assistantItem] = addChatItem(content); |
| | | resMsgContent = await questionAi(content.values); |
| | | resMsgContent = await questionAi(content.values, lifecycleCall); |
| | | handleAfterQuestion(userItem, assistantItem, resMsgContent, { |
| | | historyId: questionRes?.history_id, |
| | | question: questionRes?.question, |
| | |
| | | sendChatMessage(messageContent.value); |
| | | }; |
| | | |
| | | const autoSendMessage = (question: string, lifecycleCall?: QuestionLifecycle) => { |
| | | messageContent.value.values = question; |
| | | sendChatMessage(messageContent.value, lifecycleCall); |
| | | }; |
| | | |
| | | const { loadRangeData, onChatListScroll, moreIsLoading, updateLoadIndex } = useScrollLoad({ |
| | | container: chatListDom, |
| | | historyGroupId: currentRouteId, |
| | |
| | | <i class="ywifont ywicon-xiangxiajiantou !text-[20px]" /> |
| | | </div> |
| | | </div> |
| | | <div v-show="digitalHumanIsShow" v-loading="humanIsLoading" class="absolute right-28 bottom-[250px] w-[303.75px] h-[540px]"> |
| | | <span class="ywifont ywicon-guanbi text-[20px] cursor-pointer absolute top-2 right-2 z-[1]" @click="closeDigitalHuman"></span> |
| | | <div |
| | | v-show="digitalHumanIsShow" |
| | | v-loading="humanIsLoading" |
| | | class="absolute right-0 bottom-0 z-[2]" |
| | | :style="{ width: digitalHumanWidth, height: `calc(${digitalHumanWidth} * 16 / 9)` }" |
| | | > |
| | | <span class="ywifont ywicon-guanbi text-[20px] cursor-pointer absolute top-7 right-7 z-[1]" @click="closeDigitalHuman"></span> |
| | | <div class="duix-container h-full w-full"></div> |
| | | </div> |
| | | |
| | | <div v-if="!digitalHumanIsShow" class="absolute right-28 bottom-[250px]"> |
| | | <div v-if="!digitalHumanIsShow" class="absolute right-[24px] bottom-[100px] z-[2]"> |
| | | <el-tooltip content="æ°å人" placement="top"> |
| | | <div |
| | | class="flex items-center justify-center size-[38px] cursor-pointer hover:text-[#0284ff] border rounded-full hover:bg-[#f6f7f9] shadow bg-white" |
| | |
| | | import { onActivated, onDeactivated, ref } from 'vue'; |
| | | import { useChatWidth } from '../hooks/useChatWidth'; |
| | | import { useScroll } from '../hooks/useScroll'; |
| | | import emitter from '/@/utils/mitt'; |
| | | import type { QuestionLifecycle } from '../types'; |
| | | import { useDigitalHuman } from './playBar/hook/useDigitalHuman'; |
| | | |
| | | import emitter from '/@/utils/mitt'; |
| | | const props = defineProps<{ |
| | | loading?: boolean; |
| | | moreIsLoading?: boolean; |
| | | isSharePage?: boolean; |
| | | }>(); |
| | | |
| | | const emit = defineEmits<{ |
| | | autoSendMessage: [string, QuestionLifecycle]; |
| | | }>(); |
| | | |
| | | const chatListDom = ref<HTMLDivElement>(); |
| | | const { openDigitalHuman, isHumanTalking, humanIsLoading,digitalHumanIsShow, closeDigitalHuman } = useDigitalHuman({ |
| | | container: '.duix-container', |
| | | }); |
| | | const { openDigitalHuman, isHumanTalking, humanIsLoading, digitalHumanIsShow, closeDigitalHuman, digitalHumanWidth } = useDigitalHuman( |
| | | { |
| | | container: '.duix-container', |
| | | autoSendMessage: (question: string, lifecycleCall?: QuestionLifecycle) => { |
| | | emit('autoSendMessage', question, lifecycleCall); |
| | | }, |
| | | } |
| | | ); |
| | | const { scrollToBottom, isBottom } = useScroll({ |
| | | chatListDom, |
| | | }); |
| | |
| | | import { SignJWT } from 'jose'; |
| | | import { nextTick, onDeactivated, onMounted, ref } from 'vue'; |
| | | |
| | | import axios from 'axios'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import { markdownToTxt } from 'markdown-to-txt'; |
| | | import './libs/duix.js'; |
| | | import { questionStreamByPost } from '/@/api/ai/chat'; |
| | | import { activeGroupType, activeRoomId } from '/@/stores/chatRoom'; |
| | | import axios from 'axios'; |
| | | import { ElMessage } from 'element-plus'; |
| | | |
| | | import type { QuestionLifecycle } from '../../../types'; |
| | | export type UseDigitalHumanProps = { |
| | | container: string; |
| | | autoSendMessage: (question: string, lifecycleCall?: QuestionLifecycle) => void; |
| | | }; |
| | | |
| | | export const getKnowledgePlainText = (item) => { |
| | | let result = ''; |
| | | const knowledgeText = item.knowledge.reduce((acc, cur) => { |
| | | const mdText = cur.answer; |
| | | const linkText = cur.metadata?.Title; |
| | | if (linkText) { |
| | | return `${mdText}\n\n${linkText}`; |
| | | } |
| | | return acc + mdText; |
| | | }, ''); |
| | | // const conclusionText = |
| | | // item.conclusion |
| | | // ?.filter((item) => !!item.report) |
| | | // .map((item) => item.report) |
| | | // .join('\n\n') ?? ''; |
| | | // result += knowledgeText + conclusionText; |
| | | result = knowledgeText; |
| | | return markdownToTxt(result); |
| | | }; |
| | | |
| | | export const useDigitalHuman = (props: UseDigitalHumanProps) => { |
| | | const { container } = props; |
| | | const { container, autoSendMessage } = props; |
| | | const digitalHumanWidth = '240px'; |
| | | const duixConfig = { |
| | | appId: '1356792813207031808', |
| | | appKey: '659b068e-900c-4fe5-bb96-3ca70fe0aae4', |
| | |
| | | // isSpeaking.value = false; |
| | | digitalHumanIsShow.value = false; |
| | | duix?.stop(); |
| | | }; |
| | | const getPlainText = (item) => { |
| | | let result = ''; |
| | | const knowledgeText = item.knowledge.reduce((acc, cur) => { |
| | | const mdText = cur.answer; |
| | | const linkText = cur.metadata?.Title; |
| | | if (linkText) { |
| | | return `${mdText}\n\n${linkText}`; |
| | | } |
| | | return acc + mdText; |
| | | }, ''); |
| | | // const conclusionText = |
| | | // item.conclusion |
| | | // ?.filter((item) => !!item.report) |
| | | // .map((item) => item.report) |
| | | // .join('\n\n') ?? ''; |
| | | // result += knowledgeText + conclusionText; |
| | | result = knowledgeText; |
| | | return markdownToTxt(result); |
| | | }; |
| | | |
| | | let isWaitingSpeak = false; |
| | |
| | | } |
| | | duix.closeAsr().then((...a) => {}); |
| | | |
| | | let hasResult = false; |
| | | isReceiveRes.value = true; |
| | | try { |
| | | isWaitingSpeak = true; |
| | | duix.speak({ |
| | | content: 'å·²æ¶å°æ¨çé®é¢ï¼æ£å¨æèä¸...请ç¨ç', |
| | | }); |
| | | questionStreamByPost( |
| | | { |
| | | question: data, |
| | | history_group_id: activeRoomId.value, |
| | | raw_mode: false, |
| | | group_type: activeGroupType.value, |
| | | is_digital_human: true, |
| | | }, |
| | | (chunkRes) => { |
| | | if (chunkRes.mode === 'result' && chunkRes.value?.answer_type === 'knowledge') { |
| | | const plainText = getPlainText(chunkRes.value); |
| | | hasResult = true; |
| | | speakContent(plainText); |
| | | } else if (!chunkRes.value?.json_ok && chunkRes.value?.err_code === 'MESSAGE') { |
| | | if (hasResult) return; |
| | | hasResult = true; |
| | | speakContent(chunkRes.value.json_msg); |
| | | } |
| | | isWaitingSpeak = true; |
| | | duix.speak({ |
| | | content: 'å·²æ¶å°æ¨çé®é¢ï¼æ£å¨æèä¸...请ç¨ç', |
| | | }); |
| | | |
| | | if (chunkRes.mode === 'finish') { |
| | | if (!hasResult) { |
| | | speakContent('ææ¶æ æ³å£å¤´æè¿°ä½ æè¯´çé®é¢'); |
| | | } else { |
| | | hasResult = false; |
| | | } |
| | | // isReceiveRes.value = false; |
| | | let content = ''; |
| | | |
| | | try { |
| | | autoSendMessage(data, { |
| | | receiveText: (text: string) => { |
| | | content += text; |
| | | }, |
| | | finish: () => { |
| | | if (!content) { |
| | | speakContent('ææ¶æ æ³å£å¤´æè¿°ä½ æè¯´çé®é¢'); |
| | | } else { |
| | | speakContent(content); |
| | | } |
| | | } |
| | | ); |
| | | }, |
| | | }); |
| | | } catch (error) { |
| | | console.error(error); |
| | | isReceiveRes.value = false; |
| | | } |
| | | }); |
| | |
| | | isHumanTalking: isReceiveRes, |
| | | closeDigitalHuman, |
| | | humanIsLoading, |
| | | digitalHumanWidth, |
| | | }; |
| | | }; |
| | |
| | | }; |
| | | |
| | | const checkIsText = (item) => { |
| | | const isText = item?.content?.values?.some((item) => item?.content?.type === AnswerType.Knowledge) || item?.conclusion?.length > 0; |
| | | const isText = |
| | | item?.content?.values?.some((item) => item?.content?.type === AnswerType.Knowledge || item?.conclusion?.length > 0) || |
| | | item?.conclusion?.length > 0; |
| | | return isText; |
| | | }; |
| | | |
| | | const getPlainText = (item) => { |
| | | let result = ''; |
| | | const knowledgeText = item.content.values |
| | | .filter((item) => { |
| | | const type = item?.content?.type; |
| | | return type === AnswerType.Knowledge; |
| | | }) |
| | | |
| | | .reduce((acc, cur) => { |
| | | const answer = cur?.content?.values |
| | | ?.map((item) => { |
| | | const mdText = item.answer; |
| | | const linkText = item.metadata?.Title; |
| | | if (linkText) { |
| | | return `${mdText}\n\n${linkText}`; |
| | | } |
| | | return mdText; |
| | | }) |
| | | .join('\n\n'); |
| | | return acc + answer; |
| | | const conclusionText = |
| | | cur.conclusion |
| | | ?.filter((cur) => !!cur.report) |
| | | .map((cur) => cur.report) |
| | | .join('\n\n') ?? ''; |
| | | acc += conclusionText; |
| | | if (cur?.content?.type === AnswerType.Knowledge) { |
| | | const answer = cur?.content?.values |
| | | ?.map((item) => { |
| | | const mdText = item.answer; |
| | | const linkText = item.metadata?.Title; |
| | | if (linkText) { |
| | | return `${mdText}\n\n${linkText}`; |
| | | } |
| | | return mdText; |
| | | }) |
| | | .join('\n\n'); |
| | | |
| | | acc += answer; |
| | | } |
| | | |
| | | return acc; |
| | | }, ''); |
| | | |
| | | const conclusionText = |
| | |
| | | :isTalking="isTalking" |
| | | /> |
| | | </div> |
| | | <div v-if="showAskMore" class="ml-4 mt-5 "> |
| | | <div v-if="showAskMore && msgList.at(-1)?.content?.askMoreList?.length > 0" class="ml-4 mt-5 "> |
| | | <div class="text-gray-600 mb-5">ä½ å¯ä»¥ç»§ç»é®æï¼</div> |
| | | <div class="space-y-2 inline-flex flex-col"> |
| | | <div |
¶Ô±ÈÐÂÎļþ |
| | |
| | | export type QuestionLifecycle = { |
| | | finish?: () => void; |
| | | receiveText?: (text: string) => void; |
| | | }; |
| | | |
| | |
| | | import vue from '@vitejs/plugin-vue'; |
| | | import autoprefixer from 'autoprefixer'; |
| | | import { CodeInspectorPlugin } from 'code-inspector-plugin'; |
| | | import { resolve } from 'path'; |
| | | import tailwindcss from 'tailwindcss'; |
| | | import type { ConfigEnv } from 'vite'; |
| | | import { defineConfig, loadEnv } from 'vite'; |
| | | import { CodeInspectorPlugin } from 'code-inspector-plugin'; |
| | | import { visualizer } from 'rollup-plugin-visualizer'; |
| | | // import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; |
| | | // import AutoImport from 'unplugin-auto-import/vite'; |
| | | // import Components from 'unplugin-vue-components/vite'; |
| | |
| | | }, |
| | | }, |
| | | build: { |
| | | // outDir: 'dist/' + mode.mode, |
| | | outDir: 'dist', |
| | | outDir:'dist', |
| | | chunkSizeWarningLimit: 1500, |
| | | |
| | | rollupOptions: { |