diff --git a/Dockerfile b/Dockerfile index 556ab0e587bb0b68c6c16b4fa63ca7cc7ade5b45..e477cfd2d2962d9303a2ea461c344df061f17a1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,33 @@ +# 第一阶段:构建前端项目 FROM node:22.14.0-alpine WORKDIR /opt -COPY . . -# ENV HTTPS_PROXY= -ENV ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" -RUN npm install pnpm -g --registry=https://registry.npmmirror.com && \ - pnpm install --registry=https://registry.npmmirror.com && \ - pnpm run build - +# 更换Alpine为国内镜像源(解决apk安装失败问题) +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories +COPY . . +# 配置国内镜像,加速Electron及依赖下载 +ENV ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" \ + ELECTRON_BUILDER_BINARIES_MIRROR="https://npmmirror.com/mirrors/electron-builder-binaries/" + +# 安装必要工具(git+ssh客户端+CA证书),并配置Git协议转换 +RUN apk add --no-cache git openssh-client ca-certificates \ + && git config --global url."https://github.com/".insteadOf git@github.com: \ + && git config --global url."https://".insteadOf git:// \ + && npm install pnpm -g --registry=https://registry.npmmirror.com \ + && pnpm config set registry https://registry.npmmirror.com \ + && pnpm config set network-timeout 600000 \ + && pnpm install \ + && pnpm run build + +# 第二阶段:部署到nginx FROM hub.oepkgs.net/openeuler/openeuler:24.03-lts-sp2 ENV TZ Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone +# 替换OpenEuler镜像源为华为云,加速依赖安装 RUN sed -i 's|repo.openeuler.org|repo.huaweicloud.com/openeuler|g' /etc/yum.repos.d/openEuler.repo && \ sed -i '/metalink/d' /etc/yum.repos.d/openEuler.repo && \ sed -i '/metadata_expire/d' /etc/yum.repos.d/openEuler.repo && \ @@ -22,6 +35,7 @@ RUN sed -i 's|repo.openeuler.org|repo.huaweicloud.com/openeuler|g' /etc/yum.repo yum install -y nginx shadow-utils passwd gettext && \ yum clean all +# 从构建阶段复制产物到nginx COPY --from=0 /opt/dist /usr/share/nginx/html COPY --from=0 /opt/public /usr/share/nginx/html COPY --from=0 /opt/deploy/nginx.conf.tmpl /opt/nginx.conf.tmpl @@ -29,5 +43,5 @@ COPY --from=0 /opt/deploy/start.sh /opt/start.sh EXPOSE 8080 WORKDIR /opt - ENTRYPOINT [ "bash", "./start.sh" ] + diff --git a/build/linux/euler-copilot-web.spec b/build/linux/euler-copilot-web.spec index 30f83bd6496845a8a41bcde2e007840e9e112dae..0a1db02337b900ffb9b64855646409ac0b7a2973 100644 --- a/build/linux/euler-copilot-web.spec +++ b/build/linux/euler-copilot-web.spec @@ -15,8 +15,8 @@ BuildArch: aarch64 x86_64 Name: euler-copilot-web -Version: 0.9.6 -Release: 6%{?dist} +Version: 0.10.0 +Release: 1%{?dist} License: MulanPSL-2.0 Summary: openEuler 智能化解决方案 Web 前端 Source0: %{name}-%{version}.tar.gz @@ -315,6 +315,9 @@ fi %changelog +* Mon Sep 15 2025 openEuler - 0.10.0-1 +- 升级至 0.10.0 版本 + * Mon Jun 16 2025 openEuler - 0.9.6-6 - 优化 RPM 打包配置:启用自动 provide 并过滤 Electron 内部库 diff --git a/build/linux/nginx.conf.local.tmpl b/build/linux/nginx.conf.local.tmpl index b10a8d2c058fdc127ba387f33094221454acf34a..d8c50ca2ba30338f5a40119b28b8b72eed486675 100644 --- a/build/linux/nginx.conf.local.tmpl +++ b/build/linux/nginx.conf.local.tmpl @@ -6,12 +6,16 @@ server { client_body_buffer_size 5120M; client_max_body_size 5120M; + add_header 'Access-Control-Allow-Origin' '*'; # 允许所有源 + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE'; # 允许的HTTP方法 + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; # 允许的请求头 + add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range'; # 允许前端访问的响应头 add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options nosniff; add_header Referrer-Policy "no-referrer"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always"; add_header Cache-Control "no-cache"; - add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: base64;"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * data: base64;"; resolver 8.8.8.8 8.8.4.4 valid=60s; resolver_timeout 5s; diff --git a/deploy/nginx.conf.tmpl b/deploy/nginx.conf.tmpl index c2ef1a64218811aeb6ac1dd4357072b3fc2af2a3..112580cf40e032884fe724e6b8d45efca643c10c 100644 --- a/deploy/nginx.conf.tmpl +++ b/deploy/nginx.conf.tmpl @@ -63,12 +63,17 @@ http { server_name localhost; charset utf-8; + add_header 'Access-Control-Allow-Origin' '*'; # 允许所有源 + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE'; # 允许的HTTP方法 + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; # 允许的请求头 + add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range'; # 允许前端访问的响应头 + add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options nosniff; add_header Referrer-Policy "no-referrer"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; always"; add_header Cache-Control "no-cache"; - add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: base64;"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * data: base64;"; limit_conn limitperip 50; resolver 8.8.8.8 8.8.4.4 valid=60s; @@ -117,6 +122,22 @@ http { proxy_pass ${FRAMEWORK_URL}/api/; } + + + location /wtd/ { + proxy_set_header X-Forwarded-For $http_x_real_ip; + add_header Cache-Control "no-cache,no-store,must-revalidate"; + add_header X-Accel-Buffering no; + proxy_buffering off; + proxy_intercept_errors on; + + error_page 404 /404.html; + limit_req zone=ratelimit burst=15 nodelay; + proxy_read_timeout 500s; + proxy_connect_timeout 500s; + + proxy_pass ${FRAMEWORK_URL}/witchaind/; + } location /witchaind/ { proxy_set_header X-Forwarded-For $http_x_real_ip; diff --git a/electron/main/common/locale.ts b/electron/main/common/locale.ts index a2d6daa99b739b11a014a09b2e91c464c8aa50f3..06078c81f4b362344ea45b90cf053f866010d1c3 100644 --- a/electron/main/common/locale.ts +++ b/electron/main/common/locale.ts @@ -30,7 +30,7 @@ export function processZhLocale(appLocale: string): string { // country codes, assume they use Simplified Chinese. // For other cases, assume they use Traditional. if (['hans', 'cn', 'sg', 'my'].includes(region)) { - return 'zh_cn'; + return 'zh'; } return 'zh_tw'; diff --git a/electron/main/common/platform.ts b/electron/main/common/platform.ts index 1d499b4250a8fd91ab390ee32c5c14dc5757f703..df78f473fa2e113dcb190ad3490b00c36352487c 100644 --- a/electron/main/common/platform.ts +++ b/electron/main/common/platform.ts @@ -9,7 +9,7 @@ // See the Mulan PSL v2 for more details. import * as nls from './nls'; -export const LANGUAGE_DEFAULT = 'zh_cn'; +export const LANGUAGE_DEFAULT = 'zh'; let _isWindows = false; let _isMacintosh = false; diff --git a/package.json b/package.json index 12a38e2f143440448c45fc1d4747d0dff758c463..3194b8afd00f4f89da7a2094d3be7c4efa73aed3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openeuler-intelligence", - "version": "0.9.6", + "version": "0.10.0", "description": "openEuler 智能化解决方案", "author": { "name": "openEuler", diff --git a/src/apis/appCenter/appCenterService.ts b/src/apis/appCenter/appCenterService.ts index 025e7899e39561faa46629f785fa4dfb8ede9d72..b5553932922e06370bb80ccd1ce174e3dd58f77b 100644 --- a/src/apis/appCenter/appCenterService.ts +++ b/src/apis/appCenter/appCenterService.ts @@ -108,6 +108,18 @@ export const getPartAppConfgUser = (): Promise< return get('/api/user'); }; +/** + * 修改用户设置 + * + * @param params + * @returns + */ +export const updateUserInfo = (params: { + autoExecute: boolean; +}): Promise<[any, FcResponse | undefined]> => { + return post(`/api/user`, {autoExecute: params.autoExecute}); +}; + export const appCenterApi = { queryAppList, createOrUpdateApp, @@ -116,4 +128,5 @@ export const appCenterApi = { releaseSingleAppData, changeSingleAppCollect, getPartAppConfgUser, + updateUserInfo, }; diff --git a/src/apis/paths/account.ts b/src/apis/paths/account.ts index 93411cdfd4d012974ef357b27183497015f9d8b4..af452185e03adf083ebb5abb22696f140b042de5 100644 --- a/src/apis/paths/account.ts +++ b/src/apis/paths/account.ts @@ -24,6 +24,7 @@ export const authorizeUser = (): Promise< organization: string; revision_number: string | null; is_admin: boolean; + auto_execute?: boolean; }> | undefined ), diff --git a/src/apis/paths/conversation.ts b/src/apis/paths/conversation.ts index 8c3057bff8a692ffa90b3a1044a9ebfc1b7d7dd0..45561aa6c261852f2e410a0fd04b9e0d857e2b08 100644 --- a/src/apis/paths/conversation.ts +++ b/src/apis/paths/conversation.ts @@ -11,16 +11,20 @@ import { get, put, post, del } from 'src/apis/server'; import type { FcResponse } from 'src/apis/server'; import { ConversationRecordList, ConversationList } from './type'; +import i18n from 'src/i18n'; + const BASE_URL = '/api/conversation'; +const { t } = i18n.global; /** * 停止生成 * @returns */ -export const stopGeneration = (): Promise< +export const stopGeneration = (taskId: string): Promise< [any, FcResponse | undefined] > => { - return post(`/api/stop`); + let url = taskId === undefined ? '/api/stop' : `/api/stop?taskId=${taskId}`; + return post(url); }; /** @@ -41,11 +45,13 @@ export const createSession = ({ appId, debug = false, llm_id = '', + title = t('history.new_conversation'), kb_ids = [], }: { appId: string; debug?: boolean; llm_id?: string; + title?: string; kb_ids?: string[]; }): Promise< [ @@ -58,7 +64,7 @@ export const createSession = ({ ), ] > => { - return post(BASE_URL, { appId, debug }, { llm_id, kb_ids }); + return post(BASE_URL, { appId, debug }, { llm_id, kb_ids, title }); }; /** diff --git a/src/apis/paths/mcp.ts b/src/apis/paths/mcp.ts index 31ec1eb56c97ad0331a06ccb4762bd0762ecdc04..1931379789366ecf35f8f71ef4629a2fdde5a537 100644 --- a/src/apis/paths/mcp.ts +++ b/src/apis/paths/mcp.ts @@ -10,6 +10,8 @@ const getMcpList = (params: { keyword?: string; page?: number; pageSize?: number; + isInstall?: boolean | null; + isActive?: boolean | null; }) => { return get<{ currentPage: number; @@ -69,21 +71,26 @@ const createOrUpdateMcpService = (params: { name: string; overview: string; description: string; - config: string; + config: object; mcpType: 'stdio' | 'sse' | 'stream'; }) => { return post<{ service_id: string; name: string; - }>(`${MCP_BASE_URL}`, params); + }>(`${MCP_BASE_URL}`, params, { 'Content-Type': 'application/json' }); }; const deleteMcpService = (id: string) => { return del<{ serviceId: string }>(`${MCP_BASE_URL}/${id}`); }; -const activeMcpService = (id: string, active: boolean) => { - return post<{ serviceId: string }>(`${MCP_BASE_URL}/${id}`, { active }); +const activeMcpService = (id: string, active: boolean, mcpEnv?: any) => { + return post<{ serviceId: string }>(`${MCP_BASE_URL}/${id}`, { active, mcpEnv }, + { 'Content-Type': 'multipart/form-data' },); +}; + +const installMcpService = (id: string) => { + return post<{ serviceId: string }>(`${MCP_BASE_URL}/${id}/install`); }; // 定义一个名为uploadMcpIcon的函数,接收两个参数serviceId和icon @@ -96,7 +103,7 @@ const uploadMcpIcon = (params: { formData.append('icon', params.icon); // 使用post方法向MCP_BASE_URL/${id}发送请求,请求体为{ icon: params.icon } return post<{ icon: string }>( - `${MCP_BASE_URL}`, + `${MCP_BASE_URL}/icon/${params.serviceId}`, formData, { serviceId: params.serviceId, @@ -111,5 +118,6 @@ export const mcpApi = { createOrUpdateMcpService, deleteMcpService, activeMcpService, + installMcpService, uploadMcpIcon }; diff --git a/src/apis/paths/type.ts b/src/apis/paths/type.ts index d610be29dfbe9ff96ffe175f54a8727bb5f05bbc..fa283f66ddb7d20f079a3c37091ee825b215a330 100644 --- a/src/apis/paths/type.ts +++ b/src/apis/paths/type.ts @@ -45,6 +45,12 @@ export interface Metadata { inputTokens: number; outputTokens: number; timeCost: number; + footNoteMetadataList?:Array<{ + footSource: string; + footType: string; + insertPosition: number; + releatedId: string; + }> } // 定义问答对数据结构 @@ -57,7 +63,8 @@ export interface ConversationRecord { content: Content; metadata: Metadata; comment: string; - created_at: string; + createdAt: string; + document?: any[]; } // 定义对话内问答列表数据结构 diff --git a/src/apis/workFlow/workFlowService.ts b/src/apis/workFlow/workFlowService.ts index 3f094d9ab2ad15d94c5f30d2fe2e196d24abc8aa..986c57e4365514f10908952888a9b2a1fe774821 100644 --- a/src/apis/workFlow/workFlowService.ts +++ b/src/apis/workFlow/workFlowService.ts @@ -13,8 +13,9 @@ import { export const queryAllFlowService = (params: { page: number; pageSize: number; + language?: string | undefined; }): Promise<[any, FcResponse | undefined]> => { - return get('/api/flow/service'); + return get(`/api/flow/service?language=${params.language}`); }; /** @@ -64,10 +65,36 @@ export const delFlowTopology = (params: { return del(`/api/flow?appId=${params.appId}&flowId=${params.flowId}`); }; +/** + * 获取Choice节点内参数 + * @param params + * @returns + */ +export const queryParameter = (params: { + appId: string; + flowId: string; + stepId: string; +}): Promise<[any, FcResponse | undefined]> => { + return get('/api/parameter', params); +}; + +/** + * 获取Choice节点内参数的操作内容 + * @param params + * @returns + */ +export const queryParameterOperate = (params: { + ParamType: string; +}): Promise<[any, FcResponse | undefined]> => { + return get('/api/parameter/operate', params); +}; + export const workFlowApi = { queryAllFlowService, querySingleFlowServiceNode, querySingleFlowTopology, delFlowTopology, createOrUpdateFlowTopology, + queryParameter, + queryParameterOperate, }; diff --git a/src/assets/svgs/file_download.svg b/src/assets/svgs/file_download.svg new file mode 100644 index 0000000000000000000000000000000000000000..283d57e696c0cc738161a593fe5a5af61eb8b81f --- /dev/null +++ b/src/assets/svgs/file_download.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/file_download_active.svg b/src/assets/svgs/file_download_active.svg new file mode 100644 index 0000000000000000000000000000000000000000..c072db44196d37edee48be9499d18f0e67744582 --- /dev/null +++ b/src/assets/svgs/file_download_active.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/file_download_hover.svg b/src/assets/svgs/file_download_hover.svg new file mode 100644 index 0000000000000000000000000000000000000000..09d8b74fef906ba4786dc0809cf4e1bdf238b54b --- /dev/null +++ b/src/assets/svgs/file_download_hover.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/html.svg b/src/assets/svgs/html.svg new file mode 100644 index 0000000000000000000000000000000000000000..80482d0f812be11cf7b2d6f20227eab884ac2226 --- /dev/null +++ b/src/assets/svgs/html.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/jpeg.svg b/src/assets/svgs/jpeg.svg new file mode 100644 index 0000000000000000000000000000000000000000..64c1c5ad07225905221df8e791f97fb0c0c00fc1 --- /dev/null +++ b/src/assets/svgs/jpeg.svg @@ -0,0 +1,27 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/json.svg b/src/assets/svgs/json.svg new file mode 100644 index 0000000000000000000000000000000000000000..0bbf3ab1e8f6388db6e32321c578141429db1d3a --- /dev/null +++ b/src/assets/svgs/json.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/png.svg b/src/assets/svgs/png.svg new file mode 100644 index 0000000000000000000000000000000000000000..f3cf7bc416c2ce7407dfca546b9e8f9fcf7e1012 --- /dev/null +++ b/src/assets/svgs/png.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/pptx.svg b/src/assets/svgs/pptx.svg new file mode 100644 index 0000000000000000000000000000000000000000..76dbe7ba5d9e0dc2ce4e77413a5816e0d5b86e80 --- /dev/null +++ b/src/assets/svgs/pptx.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/taskChoice.svg b/src/assets/svgs/taskChoice.svg index ebace828de164ed949e86d75dfc9aeed628d5b75..381b2ddd065951c1cee2b004cf82b1d44ef3cee3 100644 --- a/src/assets/svgs/taskChoice.svg +++ b/src/assets/svgs/taskChoice.svg @@ -11,7 +11,7 @@ - + diff --git a/src/assets/svgs/txt_green.svg b/src/assets/svgs/txt_green.svg new file mode 100644 index 0000000000000000000000000000000000000000..6256758d15ae950973284b32f03dfe85b650b995 --- /dev/null +++ b/src/assets/svgs/txt_green.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/warning.svg b/src/assets/svgs/warning.svg new file mode 100644 index 0000000000000000000000000000000000000000..71554801a5aa7005cbc4643970ba1f70daac34a2 --- /dev/null +++ b/src/assets/svgs/warning.svg @@ -0,0 +1,8 @@ + + + Created with Pixso. + + + + + diff --git a/src/assets/svgs/yaml.svg b/src/assets/svgs/yaml.svg new file mode 100644 index 0000000000000000000000000000000000000000..572476042d634abda0338cd4d76eb9826a1d74cd --- /dev/null +++ b/src/assets/svgs/yaml.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/zip.svg b/src/assets/svgs/zip.svg new file mode 100644 index 0000000000000000000000000000000000000000..a971b21aef314924421a19def5be7eeb52599b33 --- /dev/null +++ b/src/assets/svgs/zip.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/commonFooter/CommonFooter.vue b/src/components/commonFooter/CommonFooter.vue index 527eb53a372eb681a3b5da62bb6f11eab9ead125..4897990521b8093d4f3043be304b8e71310a090f 100644 --- a/src/components/commonFooter/CommonFooter.vue +++ b/src/components/commonFooter/CommonFooter.vue @@ -8,12 +8,33 @@ const agreeDialogVisiable = ref(false); // 协议内容 const agreement = ref(''); const policy = ref(''); + +/** + * 获取当前语言 + */ +const getLocale = () => { + const localLang = localStorage.getItem('copilot_language'); + let locale = 'zh'; // 默认值 + + if (localLang) { + try { + const parsed = JSON.parse(localLang); + // 处理可能的 language 值:zh, zh_cn, zh-tw 等 + if (parsed.language) { + locale = parsed.language.startsWith('zh') ? 'zh' : parsed.language; + } + } catch (e) { + console.error('解析语言设置失败:', e); + } + } + return locale; +}; + /** * 读取协议 */ const readAgreement = async () => { - const localLang = localStorage.getItem('copilot_language'); - const locale = (localLang && JSON.parse(localLang).language) || 'zh_cn'; + const locale = getLocale(); const response = locale === 'en' ? await import('src/conf/agreement-en.md?raw') @@ -23,8 +44,7 @@ const readAgreement = async () => { }; const readPolicy = async () => { - const localLang = localStorage.getItem('copilot_language'); - const locale = (localLang && JSON.parse(localLang).language) || 'zh_cn'; + const locale = getLocale(); const response = locale === 'en' ? await import('src/conf/policy-en.md?raw') diff --git a/src/components/dialoguePanel/DialogueFlow.vue b/src/components/dialoguePanel/DialogueFlow.vue index e0c6a5d7920ff06e83476ffc6f8663097ce91385..e986a1511db9a6b30c5ca4f224ad5e36f33a6d19 100644 --- a/src/components/dialoguePanel/DialogueFlow.vue +++ b/src/components/dialoguePanel/DialogueFlow.vue @@ -1,9 +1,14 @@ diff --git a/src/components/dialoguePanel/ParamsModel.vue b/src/components/dialoguePanel/ParamsModel.vue new file mode 100644 index 0000000000000000000000000000000000000000..8a5ab98a41b93cd4a351ac132ff08e099f2a0985 --- /dev/null +++ b/src/components/dialoguePanel/ParamsModel.vue @@ -0,0 +1,137 @@ + + + diff --git a/src/components/sessionCard/SessionCard.vue b/src/components/sessionCard/SessionCard.vue index 9384c130e404bc349c2963f1e76d3e3408ecb183..7eb6f10027b3d26cff5dc039c29a726a9b1631ef 100644 --- a/src/components/sessionCard/SessionCard.vue +++ b/src/components/sessionCard/SessionCard.vue @@ -288,9 +288,8 @@ const deleteOne = (name: string, list: string[]) => { flex: 1; border-radius: 8px; width: 264px; - height: 66px; background-color: var(--o-bg-color-light); - padding: 0px 5px 12px 15px; + padding: 12px 15px; display: flex; justify-content: center; flex-direction: column; @@ -319,7 +318,6 @@ const deleteOne = (name: string, list: string[]) => { .conversation-title { display: flex; justify-content: space-between; - margin-top: 12px; &__text { display: flex; font-weight: 500; @@ -332,6 +330,10 @@ const deleteOne = (name: string, list: string[]) => { white-space: nowrap; height: 22px; align-self: center; + &-span { + overflow: hidden; + text-overflow: ellipsis; + } &-input { width: 100%; height: 22px; @@ -372,15 +374,15 @@ const deleteOne = (name: string, list: string[]) => { align-items: center; margin-top: 4px; img { - width: 18px; - height: 18px; + width: 16px; + height: 16px; margin-right: 4px; display: inline-block; cursor: pointer; } div { - height: 18px; - line-height: 18px; + height: 16px; + line-height: 16px; color: var(--o-text-color-secondary); } span { diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 6139cede76c42987bc0e10ac5cde63c50e1474fe..e830bea8206a4657683e879d921705650b61a436 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -1,16 +1,28 @@ import { createI18n } from 'vue-i18n'; // 语言包 -import zh_cn from './lang/zh-cn'; +import zh from './lang/zh-cn'; import en from './lang/en'; const localLang = localStorage.getItem('copilot_language'); -const locale = (localLang && JSON.parse(localLang).language) || 'zh_cn'; +let locale = 'zh'; // 默认值 + +if (localLang) { + try { + const parsed = JSON.parse(localLang); + // 处理可能的 language 值:zh, zh_cn, zh-tw 等 + if (parsed.language) { + locale = parsed.language.startsWith('zh') ? 'zh' : parsed.language; + } + } catch (e) { + console.error('解析语言设置失败:', e); + } +} const i18n = createI18n({ legacy: false, // 设置为 false,启用 composition API 模式 locale, messages: { - zh_cn, + zh, en, }, }); diff --git a/src/i18n/lang/en.ts b/src/i18n/lang/en.ts index e87ce9ea47d3211380319b4374cab488c2be6de6..188240afc00065ff1ac757a74c40bd6f3892c043 100644 --- a/src/i18n/lang/en.ts +++ b/src/i18n/lang/en.ts @@ -7,90 +7,90 @@ export default { edit: 'Edit {name}', delete: 'Delete', search: 'Search', - null: 'No Data', + null: 'No data available.', createTime: 'Create Time', - operate: 'Operate', + operate: 'Operation', copy: 'Copy', icon: 'Icon', tip: 'Tip', - delete_success: 'Delete successfully', + delete_success: 'Deleted successfully.', }, settings: { model: 'Model', setting: 'Settings', model_setting: 'Models', - api_key_management: 'API KEY', + api_key_management: 'API Key', added_model: 'Added Models', - model_provider: 'Provider', - select_model: 'Select Model', - new_api_key: 'New API KEY', + model_provider: 'Providers', + select_model: 'Model', + new_api_key: 'New API Key', secret_key: 'Secret Key', secret_key_desc: 'Description', - confirm_to_delete: 'Confirm to delete this model?', + confirm_to_delete: 'Are you sure you want to delete this model?', placeHolder: { - url: 'Please enter the URL', - api_key: 'Please enter the API KEY', - model_name: 'Please enter the model', - max_token: 'Please enter the maximum number of tokens', + url: 'URL', + api_key: 'Enter the API key.', + model_name: 'Enter the model.', + max_token: 'Enter the maximum number of tokens.', }, }, home: { name: 'openEuler Intelligence', }, - tabs: { - work: 'Work', - private: 'Private', - collect: 'Collect', - like: 'Like', - }, + //tabs: { + //work: 'Work', + //private: 'Private', + //collect: 'Collect', + //like: 'Like', + //}, menu: { - dialogue: 'Dialogue', + dialogue: 'Chats',//前端整体统一使用Chat,infotip和错误信息酌情使用conversation,全局不使用dialog plugin_center: 'Plugin Center', app_center: 'App Center', - sql: 'witchainD', + sql: 'WitChainD',//WitChainD全局统一首字母大写 settings: 'Settings', }, semantic: { close: 'Close', - semantic_interface: 'Semantic Interface', - max_select_mcp_server: 'Choose up to {num} mcp services', - mcp_service: 'MCP Service', + semantic_interface: 'Semantic Interfaces',//tab名使用复数Semantic Interfaces,Create Plugin下拉选项使用单数Semantic Interface,需新增一个词条 + max_select_mcp_server: 'Select up to {num} MCP services.',//前端MCP统一用service,后端调用使用server + mcp_service: 'MCP Services', create_plugin: 'Create Plugin', - semantic_interface_center: 'Semantic interface center', + semantic_interface_center: 'Semantic Interface Center', all_select: 'All', - interface_name: 'Interface name', - interface_introduction: 'Interface introduction', + interface_name: 'Interface Name', + interface_introduction: 'Interface Introduction', username: 'Username', interface_search: 'Search', interface_upload: 'Upload', - all_interface: 'All interface', - my_upload: 'My upload', - my_favorite: 'My favorite', + //all_interface: 'All interface', + my_upload: 'My Uploads', + my_favorite: 'My Favorites', interface_edit: 'Edit', interface_delete: 'Delete', - no_data: 'No data', + no_data: 'No data available.', upload_semantic_interface: 'Upload Interface', edit_semantic_interface: 'Edit Interface', view_semantic_interface: 'View Interface', - choose_file: 'Choose File', - tip1: 'Drag file here', - tip2: 'Format: YAML, Size < 2M', + choose_file: 'Select File', + tip1: 'Drag a file here or click Select File.', + tip2: 'Format: YAML; Size < 2 MB', + interface_path: 'Interface Path', + interface_description: 'Interface Description', cancel: 'Cancel', submit: 'Submit', edit: 'Edit', - analyze: 'Analyze', - interface_path: 'Interface path', - interface_description: 'Interface description', - pleaseEnter: 'Please enter', + analyze: 'Parse', + pleaseEnter: 'Please enter.', save: 'Save', + publish_condition: 'Debug all workflows in the application successfully before publishing it.', + icon: 'Icon', + baseMessage: 'Basic Info', preview: 'Preview', publish: 'Publish', - publish_condition: - 'Need to debug all workflows in the application successfully before publishing the application', - icon: 'Icon', - baseMessage: 'Base Message', - copyFailed: 'Copy failed', - checkFormat: 'Please check the format', + copyFailed: 'Copy failed.', + checkFormat: 'Check the format.', + check_connect: 'Please check the connecting cable.', }, plugin_center: { plugin_name: 'Plugin Name', @@ -106,91 +106,97 @@ export default { mcp_type: 'MCP Type', activate: 'Activate', deactivate: 'Deactivate', + install: 'Install', + install_success: 'Install Success', + not_installed: 'Not Installed', + cancel: 'Cancel', + all_select: 'All', + installed: 'Installed', + not_active: 'Not Active', + active: 'Active', + active_mcp_service: 'Active MCP Service', }, upload_icon: 'Upload Icon', - please_upload_icon: 'Please upload icon', - please_input_mcp_name: 'Please input MCP name', - please_input_mcp_overview: 'Please input MCP overview', - please_select_mcp_description: 'Please select MCP description', - server_detail: 'Server Details', - server_tool: 'Server Tools', - server_description: 'Server Description', + please_upload_icon: 'Upload an icon.', + please_input_mcp_name: 'Enter an MCP name.', + please_input_mcp_overview: 'Enter an MCP overview.', + please_select_mcp_description: 'Enter an MCP description.', + server_detail: 'Service Details', + server_tool: 'Service Tools', + server_description: 'Service Description', tool_input_schema: 'Tool Input Schema', tool_output_schema: 'Tool Output Schema', - confirm_delete_interface: 'Are you sure to delete this interface?', - confirm_delete_server: 'Are you sure to delete this server?', - no_brief_description_yet: 'No brief description yet', + confirm_delete_interface: 'Are you sure you want to delete this interface?', + confirm_delete_server: 'Are you sure you want to delete this service?', + no_brief_description_yet: 'No description available.', }, app: { app_center: 'App Center', - flow: 'Flow', + flow: 'Workflow', agent: 'Agent', all_select: 'All', - app_name: 'App name', - app_introduction: 'App introduction', + app_name: 'App Name', + app_introduction: 'App Introduction', username: 'Username', app_search: 'Search', app_create: 'Create App', - all_app: 'All App', - my_created: 'My creations', - my_favorite: 'My favorite', + all_app: 'All', + my_created: 'My Creations', + my_favorite: 'My Favorites', app_edit: 'Edit', app_delete: 'Delete', - no_data: 'No data', + no_data: 'No data available.', upload_app: 'Upload App', - edit_app: 'Edit App', - view_app: 'View App', + //edit_app: 'Edit App', + //view_app: 'View App', cancel: 'Cancel', - submit: 'Submit', + submit: 'Confirm', edit: 'Edit', - analyze: 'Analyze', + analyze: 'Parse', unpublished: 'Unpublished', - publishSuccess: 'Publish successfully', - publishFailed: 'Publish failed', + publishSuccess: 'Published successfully.', + publishFailed: 'Publish failed.', create_app: 'Create App', app_published: 'Published', app_config: 'App Config', - workflow_app: 'WorkFlow App', - create_workflow_app: 'Create WorkFlow App', - workflow_app_desc: '(待翻译)建拖拽式工作流应用,精筑生产级AI', - mcp_app: 'MCP App', + workflow_app: 'Create Workflow App', + mcp_app: 'Create Agent App', create_mcp_app: 'Create MCP App', - mcp_app_desc: '(待翻译)创插件Agent应用,速构个人AI助手', - confirm_delete_app: 'Are you sure to delete this application?', - create_or_edit_workflow_first: 'Please create/edit a workflow first', + create_workflow_app: 'Create Workflow App', + mcp_app_desc: 'Build plugin/Agent apps to quickly create personal AI assistants', + workflow_app_desc: 'Create drag-and-drop workflow apps to craft production-grade AI solutions', + confirm_delete_app: 'Are you sure you want to delete this APP?', + create_or_edit_workflow_first: 'Create/Edit a workflow first.', ui_preview: 'UI Preview', appName: 'Agent Name', - appName_input: 'Please enter the agent name', + appName_input: 'Enter an agent name.', appDescription: 'Agent Description', - appDescription_input: 'Please enter the agent description', - modelSelected: 'Model Selection', - modelSelected_input: 'Please select a model', - multi_Dialogue: 'Multi-turn Dialogue', - multi_Dialogue_select: 'select the number of turns', - ability_Configuration: 'Ability Configuration', - MCPService: 'MCP Service', - MCPService_add: 'Add the MCP service', - permissionConfiguration: 'Permission Configuration', - permission: 'Permission', - permission_public: 'Public (visible to everyone)', - permission_private: 'Private (visible only to you)', - somePeople: 'Visible to some people', - updateSuccessfully: 'Update successfully', - pleasemodifyTheName: - 'The workflow name already exists under the current application, please modify the name and try again', - deleteWorkflowSuccessfully: 'Delete workflow successfully', - createSuccessfully: 'Create successfully', - createFailed: 'Create failed', - successfully: 'Successfully', - editable: 'Editable', - failed: 'Failed', - inputContent: 'Input content', - outputContent: 'Output content', - link: 'Related links', - addLink: 'Add link', - addFiveLinks: 'Up to 5 links can be added', - optional: 'Optional', - searchUser: 'Search user', + appDescription_input: 'Enter an agent description.', + modelSelected: 'Model', + modelSelected_input: 'Select a model.', + multi_Dialogue: 'Multi-turn Conversation', + multi_Dialogue_select: 'Number of Turns', + ability_Configuration: 'Capabilities', + MCPService: 'MCP Services', + MCPService_add: 'Add MCP Service', + permissionConfiguration: 'Permissions', + permission: 'Permissions', + permission_public: 'Public (visible to all)', + permission_private: 'Private (visible only to me)', + somePeople: 'Custom (visible to selected users)', + updateSuccessfully: 'Updated successfully.', + pleasemodifyTheName: 'The workflow name already exists. Modify the name and try again.', + deleteWorkflowSuccessfully: 'Workflow deleted successfully.', + createSuccessfully: 'Created successfully.', + //successfully: 'Done.', + //failed: 'Failed.', + inputContent: 'Input', + outputContent: 'Output', + link: 'Related Links', + addLink: 'Add Link', + addFiveLinks: 'Add up to 5 links.', + optional: 'Available', + searchUser: 'Search for users.', selected: 'Selected', }, main: { @@ -200,52 +206,50 @@ export default { describe2: ", and I'm happy to be of service.", left_describe: 'Popular Apps', os_knowledge: 'CVE Hotfix Assistant', - os_knowledge_describe: 'CVE Hotfix', + os_knowledge_describe: 'CVE hotfix', openEuler_expertise: 'Diagnostics Assistant', - openEuler_expertise_describe: 'Intelligent Diagnostics', + openEuler_expertise_describe: 'Intelligent diagnostics', beyond_openEuler: 'Tuning Assistant', - beyond_openEuler_describe: 'Intelligent Tuning', - openEuler_use_cases: 'Container Stack', - openEuler_use_cases_describe: 'AI Container Stack assistant', - question: 'Recommendation qustions', - addQuestion: 'Add question', - addFiveQuestions: 'Up to 5 questions can be added', - smart_shell_describe: - 'Experience the future of OSs with our smart shell! Use natural language to diagnose and optimize your system.', - try_app: 'Enter the App Center', + beyond_openEuler_describe: 'Intelligent tuning', + openEuler_use_cases: 'Container Stack Assistant', + openEuler_use_cases_describe: 'AI container stack assistant', + question: 'Suggested Questions', + addQuestion: 'Add Question', + addFiveQuestions: 'Add up to 5 questions.', + //smart_shell_describe: '欢迎探索首款自然语言交互的智能操作系统:一句话,即享智能诊断与优化', + try_app: 'Access App Center', refresh: 'Refresh', - query_interpretation: 'Query Interpretation', - Automatic: 'Automatic', - ask_me_anything: - 'Ask me anything about openEuler. Press Shift+Enter to start a new line.', - you_might_want_to_know: 'You might want to know:', + //query_interpretation: '请选择识别方式', + //Automatic: '自动识别', + ask_me_anything: 'Ask me anything about openEuler. Press Shift+Enter to start a new line.', + //you_might_want_to_know: 'You might want to know: ', close: 'Close', confirm: 'Confirm', - email1: 'Email:', + email1: 'Email: ', email2: 'contact@openeuler.io', - opinions: - 'AI-generated responses are for reference only and do not reflect the opinions of openEuler.', + opinions: 'AI-generated responses are provided for reference only and do not reflect the opinions of openEuler.', service_agreement: 'Service Agreement', privacy_policy: 'Privacy Policy', contact_us: 'Contact Us', - version: 'Version 0.9.6-Beta', + version: 'Version 0.10.0-Beta', }, history: { + new_conversation: 'New Chat', new_chat: 'New Chat', - latestConversation: 'This is the latest conversation', + latestConversation: 'This is the latest conversation.', recent_chats: 'Recent Chats', delete_chats: 'Delete Chats', find_recent_chats: 'Find recent chats.', rename: 'Rename', - delete: 'Delete', // 注意:这个字段可能与批量删除操作冲突,具体取决于应用逻辑 - delete_successfully: 'Delete successfully', - delete_failed: 'Delete failed', + delete: 'Delete', + delete_successfully: 'Deleted successfully.', + delete_failed: 'Delete failed.', cancel: 'Cancel', - confirmation_message: 'Delete Chats?', - confirmation_message1: 'Tip', select_all: 'Select All', - confirmation_content1: 'The selected ', - confirmation_content2: ' chats will be deleted.', + confirmation_message: 'Delete Chat', + confirmation_message1: 'Tip', + confirmation_content1: 'The selected', + confirmation_content2: 'chats will be deleted.', delete_content1: 'This chat', delete_content2: 'will be deleted.', ok: 'OK', @@ -256,39 +260,36 @@ export default { time_filter_last_30_days: 'Last 30 days', time_filter_last_6_months: 'Last 6 months', links: 'Links', - hiss_basic_software_service_capability_platform: - 'HiSS Basic Software Service Capability Platform', // 注意:这里我保留了原始的大小写,但通常我们会将首字母大写 + hiss_basic_software_service_capability_platform: 'HiSS Basic Software Service Capability Platform', collapse: 'Collapse', - no_chat_history: 'No chat history available.', // 假设需要一个字段来表示没有历史对话的情况 - expand: 'Expand', // 假设界面中有展开历史对话的功能 + no_chat_history: 'No chat history available.', + expand: 'Expand', myApp: 'My Apps', + auto_execute: 'Auto Execute', }, feedback: { - noCopyMessage: 'No information to copy', - feedbackSuccesful: 'Feedback succeeded.', - regenerate: 'Regenerate', // 这里我保留了原样,因为通常键名不加双引号 - try_ask_me: 'Try ask me:', - eulercopilot_is_thinking: 'openEuler Intelligence is thinking…', - generation_stopped: 'Generation stopped.', + noCopyMessage: 'No information to copy.', + feedbackSuccesful: 'Feedback sent. Thank you!', + regenerate: 'Regenerate', + try_ask_me: 'Try ask me: ', + eulercopilot_is_thinking: 'openEuler Intelligence is thinking...', + generation_stopped: 'Response stopped.', stop: 'Stop', - stopSuccessful: 'Stop Successfully', + stopSuccessful: 'Paused', systemBusy: 'The system is busy. Please try again later.', - onlySupport: - "I'm sorry, but for now, we only support questions related to the fields of openEuler and Linux.", + onlySupport: "I'm sorry, but for now, we only support questions related to openEuler and Linux.", copy: 'Copy', copied_successfully: 'Copied successfully.', - copied_failed: 'Copied failed', - edit_successful: 'Edit successful', - edit_failed: 'Edit failed', + copied_failed: 'Copy failed.', + edit_successful: 'Edited successfully.', + edit_failed: 'Edit failed.', good_answer: 'Good Answer', bad_answer: 'Bad Answer', your_feedback_helps_us_improve: 'Your feedback helps us improve.', - the_information_is_inappropriate_or_illegal: - 'The information is inappropriate or illegal.', + the_information_is_inappropriate_or_illegal: 'The information is inappropriate or illegal.', the_answer_is_not_helpful: 'The answer is not helpful.', - i_found_an_error: 'I found an error!', - enter_the_link_to_the_correct_answer: - 'Enter the link to the correct answer.', + i_found_an_error: 'I found an error.', + enter_the_link_to_the_correct_answer: 'Enter the URL to the correct answer.', describe_the_error: 'Describe the error.', submit: 'Submit', report: 'Report', @@ -298,146 +299,163 @@ export default { Report: { pornographic_content: 'Pornographic content', account_violation: 'Account violation', - politically_sensitive_content: 'Politically sensitive content', + politically_sensitive_content: 'Political content', violence_or_terrorism: 'Violence or terrorism', - defamation_or_rumor_spreading: 'Defamation or rumor spreading', + defamation_or_rumor_spreading: 'Defamation or misinformation', insult_to_heroes_or_martyrs: 'Insult to heroes or martyrs', spam: 'Spam', ethnic_or_religious_incitement: 'Ethnic or religious incitement', disturbing_content: 'Disturbing content', abuse_or_harassment: 'Abuse or harassment', gambling_or_fraud: 'Gambling or fraud', - consumer_manipulation: 'Consumer manipulation', + consumer_manipulation: 'Misleading promotions', harm_to_minors: 'Harm to minors', illegal_or_prohibited_items: 'Illegal or prohibited items', - other_violations: 'Other violations', + other_violations: 'Other', }, Login: { login: 'Log In', logout: 'Log Out', - login_now: 'Log In Now', // 假设这里“立即登录”需要更明确的英文表达 - account: 'Account', - enter_account: 'Please enter your account', - password: 'Password', - enter_password: 'Please enter your password', - incorrect_password: 'Incorrect password', - api_key_management: 'API Key Management', - no_api_key_available: 'No API key is available', - create_api_key: 'Create API Key', - api_key_display_once: - 'This API Key will be displayed only once. Please copy and save it securely.', - revoke: 'Revoke', // 为了与英文操作保持一致,我将“撤销”明确为对API Key的操作 + //login_now: '立即登录', + //account: '账号', + //enter_account: '请输入账号', + //password: '密码', + //enter_password: '请输入密码', + //incorrect_password: '密码输入有误', + //api_key_management: 'API key management', + //no_api_key_available: '暂无可用的 API Key', + //create_api_key: '新建 API Key', + //api_key_display_once: '此 API Key 只展示一次,请复制后妥善保存。', + revoke: 'Revoke', refresh: 'Refresh', - unauthorized: 'Unauthorized page, please login first', + unauthorized: 'Unauthorized page. Please log in first.', }, question: { - open_euler_community_edition_categories: 'Community Edition Categories', + open_euler_community_edition_categories: 'What are the categories of openEuler community distributions?', lts_release_cycle_and_support: - 'LTS Release Cycle and Community Support Duration', + 'What is the release cycle and community support duration for openEuler LTS versions?', innovation_release_cycle_and_support: - 'Innovation Release Cycle and Community Support Duration', + 'What is the release cycle and community support duration for openEuler innovation versions?', container_cloud_platform_solution: - 'Container Cloud Platform Solution (CCPS) of the openEuler Community', - sec_gear_main_functions: 'Main Functions of secGear', - dde_description: 'What is DDE?', - lustre_description: 'What is Lustre?', - open_euler_testing_management_platform: - 'Testing Management Platform of the openEuler Community', + 'What is the Container Cloud Platform Solution (CCPS) of the openEuler community?', + sec_gear_main_functions: 'What are the three main capabilities of secGear?', + dde_description: 'What is the DDE component?', open_euler_pkgship: 'What is pkgship in openEuler?', - open_euler_software_package_introduction_principles: - 'Introduction Principles of openEuler Software Packages', - download_rpm_without_installing: - 'How to download an RPM package to the local system without installing it in openEuler?', - count_the_occurrences_of_the_hello: - "Generate a shell command to count the occurrences of the 'hello' string in the 'test.txt' file.", - convert_uppercase_to_lowercase: - 'Give me a shell command to convert uppercase letters to lowercase in text files in the current directory and its subdirectories', - list_files_with_specific_permissions: - 'Give me a shell command to find and list files with specific permissions in the current directory', - search_error_keyword_with_context: - "Give me a shell command to search for the keyword 'error' in text files in the /home directory and its subdirectories, and output the matching lines along with the 3 lines before and after them to a file named 'result.txt'", - clear_dependencies_for_software_package: - 'How to clear dependencies for software packages in openEuler?', - gpgcheck_purpose_in_dnf: - 'What is the purpose of the gpgcheck parameter in DNF in openEuler?', - installonly_limit_function_in_dnf: - 'What is the function of the installonly_limit parameter in DNF in openEuler?', - clean_requirement_on_remove_function_in_dnf: - 'What is the function of the clean_requirement_on_remove parameter in DNF in openEuler?', - hunan_tobacco_monopoly_applications_on_openeuler: - 'What are the applications of Hunan Tobacco Monopoly based on openEuler?', - xsky_applications_on_openeuler: - 'What are the applications of XSKY based on openEuler?', }, upload: { upload_tip_text: - 'Maximum 10 files, 64 MB in total. Formats: pdf, docx, doc, txt, md, xlsx', - uploading: 'Upload...', - upload_fail: 'Upload failed', + 'Maximum 10 files, 64 MB in total. Formats: PDF, DOCX, DOC, TXT, MD, XLSX', + uploading: 'Uploading...', + upload_fail: 'Upload failed.', resolving: 'Parsing...', - resolve_fail: 'Parsing failed', - error_type_msg: 'Upload failed, Invalid file type.', - error_size_msg: - 'Upload failed, The number or size of files exceeds the upper limits.', - error_name_msg: 'Upload failed, Duplicate file names exit.', + resolve_fail: 'Parsing failed.', + error_type_msg: 'Upload failed: File type not supported.', + error_size_msg: 'Upload failed: File count or size exceeds the upper limit.', + error_name_msg: 'Upload failed: Duplicate file name exits.', aside_session_file_count_front: '', aside_session_file_count_back: 'files', + quote_front: 'Quote', + quote_back: 'pieces of knowledge base materials', + reference_source: 'Reference source', }, apikey: { - save_apikey: - 'This API KEY is only displayed once, please copy and save it properly', - no_apikey: 'No available API key', - create_apikey: 'Create new API key', + save_apikey: 'This API key is displayed only once. Please copy and keep it secure.', + no_apikey: 'No available API key.', + create_apikey: 'New API Key', cancel: 'Cancel', - confirm: 'Please enter the description of the API key', }, witChainD: { witChainD: 'WitChainD', - witChainD_id: 'id', - describe_the_witChainD: 'Please enter witChainD id', - find_witChainD: 'Please enter witChainD name/id', - knowledge: 'Knowledge', - fiveKnowledge: 'You can select up to 5 knowledge bases', + witChainD_id: 'ID', + describe_the_witChainD: 'Enter a WitChainD ID.', + find_witChainD: 'Enter a WitChainD name or ID.', + knowledge: 'Knowledge Base', + fiveKnowledge: 'Select up to 5 knowledge bases.', }, flow: { flow_start: 'Workflow in progress', - flow_end: 'Workflow completed', - flow_params_error: 'Missing parameters', + flow_end: 'Workflow completed.', + flow_cancel: 'Cancel running', + flow_risk: 'Risk statement', + flow_params_error: 'Parameter missing', flow_pause: 'Workflow paused', - edit_flow: 'Edit workflow', - edit_workflow: 'Edit workflow', - flow_name: 'Workflow name', - flow_description: 'Workflow description', - create_flow: 'Create workflow', - step_configuration: 'Step configuration', - debug_after_connection: - 'Debugging can only be performed after node connection is complete', - no_flow: 'No workflow', - choose_flow: 'Please choose a workflow', + edit_flow: 'Edit', + edit_workflow: 'Edit Workflow', + flow_name: 'Workflow Name', + flow_description: 'Workflow Description', + create_flow: 'Create Workflow', + step_configuration: 'Step Configuration', + debug_after_connection: 'Connect to a node before debugging.', + no_flow: 'No workflow available.', + choose_flow: 'Select a workflow.', debug: 'Debug', - enterWorkflowName: 'Please enter workflow name', + enterWorkflowName: 'Enter a workflow name.', + enterWorkflowDesc: 'Enter a workflow Description.', default: '', - success: 'successful', - error: 'failed', + success: 'Workflow succeeded', + error: 'Workflow failed', running: 'Running', pending: 'Running', result: 'Result', input: 'Input', output: 'Output', params: 'Parameters', - supplementaryParameters: 'Supplementary Parameters', + supplementaryParameters: 'Additional Parameters', + parameterConfiguration: 'Parameter Configuration', + additionalNotes: 'Additional notes', }, pagination: { prev: 'Previous', next: 'Next', }, zoom: { - reduce: "Zoom Out", - amplify: "Zoom In", - adaptive: "Fit to Screen", - scaleTo50: "Zoom to 50%", - scaleTo100: "Zoom to 100%", - scaleTo150: "Zoom to 150%", - scaleTo200: "Zoom to 200%" + reduce: 'Zoom Out', + amplify: 'Zoom In', + adaptive: 'Fit to Screen', + scaleTo50: 'Zoom to 50%', + scaleTo100: 'Zoom to 100%', + scaleTo150: 'Zoom to 150%', + scaleTo200: 'Zoom to 200%', + }, + yaml: { + else: 'else', + if: 'if', + else_if: 'else if', + please_select: 'Please select', + please_select_condition: 'Please select the condition.', + parameter_value: 'Input or reference parameter value', + add_conditional_branches: 'Add conditional branches', + and: 'and', + or: 'or', + add: 'Add', + input_type_error: 'Input type error', + please_improve_the_conditions: 'Please improve the conditions.', + }, + opertion: { + api: 'HTTP request', + mcp: 'MCP', + sql: 'SQL query', + gcraph: 'Chart', + llm: 'Large model', + rag: 'Knowledge base', + suggestion: 'Question recommendation', + equal: 'Equal', + not_equal: 'Not equal', + contains: 'Contains', + does_not_contain: 'Does not contain', + starts_with: 'Starts with', + ends_with: 'Ends with', + length_equal: 'Length equal', + length_greater_than: 'Length greater than', + length_greater_than_or_equal: 'Length greater than or equal', + length_less_than: 'Length less than', + length_less_than_or_equal: 'Length less than or equal', + regex_match: 'Regex match', + greater_than: 'Greater than', + less_than: 'Less than', + greater_than_or_equal: 'Greater than or equal', + less_than_or_equal: 'Less than or equal', + contains_key: 'Contains key', + does_not_contain_key: 'Does not contain key', } }; diff --git a/src/i18n/lang/zh-cn.ts b/src/i18n/lang/zh-cn.ts index 913e215c55f4bc017743eeaf48a510c46aae4c3e..d11a438fff78ba76d791ea83e96ba5099ea040af 100644 --- a/src/i18n/lang/zh-cn.ts +++ b/src/i18n/lang/zh-cn.ts @@ -90,6 +90,7 @@ export default { publish: '发布', copyFailed: '复制失败', checkFormat: '请检查格式', + check_connect: '请检查连接线', }, plugin_center: { plugin_name: '插件名称', @@ -105,6 +106,15 @@ export default { mcp_type: 'MCP类型', activate: '激活', deactivate: '取消激活', + install: '安装', + install_success: '安装成功', + not_installed: '未安装', + cancel: '取消', + all_select: '全部', + installed: '已安装', + not_active: '未激活', + active: '已激活', + active_mcp_service: '激活MCP服务', }, upload_icon: '上传图标', please_upload_icon: '请上传图标', @@ -224,9 +234,10 @@ export default { service_agreement: '服务协议', privacy_policy: '隐私政策', contact_us: '联系我们', - version: '版本号0.9.6-内测版', + version: '版本号0.10.0-内测版', }, history: { + new_conversation: '新会话', new_chat: '新建对话', latestConversation: '已是最新对话', recent_chats: '历史记录', @@ -257,6 +268,7 @@ export default { no_chat_history: '暂无历史对话', expand: '展开', myApp: '我的应用', + auto_execute: '自动执行', }, feedback: { noCopyMessage: '无可复制的信息', @@ -372,6 +384,9 @@ export default { error_name_msg: '上传失败,存在重名文件。', aside_session_file_count_front: '共', aside_session_file_count_back: '个文档', + quote_front: '引用', + quote_back: '篇知识库资料', + reference_source: '引用来源', }, apikey: { save_apikey: '此 API KEY 只展示一次,请复制后妥善保存', @@ -391,6 +406,8 @@ export default { flow: { flow_start: '工作流进行中', flow_end: '工作流结束', + flow_cancel: '取消运行', + flow_risk: '风险提示', flow_params_error: '缺少参数', flow_pause: '工作流暂停', edit_flow: '编辑工作流', @@ -404,6 +421,7 @@ export default { choose_flow: '请选择工作流', debug: '调试', enterWorkflowName: '请输入工作流名称', + enterWorkflowDesc: '请输入工作流名称', default: '', success: '运行成功', error: '运行失败', @@ -414,18 +432,61 @@ export default { output: '输出', params: '参数', supplementaryParameters: '补充参数', + parameterConfiguration: '参数配置', + additionalNotes: '补充说明', }, pagination: { prev: 'Previous', next: 'Next', }, zoom: { - reduce: "缩小", - amplify: "放大", - adaptive: "自适应", - scaleTo50: "缩放到50%", - scaleTo100: "缩放到100%", - scaleTo150: "缩放到150%", - scaleTo200: "缩放到200%" + reduce: '缩小', + amplify: '放大', + adaptive: '自适应', + scaleTo50: '缩放到50%', + scaleTo100: '缩放到100%', + scaleTo150: '缩放到150%', + scaleTo200: '缩放到200%', + }, + yaml: { + else: '否则', + if: '如果', + else_if: '否则如果', + please_select: '请选择', + please_select_condition: '请选择条件', + parameter_value: '输入或引用参数值', + add_conditional_branches: '添加条件分支', + and: '且', + or: '或', + add: '新增', + input_type_error: '输入类型错误', + please_improve_the_conditions: '请完善条件', + }, + opertion: { + api: 'HTTP请求', + mcp: 'MCP', + sql: 'SQL查询', + gcraph: '图表', + llm: '大模型', + rag: '知识库', + suggestion: '问题推荐', + equal: '等于', + not_equal: '不等于', + contains: '包含', + does_not_contain: '不包含', + starts_with: '起始等于', + ends_with: '结束等于', + length_equal: '长度等于', + length_greater_than: '长度大于', + length_greater_than_or_equal: '长度大于等于', + length_less_than: '长度小于', + length_less_than_or_equal: '长度小于等于', + regex_match: '正则匹配', + greater_than: '大于', + less_than: '小于', + greater_than_or_equal: '大于等于', + less_than_or_equal: '小于等于', + contains_key: '包含键', + does_not_contain_key: '不包含键', } }; diff --git a/src/store/account.ts b/src/store/account.ts index fe7ab5e5e0841d560a3b121776702fd527705370..b13c06f59d8713de010e146491bdafddfcbe51ae 100644 --- a/src/store/account.ts +++ b/src/store/account.ts @@ -25,11 +25,13 @@ export const useAccountStore = defineStore('account', () => { organization: string; user_sub: string; is_admin?: boolean; + auto_execute?: boolean; }>({ username: '', revsionNumber: null, organization: '', user_sub: '', // 用户唯一标识 + auto_execute: false, }); /** @@ -98,6 +100,7 @@ export const useAccountStore = defineStore('account', () => { userinfo.organization = organization; userinfo.revsionNumber = revision_number; userinfo.is_admin = res.result.is_admin; + userinfo.auto_execute = res.result.auto_execute; return true; } return false; diff --git a/src/store/conversation.ts b/src/store/conversation.ts index 4dcfa1217a4787e5df2e5a66b11c2ef3fac2b404..ed830a9d3e384bb7b535b2365d71f5915441f3d7 100644 --- a/src/store/conversation.ts +++ b/src/store/conversation.ts @@ -51,12 +51,12 @@ export const useSessionStore = defineStore('conversation', () => { // #endregion - const { language } = useLangStore(); - + const langStore = useLangStore(); // 是否暂停回答 const isPaused = ref(false); // 会话列表 const conversationList = ref([]); + const currentMessage = ref({}); const app = ref({ appId: '', name: '', @@ -66,6 +66,8 @@ export const useSessionStore = defineStore('conversation', () => { // ai回复是否还在生成中 const isAnswerGenerating = ref(false); + const currentTaskId = ref(null); + // 方法集合 - 用于处理不同类型的event message const dataTransfers = { textAdd: ( @@ -85,7 +87,9 @@ export const useSessionStore = defineStore('conversation', () => { conversationItem: RobotConversationItem, message: Record, ) => { - conversationItem.message[conversationItem.currentInd] += message.content; + if (!conversationItem.files) { + conversationItem.files = []; + } conversationItem.files = [...conversationItem.files, message.content]; }, suggestionFunc: ( @@ -143,7 +147,7 @@ export const useSessionStore = defineStore('conversation', () => { ); if (target) { target.data.output = message.content; - target.status = flow.stepStatus; + target.status = 'success'; // 工作流添加每阶段的时间耗时 target['costTime'] = metadata.timeCost; if (flow.step_status === 'error' && conversationItem.flowdata) { @@ -172,11 +176,12 @@ export const useSessionStore = defineStore('conversation', () => { } else if (content.type !== 'schema' && conversationItem.flowdata) { // 删除 end 逻辑 conversationItem.flowdata = { - id: contentFlow.stepId, - title: i18n.global.t('flow.flow_end'), - progress: contentFlow.stepProgress, - status: 'success', + id: messageFlow.stepId, + title: currentTaskId ? i18n.global.t('flow.flow_start') : i18n.global.t('flow.flow_end'), + progress: messageFlow.stepProgress, + status: currentTaskId ? messageFlow.flowStatus : 'success', display: true, + taskId: currentTaskId.value, data: conversationItem.flowdata.data, }; } else { @@ -205,6 +210,81 @@ export const useSessionStore = defineStore('conversation', () => { $bus.emit('debugChatEnd'); } }, + waitingForStart: ( + conversationItem: RobotConversationItem, + message: Record, + ) => { + const flow = (message.flow || {}) as Record; + const content = (message.content || {}) as Record; + conversationItem.flowdata = { + id: flow.stepId, + title: flow.stepName, + status: flow.stepStatus, + taskId: currentTaskId, + data: { + exData: content, + }, + }; + if (conversationItem.flowdata) { + conversationItem.flowdata.progress = flow.stepProgress; + conversationItem.flowdata.status = flow.stepStatus; + } + }, + waitingForParam: ( + conversationItem: RobotConversationItem, + message: Record, + ) => { + const flow = (message.flow || {}) as Record; + const content = (message.content || {}) as Record; + conversationItem.flowdata = { + id: flow.stepId, + title: flow.stepName, + status: flow.stepStatus, + taskId: currentTaskId, + data: { + exParam: content, + }, + }; + if (conversationItem.flowdata) { + conversationItem.flowdata.progress = flow.stepProgress; + conversationItem.flowdata.status = flow.stepStatus; + } + }, + flowCancel: ( + conversationItem: RobotConversationItem, + message: Record, + ) => { + const content = (message.content || {}) as Record; + const contentFlow = (content.flow || {}) as Record; + const messageFlow = (message.flow || {}) as Record; + + // 取消运行 + conversationItem.flowdata = { + id: contentFlow.stepId, + title: i18n.global.t('flow.flow_cancel'), + progress: contentFlow.stepProgress, + status: messageFlow.stepStatus, + display: true, + data: conversationItem?.flowdata?.data, + }; + }, + flowSuccess: ( + conversationItem: RobotConversationItem, + message: Record, + isFlowDebug: boolean, + ) => { + const content = (message.content || {}) as Record; + const contentFlow = (content.flow || {}) as Record; + const messageFlow = (message.flow || {}) as Record; + conversationItem.flowdata = { + id: contentFlow.stepId, + title: i18n.global.t('flow.flow_end'), + progress: contentFlow.stepProgress, + status: 'success', + display: true, + data: conversationItem?.flowdata?.data, + }; + }, }; // chat message回调 @@ -223,6 +303,10 @@ export const useSessionStore = defineStore('conversation', () => { dataTransfers.dataDone(conversationItem, !!params.type); return; } + if (rawMsgData === '[ERROR]') { + dataTransfers.dataDone(conversationItem, !!params.type); + return; + } // 同一时间戳传来的decodeValue是含有三条信息的合并,so需要分割 // 这里json解析 @@ -231,9 +315,11 @@ export const useSessionStore = defineStore('conversation', () => { if ('metadata' in message) { conversationItem.metadata = message.metadata; } + currentTaskId.value = message.taskId; if ('event' in message) { switch (eventType) { case 'text.add': + currentMessage.value = message; dataTransfers.textAdd(conversationItem, message); break; case 'heartbeat': @@ -243,7 +329,7 @@ export const useSessionStore = defineStore('conversation', () => { break; case 'document.add': // 遇到文档添加事件,先省略 - // dataTransfers.documentAdd(conversationItem, message); + dataTransfers.documentAdd(conversationItem, message); break; case 'Suggestion': dataTransfers.suggestionFunc(conversationItem, message); @@ -251,7 +337,7 @@ export const useSessionStore = defineStore('conversation', () => { case 'init': //初始化获取 metadata conversationItem.metadata = message.metadata; - conversationItem.createdAt = message.content.created_at; + conversationItem.createdAt = message.content.createdAt; conversationItem.groupId = message.groupId; break; case 'flow.start': @@ -264,10 +350,26 @@ export const useSessionStore = defineStore('conversation', () => { case 'step.output': dataTransfers.stepOutput(conversationItem, message); break; + case 'step.waiting_for_start': + // 事件流等待开始 + dataTransfers.waitingForStart(conversationItem, message); + break; + case 'step.waiting_for_param': + // 事件流等待参数 + dataTransfers.waitingForParam(conversationItem, message); + break; + case 'flow.cancel': + // 事件流取消 + dataTransfers.flowCancel(conversationItem, message); + break; case 'flow.stop': //时间流结束 dataTransfers.flowStop(conversationItem, message, !!params.type); break; + case 'flow.success': + //时间流结束 + dataTransfers.flowSuccess(conversationItem, message, !!params.type); + break; default: break; } @@ -299,7 +401,7 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: params.conversationId, features: features, groupId: params.groupId, - language, + language: langStore.language, question: params.question, // record_id: params.qaRecordId, }), @@ -318,6 +420,7 @@ export const useSessionStore = defineStore('conversation', () => { }, conversationId: params.conversationId, debug: true, + language: langStore.language, question: params.question, }), openWhenHidden: true, @@ -328,6 +431,7 @@ export const useSessionStore = defineStore('conversation', () => { params: Record, innerParams: Record, fetchParams: Record, + isDebug: boolean, ) => { await fetchEventSource(url, { ...fetchParams, @@ -338,9 +442,10 @@ export const useSessionStore = defineStore('conversation', () => { flowId: '', params: innerParams || {}, }, + ...(isDebug && { debug: isDebug }), conversationId: params.conversationId, features: features, - language, + language: langStore.language, groupId: params.groupId, question: params.question, record_id: params.qaRecordId, @@ -366,13 +471,25 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: params.conversationId, features: features, groupId: params.groupId, - language, + language: langStore.language, question: params.question, record_id: params.qaRecordId, }), openWhenHidden: true, }); }, + fetchWait: async ( + url: string, + params: Record, + innerParams: Record, + fetchParams: Record, + ) => { + await fetchEventSource(url, { + ...fetchParams, + body: JSON.stringify({ taskId: currentTaskId.value, params: params }), + openWhenHidden: true, + }); + }, }; const judgeResp = async (resp) => { @@ -404,6 +521,8 @@ export const useSessionStore = defineStore('conversation', () => { type?: any; }, ind?: number, + waitType?: string, + isDebug?: boolean, ): Promise => { const { currentSelectedSession } = useHistorySessionStore(); params.conversationId = currentSelectedSession; @@ -449,15 +568,20 @@ 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) { // 新的工作流调试记录 await funcFetch.fetchAppNew(streamUrl, params, pp, fetchParams); - // } else if (false) { - // //写传参数情况 + } else if (waitType) { + if (waitType === 'params') { + await funcFetch.fetchWait(streamUrl, params, pp, fetchParams); + } else { + await funcFetch.fetchWait(streamUrl, params.params, pp, fetchParams); + } } else { await funcFetch.fetchDefault(streamUrl, params, pp, fetchParams); } @@ -507,6 +631,8 @@ export const useSessionStore = defineStore('conversation', () => { * @param user_selected_flow * @param params * @param type + * @param waitType + * @param isDebug */ const sendQuestion = async ( groupId: string | undefined, @@ -517,6 +643,8 @@ export const useSessionStore = defineStore('conversation', () => { user_selected_flow?: string, params?: any, type?: any, + waitType?: string, + isDebug?: boolean, ): Promise => { const { updateSessionTitle, currentSelectedSession } = useHistorySessionStore(); @@ -540,7 +668,7 @@ export const useSessionStore = defineStore('conversation', () => { comment: 'none', }); targetItem.currentInd = targetItem.message.length - 1; //123 - } else { + } else if (!waitType) { // 初次生成 ,创建一个问题和一个回答 const ind = conversationList.value.length - 1; const messageList = new MessageArray(); @@ -589,7 +717,10 @@ export const useSessionStore = defineStore('conversation', () => { params: params || undefined, }; } - await getStream(getStreamParams, regenerateInd ?? undefined); + if (waitType) { + getStreamParams = params; + } + await getStream(getStreamParams, regenerateInd ?? undefined, waitType, isDebug); }; /** @@ -608,7 +739,7 @@ export const useSessionStore = defineStore('conversation', () => { targetItem.message[0] += '暂停生成'; targetItem.isFinish = true; cancel(); - const resp = await api.stopGeneration(); + const resp = await api.stopGeneration(currentMessage.value.taskId); if (resp?.[1]?.code === 200) { isAnswerGenerating.value = false; } @@ -678,6 +809,21 @@ export const useSessionStore = defineStore('conversation', () => { }; // #endregion + /** + * 处理历史对话数据中尾注位置 + */ + const handleMessage = (record: any): string => { + let message = record.content.answer; + record.metadata.footNoteMetadataList?.reverse().forEach((footNoteMetadata: any) => { + const insertFile = record.document?.filter((file: any) => file._id === footNoteMetadata.releatedId); + const insertNumber = insertFile[0]?.order; + if (!insertNumber) return; + const pos = footNoteMetadata.insertPosition; + message = message.slice(0, pos) + `[[${insertNumber}]]` + message.slice(pos) + }); + return message; + } + /** * 获取历史对话数据 * @param conversationId @@ -715,12 +861,12 @@ export const useSessionStore = defineStore('conversation', () => { cid: conversationList.value.length + 1, belong: 'user', message: record.content.question, - createdAt: record.created_at, + createdAt: record.createdAt, }, { cid: conversationList.value.length + 2, belong: 'robot', - message: [record.content.answer], + message: [handleMessage(record)], messageList, currentInd: 0, isAgainst: false, @@ -730,6 +876,7 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: record.conversationId, groupId: record.groupId, metadata: record.metadata, + document: record.document, flowdata: record?.flow ? (generateFlowData(record.flow) as FlowType) : undefined, @@ -744,22 +891,31 @@ export const useSessionStore = defineStore('conversation', () => { const generateFlowData = (record: any): FlowDataType => { const flowData = { id: record.recordId, - title: record.id, - status: 'success', + title: record.flowName, + status: record.flowStatus, display: true, flowId: record.flowId, data: [[]] as any[], }; + // 看有没有取消的 + let isCancelled = false; for (let i = 0; i < record.steps.length; i++) { flowData.data[0].push({ id: record.steps[i].stepId, - title: record.steps[i].stepId, + title: record.steps[i].stepName, status: record.steps[i].stepStatus, data: { input: record.steps[i].input, output: record.steps[i].output, + ...(record.steps[i].exData && { exData: record.steps[i].exData }), }, }); + if (record.steps[i].stepStatus === 'cancelled') { + isCancelled = true; + } + } + if (isCancelled) { + flowData.status = 'cancelled'; } return flowData; }; @@ -771,11 +927,11 @@ export const useSessionStore = defineStore('conversation', () => { isPaused.value = true; ( conversationList.value[ - conversationList.value.length - 1 + conversationList.value.length - 1 ] as RobotConversationItem ).isFinish = true; cancel(); - const resp = await api.stopGeneration(); + const resp = await api.stopGeneration(currentMessage.value.taskId); if (resp?.[1]?.code === 200) { isAnswerGenerating.value = false; } @@ -792,6 +948,7 @@ export const useSessionStore = defineStore('conversation', () => { dialogueRef, app, appList, + currentMessage, sendQuestion, pausedStream, stopDebug, diff --git a/src/store/historySession.ts b/src/store/historySession.ts index 9b702d5e0527149a8893773ea7da63ef157b9d70..eb9638aea3f1a8e33f3de8469a2938a84055e683 100644 --- a/src/store/historySession.ts +++ b/src/store/historySession.ts @@ -216,8 +216,9 @@ export const useHistorySessionStore = defineStore( /** * 创建一个新的会话 */ - const generateSession = async (): Promise => { - const [_, res] = await api.createSession(user_selected_app.value); + const generateSession = async (isDebug): Promise => { + const appId = user_selected_app.value ?? ''; + const [_, res] = await api.createSession({appId, debug: isDebug}); if (!_ && res) { currentSelectedSession.value = res.result.conversationId; await getHistorySession(); diff --git a/src/store/lang.ts b/src/store/lang.ts index 4c5c359b3e47fa3a1a9330351196c91a42d1cecc..bdfec2e9f35755f7787d2cf9a450b28b4610f5ef 100644 --- a/src/store/lang.ts +++ b/src/store/lang.ts @@ -9,7 +9,7 @@ export const useLangStore = defineStore( const i18n = useI18n(); const language = ref(); - const changeLanguage = (lang: 'zh_cn' | 'en') => { + const changeLanguage = (lang: 'zh' | 'en') => { language.value = lang; i18n.locale.value = language.value; if (ipcRenderer) { @@ -26,7 +26,7 @@ export const useLangStore = defineStore( electronProcess.env['EULERCOPILOT_NLS_CONFIG'] && changeLanguage(nlsConfig.userLocale); } else { - !language.value && changeLanguage('zh_cn'); + !language.value && changeLanguage('zh'); } }); return { diff --git a/src/views/api/components/ActiveModel.vue b/src/views/api/components/ActiveModel.vue new file mode 100644 index 0000000000000000000000000000000000000000..835e22ceb05d94d029b978f63297fb26775ec4e6 --- /dev/null +++ b/src/views/api/components/ActiveModel.vue @@ -0,0 +1,167 @@ + + + + diff --git a/src/views/api/components/McpDrawer.vue b/src/views/api/components/McpDrawer.vue index a44eff4a07646c559d7f9fb9f33e564b653c460b..e421cd9e29aef5a1c066cadada5802bbb3fe0372 100644 --- a/src/views/api/components/McpDrawer.vue +++ b/src/views/api/components/McpDrawer.vue @@ -32,25 +32,20 @@ const COMMAND_TEMPLATE = { command: '', args: [], env: {}, - autoApprove: [], - autoInstall: true, - disabled: false, }; const URL_TEMPLATE = { + headers: {}, url: '', - env: {}, - autoApprove: [], - autoInstall: true, - disabled: false, }; +const STREAM = {}; const loading = ref(false); -const mcpConfigTemplate = { +const mcpConfigTemplate = ref({ stdio: COMMAND_TEMPLATE, sse: URL_TEMPLATE, - stream: URL_TEMPLATE, -}; + stream: STREAM, +}); const form = reactive({ icon: '', @@ -149,7 +144,7 @@ async function onConfirm(formEl: FormInstance | undefined) { icon: form.icon, name: form.name, description: form.description, - config: form.mcpConfig, + config: JSON.parse(form.mcpConfig), mcpType: form.type, }); @@ -178,7 +173,6 @@ async function onConfirm(formEl: FormInstance | undefined) { jsonEditorRef.value.setJsonValue('{\n \n}'); emits('success'); loading.value = false; - } catch (error) { console.error('Create or update MCP service failed:', error); ElMessage({ @@ -200,13 +194,23 @@ async function getMcpServiceDetail(serviceId: string) { form.description = description; form.type = mcpType; form.mcpConfig = data; - jsonEditorRef.value.setJsonValue(form.mcpConfig); + let json = {}; + if (mcpType === 'stdio') { + json.command = data.command; + json.args = data.args; + json.env = data.env; + } else if (mcpType === 'sse') { + json.headers = data.headers; + json.url = data.url; + } + jsonEditorRef.value.setJsonValue(JSON.stringify(json, null, 2)); + mcpConfigTemplate.value[mcpType] = json; } } async function setMcpConfig(type: string) { jsonEditorRef.value.setJsonValue( - JSON.stringify(mcpConfigTemplate[type], null, 2), + JSON.stringify(mcpConfigTemplate.value[type], null, 2), ); } @@ -222,6 +226,12 @@ watch( } getMcpServiceDetail(props.serviceId); } else { + // 初始化 + mcpConfigTemplate.value = { + stdio: COMMAND_TEMPLATE, + sse: URL_TEMPLATE, + stream: STREAM, + }; if (formRef.value) formRef.value.resetFields(); setMcpConfig(form.type); } @@ -240,7 +250,7 @@ watch( :model-value="visible" @close="emits('update:visible', false)" > - +
(); display: flex; flex-direction: column; gap: 4px; + width: 70%; &__top { min-height: 24px; @@ -66,7 +67,7 @@ const props = defineProps(); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - width: 80%; + width: 70%; font-weight: 700; } } diff --git a/src/views/api/index.vue b/src/views/api/index.vue index a566f503aadc2508f54681e7026f09bc8b64b6b6..c313e5ba90acc3683ef7ddf480b53bd20f1ec025 100644 --- a/src/views/api/index.vue +++ b/src/views/api/index.vue @@ -103,6 +103,30 @@ :lazy="true" > + + + + +
+
-
+ +
{{ t('plugin_center.mcp.install_failed') }}
+ +
+ +
+ {{ t('plugin_center.mcp.install_success') }} +
+
+ +
+ +
+ {{ t('plugin_center.mcp.not_installed') }} +
+
+ + \ No newline at end of file diff --git a/src/views/createapp/components/types.ts b/src/views/createapp/components/types.ts index 8633df27cbc22404495a41cd0411d713364249c3..539b183c19c015309db711bcf5529d8178040f7d 100644 --- a/src/views/createapp/components/types.ts +++ b/src/views/createapp/components/types.ts @@ -30,6 +30,8 @@ import TASK_CHOICE from '@/assets/svgs/taskChoice.svg'; import USER_CODE from '@/assets/svgs/userCode.svg'; import USER_DATABASE_CLASS from '@/assets/svgs/userDatabaseClass.svg'; import USER_DOCUMENT_CLASS from '@/assets/svgs/userDatabaseClass.svg'; +import i18n from 'src/i18n'; + // 工具类型 export type LinkType = 'redirect' | 'action'; @@ -77,14 +79,14 @@ export const nodeTypeToIcon = { // 这里是对应的图标 export const iconTypeList = [ - { name: 'HTTP请求', value: 'API', icon: API, class: 'otherNode' }, - { name: 'MCP', value: 'MCP', icon: API, class: 'otherNode' }, - { name: 'SQL查询', value: 'SQL', icon: API, class: 'otherNode' }, - { name: '图表', value: 'Graph', icon: API, class: 'otherNode' }, - { name: '大模型', value: 'LLM', icon: LLM, class: 'systemNode' }, - { name: '知识库', value: 'RAG', icon: KENOWLEDGE_BASE, class: 'systemNode' }, + { name: i18n.global.t('opertion.api'), value: 'API', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.mcp'), value: 'MCP', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.sql'), value: 'SQL', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.gcraph'), value: 'Graph', icon: API, class: 'otherNode' }, + { name: i18n.global.t('opertion.llm'), value: 'LLM', icon: LLM, class: 'systemNode' }, + { name: i18n.global.t('opertion.rag'), value: 'RAG', icon: KENOWLEDGE_BASE, class: 'systemNode' }, { - name: '问题推荐', + name: i18n.global.t('opertion.suggestion'), value: 'Suggestion', icon: get_CVE_DETAIL, class: 'aposNode', @@ -145,3 +147,50 @@ export interface ListItem { id: string; value: string | null; } + +const opertionList = [ + // Number operations + {value: 'number_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'number_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'number_greater_than', label: i18n.global.t('opertion.greater_than'), str: '>'}, + {value: 'number_less_than', label: i18n.global.t('opertion.less_than'), str: '<'}, + {value: 'number_greater_than_or_equal', label: i18n.global.t('opertion.greater_than_or_equal'), str: '≥'}, + {value: 'number_less_than_or_equal', label: i18n.global.t('opertion.less_than_or_equal'), str: '≤'}, + + // String operations + {value: 'string_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'string_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'string_contains', label: i18n.global.t('opertion.contains'), str: ''}, + {value: 'string_not_contains', label: i18n.global.t('opertion.does_not_contain'), str: ''}, + {value: 'string_starts_with', label: i18n.global.t('opertion.starts_with'), str: '='}, + {value: 'string_ends_with', label: i18n.global.t('opertion.ends_with'), str: '='}, + {value: 'string_length_equal', label: i18n.global.t('opertion.length_equal'), str: '|...|='}, + {value: 'string_length_greater_than', label: i18n.global.t('opertion.length_greater_than'), str: '|...|>'}, + {value: 'string_length_greater_than_or_equal', label: i18n.global.t('opertion.length_greater_than_or_equal'), str: '|...|≥'}, + {value: 'string_length_less_than', label: i18n.global.t('opertion.length_less_than'), str: '|...|<'}, + {value: 'string_length_less_than_or_equal', label: i18n.global.t('opertion.length_less_than_or_equal'), str: '|...|≤'}, + {value: 'string_regex_match', label: i18n.global.t('opertion.regex_match'), str: '\\+'}, + + // List operations + {value: 'list_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'list_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'list_contains', label: i18n.global.t('opertion.contains'), str: ''}, + {value: 'list_not_contains', label: i18n.global.t('opertion.does_not_contain'), str: ''}, + {value: 'list_length_equal', label: i18n.global.t('opertion.length_equal'), str: '|...|='}, + {value: 'list_length_greater_than', label: i18n.global.t('opertion.length_greater_than'), str: '|...|>'}, + {value: 'list_length_greater_than_or_equal', label: i18n.global.t('opertion.length_greater_than_or_equal'), str: '|...|≥'}, + {value: 'list_length_less_than', label: i18n.global.t('opertion.length_less_than'), str: '|...|<'}, + {value: 'list_length_less_than_or_equal', label: i18n.global.t('opertion.length_less_than_or_equal'), str: '|...|≤'}, + + // Boolean operations + {value: 'bool_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'bool_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + + // Dictionary operations + {value: 'dict_equal', label: i18n.global.t('opertion.equal'), str: '='}, + {value: 'dict_not_equal', label: i18n.global.t('opertion.not_equal'), str: '≠'}, + {value: 'dict_contains_key', label: i18n.global.t('opertion.contains_key'), str: ''}, + {value: 'dict_not_contains_key', label: i18n.global.t('opertion.does_not_contain_key'), str: ''} +] + + export const opertionListMap = new Map(opertionList.map(item => [item.value, item])); diff --git a/src/views/createapp/components/workFlow.vue b/src/views/createapp/components/workFlow.vue index dff72018c11bdd82cd6563af454b1d19c1332f72..3958b2df8547eff389ed696917fa974c45d8c491 100644 --- a/src/views/createapp/components/workFlow.vue +++ b/src/views/createapp/components/workFlow.vue @@ -1,10 +1,9 @@