Compare commits

..

13 Commits

Author SHA1 Message Date
zhangkai
94be64311c 1 2024-09-03 11:12:51 +08:00
zhangkai
e12f955105 1 2024-08-12 11:27:32 +08:00
zhangkai
5199dbb6e5 1 2024-07-16 13:34:21 +08:00
zhangkai
a71c43fa9a 1 2024-07-08 12:14:59 +08:00
zhangkai
a7677c32fb 1 2024-07-02 16:14:12 +08:00
zhangkai
8b8f4111e2 1 2024-06-25 14:55:48 +08:00
zhangkai
68c9acc85b 高级编排分组 2024-06-24 15:28:24 +08:00
zhangkai
1c07d4b9df 1 2024-06-22 18:34:39 +08:00
zhangkai
fb39b66431 1 2024-06-13 15:11:41 +08:00
1025859868@qq.com
2734110636 知识库配置 2024-06-11 11:29:37 +08:00
1025859868@qq.com
8af9a4cb61 知识库以及文件重命名、移动、删除 2024-06-07 13:49:49 +08:00
1025859868@qq.com
2e6312e84f 由于hooks更改 useTable((param) => 改成useTable({}, (param) => 2024-06-05 16:29:36 +08:00
1025859868@qq.com
5637a34169 1 2024-06-05 15:51:37 +08:00
294 changed files with 16744 additions and 6128 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@@ -1 +1,2 @@
node_modules
build

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,25 +1 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="/favicon.ico" />
<script src="/node_modules/ace-builds/src-min-noconflict/ace.js" type="text/javascript"></script>
<title>NPCs</title>
<script type="module" crossorigin src="/assets/index-f344eac6.js"></script>
<link rel="modulepreload" crossorigin href="/assets/acebuilds-fbc0ccc6.js">
<link rel="modulepreload" crossorigin href="/assets/reactflow-10657c96.js">
<link rel="modulepreload" crossorigin href="/assets/reactdrop-61b79f7b.js">
<link rel="modulepreload" crossorigin href="/assets/pdfjs-36654f0a.js">
<link rel="stylesheet" href="/assets/index-e34caae6.css">
</head>
<body id='body' style="width: 100%; height:100%">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div style="width: 100vw; height:100vh" id='root'></div>
</body>
</html>
<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"/><script src="/node_modules/ace-builds/src-min-noconflict/ace.js"></script><title>NPCs</title><script type="module" crossorigin src="/assets/index-37491850.js"></script><link rel="modulepreload" crossorigin href="/assets/acebuilds-fbc0ccc6.js"><link rel="modulepreload" crossorigin href="/assets/reactflow-0cf1aa21.js"><link rel="modulepreload" crossorigin href="/assets/reactdrop-d9c7a74b.js"><link rel="modulepreload" crossorigin href="/assets/pdfjs-36654f0a.js"><link rel="stylesheet" href="/assets/index-59c5ed60.css"></head><body id="body" style="width:100%;height:100%"><noscript>You need to enable JavaScript to run this app.</noscript><div style="width:100vw;height:100vh" id="root"></div></body></html>

View File

@@ -21,25 +21,34 @@
"pleaseEnterCaptcha": "Please enter captcha"
},
"menu": {
"app": "App",
"skills": "Skills",
"app": "Chat",
"user": "User",
"github": "GitHub",
"bookopen": "Document",
"skills": "Build",
"knowledge": "Knowledge",
"evaluation": "Evaluation",
"models": "Models",
"system": "System",
"log": "Logs",
"themeSwitch": "Theme Switch",
"document": "Documentation",
"logout": "Logout",
"logoutDescription": "Log out",
"logoutContent": "Are you sure to log out",
"forBestExperience": "For the best experience, please access this website on a PC",
"onlineDocumentation": "Online Documentation"
"onlineDocumentation": "Online Documentation",
"changePwd": "Password"
},
"system": {
"userManagement": "User Management",
"roleManagement": "Role Management",
"userGroupsM": "UserGroup Management",
"systemConfiguration": "System Configuration",
"username": "Username",
"confirmDisable": "Confirm disabling this user?",
"roleSelect": "Select Role",
"roleSelect": "Select Roles",
"userGroupsSel": "Select UserGroups",
"roleList": "Role List",
"confirmText": "Are you sure you want to delete",
"roleName": "Role Name",
@@ -52,12 +61,49 @@
"roleNamePrompt": "Role name cannot exceed 50 characters",
"roleNameRequired": "Role name is required",
"roleNameExists": "Role name already exists",
"groupNameExists": "UserGroup name already exists",
"groupNamePrompt": "UserGroup name cannot exceed 30 characters",
"groupNameRequired": "UserGroup name is required",
"parameterConfig": "Parameter Configuration",
"language": "Language"
"language": "Language",
"assistantAuthorization": "Assistant Authorization",
"assistantName": "Assistant Name",
"userList": "User List",
"userGroupList": "UserGroup List",
"userGroup": "userGroup",
"role": "Role",
"searchUserGroups": "Search user groups",
"searchRoles": "Search roles",
"reset": "Reset",
"confirm": "Confirm",
"userGroupName": "Enter UserGroup Name",
"groupName": "UserGroup Name",
"admins": "Admin",
"flowControl": "Overall UserGroup Flow Control",
"AssistantFlowCtrl": "Assistant Flow Control",
"SkillFlowCtrl": "Skill Flow Control",
"createdBy": "CreatedBy",
"flowCtrlStrategy": "Flow control strategy",
"limit": "Limit",
"unlimited": "Unlimited",
"iconHover": "Simultaneously constrained by the overall traffic control strategy of the user group",
"maximum": "Up to",
"perMinute": "simultaneous online sessions",
"changeTime": "Modification Time",
"deleteGroup": "After deletion 【{{name}}】 will no longer exist, Do you want to delete it?",
"currentGroup": "Current UserGroup",
"defaultGroup": "DefaultGroup",
"resetPwd": "ResetPassword",
"selectGroup": "Please select a user group",
"selectRole": "Please select a role"
},
"skills": {
"manageTemplate": "Manage Skill Templates",
"createNew": "Create New",
"createNew": "Create New Skill",
"customSkills": "Custom Skills",
"chooseOnline": "Choose the online version",
"executionSteps": "Skills are visualized through process orchestration to clarify task execution steps",
"sceneTemplates": "We provide scene templates for your use and reference",
"manageProjects": "Manage your personal projects here, including skill deployment and editing",
"skillSearch": "Skill Search",
"confirmDeleteSkill": "Confirm the deletion of this skill?",
@@ -80,6 +126,7 @@
"skillDescTooLong": "Skill description is too long, should not exceed 200 characters",
"errorTitle": "Key Information Error",
"onlineFailure": "Online failure",
"onlineSuccessful": "Online Successful",
"custom": "Custom",
"skillTemplate": "Skill Template",
"skillTemplateChoose": "You can choose a template from here to start, or create an advanced template",
@@ -92,7 +139,23 @@
"createdBy": "Created by",
"offline": "Offline",
"online": "Online",
"guideWords": "Guide Words"
"guideWords": "Guide Words",
"currentVersion": "Current Version: ",
"importLocal": "Import local components",
"save": "Saved",
"import": "Import",
"export": "Export",
"code": "Code",
"simplify": "Simplify",
"saveVersion": "SaveVersion",
"deleteOrNot": "Do you want to delete",
"version": "version",
"saveSuccessful": "Save Successful",
"supportVersions": "Support splitting into multiple version branches for development and comparison between versions.",
"guideQuestions50": "Guide questions to a maximum of 50 characters",
"promptWords1000": "The maximum number of prompt words is 1000 characters",
"contactAdmin": "Please contact the administrator's online assistant",
"deleteSure": "Are you sure you want to delete this tool?"
},
"chat": {
"newChat": "New Chat",
@@ -238,7 +301,7 @@
"rootDirectory": "Root directory",
"createOneKnowledge": "Create a knowledge base",
"webSite": "Web site synchronization",
"knowledge": "Knowledge base type",
"knowledgeType": "Knowledge base type",
"knowledgeDescription": "Knowledge base can be built by importing files, webpage links, or manually entering them",
"webSiteDescription": "Web site synchronization allows you to directly use a webpage link to build a knowledge base",
"baseSet": "Basic Settings",
@@ -262,7 +325,60 @@
"foldDesc": "This is a folder",
"noFold": "We don't have a folder yet, create one quickly",
"selectFold": "Please select a folder",
"confirmDelete": "Confirm deletion"
"confirmDelete": "Confirm deletion",
"dataset": "Dataset",
"config": "Configure",
"queueLen": "Queue length",
"allKnowledge": "All datasets",
"file": "File",
"dataTotal": "Total data volume",
"lastUpdate": "Last update time",
"state": "State",
"syncData": "Synchronize data",
"manualDataset": "Manual dataset",
"textDataset": "Text dataset",
"tableDataset": "Table Dataset",
"createImport": "Create/Import",
"knowledge": "Knowledge base",
"web": "Website",
"knowledgeId": "Knowledge Base ID",
"knowledgeImg": "Knowledge Base Avatar",
"indexModel": "Index model",
"dataUp": "Single data upper limit",
"introduce": "Introduce",
"fileUploadResult": "Out of the {{total}} files uploaded, {{failed}} failed to upload.",
"modalTitle": "File Duplicate Prompt",
"modalMessage": "The following files already exist in the knowledge base. Continuing the upload will overwrite the original files and processing strategy. Do you want to proceed with overwrite?",
"keepOriginal": "Keep Original Files",
"override": "Override",
"toolName": "ToolName"
},
"evaluation": {
"id": "任务ID",
"filename": "测试文件名称",
"skillAssistant": "能力NPC",
"status": "状态",
"score": "评测分数",
"createDate": "创建日期",
"download": "下载",
"confirmDeleteEvaluation": "确认删除该评测任务?",
"createTitle": "新建任务",
"selectLabel": "选择要评测的能力或者NPC",
"selectPlaceholder": "请选择",
"dataLabel": "测试集数据:",
"fileExpandName": "支持扩展名:",
"downloadTemplate": "下载模板文件",
"promptLabel": "评测指令文本:",
"enterExecType": "请选择要评测的能力或NPC",
"enterUniqueId": "请选择能力或NPCID",
"enterVersion": "请选择能力的版本",
"enterFile": "请选择测试集数据",
"enterPrompt": "评测指令不能为空",
"fileSizeLimit": "文件大小限制在10M以内",
"evaluationCollection": "评测集合",
"tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
"create": "创建",
"cancel": "取消"
},
"code": {
"editPythonCodeDescription": "Edit your Python code here. This code snippet accepts module imports and a function definition. Make sure your function returns a string.",
@@ -402,6 +518,174 @@
"previousPage": "Previous Page",
"nextPage": "Next Page",
"formatError": "Format Error",
"port": "PORT",
"cancle": "Cancle",
"tip": "Prompt",
"deleteAssistant": "Are you sure to delete this assistant?",
"build": {
"create": "Create",
"assistant": "Assistant",
"skill": "Skills",
"tools": "Tools",
"save": "Save",
"online": "Online",
"retry": "Retry",
"use": "Use",
"useAll": "Use All",
"assistantConfiguration": "Assistant Configuration",
"createAssistant": "Create a New Assistant",
"assistantPortrait": "Assistant Portrait",
"portraitOptimization": "Assistant portrait optimization",
"automaticOptimization": "Automatic optimization",
"createDescription": "Create your assistant by describing roles and tasks",
"nextDescription": "Assistant can call multiple skills and tools",
"searchAssistant": "Search for the assistant you need",
"manageAssistant": "Manage your assistant on this page, including online and offline activities, editing, and more",
"establishAssistant": "Create Assistant",
"assistantName": "Assistant Name",
"giveAssistantName": "Give the assistant a name",
"whatWant": "What is the role of an assistant and what specific tasks do you want to complete?",
"example": "Example",
"exampleOne": "You are XX, with XX experience, skilled in XX, ...",
"exampleTwo": "Your task is XX, and you need to follow the following steps to execute it:",
"automaticallyConfigurations": "Automatically select relevant configurations for you",
"prompt": "Provide a detailed and specific description of the interaction between the assistant and the user, such as the assistant's identity, specific methods and steps for completing tasks, tone of voice when answering questions, and what issues should be noted",
"openingRemarks": "Opening remarks",
"guidingQuestions": "Guiding Questions",
"promptReplaced": "Prompt word replaced",
"guideReplaced": "Guide word replaced",
"openingReplaced": "Opening word replaced",
"toolsReplaced": "Tools replaced",
"skillsReplaced": "Skills replaced",
"allReplaced": "All replaced",
"basicConfiguration": "Basic configuration",
"modelConfiguration": "AI Model configuration",
"model": "Model",
"temperature": "Temperature",
"openingIntroduction": "Opening Introduction",
"openingStatement": "Opening Statement",
"assistantMessageFormat": "The assistant will send this message at the beginning of each conversation, supports Markdown format",
"maximumPromptLength": "The prompt can be up to 1000 characters",
"recommendQuestionsForUsers": "Provide recommended questions for users to guide them to ask. When there are more than 3, 3 will be randomly selected.",
"maxCharacters50": "Up to 50 characters",
"enterGuidingQuestions": "Please enter guiding questions",
"knowledge": "Knowledge",
"knowledgeBase": "Knowledge Base",
"autoCall": "Auto Call",
"callingMethod": "Calling Method",
"autoCallDescription": "Retrieve and recall the added knowledge base in every conversation.",
"onDemandCall": "On-Demand Call",
"onDemandCallDescription": "Prompt to call the RecallKnowledge (copyable) method in the assistant's profile (prompt words), and only retrieve the knowledge base when needed.",
"createNewKnowledge": "Create New Knowledge Base",
"refresh": "Refresh",
"abilities": "Abilities",
"skillDescription": "Implement complex and stable business process orchestration through a visual interface, such as project planning and report analysis.",
"selectKnowledgeBase": "Please select a knowledge base",
"searchBaseName": "Search for knowledge base name",
"debugPreview": "Debug Preview",
"addTool": "Add Tool",
"search": "Search",
"empty": "Nothing here",
"onlineSA": "Go online skills&assistants",
"params": "Parameters",
"added": "Added",
"add": "Add",
"configurationUpdated": "Configuration updated",
"addSkill": "Add Skill",
"createSkill": "Create Skill",
"nameRequired": "Name cannot be empty",
"nameMaxLength": "Name can have at most 50 characters",
"descMaxLength": "Description can have at most 1000 characters",
"editAssistant": "Edit Assistant",
"enterName": "Enter a name for the assistant",
"assistantDesc": "Assistant Description",
"enterDesc": "Introduce assistant functionality, visible in conversations and assistant pages",
"cancel": "Cancel",
"confirm": "Confirm",
"forBetter": "For better assistant effect, the description needs to be greater than 20 words",
"forExample": "For example, the identity of an assistant, specific methods and steps for completing tasks, tone of voice when answering questions, and what issues to pay attention to, etc"
},
"tools1": {
"addTool": "Add Tool",
"createCustomTool": " Custom Tool",
"builtinTools": "Built-in Tools",
"customTools": "Custom Tools",
"search": "Search",
"empty": "Empty",
"manageCustomTools": "Manage your custom tools on this page, including creating and editing them.",
"name": "Name",
"enterToolName": "Enter tool name",
"openapiSchema": "OpenAPI Schema",
"enterOpenAPISchema": "Enter your OpenAPI schema",
"importFromUrl": "Import from URL",
"examples": "Examples",
"weatherJson": "Weather (JSON)",
"petShopYaml": "Pet Shop (YAML)",
"authenticationType": "Authentication Type",
"authType": "Authentication Type",
"none": "None",
"apiKey": "API Key",
"basic": "Basic",
"bearer": "Bearer",
"availableTools": "Available Tools",
"description": "Description",
"method": "Method",
"path": "Path",
"delete": "Delete",
"cancel": "Cancel",
"save": "Save"
},
"test": {
"test": "Test",
"addTest": "Please add test cases first",
"uploadTest": "Upload test cases",
"explain": "The test case is the input of the current component and only supports txt files, with a maximum of 20 lines",
"testRun": "Test Run",
"testCase": "Test Case",
"run": "Run",
"downloadResults": "Download Run Results",
"testCases": "Enter test cases...",
"parametersAndValues": "Parameters and Values",
"parameter": "Parameter",
"value": "Value",
"result": "Test Result",
"outResultPlaceholder": "Click the button to output the result"
},
"resetPassword": {
"slogen": "Securely Reset Your Password",
"currentPassword": "Current Password",
"newPassword": "New Password",
"confirmNewPassword": "Confirm New Password",
"pleaseEnterCurrentPassword": "Please enter your current password.",
"pleaseEnterNewPassword": "Please enter your new password.",
"pleaseEnterConfirmPassword": "Please confirm your new password.",
"newPasswordTooShort": "New password must be at least 8 characters.",
"passwordMismatch": "The new passwords do not match.",
"resetButton": "Change Password",
"passwordResetSuccess": "Your password has been successfully reset.",
"adminResetSuccess": "Password has been successfully reset",
"resetFailed": "Pwd Reset Failed",
"notEmpty": "The new password cannot be empty"
},
"log": {
"auditManagement": "Audit Management",
"searchButton": "Search",
"resetButton": "Reset",
"auditId": "Audit ID",
"username": "Username",
"operationTime": "Operation Time",
"systemModule": "System Module",
"operationAction": "Operation Action",
"objectType": "Operation Object Type",
"operationObject": "Operation Object",
"ipAddress": "IP Address",
"remark": "Remark",
"selectUser": "Select User",
"selectUserGroup": "Select User Group",
"startDate": "Start Date",
"endDate": "End Date",
"actionBehavior": "Action Behavior"
},
"agents": {
"AgentInitializer":{
"display_name": "AgentInitializer",

View File

@@ -14,81 +14,144 @@
"pleaseEnterAccount": "请填写账号",
"pleaseEnterPassword": "请填写密码",
"accountTooShort": "账号过短",
"passwordTooShort": "请填写密码,至少8位",
"passwordError": "密码必须包含大小写字母、数字和字符",
"passwordTooShort": "请填写密码,至少7位",
"passwordError": "密码必须包含字母、数字!",
"passwordMismatch": "两次密码不一致",
"registrationSuccess": "注册成功,请输入密码进行登录"
},
"menu": {
"app": "聊天",
"user": "用户",
"bookopen": "帮助文档",
"github": "GitHub",
"app": "会 话",
"skills": "NPC",
"knowledge": "知识库",
"models": "模型",
"system": "账号",
"evaluation": "评 测",
"models": "模 型",
"system": "账 号",
"log": "审 计",
"themeSwitch": "主题切换",
"document": "文档",
"logout": "退出",
"logoutDescription": "退出登录",
"logoutContent": "确认退出登录吗",
"forBestExperience": "为了您的良好体验,请在 PC 端访问该网站",
"onlineDocumentation": "在线文档"
"onlineDocumentation": "在线文档",
"changePwd": "修改密码"
},
"system": {
"userManagement": "用户管理",
"userManagement": "用户管理",
"userGroupsM": "用户组管理",
"roleManagement": "角色管理",
"systemConfiguration": "系统配置",
"username": "用户名",
"confirmDisable": "确认禁用该用户?",
"roleSelect": "角色选择",
"userGroupsSel": "用户组选择",
"roleList": "角色列表",
"userGroupList": "用户组列表",
"confirmText": "是否删除",
"roleName": "角色名称",
"skillAuthorization": "NPC授权",
"skillAuthorization": "能力授权",
"knowledgeAuthorization": "知识库授权",
"skillName": "NPC名称",
"skillName": "能力名称",
"creator": "创建人",
"usePermission": "使用权限",
"managePermission": "管理权限",
"roleNamePrompt": "角色名称不能超过50字符",
"roleNameRequired": "角色名称不为空",
"roleNameRequired": "角色名称不为空",
"groupNameExists": "用户组名称不可重复",
"groupNamePrompt": "用户组名称不能超过30字符",
"groupNameRequired": "用户组名称不可为空",
"roleNameExists": "角色名称已存在",
"parameterConfig": "参数配置",
"language": "语言"
"language": "语言",
"assistantAuthorization": "NPC授权",
"assistantName": "NPC名称",
"userList": "用户列表",
"userGroup": "用户组",
"role": "角色",
"searchUserGroups": "搜索用户组",
"searchRoles": "搜索角色",
"reset": "重置",
"confirm": "确认",
"userGroupName": "输入用户组名称",
"groupName": "用户组名称",
"admins": "管理员",
"flowControl": "用户组整体流量控制",
"AssistantFlowCtrl": "NPC流量控制",
"SkillFlowCtrl": "能力流量控制",
"createdBy": "创建人",
"flowCtrlStrategy": "流量控制策略",
"limit": "有限制",
"unlimited": "无限制",
"iconHover": "同时受用户组整体流量控制策略约束",
"maximum": "最多",
"perMinute": "个同时在线会话",
"changeTime": "修改时间",
"deleteGroup": "删除后 【{{name}}】 将不再存在,是否删除?",
"currentGroup": "当前用户组",
"defaultGroup": "默认用户组",
"resetPwd": "重置密码",
"selectGroup": "请选择用户组",
"selectRole": "请选择角色"
},
"skills": {
"manageTemplate": "管理NPC模板",
"createNew": "新建",
"manageProjects": "这里管理您的个人项目对NPC上下线、编辑等等",
"skillSearch": "NPC搜索",
"confirmDeleteSkill": "确认删除该NPC",
"backToSkillList": "返回NPC列表",
"skillTemplateManagement": "NPC模板管理模板对所有用户可见支持拖拽排序、删除操作",
"manageTemplate": "管理能力模板",
"createNew": "新建能力",
"customSkills": "自定义能力",
"chooseOnline": "选择上线版本",
"executionSteps": "能力通过可视化的流程编排,明确任务执行步骤",
"sceneTemplates": "我们提供场景模板供您使用和参考",
"manageProjects": "在此页面管理您的能力,对能力上下线、编辑等等",
"skillSearch": "搜索您需要的能力",
"confirmDeleteSkill": "确认删除该能力?",
"backToSkillList": "返回能力列表",
"skillTemplateManagement": "能力模板管理,模板对所有用户可见,支持拖拽排序、删除操作",
"templateName": "模板名称",
"templateDescription": "模板描述",
"confirmText": "是否确认删除该NPC模板?",
"skillSettings": "NPC设置",
"confirmText": "是否确认删除该能力模板?",
"skillSettings": "能力设置",
"basicInfo": "基础信息",
"skillName": "NPC名称",
"skillName": "能力名称",
"description": "描述",
"parameterInfo": "参数信息",
"advancedConfiguration": "高级配置",
"nextStep": "下一步,高级配置",
"skillNameRequired": "请填写NPC名称",
"skillNameTooLong": "NPC名称过长不要超过30字",
"skillNameRequired": "请填写能力名称",
"skillNameTooLong": "能力名称过长不要超过30字",
"skillNameExists": "该名称已存在",
"skillDescRequired": "请填写NPC描述",
"skillDescTooLong": "NPC描述过长不要超过200字",
"skillDescRequired": "请填写能力描述",
"skillDescTooLong": "能力描述过长不要超过200字",
"errorTitle": "关键信息有误",
"onlineFailure": "上线失败",
"onlineSuccessful": "上线成功",
"custom": "自定义",
"skillTemplate": "NPC模板",
"skillTemplate": "能力模板",
"skillTemplateChoose": "您可以从这里挑选一个模板开始,或者自定义高级模板",
"createTemplate": "创建模板",
"createSuccessTitle": "NPC创建成功",
"createSuccessTitle": "能力创建成功",
"createFailureTitle": "创建失败",
"createdBy": "创建用户",
"offline": "下线",
"online": "上线",
"guideWords": "引导词"
"guideWords": "引导词",
"currentVersion": "当前版本:",
"importLocal": "导入本地组件",
"save": "保存",
"import": "导入",
"export": "导出",
"code": "代码",
"simplify": "简化",
"saveVersion": "另存为新版本",
"deleteOrNot": "是否删除",
"version": "版本",
"saveSuccessful": "保存成功",
"supportVersions": "支持分成多个版本分支,分别进行开发以及版本间的比较。",
"guideQuestions50": "引导问题最多50个字符",
"promptWords1000": "提示词最多为1000个字符",
"contactAdmin": "请联系管理员上线NPC",
"deleteSure": "确认删除该工具?"
},
"chat": {
"newChat": "新建会话",
@@ -96,8 +159,8 @@
"inputPlaceholder": "请输入问题",
"uploadFileTooltip": "上传文件",
"sendTooltip": "发送",
"skillTempsTitle": "NPC选择",
"skillTempsDesc": "选择一个您想使用的线上NPC",
"skillTempsTitle": "能力选择",
"skillTempsDesc": "选择一个您想使用的线上能力",
"forms": "表单",
"networkError": "网络连接出现错误,请尝试以下方法",
"networkErrorList1": "操作不要过快",
@@ -123,7 +186,21 @@
"fileStorageFailure": " 文件地址失效!",
"confirmDeleteChat": "确认删除该会话?",
"roundOver": "本轮结束",
"chatDialogTip": "设置提示模板中定义的输入变量。与代理和链互动"
"chatDialogTip": "设置提示模板中定义的输入变量。与代理和链互动",
"feedback": "反馈",
"feedbackRequired": "反馈信息不能为空",
"dialogueSelection": "对话选择",
"chooseSkillOrAssistant": "选择一个您想使用的线上能力或NPC",
"search": "搜索",
"recommendationQuestions": "推荐问题",
"historicalMessages": "以上为历史消息",
"clickDownload": "点击下载",
"searchAssistantOrSkill": "搜索NPC或者能力",
"operationTips": "操作提示:在左侧选择要展示的标签,在右侧拖拽进行排序",
"selected": "已选",
"pleaseSelectAnApp": "请选择一个应用",
"allLabels": "全部标签",
"searchLabels": "搜索标签"
},
"model": {
"modelConfiguration": "模型配置",
@@ -137,7 +214,7 @@
"warningTooltip": "处理异常",
"inProgressOnlineStatus": "上线中",
"inProgressOfflineStatus": "下线中",
"confirmModelOffline": "是否确认下线该模型,下线后使用该模型服务的NPC将无法正常工作",
"confirmModelOffline": "是否确认下线该模型,下线后使用该模型服务的能力将无法正常工作",
"confirmOfflineButtonText": "下线",
"modelManagement": "模型管理",
"modelFineTune": "模型Finetune",
@@ -172,7 +249,7 @@
"searchComponent": "查找组件",
"knowledgeBaseSelection": "知识库选择",
"searchKnowledgeBase": "搜索知识库",
"minimumParamSetDescription": "您可以在此设置NPC所需的最小参数集",
"minimumParamSetDescription": "您可以在此设置能力所需的最小参数集",
"paramList": "参数列表",
"saveConfig": "保存配置",
"componentLabel": "组件",
@@ -181,9 +258,9 @@
"parameterLabel": "参数",
"notification": "消息",
"noNewNotifications": "没有新的通知",
"skillName": "NPC名",
"skillName": "能力名",
"nameTooLong": "名称过长",
"skillDescription": "NPC描述",
"skillDescription": "能力描述",
"enterVarName": "请输入变量名",
"varNameExists": "变量名重复",
"text": "文本",
@@ -230,7 +307,7 @@
"rootDirectory": "根目录",
"createOneKnowledge": "创建一个知识库",
"webSite": "Web站点同步",
"knowledge": "知识库类型",
"knowledgeType": "知识库类型",
"knowledgeDescription": "可通过导入文件、网页链接或手动录入形式构建知识库",
"webSiteDescription": "Web站点同步允许你直接使用一个网页链接构建知识库",
"baseSet": "基本设置",
@@ -254,7 +331,61 @@
"foldDesc": "这是一个文件夹",
"noFold": "还没有文件夹,快快创建一个吧~",
"selectFold": "请选择文件夹",
"confirmDelete": "确定删除"
"confirmDelete": "确定删除",
"dataset": "数据集",
"config": "配置",
"queueLen": "排队长度",
"allKnowledge": "全部数据集",
"file": "文件",
"dataTotal": "数据总量",
"lastUpdate": "最后更新时间",
"state": "状态",
"syncData": "同步数据",
"manualDataset": "手动数据集",
"textDataset": "文本数据集",
"tableDataset": "表格数据集",
"createImport": "新建/导入",
"knowledge": "知识库",
"web": "网站",
"knowledgeId": "知识库ID",
"knowledgeImg": "知识库头像",
"indexModel": "索引模型",
"dataUp": "单条数据上限",
"introduce": "介绍",
"fileUploadResult": "共上传 {{total}} 份文件,有 {{failed}} 份文件上传失败",
"modalTitle": "文件重复提示",
"modalMessage": "以下文件在知识库中已存在,继续上传将会覆盖原有文件以及处理策略,是否覆盖?",
"keepOriginal": "不覆盖,保留原文件",
"override": "覆盖",
"toolName": "工具名称"
},
"evaluation": {
"id": "任务ID",
"filename": "测试文件名称",
"skillAssistant": "能力NPC",
"status": "状态",
"score": "评测分数",
"createDate": "创建日期",
"download": "下载",
"confirmDeleteEvaluation": "确认删除该评测任务?",
"createTitle": "新建任务",
"selectLabel": "选择要评测的能力或者NPC",
"selectPlaceholder": "请选择",
"selectInputPlaceholder": "请根据名称进行搜索",
"dataLabel": "测试集数据:",
"fileExpandName": "支持扩展名:",
"downloadTemplate": "下载模板文件",
"promptLabel": "评测指令文本:",
"enterExecType": "请选择要评测的能力或NPC",
"enterUniqueId": "请选择能力或NPCID",
"enterVersion": "请选择能力的版本",
"enterFile": "请选择测试集数据",
"enterPrompt": "评测指令不能为空",
"fileSizeLimit": "文件大小限制在10M以内",
"evaluationCollection": "评测集合",
"tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
"create": "创建",
"cancel": "取消"
},
"code": {
"editPythonCodeDescription": "编辑你的 Python 代码此代码片段接受模块导入和一个函数定义。确保您的函数返回一个字符串。",
@@ -266,11 +397,11 @@
"codeError": "这段代码有问题,请检查以下",
"checkAndSave": "检查 & 保存",
"export": "导出",
"exportToJSON": "导出NPC到json文件中",
"exportToJSON": "导出能力到json文件中",
"keyInformationMissing": "您有一些关键信息没有填: ",
"skillNameMissing": "请填写NPC名称",
"skillNameMissing": "请填写能力名称",
"useOwnAPIKeys": "使用自己的API keys",
"exportSkill": "导出NPC",
"exportSkill": "导出能力",
"uploadFile": "上传文件",
"clickOrDragHere": "点击或将文件拖拽到这里上传",
"dropFileHere": "将文件拖拽到这里上传",
@@ -287,7 +418,7 @@
"file": "文件",
"sizeExceedsLimit": "超过50M",
"editDictionary": "编辑词典",
"exportCodeDialogTip": "生成代码,将流程集成到外部应用程序中 (打开此页面前请先buildNPC)。",
"exportCodeDialogTip": "生成代码,将流程集成到外部应用程序中 (打开此页面前请先build能力)。",
"chunkOverlap": "切分文本重叠长度"
},
"report": {
@@ -304,8 +435,8 @@
"varLength": "长度不能超过"
},
"status": {
"1004": "该NPC已被删除",
"1008": "当前NPC未上线,无法直接对话",
"1004": "该能力已被删除",
"1008": "当前能力未上线,无法直接对话",
"1005": ""
},
"all": "全部",
@@ -394,6 +525,185 @@
"previousPage": "上一页",
"nextPage": "下一页",
"formatError": "格式错误",
"port": "服务端口",
"cancle": "取消",
"tip": "提示",
"deleteAssistant": "确认删除该NPC",
"chatTipsTitle": "使用提示",
"updateSuccess": "修改成功",
"createSuccess": "创建成功",
"confirm": "确认",
"required": "不可为空",
"build": {
"create": "创建",
"assistant": "NPC",
"skill": "能力",
"tools": "工具",
"save": "保存",
"online": "上线",
"retry": "重试",
"use": "使用",
"useAll": "全部使用",
"assistantConfiguration": "NPC配置",
"createAssistant": "新建NPC",
"assistantPortrait": "NPC画像",
"portraitOptimization": "NPC画像优化",
"automaticOptimization": "自动优化",
"createDescription": "通过描述角色和任务来创建你的NPC",
"nextDescription": "NPC可以调用多个能力和工具",
"searchAssistant": "搜索您需要的NPC",
"manageAssistant": "在此页面管理您的NPC对NPC上下线、编辑等等",
"establishAssistant": "创建NPC",
"assistantName": "NPC名称",
"giveAssistantName": "给NPC取一个名字",
"whatWant": "你希望NPC的角色是什么具体完成什么任务",
"example": "示例",
"exampleOne": "你是 XX具有 XX 经验,擅长 XX…",
"exampleTwo": "你的任务是 XX ,需要按照以下步骤执行:",
"automaticallyConfigurations": "自动为您选择相关配置",
"prompt": "详细、具体地描述NPC与用户的交互方式例如NPC的身份、完成任务的具体方法和步骤、回答问题时的语气以及应该注意什么问题等",
"openingRemarks": "开场白",
"guidingQuestions": "引导问题",
"promptReplaced": "提示词已替换",
"guideReplaced": "引导词已替换",
"openingReplaced": "开场白已替换",
"toolsReplaced": "工具已替换",
"skillsReplaced": "能力已替换",
"allReplaced": "已全部替换",
"basicConfiguration": "基础配置",
"modelConfiguration": "AI模型配置",
"model": "模型",
"temperature": "温度",
"openingIntroduction": "开场引导",
"openingStatement": "开场白",
"assistantMessageFormat": "NPC将在每次对话开始时发送此信息支持 Markdown 格式",
"maximumPromptLength": "提示词最多为1000个字符",
"recommendQuestionsForUsers": "为用户提供推荐问题引导用户提问超过3个时将随机选取3个",
"maxCharacters50": "最多50个字符",
"enterGuidingQuestions": "请输入引导问题",
"knowledge": "知识",
"knowledgeBase": "知识库",
"autoCall": "自动调用",
"callingMethod": "调用方式",
"autoCallDescription": "每轮对话都会对添加的知识库进行检索召回。",
"onDemandCall": "按需调用",
"onDemandCallDescription": "在NPC画像提示词中提示调用 RecallKnowledge可复制方法在有需要时才对知识库进行检索。",
"createNewKnowledge": "新建知识库",
"refresh": "刷新",
"abilities": "能力",
"skillDescription": "通过可视化界面实现复杂和稳定的业务流程编排,例如项目计划和报告分析",
"selectKnowledgeBase": "请选择知识库",
"searchBaseName": "搜索知识库名称",
"debugPreview": "调试预览",
"addTool": "添加工具",
"search": "搜索",
"empty": "空空如也",
"onlineSA": "上线能力&NPC",
"params": "参数",
"added": "已添加",
"add": "添加",
"configurationUpdated": "配置已更新",
"addSkill": "添加能力",
"createSkill": "创建能力",
"nameRequired": "名称不可为空",
"nameMaxLength": "名称最多50个字符",
"descMaxLength": "最多1000个字符",
"editAssistant": "编辑NPC",
"enterName": "给NPC取一个名字",
"assistantDesc": "NPC描述",
"enterDesc": "介绍NPC功能描述在会话和NPC页面可见",
"cancel": "取消",
"confirm": "确认",
"forBetter": "为了更好的NPC效果描述需要大于20 个字",
"forExample": "例如NPC的身份、完成任务的具体方法和步骤、回答问题时的语气以及应该注意什么问题等"
},
"tools1": {
"addTool": "添加工具",
"createCustomTool": "自定义工具",
"builtinTools": "内置工具",
"customTools": "自定义工具",
"search": "搜索",
"empty": "空空如也",
"manageCustomTools": "在此页面管理您的自定义工具,对自定义工具创建、编辑等等",
"name": "名称",
"enterToolName": "输入工具名称",
"openapiSchema": "OpenAPI Schema",
"enterOpenAPISchema": "输入您的 OpenAPI schema",
"importFromUrl": "从 URL 导入",
"examples": "示例",
"weatherJson": "天气(JSON)",
"petShopYaml": "宠物商店(YAML)",
"authenticationType": "鉴权方式",
"authType": "认证类型",
"none": "无",
"apiKey": "API Key",
"basic": "Basic",
"bearer": "Bearer",
"availableTools": "可用工具",
"description": "描述",
"method": "方法",
"path": "路径",
"delete": "删除",
"cancel": "取消",
"save": "保存"
},
"test": {
"test": "测试",
"addTest": "请先填写测试用例",
"uploadTest": "上传测试用例",
"explain": "为测试用例是当前组件的输入,只支持 txt 文件,最多 20 行",
"testRun": "测试运行",
"testCase": "测试用例",
"run": "运行",
"downloadResults": "下载运行结果",
"testCases": "输入测试用例...",
"parametersAndValues": "参数和值",
"parameter": "参数",
"value": "值",
"result": "测试结果",
"outResultPlaceholder": "点击按钮,输出结果"
},
"resetPassword": {
"slogen": "安全地重置您的密码",
"currentPassword": "当前密码",
"newPassword": "新密码",
"confirmNewPassword": "确认新密码",
"pleaseEnterCurrentPassword": "请输入当前密码。",
"pleaseEnterNewPassword": "请输入新密码。",
"pleaseEnterConfirmPassword": "请确认新密码。",
"newPasswordTooShort": "新密码必须至少 7 个字符。",
"passwordMismatch": "新密码不匹配。",
"resetButton": "修改密码",
"passwordResetSuccess": "您的密码已成功修改",
"adminResetSuccess": "密码已重置",
"resetFailed": "密码重置失败",
"notEmpty": "新密码不能为空"
},
"log": {
"auditManagement": "审计管理",
"searchButton": "查询",
"resetButton": "重置",
"auditId": "审计ID",
"username": "用户名",
"operationTime": "操作时间",
"systemModule": "系统模块",
"operationAction": "操作行为",
"objectType": "操作对象类型",
"operationObject": "操作对象",
"ipAddress": "IP地址",
"remark": "备注",
"selectUser": "选择用户",
"selectUserGroup": "选择用户组",
"startDate": "开始日期",
"endDate": "结束日期",
"actionBehavior": "操作行为"
},
"tag": {
"labelMaxLength": "标签名不能超过10个字符",
"confirmDeleteLabel": "标签【{{label}}】正在使用中,确认删除?",
"createNewLabel": "创建“新标签”",
"addLabel": "添加标签"
},
"agents": {
"AgentInitializer": {
"display_name": "AgentInitializer",
@@ -613,8 +923,8 @@
},
"autogen_roles": {
"AutoGenAssistant": {
"display_name": "自动生成助手",
"description": "助手代理设计用于使用大语言模型LLM解决任务。",
"display_name": "自动生成NPC",
"description": "NPC代理设计用于使用大语言模型LLM解决任务。",
"description_url": "https://lxm3ki8hexz.feishu.cn/wiki/Yz5GwMpO7ioj8nkRSZlcsriXnJ3",
"template": {
"model_name": {
@@ -640,7 +950,7 @@
}
},
"output_types":[
"自动生成助手"
"自动生成NPC"
]
},
"AutoGenCoder": {
@@ -2237,7 +2547,7 @@
},
"NPCsChatLLM": {
"display_name": "NPCsChatLLM",
"description": "`OpenAI`聊天大型语言模型API。",
"description": "无缝切换支持的大语言模型",
"description_url": "",
"template": {
"api_base": {

716
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
"@lobehub/ui": "^1.137.4",
"@mui/material": "^5.14.7",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",
@@ -22,9 +23,11 @@
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.6",
@@ -53,7 +56,9 @@
"react": "^18.2.0",
"react-ace": "^10.1.0",
"react-beautiful-dnd": "^13.1.1",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-error-boundary": "^4.0.11",
@@ -79,7 +84,9 @@
"tailwindcss-animate": "^1.0.7",
"uuid": "^9.0.0",
"vite-plugin-svgr": "^3.2.0",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"xlsx": "^0.18.5",
"zustand": "^4.5.2"
},
"scripts": {
"dev:docker": "vite --host 0.0.0.0",
@@ -131,6 +138,7 @@
"tailwindcss": "^3.3.3",
"typescript": "^5.2.2",
"vite": "^4.5.2",
"vite-plugin-html": "^3.2.2",
"vite-plugin-static-copy": "^0.17.0"
},
"engines": {

View File

@@ -21,25 +21,34 @@
"pleaseEnterCaptcha": "Please enter captcha"
},
"menu": {
"app": "App",
"skills": "Skills",
"app": "Chat",
"user": "User",
"github": "GitHub",
"bookopen": "Document",
"skills": "Build",
"knowledge": "Knowledge",
"evaluation": "Evaluation",
"models": "Models",
"system": "System",
"log": "Logs",
"themeSwitch": "Theme Switch",
"document": "Documentation",
"logout": "Logout",
"logoutDescription": "Log out",
"logoutContent": "Are you sure to log out",
"forBestExperience": "For the best experience, please access this website on a PC",
"onlineDocumentation": "Online Documentation"
"onlineDocumentation": "Online Documentation",
"changePwd": "Password"
},
"system": {
"userManagement": "User Management",
"roleManagement": "Role Management",
"userGroupsM": "UserGroup Management",
"systemConfiguration": "System Configuration",
"username": "Username",
"confirmDisable": "Confirm disabling this user?",
"roleSelect": "Select Role",
"roleSelect": "Select Roles",
"userGroupsSel": "Select UserGroups",
"roleList": "Role List",
"confirmText": "Are you sure you want to delete",
"roleName": "Role Name",
@@ -52,12 +61,49 @@
"roleNamePrompt": "Role name cannot exceed 50 characters",
"roleNameRequired": "Role name is required",
"roleNameExists": "Role name already exists",
"groupNameExists": "UserGroup name already exists",
"groupNamePrompt": "UserGroup name cannot exceed 30 characters",
"groupNameRequired": "UserGroup name is required",
"parameterConfig": "Parameter Configuration",
"language": "Language"
"language": "Language",
"assistantAuthorization": "Assistant Authorization",
"assistantName": "Assistant Name",
"userList": "User List",
"userGroupList": "UserGroup List",
"userGroup": "userGroup",
"role": "Role",
"searchUserGroups": "Search user groups",
"searchRoles": "Search roles",
"reset": "Reset",
"confirm": "Confirm",
"userGroupName": "Enter UserGroup Name",
"groupName": "UserGroup Name",
"admins": "Admin",
"flowControl": "Overall UserGroup Flow Control",
"AssistantFlowCtrl": "Assistant Flow Control",
"SkillFlowCtrl": "Skill Flow Control",
"createdBy": "CreatedBy",
"flowCtrlStrategy": "Flow control strategy",
"limit": "Limit",
"unlimited": "Unlimited",
"iconHover": "Simultaneously constrained by the overall traffic control strategy of the user group",
"maximum": "Up to",
"perMinute": "simultaneous online sessions",
"changeTime": "Modification Time",
"deleteGroup": "After deletion 【{{name}}】 will no longer exist, Do you want to delete it?",
"currentGroup": "Current UserGroup",
"defaultGroup": "DefaultGroup",
"resetPwd": "ResetPassword",
"selectGroup": "Please select a user group",
"selectRole": "Please select a role"
},
"skills": {
"manageTemplate": "Manage Skill Templates",
"createNew": "Create New",
"createNew": "Create New Skill",
"customSkills": "Custom Skills",
"chooseOnline": "Choose the online version",
"executionSteps": "Skills are visualized through process orchestration to clarify task execution steps",
"sceneTemplates": "We provide scene templates for your use and reference",
"manageProjects": "Manage your personal projects here, including skill deployment and editing",
"skillSearch": "Skill Search",
"confirmDeleteSkill": "Confirm the deletion of this skill?",
@@ -80,6 +126,7 @@
"skillDescTooLong": "Skill description is too long, should not exceed 200 characters",
"errorTitle": "Key Information Error",
"onlineFailure": "Online failure",
"onlineSuccessful": "Online Successful",
"custom": "Custom",
"skillTemplate": "Skill Template",
"skillTemplateChoose": "You can choose a template from here to start, or create an advanced template",
@@ -92,7 +139,23 @@
"createdBy": "Created by",
"offline": "Offline",
"online": "Online",
"guideWords": "Guide Words"
"guideWords": "Guide Words",
"currentVersion": "Current Version: ",
"importLocal": "Import local components",
"save": "Saved",
"import": "Import",
"export": "Export",
"code": "Code",
"simplify": "Simplify",
"saveVersion": "SaveVersion",
"deleteOrNot": "Do you want to delete",
"version": "version",
"saveSuccessful": "Save Successful",
"supportVersions": "Support splitting into multiple version branches for development and comparison between versions.",
"guideQuestions50": "Guide questions to a maximum of 50 characters",
"promptWords1000": "The maximum number of prompt words is 1000 characters",
"contactAdmin": "Please contact the administrator's online assistant",
"deleteSure": "Are you sure you want to delete this tool?"
},
"chat": {
"newChat": "New Chat",
@@ -276,7 +339,46 @@
"textDataset": "Text dataset",
"tableDataset": "Table Dataset",
"createImport": "Create/Import",
"knowledge": "Knowledge base"
"knowledge": "Knowledge base",
"web": "Website",
"knowledgeId": "Knowledge Base ID",
"knowledgeImg": "Knowledge Base Avatar",
"indexModel": "Index model",
"dataUp": "Single data upper limit",
"introduce": "Introduce",
"fileUploadResult": "Out of the {{total}} files uploaded, {{failed}} failed to upload.",
"modalTitle": "File Duplicate Prompt",
"modalMessage": "The following files already exist in the knowledge base. Continuing the upload will overwrite the original files and processing strategy. Do you want to proceed with overwrite?",
"keepOriginal": "Keep Original Files",
"override": "Override",
"toolName": "ToolName"
},
"evaluation": {
"id": "任务ID",
"filename": "测试文件名称",
"skillAssistant": "能力NPC",
"status": "状态",
"score": "评测分数",
"createDate": "创建日期",
"download": "下载",
"confirmDeleteEvaluation": "确认删除该评测任务?",
"createTitle": "新建任务",
"selectLabel": "选择要评测的能力或者NPC",
"selectPlaceholder": "请选择",
"dataLabel": "测试集数据:",
"fileExpandName": "支持扩展名:",
"downloadTemplate": "下载模板文件",
"promptLabel": "评测指令文本:",
"enterExecType": "请选择要评测的能力或NPC",
"enterUniqueId": "请选择能力或NPCID",
"enterVersion": "请选择能力的版本",
"enterFile": "请选择测试集数据",
"enterPrompt": "评测指令不能为空",
"fileSizeLimit": "文件大小限制在10M以内",
"evaluationCollection": "评测集合",
"tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
"create": "创建",
"cancel": "取消"
},
"code": {
"editPythonCodeDescription": "Edit your Python code here. This code snippet accepts module imports and a function definition. Make sure your function returns a string.",
@@ -416,6 +518,174 @@
"previousPage": "Previous Page",
"nextPage": "Next Page",
"formatError": "Format Error",
"port": "PORT",
"cancle": "Cancle",
"tip": "Prompt",
"deleteAssistant": "Are you sure to delete this assistant?",
"build": {
"create": "Create",
"assistant": "Assistant",
"skill": "Skills",
"tools": "Tools",
"save": "Save",
"online": "Online",
"retry": "Retry",
"use": "Use",
"useAll": "Use All",
"assistantConfiguration": "Assistant Configuration",
"createAssistant": "Create a New Assistant",
"assistantPortrait": "Assistant Portrait",
"portraitOptimization": "Assistant portrait optimization",
"automaticOptimization": "Automatic optimization",
"createDescription": "Create your assistant by describing roles and tasks",
"nextDescription": "Assistant can call multiple skills and tools",
"searchAssistant": "Search for the assistant you need",
"manageAssistant": "Manage your assistant on this page, including online and offline activities, editing, and more",
"establishAssistant": "Create Assistant",
"assistantName": "Assistant Name",
"giveAssistantName": "Give the assistant a name",
"whatWant": "What is the role of an assistant and what specific tasks do you want to complete?",
"example": "Example",
"exampleOne": "You are XX, with XX experience, skilled in XX, ...",
"exampleTwo": "Your task is XX, and you need to follow the following steps to execute it:",
"automaticallyConfigurations": "Automatically select relevant configurations for you",
"prompt": "Provide a detailed and specific description of the interaction between the assistant and the user, such as the assistant's identity, specific methods and steps for completing tasks, tone of voice when answering questions, and what issues should be noted",
"openingRemarks": "Opening remarks",
"guidingQuestions": "Guiding Questions",
"promptReplaced": "Prompt word replaced",
"guideReplaced": "Guide word replaced",
"openingReplaced": "Opening word replaced",
"toolsReplaced": "Tools replaced",
"skillsReplaced": "Skills replaced",
"allReplaced": "All replaced",
"basicConfiguration": "Basic configuration",
"modelConfiguration": "AI Model configuration",
"model": "Model",
"temperature": "Temperature",
"openingIntroduction": "Opening Introduction",
"openingStatement": "Opening Statement",
"assistantMessageFormat": "The assistant will send this message at the beginning of each conversation, supports Markdown format",
"maximumPromptLength": "The prompt can be up to 1000 characters",
"recommendQuestionsForUsers": "Provide recommended questions for users to guide them to ask. When there are more than 3, 3 will be randomly selected.",
"maxCharacters50": "Up to 50 characters",
"enterGuidingQuestions": "Please enter guiding questions",
"knowledge": "Knowledge",
"knowledgeBase": "Knowledge Base",
"autoCall": "Auto Call",
"callingMethod": "Calling Method",
"autoCallDescription": "Retrieve and recall the added knowledge base in every conversation.",
"onDemandCall": "On-Demand Call",
"onDemandCallDescription": "Prompt to call the RecallKnowledge (copyable) method in the assistant's profile (prompt words), and only retrieve the knowledge base when needed.",
"createNewKnowledge": "Create New Knowledge Base",
"refresh": "Refresh",
"abilities": "Abilities",
"skillDescription": "Implement complex and stable business process orchestration through a visual interface, such as project planning and report analysis.",
"selectKnowledgeBase": "Please select a knowledge base",
"searchBaseName": "Search for knowledge base name",
"debugPreview": "Debug Preview",
"addTool": "Add Tool",
"search": "Search",
"empty": "Nothing here",
"onlineSA": "Go online skills&assistants",
"params": "Parameters",
"added": "Added",
"add": "Add",
"configurationUpdated": "Configuration updated",
"addSkill": "Add Skill",
"createSkill": "Create Skill",
"nameRequired": "Name cannot be empty",
"nameMaxLength": "Name can have at most 50 characters",
"descMaxLength": "Description can have at most 1000 characters",
"editAssistant": "Edit Assistant",
"enterName": "Enter a name for the assistant",
"assistantDesc": "Assistant Description",
"enterDesc": "Introduce assistant functionality, visible in conversations and assistant pages",
"cancel": "Cancel",
"confirm": "Confirm",
"forBetter": "For better assistant effect, the description needs to be greater than 20 words",
"forExample": "For example, the identity of an assistant, specific methods and steps for completing tasks, tone of voice when answering questions, and what issues to pay attention to, etc"
},
"tools1": {
"addTool": "Add Tool",
"createCustomTool": " Custom Tool",
"builtinTools": "Built-in Tools",
"customTools": "Custom Tools",
"search": "Search",
"empty": "Empty",
"manageCustomTools": "Manage your custom tools on this page, including creating and editing them.",
"name": "Name",
"enterToolName": "Enter tool name",
"openapiSchema": "OpenAPI Schema",
"enterOpenAPISchema": "Enter your OpenAPI schema",
"importFromUrl": "Import from URL",
"examples": "Examples",
"weatherJson": "Weather (JSON)",
"petShopYaml": "Pet Shop (YAML)",
"authenticationType": "Authentication Type",
"authType": "Authentication Type",
"none": "None",
"apiKey": "API Key",
"basic": "Basic",
"bearer": "Bearer",
"availableTools": "Available Tools",
"description": "Description",
"method": "Method",
"path": "Path",
"delete": "Delete",
"cancel": "Cancel",
"save": "Save"
},
"test": {
"test": "Test",
"addTest": "Please add test cases first",
"uploadTest": "Upload test cases",
"explain": "The test case is the input of the current component and only supports txt files, with a maximum of 20 lines",
"testRun": "Test Run",
"testCase": "Test Case",
"run": "Run",
"downloadResults": "Download Run Results",
"testCases": "Enter test cases...",
"parametersAndValues": "Parameters and Values",
"parameter": "Parameter",
"value": "Value",
"result": "Test Result",
"outResultPlaceholder": "Click the button to output the result"
},
"resetPassword": {
"slogen": "Securely Reset Your Password",
"currentPassword": "Current Password",
"newPassword": "New Password",
"confirmNewPassword": "Confirm New Password",
"pleaseEnterCurrentPassword": "Please enter your current password.",
"pleaseEnterNewPassword": "Please enter your new password.",
"pleaseEnterConfirmPassword": "Please confirm your new password.",
"newPasswordTooShort": "New password must be at least 8 characters.",
"passwordMismatch": "The new passwords do not match.",
"resetButton": "Change Password",
"passwordResetSuccess": "Your password has been successfully reset.",
"adminResetSuccess": "Password has been successfully reset",
"resetFailed": "Pwd Reset Failed",
"notEmpty": "The new password cannot be empty"
},
"log": {
"auditManagement": "Audit Management",
"searchButton": "Search",
"resetButton": "Reset",
"auditId": "Audit ID",
"username": "Username",
"operationTime": "Operation Time",
"systemModule": "System Module",
"operationAction": "Operation Action",
"objectType": "Operation Object Type",
"operationObject": "Operation Object",
"ipAddress": "IP Address",
"remark": "Remark",
"selectUser": "Select User",
"selectUserGroup": "Select User Group",
"startDate": "Start Date",
"endDate": "End Date",
"actionBehavior": "Action Behavior"
},
"agents": {
"AgentInitializer":{
"display_name": "AgentInitializer",

View File

@@ -14,32 +14,42 @@
"pleaseEnterAccount": "请填写账号",
"pleaseEnterPassword": "请填写密码",
"accountTooShort": "账号过短",
"passwordTooShort": "请填写密码,至少8位",
"passwordError": "密码必须包含大小写字母、数字和字符",
"passwordTooShort": "请填写密码,至少7位",
"passwordError": "密码必须包含字母、数字!",
"passwordMismatch": "两次密码不一致",
"registrationSuccess": "注册成功,请输入密码进行登录"
},
"menu": {
"app": "聊天",
"user": "用户",
"bookopen": "帮助文档",
"github": "GitHub",
"app": "会 话",
"skills": "NPC",
"knowledge": "知识库",
"models": "模型",
"system": "账号",
"evaluation": "评 测",
"models": "模 型",
"system": "账 号",
"log": "审 计",
"themeSwitch": "主题切换",
"document": "文档",
"logout": "退出",
"logoutDescription": "退出登录",
"logoutContent": "确认退出登录吗",
"forBestExperience": "为了您的良好体验,请在 PC 端访问该网站",
"onlineDocumentation": "在线文档"
"onlineDocumentation": "在线文档",
"changePwd": "修改密码"
},
"system": {
"userManagement": "用户管理",
"userManagement": "用户管理",
"userGroupsM": "用户组管理",
"roleManagement": "角色管理",
"systemConfiguration": "系统配置",
"username": "用户名",
"confirmDisable": "确认禁用该用户?",
"roleSelect": "角色选择",
"userGroupsSel": "用户组选择",
"roleList": "角色列表",
"userGroupList": "用户组列表",
"confirmText": "是否删除",
"roleName": "角色名称",
"skillAuthorization": "能力授权",
@@ -49,16 +59,52 @@
"usePermission": "使用权限",
"managePermission": "管理权限",
"roleNamePrompt": "角色名称不能超过50字符",
"roleNameRequired": "角色名称不为空",
"roleNameRequired": "角色名称不为空",
"groupNameExists": "用户组名称不可重复",
"groupNamePrompt": "用户组名称不能超过30字符",
"groupNameRequired": "用户组名称不可为空",
"roleNameExists": "角色名称已存在",
"parameterConfig": "参数配置",
"language": "语言"
"language": "语言",
"assistantAuthorization": "NPC授权",
"assistantName": "NPC名称",
"userList": "用户列表",
"userGroup": "用户组",
"role": "角色",
"searchUserGroups": "搜索用户组",
"searchRoles": "搜索角色",
"reset": "重置",
"confirm": "确认",
"userGroupName": "输入用户组名称",
"groupName": "用户组名称",
"admins": "管理员",
"flowControl": "用户组整体流量控制",
"AssistantFlowCtrl": "NPC流量控制",
"SkillFlowCtrl": "能力流量控制",
"createdBy": "创建人",
"flowCtrlStrategy": "流量控制策略",
"limit": "有限制",
"unlimited": "无限制",
"iconHover": "同时受用户组整体流量控制策略约束",
"maximum": "最多",
"perMinute": "个同时在线会话",
"changeTime": "修改时间",
"deleteGroup": "删除后 【{{name}}】 将不再存在,是否删除?",
"currentGroup": "当前用户组",
"defaultGroup": "默认用户组",
"resetPwd": "重置密码",
"selectGroup": "请选择用户组",
"selectRole": "请选择角色"
},
"skills": {
"manageTemplate": "管理能力模板",
"createNew": "新建",
"manageProjects": "这里管理您的个人项目,对能力上下线、编辑等等",
"skillSearch": "能力搜索",
"createNew": "新建能力",
"customSkills": "自定义能力",
"chooseOnline": "选择上线版本",
"executionSteps": "能力通过可视化的流程编排,明确任务执行步骤",
"sceneTemplates": "我们提供场景模板供您使用和参考",
"manageProjects": "在此页面管理您的能力,对能力上下线、编辑等等",
"skillSearch": "搜索您需要的能力",
"confirmDeleteSkill": "确认删除该能力?",
"backToSkillList": "返回能力列表",
"skillTemplateManagement": "能力模板管理,模板对所有用户可见,支持拖拽排序、删除操作",
@@ -79,6 +125,7 @@
"skillDescTooLong": "能力描述过长不要超过200字",
"errorTitle": "关键信息有误",
"onlineFailure": "上线失败",
"onlineSuccessful": "上线成功",
"custom": "自定义",
"skillTemplate": "能力模板",
"skillTemplateChoose": "您可以从这里挑选一个模板开始,或者自定义高级模板",
@@ -88,7 +135,23 @@
"createdBy": "创建用户",
"offline": "下线",
"online": "上线",
"guideWords": "引导词"
"guideWords": "引导词",
"currentVersion": "当前版本:",
"importLocal": "导入本地组件",
"save": "保存",
"import": "导入",
"export": "导出",
"code": "代码",
"simplify": "简化",
"saveVersion": "另存为新版本",
"deleteOrNot": "是否删除",
"version": "版本",
"saveSuccessful": "保存成功",
"supportVersions": "支持分成多个版本分支,分别进行开发以及版本间的比较。",
"guideQuestions50": "引导问题最多50个字符",
"promptWords1000": "提示词最多为1000个字符",
"contactAdmin": "请联系管理员上线NPC",
"deleteSure": "确认删除该工具?"
},
"chat": {
"newChat": "新建会话",
@@ -123,7 +186,21 @@
"fileStorageFailure": " 文件地址失效!",
"confirmDeleteChat": "确认删除该会话?",
"roundOver": "本轮结束",
"chatDialogTip": "设置提示模板中定义的输入变量。与代理和链互动"
"chatDialogTip": "设置提示模板中定义的输入变量。与代理和链互动",
"feedback": "反馈",
"feedbackRequired": "反馈信息不能为空",
"dialogueSelection": "对话选择",
"chooseSkillOrAssistant": "选择一个您想使用的线上能力或NPC",
"search": "搜索",
"recommendationQuestions": "推荐问题",
"historicalMessages": "以上为历史消息",
"clickDownload": "点击下载",
"searchAssistantOrSkill": "搜索NPC或者能力",
"operationTips": "操作提示:在左侧选择要展示的标签,在右侧拖拽进行排序",
"selected": "已选",
"pleaseSelectAnApp": "请选择一个应用",
"allLabels": "全部标签",
"searchLabels": "搜索标签"
},
"model": {
"modelConfiguration": "模型配置",
@@ -268,7 +345,47 @@
"textDataset": "文本数据集",
"tableDataset": "表格数据集",
"createImport": "新建/导入",
"knowledge": "知识库"
"knowledge": "知识库",
"web": "网站",
"knowledgeId": "知识库ID",
"knowledgeImg": "知识库头像",
"indexModel": "索引模型",
"dataUp": "单条数据上限",
"introduce": "介绍",
"fileUploadResult": "共上传 {{total}} 份文件,有 {{failed}} 份文件上传失败",
"modalTitle": "文件重复提示",
"modalMessage": "以下文件在知识库中已存在,继续上传将会覆盖原有文件以及处理策略,是否覆盖?",
"keepOriginal": "不覆盖,保留原文件",
"override": "覆盖",
"toolName": "工具名称"
},
"evaluation": {
"id": "任务ID",
"filename": "测试文件名称",
"skillAssistant": "能力NPC",
"status": "状态",
"score": "评测分数",
"createDate": "创建日期",
"download": "下载",
"confirmDeleteEvaluation": "确认删除该评测任务?",
"createTitle": "新建任务",
"selectLabel": "选择要评测的能力或者NPC",
"selectPlaceholder": "请选择",
"selectInputPlaceholder": "请根据名称进行搜索",
"dataLabel": "测试集数据:",
"fileExpandName": "支持扩展名:",
"downloadTemplate": "下载模板文件",
"promptLabel": "评测指令文本:",
"enterExecType": "请选择要评测的能力或NPC",
"enterUniqueId": "请选择能力或NPCID",
"enterVersion": "请选择能力的版本",
"enterFile": "请选择测试集数据",
"enterPrompt": "评测指令不能为空",
"fileSizeLimit": "文件大小限制在10M以内",
"evaluationCollection": "评测集合",
"tooltip": "该指令文本用于指导大模型对 ground truth 和 answer 提取要点,如无特别需求请勿修改",
"create": "创建",
"cancel": "取消"
},
"code": {
"editPythonCodeDescription": "编辑你的 Python 代码此代码片段接受模块导入和一个函数定义。确保您的函数返回一个字符串。",
@@ -408,6 +525,185 @@
"previousPage": "上一页",
"nextPage": "下一页",
"formatError": "格式错误",
"port": "服务端口",
"cancle": "取消",
"tip": "提示",
"deleteAssistant": "确认删除该NPC",
"chatTipsTitle": "使用提示",
"updateSuccess": "修改成功",
"createSuccess": "创建成功",
"confirm": "确认",
"required": "不可为空",
"build": {
"create": "创建",
"assistant": "NPC",
"skill": "能力",
"tools": "工具",
"save": "保存",
"online": "上线",
"retry": "重试",
"use": "使用",
"useAll": "全部使用",
"assistantConfiguration": "NPC配置",
"createAssistant": "新建NPC",
"assistantPortrait": "NPC画像",
"portraitOptimization": "NPC画像优化",
"automaticOptimization": "自动优化",
"createDescription": "通过描述角色和任务来创建你的NPC",
"nextDescription": "NPC可以调用多个能力和工具",
"searchAssistant": "搜索您需要的NPC",
"manageAssistant": "在此页面管理您的NPC对NPC上下线、编辑等等",
"establishAssistant": "创建NPC",
"assistantName": "NPC名称",
"giveAssistantName": "给NPC取一个名字",
"whatWant": "你希望NPC的角色是什么具体完成什么任务",
"example": "示例",
"exampleOne": "你是 XX具有 XX 经验,擅长 XX…",
"exampleTwo": "你的任务是 XX ,需要按照以下步骤执行:",
"automaticallyConfigurations": "自动为您选择相关配置",
"prompt": "详细、具体地描述NPC与用户的交互方式例如NPC的身份、完成任务的具体方法和步骤、回答问题时的语气以及应该注意什么问题等",
"openingRemarks": "开场白",
"guidingQuestions": "引导问题",
"promptReplaced": "提示词已替换",
"guideReplaced": "引导词已替换",
"openingReplaced": "开场白已替换",
"toolsReplaced": "工具已替换",
"skillsReplaced": "能力已替换",
"allReplaced": "已全部替换",
"basicConfiguration": "基础配置",
"modelConfiguration": "AI模型配置",
"model": "模型",
"temperature": "温度",
"openingIntroduction": "开场引导",
"openingStatement": "开场白",
"assistantMessageFormat": "NPC将在每次对话开始时发送此信息支持 Markdown 格式",
"maximumPromptLength": "提示词最多为1000个字符",
"recommendQuestionsForUsers": "为用户提供推荐问题引导用户提问超过3个时将随机选取3个",
"maxCharacters50": "最多50个字符",
"enterGuidingQuestions": "请输入引导问题",
"knowledge": "知识",
"knowledgeBase": "知识库",
"autoCall": "自动调用",
"callingMethod": "调用方式",
"autoCallDescription": "每轮对话都会对添加的知识库进行检索召回。",
"onDemandCall": "按需调用",
"onDemandCallDescription": "在NPC画像提示词中提示调用 RecallKnowledge可复制方法在有需要时才对知识库进行检索。",
"createNewKnowledge": "新建知识库",
"refresh": "刷新",
"abilities": "能力",
"skillDescription": "通过可视化界面实现复杂和稳定的业务流程编排,例如项目计划和报告分析",
"selectKnowledgeBase": "请选择知识库",
"searchBaseName": "搜索知识库名称",
"debugPreview": "调试预览",
"addTool": "添加工具",
"search": "搜索",
"empty": "空空如也",
"onlineSA": "上线能力&NPC",
"params": "参数",
"added": "已添加",
"add": "添加",
"configurationUpdated": "配置已更新",
"addSkill": "添加能力",
"createSkill": "创建能力",
"nameRequired": "名称不可为空",
"nameMaxLength": "名称最多50个字符",
"descMaxLength": "最多1000个字符",
"editAssistant": "编辑NPC",
"enterName": "给NPC取一个名字",
"assistantDesc": "NPC描述",
"enterDesc": "介绍NPC功能描述在会话和NPC页面可见",
"cancel": "取消",
"confirm": "确认",
"forBetter": "为了更好的NPC效果描述需要大于20 个字",
"forExample": "例如NPC的身份、完成任务的具体方法和步骤、回答问题时的语气以及应该注意什么问题等"
},
"tools1": {
"addTool": "添加工具",
"createCustomTool": "自定义工具",
"builtinTools": "内置工具",
"customTools": "自定义工具",
"search": "搜索",
"empty": "空空如也",
"manageCustomTools": "在此页面管理您的自定义工具,对自定义工具创建、编辑等等",
"name": "名称",
"enterToolName": "输入工具名称",
"openapiSchema": "OpenAPI Schema",
"enterOpenAPISchema": "输入您的 OpenAPI schema",
"importFromUrl": "从 URL 导入",
"examples": "示例",
"weatherJson": "天气(JSON)",
"petShopYaml": "宠物商店(YAML)",
"authenticationType": "鉴权方式",
"authType": "认证类型",
"none": "无",
"apiKey": "API Key",
"basic": "Basic",
"bearer": "Bearer",
"availableTools": "可用工具",
"description": "描述",
"method": "方法",
"path": "路径",
"delete": "删除",
"cancel": "取消",
"save": "保存"
},
"test": {
"test": "测试",
"addTest": "请先填写测试用例",
"uploadTest": "上传测试用例",
"explain": "为测试用例是当前组件的输入,只支持 txt 文件,最多 20 行",
"testRun": "测试运行",
"testCase": "测试用例",
"run": "运行",
"downloadResults": "下载运行结果",
"testCases": "输入测试用例...",
"parametersAndValues": "参数和值",
"parameter": "参数",
"value": "值",
"result": "测试结果",
"outResultPlaceholder": "点击按钮,输出结果"
},
"resetPassword": {
"slogen": "安全地重置您的密码",
"currentPassword": "当前密码",
"newPassword": "新密码",
"confirmNewPassword": "确认新密码",
"pleaseEnterCurrentPassword": "请输入当前密码。",
"pleaseEnterNewPassword": "请输入新密码。",
"pleaseEnterConfirmPassword": "请确认新密码。",
"newPasswordTooShort": "新密码必须至少 7 个字符。",
"passwordMismatch": "新密码不匹配。",
"resetButton": "修改密码",
"passwordResetSuccess": "您的密码已成功修改",
"adminResetSuccess": "密码已重置",
"resetFailed": "密码重置失败",
"notEmpty": "新密码不能为空"
},
"log": {
"auditManagement": "审计管理",
"searchButton": "查询",
"resetButton": "重置",
"auditId": "审计ID",
"username": "用户名",
"operationTime": "操作时间",
"systemModule": "系统模块",
"operationAction": "操作行为",
"objectType": "操作对象类型",
"operationObject": "操作对象",
"ipAddress": "IP地址",
"remark": "备注",
"selectUser": "选择用户",
"selectUserGroup": "选择用户组",
"startDate": "开始日期",
"endDate": "结束日期",
"actionBehavior": "操作行为"
},
"tag": {
"labelMaxLength": "标签名不能超过10个字符",
"confirmDeleteLabel": "标签【{{label}}】正在使用中,确认删除?",
"createNewLabel": "创建“新标签”",
"addLabel": "添加标签"
},
"agents": {
"AgentInitializer": {
"display_name": "AgentInitializer",
@@ -627,8 +923,8 @@
},
"autogen_roles": {
"AutoGenAssistant": {
"display_name": "自动生成助手",
"description": "助手代理设计用于使用大语言模型LLM解决任务。",
"display_name": "自动生成NPC",
"description": "NPC代理设计用于使用大语言模型LLM解决任务。",
"description_url": "https://lxm3ki8hexz.feishu.cn/wiki/Yz5GwMpO7ioj8nkRSZlcsriXnJ3",
"template": {
"model_name": {
@@ -654,7 +950,7 @@
}
},
"output_types":[
"自动生成助手"
"自动生成NPC"
]
},
"AutoGenCoder": {
@@ -2251,7 +2547,7 @@
},
"NPCsChatLLM": {
"display_name": "NPCsChatLLM",
"description": "`OpenAI`聊天大型语言模型API。",
"description": "无缝切换支持的大语言模型",
"description_url": "",
"template": {
"api_base": {

BIN
public/points.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/.DS_Store vendored

Binary file not shown.

View File

@@ -191,9 +191,9 @@
}
path.react-flow__edge-interaction:hover {
stroke: #eebbbb;
stroke: #ffe999;
stroke-opacity: 1;
stroke-width: 12;
stroke-width: 10;
}
.selection-tool-box {
@@ -208,3 +208,12 @@ path.react-flow__edge-interaction:hover {
z-index: 99;
cursor: pointer;
}
.bs-chat-bg {
width: 100%;
/* background: url(/points.png) 0 100% repeat-x; */
background-size: 10px 432px;
/* background: rgba(0,0,0,0.9); */
/* background-color: #000; */
}

View File

@@ -8,6 +8,7 @@ import "./App.css";
import ErrorAlert from "./alerts/error";
import NoticeAlert from "./alerts/notice";
import SuccessAlert from "./alerts/success";
import { Toaster } from "./components/bs-ui/toast";
import { alertContext } from "./contexts/alertContext";
import { locationContext } from "./contexts/locationContext";
import { userContext } from "./contexts/userContext";
@@ -215,6 +216,8 @@ export default function App() {
</div>
))}
</div>
{/* 新弹窗 */}
<Toaster></Toaster>
</div>
);
}

View File

@@ -34,7 +34,7 @@ import {
nodeIconsLucide,
nodeIMgsLucide
} from "../../../../utils";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import KnowledgeSelect from "@/components/bs-comp/selectComponent/knowledge";
export default function ParameterComponent({
left,
@@ -61,7 +61,7 @@ export default function ParameterComponent({
const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0);
const { closePopUp } = useContext(PopUpContext);
const { setTabsState, flow, setFlow } = useContext(TabsContext);
const { setTabsState, flow, version } = useContext(TabsContext);
const groupedEdge = useRef(null); // 用yu过滤菜单的数据
@@ -91,7 +91,7 @@ export default function ParameterComponent({
}, [id, data, reactFlowInstance])
// milvus 组件,知识库不为空是 embbeding取消必填限制
useEffect(() => {
const {embedding, index_name, collection_name, connection_args} = data.node.template
const { embedding, index_name, collection_name, connection_args } = data.node.template
if ((index_name || collection_name) && embedding) {
const hidden = disabled ? false : !!(collection_name || index_name).value
data.node.template.embedding.required = !hidden
@@ -101,7 +101,7 @@ export default function ParameterComponent({
}
}, [data, disabled])
const handleRemoveMilvusEmbeddingEdge = (nodeId) => {
const edges = reactFlowInstance.getEdges().filter(edge => edge.targetHandle.indexOf('Embeddings|embedding|'+nodeId) === -1)
const edges = reactFlowInstance.getEdges().filter(edge => edge.targetHandle.indexOf('Embeddings|embedding|' + nodeId) === -1)
reactFlowInstance.setEdges(edges)
}
const [myData, setMyData] = useState(useContext(typesContext).data);
@@ -219,7 +219,6 @@ export default function ParameterComponent({
}}
/> */}
<img src={Icon} className="w-[14px]" alt="" />
</div>
<span className="ps-2 text-xs text-foreground w-[400px]">
{getNodeNames()[item.family] ?? "Other"}{" "}
@@ -268,34 +267,6 @@ export default function ParameterComponent({
}
}, [tooltipTitle]);
// 记录快照
const {takeSnapshot} = useContext(undoRedoContext);
const { types, deleteNode } = useContext(typesContext);
// const onNodeDragStart: NodeDragHandler = useCallback(() => {
// // 👇 make dragging a node undoable
// takeSnapshot();
// // 👉 you can place your event handlers here
// }, [takeSnapshot]);
const onMouseDownColor = useCallback(() => {
// console.log(nodeColorsP)
// console.log(data,color,nodeColorsP,nodeColorsP[types[data.type]]);
// const type = types[data.type];
// Object.keys(nodeColors).forEach(element => {
// if(element != type){
// nodeColorsP[element] = "#000000"
// }
// });
// console.log(nodeColors);
// takeSnapshot();
// data.node.display_name = "1";
// console.log(flow)
},[takeSnapshot]);
// useEffect(() => {
// takeSnapshot();
// }, [nodeColors, nodeColorsP]);
return (
<div
ref={ref}
@@ -330,37 +301,36 @@ export default function ParameterComponent({
type === "int" ||
type === "variable" ||
type === "button" ||
type === "knowledge_one" ||
type === "knowledge_list" ||
type === "NestedDict" ||
type === "dict") &&
!optionalHandle ? (
<></>
) : (
<ShadTooltip
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
delayDuration={0}
content={refHtml.current}
side={left ? "left" : "right"}
>
<Handle
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
id={id}
isValidConnection={(connection) =>
isValidConnection(connection, reactFlowInstance)
}
onConnect={(params) => console.log('handle onConnect', params)}
className={classNames(
left ? "-ml-0.5 " : "-mr-0.5 ",
"h-3 w-3 rounded-full border-2 bg-background"
)}
onMouseDown={onMouseDownColor}
style={{
borderColor: color,
top: position,
}}
></Handle>
</ShadTooltip>
)}
!optionalHandle ? (<></>)
: (
<ShadTooltip
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
delayDuration={0}
content={refHtml.current}
side={left ? "left" : "right"}
>
<Handle
type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right}
id={id}
isValidConnection={(connection) =>
isValidConnection(connection, reactFlowInstance)
}
className={classNames(
left ? "-ml-0.5 " : "-mr-0.5 ",
"h-3 w-3 rounded-full border-2 bg-background"
)}
style={{
borderColor: color,
top: position,
}}
></Handle>
</ShadTooltip>
)}
{/* 左侧input输入项 */}
{!data.node.template[name] ? null : left === true &&
@@ -390,10 +360,6 @@ export default function ParameterComponent({
) : ['index_name', 'collection_name'].includes(name) ? (
// 知识库选择
<CollectionNameComponent
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={disabled}
id={data.node.template[name].collection_id ?? ""}
value={data.node.template[name].value ?? ""}
@@ -410,6 +376,35 @@ export default function ParameterComponent({
/>
)}
</div>
) : left === true && type === "knowledge_one" ? (
// 单选知识库
<div className="mt-2 w-full">
<CollectionNameComponent
disabled={disabled}
id={data.node.template[name].collection_id ?? ""}
value={data.node.template[name].value ?? ""}
onSelect={(val, id) => { handleOnNewLibValue(val, id); val && handleRemoveMilvusEmbeddingEdge(data.id) }}
onChange={() => { }}
/>
</div>
) : left === true && type === "knowledge_list" ? (
// 多选知识库
<div className="mt-2 w-full">
<KnowledgeSelect
multiple
disabled={disabled}
value={data.node.template[name].value?.map?.((item) => ({
label: item.value,
value: item.key,
})) || []}
onChange={(vals) => {
handleOnNewValue(vals.map(v => ({
key: v.value,
value: v.label
})))
}}
/>
</div>
) : left === true && type === "bool" ? (
<div className="mt-2 w-full">
{/* switch */}
@@ -480,7 +475,6 @@ export default function ParameterComponent({
<PromptAreaComponent
field_name={name}
setNodeClass={(nodeClass, code) => {
console.log(nodeClass)
if (reactFlowInstance) {
reactFlowInstance.setNodes((nds) =>
nds.map((nd) => {
@@ -548,7 +542,7 @@ export default function ParameterComponent({
</div>
) : left === true && type === "variable" ? (
<div className="mt-2 w-full">
<VariablesComponent nodeId={data.id} flowId={flowId} onChange={(newValue) => {
<VariablesComponent vid={version?.id} nodeId={data.id} flowId={flowId} onChange={(newValue) => {
data.node!.template[name].value = newValue;
handleOnNewValue(newValue);
}} />

View File

@@ -171,12 +171,14 @@ export default function GenericNode({ data, xPos, yPos, selected }: {
</div >
<div className="generic-node-desc nodrag" onKeyDown={e => e.stopPropagation()}>
<div className="generic-node-desc-text" onClick={() => data.node.description_url && openPopUp(<DescriptionModel data={data.node.description_url} />)}>{data.node.description}</div>
{/* <div className="generic-node-desc nodrag" onKeyDown={e => e.stopPropagation()}>
<div className="generic-node-desc-text" onClick={() => data.node.description_url && openPopUp(<DescriptionModel data={data.node.description_url} />)}>{data.node.description}</div> */}
{/*=======*/}
{/* <div className="generic-node-desc nodrag">*/}
{/* <div className="generic-node-desc-text">{data.node.description}</div>*/}
{/*>>>>>>> bisheng_github*/}
<div className="generic-node-desc nodrag">
<div className="generic-node-desc-text">{data.node.description}</div>
<>
{Object.keys(data.node.template)
.filter((t) => t.charAt(0) !== "_")
@@ -230,7 +232,7 @@ export default function GenericNode({ data, xPos, yPos, selected }: {
type={data.node.template[t].type}
optionalHandle={data.node.template[t].input_types}
onChange={() => fouceUpdateNode(!_)}
nodeColorsP={nodeColors}
// nodeColorsP={nodeColors}
/>
) : (
<></>
@@ -258,7 +260,7 @@ export default function GenericNode({ data, xPos, yPos, selected }: {
id={[data.type, data.id, ...data.node.base_classes].join("|")}
type={data.node.base_classes.join("|")}
left={false}
nodeColorsP={nodeColors}
// nodeColorsP={nodeColors}
/>
{data.type === 'Report' && <div className="w-full bg-muted px-5 py-2">
<Link to={`/report/${flowId}`}><Button variant="outline" className="px-10">Edit</Button></Link>

BIN
src/assets/.DS_Store vendored

Binary file not shown.

Binary file not shown.

BIN
src/assets/Login/reset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/chat/copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
src/assets/chat/xiazai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

BIN
src/assets/nav/admin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
src/assets/nav/admin1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
src/assets/nav/icon6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
src/assets/nav/icon7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/nav/mima.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

BIN
src/assets/nav/tuichu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/assets/npc/border-r.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/assets/npc/jianhua.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/npc/lingcun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/assets/npc/npcIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/npc/tuichu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/assets/npc/xiaoxi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/assets/toolbar/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/components/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -17,7 +17,8 @@ import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
* 通过子组件VarDialog编辑每一项
*/
export default function VariablesComponent({ nodeId, flowId, onChange }: {
export default function VariablesComponent({vid, nodeId, flowId, onChange }: {
vid: number
nodeId: string
flowId: string
onChange: (val: any) => void
@@ -27,11 +28,12 @@ export default function VariablesComponent({ nodeId, flowId, onChange }: {
useEffect(() => {
// api nodeId -> items
flowId && getVariablesApi({
flowId && vid && getVariablesApi({
version_id: vid,
flow_id: flowId,
node_id: nodeId
}).then(arr => setItems(arr))
}, [flowId])
}, [flowId, vid])
const { openPopUp, closePopUp } = useContext(PopUpContext);
const { setErrorData } = useContext(alertContext);
@@ -60,6 +62,7 @@ export default function VariablesComponent({ nodeId, flowId, onChange }: {
const param: any = {
"flow_id": flowId,
"node_id": nodeId,
version_id: vid,
"variable_name": _item.name,
"value_type": Number(_item.type === VariableType.Select) + 1,
"value": _item.type === VariableType.Text ? _item.maxLength : _item.options.map(el => el.value).join(',')
@@ -68,7 +71,7 @@ export default function VariablesComponent({ nodeId, flowId, onChange }: {
param.id = _item.id
}
captureAndAlertRequestErrorHoc(saveVariableApi(param).then(res => {
const _items = items.map(item => item.id === _item.id ? { ..._item, id: res.id } : item)
const _items = items.map(item => item.id === _item.id ? { ..._item, id: res.id, update: true } : item)
// const hasValue = _items.find(item => item.name)
// 保存时 id传出去保存用来校验必填项
onChange(_items.map(el => el.name))

BIN
src/components/bs-comp/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,110 @@
import { LabelIcon } from "@/components/bs-icons/label";
import LabelSelect from "../selectComponent/LabelSelect";
import { UPDATETYPE } from "../selectComponent/LabelSelect";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { BookmarkFilledIcon } from "@radix-ui/react-icons";
import tianjiabiaoqian from "../../../assets/npc/tianjiabiaoqian.png";
export default function LabelShow({ show, isOperator, labels, all, resource, reload, AllLabelsApi }) {
const { t } = useTranslation()
const [freshData, setFreshData] = useState(labels)
const [allData, setAllData] = useState(all)
const [isShow, setIsShow] = useState(show)
const handleUpdate = (obj: { type: string, data: any }) => {
console.log(UPDATETYPE,obj)
switch (obj.type) {
case UPDATETYPE.DELETELINK: {
reload();
AllLabelsApi();
setFreshData(pre => pre.filter(l => l.value !== obj.data.value))
break
}
case UPDATETYPE.CREATELINK: {
reload();
AllLabelsApi();
setFreshData(pre => [obj.data, ...pre])
break
}
case UPDATETYPE.UPDATENAME: {
reload();
AllLabelsApi();
setFreshData(pre => pre.map(d => d.value === obj.data.value ? { ...d, label: obj.data.label } : d))
break
}
case UPDATETYPE.CREATELABEL: {
// 什么也不用做
break
}
case UPDATETYPE.DELETELABEL: {
reload();
AllLabelsApi();
setFreshData(pre => pre.filter(d => d.value !== obj.data.value))
setAllData(pre => pre.filter(a => a.value !== obj.data.value))
break
}
default: console.log('error>>事件类型错误!!!')
}
}
useEffect(() => {
setIsShow(freshData.length > 0)
}, [freshData])
return (
<div className="w-full">
{isShow ? (
isOperator ? (
<LabelSelect onUpdate={handleUpdate} labels={labels} resource={resource} all={allData}>
<div onClick={(e) => e.stopPropagation()} className="mb-[10px] max-w-[100%] flex place-items-center rounded-sm p-1 border border-transparent group-hover:bg-search-input group-hover:border-input">
{/* <BookmarkFilledIcon className="mr-2 text-muted-foreground" />
<div className="text-sm text-muted-foreground max-w-[250px] truncate">
{freshData.map((l, index) => <span>{l.label}{index !== freshData.length - 1 && ''}</span>)}
</div> */}
<div className="biaoqian">
<img src={tianjiabiaoqian} className="w-[12px] mr-[7px]" alt="" />
<div>
{freshData.map((l, index) => <div>{l.label}</div>)}
{/* <div>知识库</div> */}
{/* <div>法律</div> */}
</div>
</div>
</div>
</LabelSelect>
) : (
<div className="mb-[10px] flex place-items-center max-w-[100%] rounded-sm p-1">
{/* <BookmarkFilledIcon className="mr-2 text-muted-foreground" />
<div className="text-sm text-muted-foreground max-w-[250px] truncate">
{freshData.map((l, index) => <span>{l.label}{index !== freshData.length - 1 && ''}</span>)}
</div> */}
<div className="biaoqian">
{/* <img src={tianjiabiaoqian} className="w-[12px]" alt="" /> */}
<div>
{freshData.map((l, index) => <div>{l.label}</div>)}
{/* <div>知识库</div>
<div>法律</div> */}
</div>
</div>
</div>
)
) : (
isOperator ? (
<LabelSelect onUpdate={handleUpdate} labels={labels} resource={resource} all={allData}>
<div onClick={(e) => e.stopPropagation()} className="">
{/* <BookmarkFilledIcon className="mr-2 text-muted-foreground" />
<div className="text-sm text-muted-foreground">
<span>{t('tag.addLabel')}</span>
</div> */}
<div className="biaoqian">
<img src={tianjiabiaoqian} className="w-[12px]" alt="" />
</div>
</div>
</LabelSelect>
) : (
<div></div>
)
)}
</div>
)
}

View File

@@ -35,11 +35,11 @@ interface IProps<T> {
}
export const gradients = [
'bg-amber-500',
'bg-orange-600',
'bg-teal-500',
'bg-purple-600',
'bg-blue-700'
'bg-[#FF9B25]',
'bg-[#04BCD2]',
'bg-[#2586FF]',
'bg-[#D855D3]',
'bg-[#C04B51]'
]
// 'bg-slate-600',
@@ -60,8 +60,11 @@ export const gradients = [
// 'bg-fuchsia-700',
// 'bg-pink-600',
// 'bg-rose-600'
export function TitleIconBg({ id, className = '', children = <SkillIcon /> }) {
return <div className={cname(`rounded-sm flex justify-center items-center ${gradients[parseInt(id + '', 16) % gradients.length]}`, className)}>{children}</div>
export function TitleIconBg({ img, id, className = '', children = <SkillIcon /> }) {
if(img) return <div className={cname(`flex justify-center items-center cursor-pointer overflow-hidden`, className)} style={{borderRadius:"7px"}}>
{children}
</div>
return <div className={cname(`flex justify-center items-center cursor-pointer ${id!="" && gradients[parseInt(id + '', 16) % gradients.length]}`, className)} style={{borderRadius:"7px"}}>{children}</div>
}
export default function CardComponent<T>({

View File

@@ -1,13 +1,18 @@
import { ClearIcon } from "@/components/bs-icons/clear";
import { FormIcon } from "@/components/bs-icons/form";
import { SendIcon } from "@/components/bs-icons/send";
import { Button } from "@/components/bs-ui/button";
import { Textarea } from "@/components/bs-ui/input";
import { useToast } from "@/components/bs-ui/toast/use-toast";
import { locationContext } from "@/contexts/locationContext";
import { PauseIcon } from "@radix-ui/react-icons";
import { useContext, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useMessageStore } from "./messageStore";
import GuideQuestions from "./GuideQuestions";
import { ClearIcon } from "@/components/bs-icons/clear";
import { useMessageStore } from "./messageStore";
import { formatDate } from "@/util/utils";
import { StopIcon } from "@radix-ui/react-icons";
import duihua_send from "../../../assets/chat/duihua-send.png";
export default function ChatInput({ clear, form, questions, inputForm, wsUrl, onBeforSend }) {
const { toast } = useToast()
@@ -18,10 +23,15 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
const [showWhenLocked, setShowWhenLocked] = useState(false) // 强制开启表单按钮不限制于input锁定
const [inputLock, setInputLock] = useState({ locked: false, reason: '' })
const { messages, chatId, createSendMsg, createWsMsg, updateCurrentMessage, destory, setShowGuideQuestion } = useMessageStore()
const { messages, hisMessages, chatId, createSendMsg, createWsMsg, updateCurrentMessage, destory, setShowGuideQuestion } = useMessageStore()
const currentChatIdRef = useRef(null)
const inputRef = useRef(null)
const continueRef = useRef(false)
// 停止状态
const [stop, setStop] = useState({
show: false,
disable: false
})
/**
* 记录会话切换状态,等待消息加载完成时,控制表单在新会话自动展开
*/
@@ -32,16 +42,17 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
if (changeChatedRef.current) {
changeChatedRef.current = false
// 新建的 form 技能,弹出窗口并锁定 input
if (form && messages.length === 0) {
if (form && messages.length === 0 && hisMessages.length === 0) {
setInputLock({ locked: true, reason: '' })
setFormShow(true)
setShowWhenLocked(true)
}
}
}, [messages])
}, [messages, hisMessages])
useEffect(() => {
if (!chatId) return
continueRef.current = false
setInputLock({ locked: false, reason: '' })
// console.log('message chatid', messages, form, chatId);
setShowWhenLocked(false)
@@ -81,7 +92,9 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
const event = new Event('input', { bubbles: true, cancelable: true });
inputRef.current.value = ''
inputRef.current.dispatchEvent(event); // 触发调节input高度
const [wsMsg, inputKey] = onBeforSend('', value)
const contunue = continueRef.current ? 'continue' : ''
continueRef.current = false
const [wsMsg, inputKey] = onBeforSend(contunue, value)
// msg to store
createSendMsg(wsMsg.inputs, inputKey)
// 锁定 input
@@ -96,8 +109,12 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
}
}
const diffRef = useRef(0)
const sendWsMsg = async (msg) => {
try {
diffRef.current = Date.now()
// console.log('WebSocket send: ' + diffRef.current + ' 毫秒');
wsRef.current.send(JSON.stringify(msg))
} catch (error) {
toast({
@@ -117,14 +134,23 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
return new Promise((res, rej) => {
try {
let startTime = Date.now();
const ws = new WebSocket(`${webSocketProtocol}://${wsUrl}&chat_id=${chatId}`)
wsRef.current = ws
// websocket linsen
ws.onopen = () => {
// 记录连接成功的时间
let endTime = Date.now();
// 计算连接建立所需的时间
let connectionTime = endTime - startTime;
// console.log('WebSocket 连接建立时间: ' + connectionTime + ' 毫秒');
console.log("WebSocket connection established!");
res('ok')
};
ws.onmessage = (event) => {
// console.log(`WebSocket get: ${Date.now()} 毫秒与send差值${Date.now() - diffRef.current}毫秒`);
const data = JSON.parse(event.data);
const errorMsg = data.category === 'error' ? data.intermediate_steps : ''
// 异常类型处理,提示
@@ -134,13 +160,17 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
handleWsMessage(data)
// 群聊@自己时开启input
if (['end', 'end_cover'].includes(data.type) && data.receiver?.is_self) {
setInputLock({ locked: true, reason: '' })
setInputLock({ locked: false, reason: '' })
setStop({ show: false, disable: false })
continueRef.current = true
}
}
ws.onclose = (event) => {
wsRef.current = null
console.error('链接手动断开 event :>> ', event);
if ([1005, 1008].includes(event.code)) {
setStop({ show: false, disable: false })
if ([1005, 1008, 1009].includes(event.code)) {
console.warn('即将废弃 :>> ');
setInputLock({ locked: true, reason: event.reason })
} else {
@@ -156,6 +186,7 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
};
ws.onerror = (ev) => {
wsRef.current = null
setStop({ show: false, disable: false })
console.error('链接异常error', ev);
toast({
title: `${t('chat.networkError')}:`,
@@ -179,8 +210,11 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
const handleWsMessage = (data) => {
if (Array.isArray(data) && data.length) return
if (data.type === 'start') {
// 非continue时展示stop按钮
!continueRef.current && setStop({ show: true, disable: false })
createWsMsg(data)
} else if (data.type === 'stream') {
//@ts-ignore
updateCurrentMessage({
chat_id: data.chat_id,
message: data.message,
@@ -193,15 +227,16 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
thought: data.intermediate_steps || '',
messageId: data.message_id,
noAccess: false,
liked: 0
liked: 0,
update_time: formatDate(new Date(), 'yyyy-MM-ddTHH:mm:ss')
}, data.type === 'end_cover')
} else if (data.type === "close") {
setStop({ show: false, disable: false })
setInputLock({ locked: false, reason: '' })
}
}
// 监听重发消息事件
// 触发发送消息事件(重试、表单)
useEffect(() => {
const handleCustomEvent = (e) => {
if (!showWhenLocked && inputLock.locked) return console.error('弹窗已锁定,消息无法发送')
@@ -230,12 +265,12 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
// setInputEmpty(textarea.value.trim() === '')
}
return <div className="absolute bottom-0 w-full pt-1 bg-[#fff] dark:bg-[#1B1B1B]">
return <div className="absolute bottom-0 w-full pt-1 bg-[#fff] dark:bg-[#000]">
<div className={`relative ${clear && 'pl-9'}`}>
{/* form */}
{
formShow && <div className="relative">
<div className="absolute left-0 border bottom-2 bg-[#fff] px-4 py-2 rounded-md w-[50%] min-w-80">
<div className="absolute left-0 bottom-2 bg-[#1a1a1a] px-4 py-2 rounded-md w-[50%] min-w-80 z-50">
{inputForm}
</div>
</div>
@@ -253,44 +288,66 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
clear && <div
className={`w-6 h-6 rounded-sm hover:bg-gray-200 cursor-pointer flex justify-center items-center `}
onClick={() => { !inputLock.locked && destory() }}
><ClearIcon className={!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-950'} ></ClearIcon></div>
><ClearIcon className={`${!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-950'} dark:text-slate-50 dark:hover:bg-[#282828]`} ></ClearIcon></div>
}
</div>
{/* form */}
{/* form switch */}
<div className="flex absolute left-3 top-4 z-10">
{
form && <div
className={`w-6 h-6 rounded-sm hover:bg-gray-200 cursor-pointer flex justify-center items-center `}
className={`w-6 h-6 rounded-sm hover:bg-[#ffd025] hover:text-[#333] cursor-pointer flex justify-center items-center `}
onClick={() => (showWhenLocked || !inputLock.locked) && setFormShow(!formShow)}
><FormIcon className={!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-950'}></FormIcon></div>
><FormIcon className={!showWhenLocked && inputLock.locked ? 'text-gray-400' : 'text-gray-800'}></FormIcon></div>
}
</div>
{/* send */}
<div className="flex gap-2 absolute right-3 top-4 z-10">
<div
id="bs-send-btn"
className="w-6 h-6 rounded-sm hover:bg-gray-200 cursor-pointer flex justify-center items-center"
onClick={() => { !inputLock.locked && handleSendClick() }}
><SendIcon className={inputLock.locked ? 'text-gray-400' : 'text-gray-950'}></SendIcon></div>
<div className="flex gap-2 absolute right-[2.5%] z-10">
{stop.show ?
<div
id="bs-send-btn"
className="w-[68px] h-[40px] bg-[#FFD54C] cursor-pointer flex justify-center items-center"
style={{borderRadius:"20px"}} onClick={() => {
if (stop.disable) return
setStop({ show: true, disable: true });
sendWsMsg({ "action": "stop" });
}}>
{/* <SendIcon className={`${inputLock.locked ? 'text-muted-foreground' : 'text-foreground'}`} /> */}
{/* <StopIcon className={`mt-1 rounded-sm bg-[#000000] cursor-pointer ${stop.disable && 'bg-muted-foreground text-muted-foreground'}`}
onClick={() => {
if (stop.disable) return
setStop({ show: true, disable: true });
sendWsMsg({ "action": "stop" });
}} /> */}
<div className="w-[16px] h-[16px] bg-[#000000]" style={{borderRadius:"3px"}}></div>
</div>
: <div
id="bs-send-btn"
className="w-[68px] h-[40px] bg-[#FFD54C] cursor-pointer flex justify-center items-center"
onClick={() => { !inputLock.locked && handleSendClick() }} style={{borderRadius:"20px"}}>
{/* <SendIcon className={`${inputLock.locked ? 'text-muted-foreground' : 'text-foreground'}`} /> */}
<img src={duihua_send} className="w-[20px]" alt="" />
</div>
}
</div>
{/* question */}
<Textarea
<textarea
id="bs-send-input"
ref={inputRef}
rows={1}
style={{ height: 56 }}
style={{ height: 34 }}
disabled={inputLock.locked}
onInput={handleTextAreaHeight}
placeholder={inputLock.locked ? inputLock.reason : t('chat.inputPlaceholder')}
className={"resize-none py-4 pr-10 text-md min-h-6 max-h-[200px] scrollbar-hide dark:bg-[#2A2B2E] text-gray-800" + (form && ' pl-10')}
className="questionTextarea w-full resize-none border-none bg-transparent outline-none max-h-[160px] pr-[68px]"
// className={"resize-none py-4 pr-10 text-md min-h-6 max-h-[200px] scrollbar-hide dark:bg-[#2A2B2E] text-gray-800" + (form && ' pl-10')}
onKeyDown={(event) => {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
!inputLock.locked && handleSendClick()
}
}}
></Textarea>
></textarea>
</div>
<p className="text-center text-sm pt-2 pb-4 text-gray-400">{appConfig.dialogTips}</p>
</div>
};
};

View File

@@ -3,6 +3,9 @@ import { WordIcon } from "@/components/bs-icons/office";
import { checkSassUrl } from "@/pages/ChatAppPage/components/FileView";
import { downloadFile } from "@/util/utils";
import { useTranslation } from "react-i18next";
import npcIcon from "../../../assets/npc/npcIcon.png";
import nengliIcon from "../../../assets/npc/nengliIcon.png";
import { TitleIconBg } from "../cardComponent";
// 颜色列表
const colorList = [
@@ -19,7 +22,7 @@ const colorList = [
"#95A5A6"
]
export default function FileBs({ data }) {
export default function FileBs({ data,flow_type }) {
const { t } = useTranslation()
const avatarColor = colorList[(data.sender?.split('').reduce((num, s) => num + s.charCodeAt(), 0) || 0) % colorList.length]
@@ -33,15 +36,18 @@ export default function FileBs({ data }) {
<div className="w-fit min-h-8 rounded-2xl px-6 py-4 max-w-[90%]">
{data.sender && <p className="text-primary text-xs mb-2" style={{ background: avatarColor }}>{data.sender}</p>}
<div className="flex gap-2 ">
<div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div>
{/* {data.flow_id && <TitleIconBg className="w-[40px] h-[40px]" img={data.avatar_img} id={data.avatar_color ? data.avatar_color : data.flow_id} ><img src={data.avatar_img ? data.avatar_img : (flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>} */}
{flow_type.id && <TitleIconBg className="w-[40px] h-[40px] mr-[10px]" img={flow_type.avatar_img} id={flow_type.avatar_color ? flow_type.avatar_color : flow_type.id} ><img src={flow_type.avatar_img ? flow_type.avatar_img : (flow_type.type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
{/* <div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div> */}
<div
className="flex gap-2 w-52 border border-gray-200 shadow-sm bg-gray-50 px-4 py-2 rounded-sm cursor-pointer"
className="flex gap-2 w-52 shadow-sm bg-[#1a1a1a] px-4 py-2 rounded-sm cursor-pointer"
onClick={() => handleDownloadFile(data.files[0])}
>
<div className="flex items-center"><WordIcon /></div>
<div className="flex items-center text-[#43AFD2]"><WordIcon /></div>
<div>
<h1 className="text-sm font-bold">{data.files[0]?.file_name}</h1>
<p className="text-xs text-gray-400 mt-1"></p>
<h1 className="text-sm font-bold text-[#43AFD2]">{data.files[0]?.file_name}</h1>
<p className="text-xs text-[#666] mt-1"></p>
</div>
</div>
</div>

View File

@@ -27,13 +27,13 @@ export default function GuideQuestions({ locked, chatId, questions, onClick }) {
if (locked || !words.length) return null
if (showGuideQuestion) return <div className="relative">
<div className="absolute left-0 bottom-0">
<p className="text-gray-950 text-sm mb-2 bg-[rgba(255,255,255,0.8)] rounded-md w-fit px-2 py-1">{t('chat.recommendationQuestions')}</p>
<div className="absolute left-[14px] bottom-0">
<p className="text-[#fff] mb-[10px] bg-[]"></p>
{
words.map((question, index) => (
<div
key={index}
className="w-fit bg-[#d4dffa] border-2 border-gray-50 shadow-md text-gray-600 rounded-md mb-1 px-4 py-1 text-sm cursor-pointer"
className="w-fit bg-[#52430c] shadow-md text-[#fff] rounded-md mb-1 px-4 py-1 text-sm cursor-pointer"
onClick={() => {
setShowGuideQuestion(false)
onClick(question)

View File

@@ -11,6 +11,17 @@ import remarkMath from "remark-math";
import MessageButtons from "./MessageButtons";
import SourceEntry from "./SourceEntry";
import { useMessageStore } from "./messageStore";
import robot from "../../../assets/robot.png";
import robotU from "../../../assets/robotU.png";
import robot2 from "../../../assets/robot2.png";
import robot3 from "../../../assets/robot3.png";
import btnEdit from "../../../assets/chat/btn-edit.png";
import btnReSend from "../../../assets/chat/btn-reSend.png";
import btnDel from "../../../assets/chat/btn-del.png";
import npcIcon from "../../../assets/npc/npcIcon.png";
import nengliIcon from "../../../assets/npc/nengliIcon.png";
import { TitleIconBg } from "../cardComponent";
import Thumbs from "@/pages/ChatAppPage/components/Thumbs";
// 颜色列表
const colorList = [
@@ -27,7 +38,8 @@ const colorList = [
"#95A5A6"
]
export default function MessageBs({ data, onUnlike = () => { }, onSource }: { data: ChatMessageType, onUnlike?: any, onSource?: any }) {
export default function MessageBs({ data, onUnlike = () => { }, flow_type, onSource }: { data: ChatMessageType, flow_type: any, onUnlike?: any, onSource?: any }) {
// export default function MessageBs({ logo, data, onUnlike = () => { }, flow_type, onSource }: { logo: string, data: ChatMessageType, flow_type: any, onUnlike?: any, onSource?: any }) {
const avatarColor = colorList[
(data.sender?.split('').reduce((num, s) => num + s.charCodeAt(), 0) || 0) % colorList.length
]
@@ -74,17 +86,26 @@ export default function MessageBs({ data, onUnlike = () => { }, onSource }: { da
const handleCopyMessage = () => {
copyText(messageRef.current)
}
const chatId = useMessageStore(state => state.chatId)
return <div className="flex w-full py-1">
<div className="w-fit max-w-[90%]">
<div className="w-[100%]">
{data.sender && <p className="text-gray-600 text-xs mb-2">{data.sender}</p>}
<div className="min-h-8 px-6 py-4 rounded-2xl bg-[#F5F6F8] dark:bg-[#313336]">
<div className="flex gap-2">
<div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div>
<div className="flex items-start avatarZk">
{/* {(data.flow_id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || data.flow_id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[50px]" alt=""/>}
{data.flow_id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[50px]" alt=""/>}
{(data.flow_id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && data.flow_id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && data.flow_id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[50px]" alt=""/>} */}
{flow_type && flow_type.id && <TitleIconBg className="w-[40px] h-[40px] mr-[10px]" img={flow_type.avatar_img} id={flow_type.avatar_color ? flow_type.avatar_color : flow_type.id} ><img src={flow_type.avatar_img ? flow_type.avatar_img : (flow_type.type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
<div ref={messageRef} className="text-sm max-w-[calc(100%-100px)]">
{/* <div className="chat-start-zk relative">
{data.message.toString() ? mkdown : <span className="loading loading-ring loading-md"></span>} */}
{/* @user */}
{/* {data.receiver && <p className="text-blue-500 text-sm">@ {data.receiver.user_name}</p>} */}
{/* 光标 */}
{/* {data.message.toString() && !data.end && <div className="animate-cursor absolute w-2 h-5 ml-1 bg-gray-600" style={{ left: cursor.x, top: cursor.y }}></div>} */}
{/* </div> */}
{data.message.toString() ?
<div ref={messageRef} className="text-sm max-w-[calc(100%-24px)]">
<div className="chat-start-zk relative">
{mkdown}
{/* @user */}
{data.receiver && <p className="text-blue-500 text-sm">@ {data.receiver.user_name}</p>}
@@ -93,10 +114,33 @@ export default function MessageBs({ data, onUnlike = () => { }, onSource }: { da
</div>
: <div><LoadIcon className="text-gray-400" /></div>
}
{/* 赞 踩 */}
{!!data.id && data.end && <Thumbs
id={data.id}
data={data.liked}
onCopy={handleCopyMessage}
onDislike={onUnlike}
className="chat-start-btn"
></Thumbs>
}
</div>
{/* <div className="flex gap-2">
<div className="w-6 h-6 min-w-6 flex justify-center items-center rounded-full" style={{ background: avatarColor }} ><AvatarIcon /></div>
{data.message.toString() ?
<div ref={messageRef} className="text-sm max-w-[calc(100%-24px)]">
{mkdown} */}
{/* @user */}
{/* {data.receiver && <p className="text-blue-500 text-sm">@ {data.receiver.user_name}</p>} */}
{/* 光标 */}
{/* {data.message.toString() && !data.end && <div className="animate-cursor absolute w-2 h-5 ml-1 bg-gray-600" style={{ left: cursor.x, top: cursor.y }}></div>} */}
{/* </div>
: <div><LoadIcon className="text-gray-400" /></div>
}
</div> */}
</div>
{/* 附加信息 */}
{
{/* {
!!data.id && data.end && <div className="flex justify-between mt-2">
<SourceEntry
extra={data.extra}
@@ -115,7 +159,7 @@ export default function MessageBs({ data, onUnlike = () => { }, onSource }: { da
onCopy={handleCopyMessage}
></MessageButtons>
</div>
}
} */}
</div>
</div>
};

View File

@@ -10,7 +10,7 @@ import RunLog from "./RunLog";
import Separator from "./Separator";
import { useMessageStore } from "./messageStore";
export default function MessagePanne({ useName, guideWord, loadMore }) {
export default function MessagePanne({logo, useName, guideWord, loadMore, flow_type }) {
const { t } = useTranslation()
const { chatId, messages } = useMessageStore()
@@ -56,7 +56,7 @@ export default function MessagePanne({ useName, guideWord, loadMore }) {
return () => messagesRef.current?.removeEventListener('scroll', handleScroll)
}, [messagesRef.current, messages, chatId]);
return <div id="message-panne" ref={messagesRef} className="h-full overflow-y-auto scrollbar-hide pt-12 pb-60">
return <div id="message-panne" ref={messagesRef} className="h-full overflow-y-auto scrollbar-hide pt-[50px] pb-60 px-3">
{guideWord && <MessageBs
key={9999}
data={{ message: guideWord, isSend: false, chatKey: '', end: true, user_name: '' }} />}
@@ -75,7 +75,6 @@ export default function MessagePanne({ useName, guideWord, loadMore }) {
} else if (msg.thought) {
type = 'system'
}
switch (type) {
case 'user':
return <MessageUser key={msg.id} useName={useName} data={msg} />;
@@ -83,15 +82,17 @@ export default function MessagePanne({ useName, guideWord, loadMore }) {
return <MessageBs
key={msg.id}
data={msg}
flow_type={flow_type}
onUnlike={(chatId) => { thumbRef.current?.openModal(chatId) }}
onSource={(data) => { sourceRef.current?.openModal(data) }}
/>;
case 'system':
return <MessageSystem key={msg.id} data={msg} />;
// return <RunLog key={msg.id} data={msg} />;
case 'separator':
return <Separator key={msg.id} text={msg.message || t('chat.roundOver')} />;
case 'file':
return <FileBs key={msg.id} data={msg} />;
return <FileBs key={msg.id} data={msg} flow_type={flow_type}/>;
case 'runLog':
return <RunLog key={msg.id} data={msg} />;
default:

View File

@@ -24,7 +24,7 @@ export default function MessageSystem({ data }) {
() => (
data.thought && <ReactMarkdown
linkTarget="_blank"
className="bs-mkdown text-gray-600 dark:text-[white] inline-block break-all max-w-full text-sm [&>pre]:text-wrap"
className="markdown text-gray-600 inline-block break-all max-w-full text-sm"
>
{data.thought.toString()}
</ReactMarkdown>
@@ -33,9 +33,10 @@ export default function MessageSystem({ data }) {
)
const border = { system: 'border-slate-500', question: 'border-amber-500', processing: 'border-cyan-600', answer: 'border-lime-600', report: 'border-slate-500', guide: 'border-none' }
const style = { system: 'style-system', question: 'style-question', processing: 'border-cyan-600', answer: 'style-answer', report: 'style-system' }
return <div className="py-1">
<div className={`relative rounded-sm px-6 py-4 border text-sm ${data.category === 'guide' ? 'bg-[#EDEFF6]' : 'bg-slate-50'} ${border[data.category || 'system']}`}>
<div className={`log rounded-xl whitespace-pre-wrap mt-[14px] relative ${style[data.category || 'system']}`}>
{logMkdown}
{data.category === 'report' && <CopyIcon className=" absolute right-4 top-2 cursor-pointer" onClick={(e) => handleCopy(e.target.parentNode)}></CopyIcon>}
</div>

View File

@@ -3,8 +3,12 @@ import { ChatMessageType } from "@/types/chat";
import { MagnifyingGlassIcon, Pencil2Icon, ReloadIcon } from "@radix-ui/react-icons";
import { useContext } from "react";
import { useMessageStore } from "./messageStore";
import robotU from "../../../assets/robotU.png";
import btnEdit from "../../../assets/chat/btn-edit.png";
import btnReSend from "../../../assets/chat/btn-reSend.png";
export default function MessageUser({ useName, data }: { data: ChatMessageType }) {
export default function MessageUser({ useName = 'xxx', data }: {useName: string, data: ChatMessageType }) {
// export default function MessageUser({ useName, data }: { data: ChatMessageType }) {
const msg = data.message[data.chatKey]
const { appConfig } = useContext(locationContext)
@@ -25,18 +29,34 @@ export default function MessageUser({ useName, data }: { data: ChatMessageType }
}
return <div className="flex justify-end w-full py-1">
<div className="w-fit min-h-8 max-w-[90%]">
<div className="flex items-start avatarZk">
<div className="mr-[10px]">
<div className="chat-end-zk">
{/* {chat.category === 'loading' && <span className="loading loading-spinner loading-xs mr-4 align-middle"></span>} */}
{msg}
</div>
{!Array.isArray(data.message.data) && <div className='chat-end-btn'>
{!running && <img src={btnEdit} onClick={() => handleResend(false)} className="w-[28px] cursor-pointer" alt=""/>}
{!running && <img src={btnReSend} onClick={() => handleResend(true)} className="w-[28px] cursor-pointer" alt=""/>}
{/* <img src={btnDel} className="w-[28px] cursor-pointer" alt=""/> */}
{/* {!showSearch && <Search size={18} className="cursor-pointer hover:text-blue-600 text-blue-400" onClick={() => onSearch(chat.message[chat.chatKey])}></Search>} */}
</div>}
</div>
{/* <p className="mr-[20px] text-[14px]">{userName}</p> */}
<img src={robotU} className="w-[50px]" alt=""/>
</div>
{/* <div className="w-fit min-h-8 max-w-[90%]">
{useName && <p className="text-gray-600 text-xs mb-2 text-right">{useName}</p>}
<div className="rounded-2xl px-6 py-4 bg-[#EEF2FF] dark:bg-[#333A48]">
<div className="mr-[14px] rounded-2xl px-6 py-4 bg-[#EEF2FF] dark:bg-[#333A48]">
<div className="flex gap-2 ">
<div className="text-[#0D1638] dark:text-[#CFD5E8] text-sm break-all whitespace-break-spaces">{msg}</div>
<div className="w-6 h-6 min-w-6"><img src="/user.png" alt="" /></div>
</div>
</div>
</div> */}
{/* 附加信息 */}
{
{/* {
// 数组类型的 data通常是文件上传消息不展示附加按钮
!Array.isArray(data.message.data) && <div className="flex justify-between mt-2">
!Array.isArray(data.message.data) && <div className="flex justify-between mt-2 mr-[14px]">
<span></span>
<div className="flex gap-2 text-gray-400 cursor-pointer self-end">
{!running && <Pencil2Icon className="hover:text-gray-500" onClick={() => handleResend(false)} />}
@@ -45,6 +65,6 @@ export default function MessageUser({ useName, data }: { data: ChatMessageType }
</div>
</div>
}
</div>
</div> */}
</div>
};

View File

@@ -34,6 +34,8 @@ export default function RunLog({ data }) {
// if (!knowledge) throw new Error('调试日志无法匹配到使用的知识库详情id:' + data.message.tool_key)
title = knowledge ? `${data.end ? '已搜索' : '正在搜索'} ${knowledge.name}` : '知识库已被删除,无法获取知识库名'
} else if (data.category === 'processing') {
title = data.thought.slice(0, 50)+"..."
}
return [title, lost]
}, [assistantState, data])
@@ -41,16 +43,16 @@ export default function RunLog({ data }) {
return <div className="py-1">
<div className="rounded-sm border">
<div className="flex justify-between items-center px-4 py-2 cursor-pointer" onClick={() => setOpen(!open)}>
<div className="flex items-center font-bold gap-2 text-sm">
<div className="flex items-center font-bold gap-2 text-sm text-[#fff]">
{
data.end ? <ToastIcon type={lost ? 'error' : 'success'} /> :
<LoadIcon className="text-primary duration-300" />
}
<span>{title}</span>
<span className="text-[#666]">{title}</span>
</div>
<CaretDownIcon className={open && 'rotate-180'} />
</div>
<div className={cname('bg-gray-100 px-4 py-2 text-gray-500 overflow-hidden text-sm ', open ? 'h-auto' : 'h-0 p-0')}>
<div className={cname('bg-[#0B1F26] px-4 py-2 text-[#ccc] overflow-hidden text-sm ', open ? 'h-auto' : 'h-0 p-0')}>
<p>{data.thought}</p>
</div>
</div>

View File

@@ -1,10 +1,22 @@
import ChatInput from "./ChatInput";
import MessagePanne from "./MessagePanne";
export default function ChatComponent({ clear = false, questions = [], form = false, useName, inputForm = null, guideWord, wsUrl, onBeforSend, loadMore = () => { } }) {
export default function ChatComponent({
stop = false,
logo = '',
clear = false,
questions = [],
form = false,
useName,
inputForm = null,
guideWord,
wsUrl,
onBeforSend,
type,
loadMore = () => { }
}) {
return <div className="relative h-full">
<MessagePanne useName={useName} guideWord={guideWord} loadMore={loadMore}></MessagePanne>
<MessagePanne logo={logo} useName={useName} guideWord={guideWord} loadMore={loadMore} flow_type={type}></MessagePanne>
<ChatInput clear={clear} questions={questions} form={form} wsUrl={wsUrl} inputForm={inputForm} onBeforSend={onBeforSend} ></ChatInput>
</div>
};

View File

@@ -4,6 +4,7 @@ import { MessageDB, getChatHistory } from '@/controllers/API'
import { ChatMessageType } from '@/types/chat'
import { cloneDeep } from 'lodash'
import { create } from 'zustand'
import { formatDate } from '@/util/utils';
/**
* 会话消息管理
@@ -19,6 +20,8 @@ type State = {
/** 没有更多历史纪录 */
historyEnd: boolean,
messages: ChatMessageType[]
/** 历史回话独立存储 */
hisMessages: ChatMessageType[]
/**
* 控制引导问题的显示状态
*/
@@ -26,8 +29,8 @@ type State = {
}
type Actions = {
loadHistoryMsg: (flowid: string, chatId: string) => Promise<void>;
loadMoreHistoryMsg: (flowid: string) => Promise<void>;
loadHistoryMsg: (flowid: string, chatId: string, data: { appendHistory: boolean, lastMsg: string }, flow_type: string) => Promise<void>;
loadMoreHistoryMsg: (flowid: string, appendHistory: boolean, flow_type: string) => Promise<void>;
destory: () => void;
createSendMsg: (inputs: any, inputKey?: string) => void;
createWsMsg: (data: any) => void;
@@ -38,6 +41,7 @@ type Actions = {
insetSystemMsg: (text: string) => void;
insetBsMsg: (text: string) => void;
setShowGuideQuestion: (text: boolean) => void;
clearMsgs: () => void;
}
@@ -76,36 +80,55 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
running: false,
chatId: '',
messages: [],
hisMessages: [],
historyEnd: false,
showGuideQuestion: false,
setShowGuideQuestion(bln: boolean) {
set({ showGuideQuestion: bln })
},
async loadHistoryMsg(flowid, chatId) {
const res = await getChatHistory(flowid, chatId, 30, 0)
async loadHistoryMsg(flowid, chatId, { appendHistory, lastMsg }, flow_type) {
const res = await getChatHistory(flowid, chatId, 30, 0, flow_type)
const msgs = handleHistoryMsg(res)
currentChatId = chatId
set({ historyEnd: false, messages: msgs.reverse() })
const hisMessages = appendHistory ? [] : msgs.reverse()
if (hisMessages.length) {
hisMessages.push({
...bsMsgItem,
id: Math.random() * 1000000,
category: 'divider',
message: lastMsg,
})
}
set({
historyEnd: false,
messages: appendHistory ? msgs.reverse() : [],
hisMessages
})
},
async loadMoreHistoryMsg(flowid) {
async loadMoreHistoryMsg(flowid, appendHistory, flow_type) {
if (get().running) return // 会话进行中禁止加载more历史
if (get().historyEnd) return // 没有更多历史纪录
const chatId = get().chatId
const prevMsgs = get().messages
// 最后一条消息id不存在忽略 loadmore
if (!prevMsgs[0]?.id) return
const res = await getChatHistory(flowid, chatId, 10, prevMsgs[0]?.id || 0)
const res = await getChatHistory(flowid, chatId, 10, prevMsgs[0]?.id || 0, flow_type)
// 过滤非同一会话消息
if (res[0]?.chat_id !== currentChatId) {
return console.warn('loadMoreHistoryMsg chatId not match, ignore')
}
const msgs = handleHistoryMsg(res)
if (msgs.length) {
set({ messages: [...msgs.reverse(), ...prevMsgs] })
set({ [appendHistory ? 'messages' : 'hisMessages']: [...msgs.reverse(), ...prevMsgs] })
} else {
set({ historyEnd: true })
}
},
clearMsgs() {
setTimeout(() => {
set({ hisMessages: [], messages: [], historyEnd: true })
}, 0);
},
destory() {
set({ chatId: '', messages: [] })
},
@@ -122,7 +145,8 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
category: '',
files: [],
end: false,
user_name: ""
user_name: "",
update_time: formatDate(new Date(), 'yyyy-MM-ddTHH:mm:ss')
}]
}))
},
@@ -151,13 +175,20 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
// if (wsdata.end) {
// debugger
// }
console.log('change updateCurrentMessage');
// console.log('change updateCurrentMessage');
const messages = get().messages
const isRunLog = runLogsTypes.includes(wsdata.category);
// run log类型存在嵌套情况使用 extra 匹配 currentMessage; 否则取最近
const currentMessageIndex = isRunLog ?
messages.findLastIndex((msg) => msg.extra === wsdata.extra)
: messages.findLastIndex((msg) => !runLogsTypes.includes(msg.category))
let currentMessageIndex = 0
for (let i = messages.length - 1; i >= 0; i--) {
if (isRunLog && messages[i].extra === wsdata.extra) {
currentMessageIndex = i;
break;
} else if (!isRunLog && !runLogsTypes.includes(messages[i].category)) {
currentMessageIndex = i;
break;
}
}
const currentMessage = messages[currentMessageIndex]
const newCurrentMessage = {
@@ -166,10 +197,15 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
id: isRunLog ? wsdata.extra : wsdata.messageId, // 每条消息必唯一
message: isRunLog ? JSON.parse(wsdata.message) : currentMessage.message + wsdata.message,
thought: currentMessage.thought + (wsdata.thought ? `${wsdata.thought}\n` : ''),
files: wsdata.files || null,
files: wsdata.files || [],
category: wsdata.category || '',
source: wsdata.source
}
// 无id补上如文件解析完成消息后端无返回messageid
if (!newCurrentMessage.id) {
newCurrentMessage.id = Math.random() * 1000000
// console.log('msg:', newCurrentMessage);
}
messages[currentMessageIndex] = newCurrentMessage
// 会话特殊处理,兼容后端的缺陷

View File

@@ -0,0 +1,24 @@
import { useEffect, useRef } from "react";
export default function LoadMore({ onScrollLoad }) {
// scroll load
const footerRef = useRef<HTMLDivElement>(null)
useEffect(function () {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
onScrollLoad()
}
});
}, {
// root: null, // 视口
rootMargin: '0px', // 视口的边距
threshold: 0.1 // 目标元素超过视口的10%即触发回调
});
observer.observe(footerRef.current);
return () => footerRef.current && observer.unobserve(footerRef.current);
}, [])
return <div ref={footerRef} style={{ height: 20 }}></div>
};

View File

@@ -0,0 +1,200 @@
import { Popover, PopoverContent, PopoverTrigger } from "@/components/bs-ui/popover";
import { useEffect, useMemo, useRef, useState } from "react";
import { SearchInput } from "@/components/bs-ui/input";
import { Checkbox } from "@/components/bs-ui/checkBox";
import { Label } from "@/components/bs-ui/label";
import { Pencil2Icon } from "@radix-ui/react-icons";
import { Trash2 } from "lucide-react";
import { Input } from "@/components/bs-ui/input";
import { useToast } from "@/components/bs-ui/toast/use-toast";
import { useTranslation } from "react-i18next";
import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm";
import { PlusIcon } from "@radix-ui/react-icons";
import { useContext } from "react";
import { userContext } from "@/contexts/userContext";
import {
createLabelApi, updateLabelApi,
createLinkApi, deleteLinkApi,
deleteLabelApi
} from "@/controllers/API/label";
import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
import biaoqianBian from "../../../assets/npc/biaoqian-bian.png"
import biaoqianDel from "../../../assets/npc/biaoqian-del.png"
export enum UPDATETYPE {
DELETELINK = 'deleteLink',
CREATELINK = 'createLink',
UPDATENAME = 'updateName',
CREATELABEL = 'createLabel',
DELETELABEL = 'deleteLabel'
}
export default function LabelSelect({ labels, all, children, resource, onUpdate }) {
const [open, setOpen] = useState(false)
const [data, setData] = useState([])
const { user } = useContext(userContext)
const dataRef = useRef([])
const { message } = useToast()
const { t } = useTranslation()
useEffect(() => {
const newData = all.map(d => {
const res = labels.find(l => l.value === d.value)
return res ? { ...d, selected: true } : d
})
dataRef.current = newData
setData(newData)
}, [all])
const handleEdit = (id) => {
setData(pre => pre.map(d => ({ ...d, edit: d.value === id })))
}
const handleChecked = (id) => {
const type = resource.type === 'assist' ? 3 : 2
setData(pre => {
const newData = pre.map(d => d.value === id ? { ...d, selected: !d.selected } : d)
const cur = newData.find(d => d.value === id)
captureAndAlertRequestErrorHoc(
(cur.selected ? createLinkApi(id, resource.id, type) : deleteLinkApi(id, resource.id, type)).then(() => {
onUpdate({
type: cur.selected ? UPDATETYPE.CREATELINK : UPDATETYPE.DELETELINK,
data: cur
})
})
)
return newData
})
}
const nameRef = useRef('')
const handleChange = (e, id) => {
nameRef.current = id ? dataRef.current.find(d => d.value === id).label : ''
setData(pre => pre.map(d => d.value === id ? { ...d, label: e.target.value } : d))
}
const errorRestName = (preName, id) => { //错误发生回退初值
preName
? setData(pre => pre.map(d => d.value === id ? { ...d, label: nameRef.current } : d))
: setData(pre => pre.filter(d => d.value))
}
const handleSave = async (e, id) => {
if (e.key === 'Enter') {
setData(pre => pre.map(d => d.value === id ? { ...d, edit: false } : d))
const label = data.find(d => d.value === id)
if (label.label.length > 10) {
errorRestName(nameRef.current, id)
return message({ title: t('prompt'), variant: 'warning', description: t('tag.labelMaxLength') })
}
const err = await captureAndAlertRequestErrorHoc(updateLabelApi(id, label.label).then((res: any) => {
setData(pre => {
const newData = pre.map(d => d.value ? d : { ...d, label: res.name, value: res.id })
dataRef.current = newData
return newData
})
onUpdate({
type: UPDATETYPE.UPDATENAME,
data: label
})
return message({ title: t('prompt'), variant: 'success', description: id ? t('updateSuccess') : t('createSuccess') })
}))
if (!err) {
errorRestName(nameRef.current, id)
}
}
}
const handleDelete = (label) => {
bsConfirm({
title: t('prompt'),
desc: t('tag.confirmDeleteLabel', { label: label.label }),
okTxt: "确认",
onOk(next) {
captureAndAlertRequestErrorHoc(deleteLabelApi(label.value).then(() => {
onUpdate({
type: UPDATETYPE.DELETELABEL,
data: label
})
message({ title: t('prompt'), variant: 'success', description: t('deleteSuccess') })
}))
next()
}
})
}
const handleOpenChange = (b) => { // 可用于整体保存
setOpen(b)
setData(pre => pre.map(d => ({ ...d, edit: false })))
}
const [keyword, setKeyword] = useState('')
const handleSearch = (e) => {
const key = e.target.value
setKeyword(key)
const newData = dataRef.current.filter(d => d.label.toUpperCase().includes(key.toUpperCase()))
setData(newData)
}
const handleAdd = () => {
if (keyword.length > 10) {
return message({ title: t('prompt'), variant: 'warning', description: t('tag.labelMaxLength') })
}
createLabelApi(keyword).then((res: any) => {
const addItem = { label: res.name, value: res.id, edit: false, selected: false }
dataRef.current = [addItem, ...dataRef.current]
setData([addItem])
onUpdate({
type: UPDATETYPE.CREATELABEL,
data: res.name
})
})
}
const showAdd = useMemo(() => {
if (data.length === 1 && data[0].label === keyword) {
return false
}
return true
}, [data])
return <Popover open={open} onOpenChange={handleOpenChange}>
<PopoverTrigger asChild>
{children}
</PopoverTrigger>
<PopoverContent className="z-[20]" onClick={(e) => e.stopPropagation()}>
<div>
<SearchInput placeholder={t('chat.searchLabels')} value={keyword} onChange={handleSearch} className="w-[240px]"
onKeyDown={(e) => {
if (e.key === 'Enter') {
(!data.length && user.role === 'admin') ? handleAdd() : null
}
}} />
</div>
<div className="mt-4 h-[200px] overflow-y-auto relative biaoqianTab">
{data.map(d => <div className="flex group justify-between px-[10px] h-8 rounded-sm hover:bg-[#2E2406] ">
<div className="flex place-items-center space-x-2">
<Checkbox id={d.value} checked={d.selected} onCheckedChange={() => handleChecked(d.value)} />
{
d.edit
? <Input autoFocus className="h-6 bg-[#1a1a1a] text-[#fff]" type="text" value={d.label || ''}
onChange={(e) => handleChange(e, d.value)}
onKeyDown={(e) => handleSave(e, d.value)} />
: <Label htmlFor={d.value} className="cursor-pointer text-[#999999]">{d.label}</Label>
}
</div>
{user.role === 'admin' && <div className="flex place-items-center space-x-4 opacity-0 group-hover:opacity-100">
<img src={biaoqianBian} alt="" onClick={() => handleEdit(d.value)} className="w-[14px] cursor-pointer"/>
<img src={biaoqianDel} alt="" onClick={() => handleDelete(d)} className="w-[14px] ml-[14px] cursor-pointer"/>
{/* <Pencil2Icon className="cursor-pointer" onClick={() => handleEdit(d.value)} /> */}
{/* <Trash2 size={16} onClick={() => handleDelete(d)} className="text-gray-600 cursor-pointer" /> */}
</div>}
</div>)}
{(showAdd && keyword != '' && user.role === 'admin') && <div onClick={handleAdd}
className="absolute cursor-pointer w-[68px] h-[20px] border border-[#FFD025] text-[9px] text-[#FFD025] flex items-center justify-center right-0 bottom-0" style={{borderRadius:"3px"}}>
</div>}
</div>
</PopoverContent>
</Popover>
}

View File

@@ -0,0 +1,48 @@
import MultiSelect from "@/components/bs-ui/select/multi";
import { getUsersApi } from "@/controllers/API/user";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
export default function UsersSelect({ multiple = false, lockedValues = [], value, disabled = false, onChange, children }:
{ multiple?: boolean, lockedValues?: any[], value: any, disabled?: boolean, onChange: (a: any) => any, children?: (fun: any) => React.ReactNode }) {
const { t } = useTranslation()
const [options, setOptions] = useState<any>([]);
const originOptionsRef = useRef([])
const pageRef = useRef(1)
const reload = (page, name) => {
getUsersApi({ page, pageSize: 40, name }).then(res => {
pageRef.current = page
originOptionsRef.current = res.data
const opts = res.data.map(el => ({ label: el.user_name, value: el.user_id }))
setOptions(_ops => page > 1 ? [..._ops, ...opts] : opts)
})
}
useEffect(() => {
reload(1, '')
}, [])
// 加载更多
const loadMore = (name) => {
reload(pageRef.current + 1, name)
}
return <MultiSelect
className=" max-w-[630px]"
multiple={multiple}
value={value}
lockedValues={lockedValues}
disabled={disabled}
options={options}
placeholder={'请选择用户'}
searchPlaceholder={'搜索用户名称'}
onChange={onChange}
onLoad={() => reload(1, '')}
onSearch={(val) => reload(1, val)}
onScrollLoad={(val) => loadMore(val)}
>
{children?.(reload)}
</MultiSelect>
};

Some files were not shown because too many files have changed in this diff Show More