From de93d5dacad748be46d33ee2f7de88fed57941d8 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Mon, 3 Nov 2025 18:08:47 +0800 Subject: [PATCH 01/75] feat: init knowledge workflow --- ui/src/locales/lang/en-US/views/index.ts | 4 +- .../lang/en-US/views/knowledge-workflow.ts | 479 ++++++++++++++ ui/src/locales/lang/en-US/views/knowledge.ts | 3 + ui/src/locales/lang/zh-CN/views/index.ts | 4 +- .../lang/zh-CN/views/knowledge-workflow.ts | 492 ++++++++++++++ ui/src/locales/lang/zh-CN/views/knowledge.ts | 3 + ui/src/locales/lang/zh-Hant/views/index.ts | 4 +- .../lang/zh-Hant/views/knowledge-workflow.ts | 465 ++++++++++++++ .../locales/lang/zh-Hant/views/knowledge.ts | 3 + ui/src/permission/knowledge/system-share.ts | 277 ++------ .../permission/knowledge/workspace-share.ts | 7 +- ui/src/permission/knowledge/workspace.ts | 453 +++++++++---- ui/src/router/modules/document.ts | 353 +++++++--- ui/src/router/routes.ts | 7 + ui/src/views/application-workflow/index.vue | 2 +- .../component/DropdownMenu.vue | 350 ++++++++++ .../component/NodeContent.vue | 122 ++++ .../component/PublishHistory.vue | 157 +++++ ui/src/views/knowledge-workflow/index.vue | 605 ++++++++++++++++++ ui/src/workflow/nodes/ai-chat-node/index.vue | 10 +- ui/src/workflow/nodes/base-node/index.vue | 8 +- .../workflow/nodes/image-generate/index.vue | 6 +- .../workflow/nodes/image-to-video/index.vue | 78 +-- .../workflow/nodes/image-understand/index.vue | 57 +- .../nodes/intent-classify-node/index.vue | 6 +- ui/src/workflow/nodes/mcp-node/index.vue | 18 +- .../nodes/parameter-extraction-node/index.vue | 6 +- ui/src/workflow/nodes/question-node/index.vue | 6 +- ui/src/workflow/nodes/reranker-node/index.vue | 6 +- .../nodes/speech-to-text-node/index.vue | 14 +- .../nodes/text-to-speech-node/index.vue | 6 +- ui/src/workflow/nodes/text-to-video/index.vue | 16 +- .../workflow/nodes/video-understand/index.vue | 58 +- 33 files changed, 3485 insertions(+), 600 deletions(-) create mode 100644 ui/src/locales/lang/en-US/views/knowledge-workflow.ts create mode 100644 ui/src/locales/lang/zh-CN/views/knowledge-workflow.ts create mode 100644 ui/src/locales/lang/zh-Hant/views/knowledge-workflow.ts create mode 100644 ui/src/views/knowledge-workflow/component/DropdownMenu.vue create mode 100644 ui/src/views/knowledge-workflow/component/NodeContent.vue create mode 100644 ui/src/views/knowledge-workflow/component/PublishHistory.vue create mode 100644 ui/src/views/knowledge-workflow/index.vue diff --git a/ui/src/locales/lang/en-US/views/index.ts b/ui/src/locales/lang/en-US/views/index.ts index d5af697347c..954a5ccba46 100644 --- a/ui/src/locales/lang/en-US/views/index.ts +++ b/ui/src/locales/lang/en-US/views/index.ts @@ -16,6 +16,7 @@ import chatLog from './chat-log' import chatUser from './chat-user' import operateLog from './operate-log' import shared from './shared' +import knowledgeWorkflow from './knowledge-workflow' export default { login, model, @@ -34,5 +35,6 @@ export default { chatLog, chatUser, operateLog, - shared + shared, + knowledgeWorkflow, } diff --git a/ui/src/locales/lang/en-US/views/knowledge-workflow.ts b/ui/src/locales/lang/en-US/views/knowledge-workflow.ts new file mode 100644 index 00000000000..dc20ddd6adc --- /dev/null +++ b/ui/src/locales/lang/en-US/views/knowledge-workflow.ts @@ -0,0 +1,479 @@ +export default { + node: 'Node', + nodeName: 'Node Name', + baseComponent: 'Basic', + nodeSetting: 'Node Settings', + workflow: 'Workflow', + searchBar: { + placeholder: 'Search by name', + }, + info: { + previewVersion: 'Preview Version:', + saveTime: 'Last Saved:', + }, + setting: { + restoreVersion: 'Restore Previous Version"', + restoreCurrentVersion: 'Restore to This Version', + addComponent: 'Add', + releaseHistory: 'Release History', + autoSave: 'Auto Save', + latestRelease: 'Latest Release', + copyParam: 'Copy Parameters', + debug: 'Run', + exit: 'Exit', + exitSave: 'Save & Exit', + }, + tip: { + noData: 'No related results found', + nameMessage: 'Name cannot be empty!', + onlyRight: 'Connections can only be made from the right anchor', + notRecyclable: 'Loop connections are not allowed', + onlyLeft: 'Connections can only be made to the left anchor', + applicationNodeError: 'This application is unavailable', + toolNodeError: 'This tool node is unavailable', + repeatedNodeError: 'A node with this name already exists', + cannotCopy: 'Cannot be copied', + copyError: 'Node already copied', + paramErrorMessage: 'Parameter already exists: ', + saveMessage: 'Current changes have not been saved. Save before exiting?', + }, + delete: { + confirmTitle: 'Confirm to delete this node?', + deleteMessage: 'This node cannot be deleted', + }, + control: { + zoomOut: 'Zoom Out', + zoomIn: 'Zoom In', + fitView: 'Fit to Screen', + retract: 'Collapse All', + extend: 'Expand All', + beautify: 'Auto-Arrange', + }, + variable: { + global: 'Global Variable', + Referencing: 'Referenced Variable', + ReferencingRequired: 'Referenced variable is required', + ReferencingError: 'Invalid referenced variable', + NoReferencing: 'Referenced variable does not exist', + placeholder: 'Please select a variable', + inputPlaceholder: 'Please enter variable', + loop: 'Loop Variable', + }, + condition: { + title: 'Execution Condition', + front: 'Precondition', + AND: 'All', + OR: 'Any', + text: 'After the connected node is executed, execute the current node', + }, + validate: { + startNodeRequired: 'Start node is required', + startNodeOnly: 'Only one start node is allowed', + baseNodeRequired: 'Base information node is required', + baseNodeOnly: 'Only one base information node is allowed', + notInWorkFlowNode: 'Node not in workflow', + noNextNode: 'Next node does not exist', + nodeUnavailable: 'Node unavailable', + needConnect1: 'The branch of the node needs to be connected', + cannotEndNode: 'This node cannot be used as an end node', + loopNodeBreakNodeRequired: 'Wireless loop must have a Break node', + }, + nodes: { + classify: { + aiCapability: 'AI capability', + businessLogic: 'Business logic', + other: 'Other', + dataProcessing: 'Data Processing', + }, + startNode: { + label: 'Start', + question: 'User Question', + currentTime: 'Current Time', + }, + baseNode: { + label: 'Base Information', + appName: { + label: 'App Name', + }, + appDescription: { + label: 'App Description', + }, + fileUpload: { + label: 'File Upload', + tooltip: 'When enabled, the Q&A page will display a file upload button.', + }, + FileUploadSetting: { + title: 'File Upload Settings', + maxFiles: 'Maximum number of files per upload', + fileLimit: 'Maximum size per file (MB)', + fileUploadType: { + label: 'File types allowed for upload', + documentText: 'Requires "Document Content Extraction" node to parse document content', + imageText: 'Requires "Image Understanding" node to parse image content', + videoText: 'Requires "Video Understanding" node to parse video content', + audioText: 'Requires "Speech-to-Text" node to parse audio content', + }, + }, + }, + aiChatNode: { + label: 'AI Chat', + text: 'Chat with an AI model', + answer: 'AI Content', + returnContent: { + label: 'Return Content', + tooltip: `If turned off, the content of this node will not be output to the user. + If you want the user to see the output of this node, please turn on the switch.`, + }, + defaultPrompt: 'Known Information', + think: 'Thinking Process', + historyMessage: 'Historical chat records', + }, + searchKnowledgeNode: { + label: 'Knowledge Retrieval', + text: 'Allows you to query text content related to user questions from the Knowledge', + paragraph_list: 'List of retrieved segments', + is_hit_handling_method_list: 'List of segments that meet direct response criteria', + result: 'Search Result', + directly_return: 'Content of segments that meet direct response criteria', + searchParam: 'Retrieval Parameters', + searchQuestion: { + label: 'Question', + placeholder: 'Please select a search question', + requiredMessage: 'Please select a search question', + }, + }, + searchDocumentNode: { + label: 'Document Tag Retrieval', + text: 'Search for documents that meet the conditions based on the document label within the specified search scope', + selectKnowledge: 'Search Scope', + searchSetting: 'Search Settings', + custom: 'Manual', + customTooltip: 'Manually set tag filtering conditions', + auto: 'Automatic', + autoTooltip: 'Automatically filter setting tag conditions based on the search question', + document_list: 'Document List', + knowledge_list: 'Knowledge Base List', + result: 'Search Results', + searchParam: 'Search Parameters', + select_variable: 'Select Variable', + valueMessage: `Value or {name} `, + showKnowledge: { + label: 'Results are displayed in the knowledge source', + requiredMessage: 'Please set parameters', + }, + searchQuestion: { + label: 'Search Question', + placeholder: 'Please select a search question', + requiredMessage: 'Please select a search question', + }, + }, + questionNode: { + label: 'Question Optimization', + text: 'Optimize and improve the current question based on historical chat records to better match knowledge segments', + result: 'Optimized Question Result', + systemDefault: `#Role +You are a master of problem optimization, adept at accurately inferring user intentions based on context and optimizing the questions raised by users. + +##Skills +###Skill 1: Optimizing Problems +2. Receive user input questions. +3. Carefully analyze the meaning of the problem based on the context. +4. Output optimized problems. + +##Limitations: +-Only return the optimized problem without any additional explanation or clarification. +-Ensure that the optimized problem accurately reflects the original problem intent and does not alter the original intention.`, + }, + conditionNode: { + label: 'Conditional Branch', + text: 'Trigger different nodes based on conditions', + branch_name: 'Branch Name', + conditions: { + label: 'Conditions', + info: 'Meets the following', + requiredMessage: 'Please select conditions', + }, + valueMessage: 'Please enter a value', + addCondition: 'Add Condition', + addBranch: 'Add Branch', + }, + replyNode: { + label: 'Specified Reply', + text: 'Specify reply content, referenced variables will be converted to strings for output', + replyContent: 'Reply Content', + }, + rerankerNode: { + label: 'Multi-path Recall', + text: 'Use a re-ranking model to refine retrieval results from multiple knowledge sources', + result_list: 'Re-ranked Results List', + result: 'Re-ranking Result', + rerankerContent: { + label: 'Re-ranking Content', + requiredMessage: 'Please select re-ranking content', + }, + higher: 'Higher', + ScoreTooltip: 'The higher the Score, the stronger the relevance.', + max_paragraph_char_number: 'Maximum Character', + reranker_model: { + label: 'Rerank', + placeholder: 'Please select a rerank', + }, + }, + formNode: { + label: 'Form Input', + text: 'Collect user input during Q&A and use it in subsequent processes', + form_content_format1: 'Hello, please fill out the form below:', + form_content_format2: 'Click the [Submit] button after filling it out.', + form_data: 'All Form Content', + formContent: { + label: 'Form Output Content', + requiredMessage: + 'Please set the output content of this node, { form } is a placeholder for the form.', + tooltip: 'Define the output content of this node. { form } is a placeholder for the form', + }, + formAllContent: 'All Form Content', + formSetting: 'Form Configuration', + }, + documentExtractNode: { + label: 'Document Content Extraction', + text: 'Extract content from documents', + content: 'Document Content', + }, + imageUnderstandNode: { + label: 'Image Understanding', + text: 'Analyze images to identify objects, scenes, and provide answers', + answer: 'AI Content', + model: { + label: 'Vision Model', + requiredMessage: 'Please select a vision model', + }, + image: { + label: 'Select Image', + requiredMessage: 'Please select an image', + }, + }, + variableAssignNode: { + label: 'Variable Assign', + text: 'Update the value of the global variable', + assign: 'Set Value', + }, + variableAggregationNode: { + label: 'Variable Aggregation', + text: 'Aggregate variables of each group according to the aggregation strategy', + Strategy: 'Aggregation Strategy', + placeholder: 'Return the first non-null value of each group', + placeholder1: 'Return the set of variables for each group', + group: { + noneError: 'Name cannot be empty', + dupError: 'Name cannot be duplicated', + }, + addGroup: 'Add Group', + editGroup: 'Edit Group', + }, + mcpNode: { + label: 'MCP Node', + text: 'Call external MCP services to process data', + getToolsSuccess: 'Tools fetched successfully', + getTool: 'Fetch Tools', + toolParam: 'Tool Parameters', + mcpServerTip: 'Please enter MCP server configuration in JSON format', + mcpToolTip: 'Please select a tool', + configLabel: 'MCP Server Config (Only SSE/Streamable HTTP calls are supported)', + reference: 'Reference MCP', + }, + imageGenerateNode: { + label: 'Image Generation', + text: 'Generate images based on provided text content', + answer: 'AI Content', + model: { + label: 'Image Generation Model', + requiredMessage: 'Please select an image generation model', + }, + prompt: { + label: 'Positive Prompt', + tooltip: 'Describe elements and visual features you want in the generated image', + }, + negative_prompt: { + label: 'Negative Prompt', + tooltip: 'Describe elements you want to exclude from the generated image', + placeholder: + 'Please describe content you do not want to generate, such as color, bloody content', + }, + }, + textToVideoGenerate: { + label: 'Text-to-Video', + text: 'Generate video based on provided text content', + answer: 'AI Response Content', + model: { + label: 'Text-to-Video Model', + requiredMessage: 'Please select a text-to-video model', + }, + prompt: { + label: 'Prompt (Positive)', + tooltip: + 'Positive prompt, used to describe elements and visual features expected in the generated video', + }, + negative_prompt: { + label: 'Prompt (Negative)', + tooltip: + "Negative prompt, used to describe content you don't want to see in the video, which can restrict the video generation", + placeholder: + "Please describe video content you don't want to generate, such as: colors, bloody content", + }, + }, + imageToVideoGenerate: { + label: 'Image-to-Video', + text: 'Generate video based on provided images', + answer: 'AI Response Content', + model: { + label: 'Image-to-Video Model', + requiredMessage: 'Please select an image-to-video model', + }, + prompt: { + label: 'Prompt (Positive)', + tooltip: + 'Positive prompt, used to describe elements and visual features expected in the generated video', + }, + negative_prompt: { + label: 'Prompt (Negative)', + tooltip: + "Negative prompt, used to describe content you don't want to see in the video, which can restrict the video generation", + placeholder: + "Please describe video content you don't want to generate, such as: colors, bloody content", + }, + first_frame: { + label: 'First Frame Image', + requiredMessage: 'Please select the first frame image', + }, + last_frame: { + label: 'Last Frame Image', + requiredMessage: 'Please select the last frame image', + }, + }, + speechToTextNode: { + label: 'Speech2Text', + text: 'Convert audio to text through speech recognition model', + stt_model: { + label: 'Speech Recognition Model', + }, + audio: { + label: 'Select Audio File', + placeholder: 'Please select an audio file', + }, + }, + textToSpeechNode: { + label: 'TTS', + text: 'Convert text to audio through speech synthesis model', + tts_model: { + label: 'Speech Synthesis Model', + }, + content: { + label: 'Select Text Content', + }, + }, + toolNode: { + label: 'Custom Tool', + text: 'Execute custom scripts to achieve data processing', + }, + intentNode: { + label: 'IntentNode', + text: 'Match user questions with user-defined intent classifications', + other: 'other', + error2: 'Repeated intent', + placeholder: 'Please choose a classification option', + classify: { + label: 'Intent classify', + placeholder: 'Please input', + }, + input: { + label: 'Input', + }, + }, + applicationNode: { + label: 'APP Node', + }, + loopNode: { + label: 'Loop Node', + text: 'Repeat a series of tasks by setting the number of loops and logic', + loopType: { + label: 'Loop Type', + requiredMessage: 'Please select a loop type', + arrayLoop: 'Array Loop', + numberLoop: 'Loop for Specified Times', + infiniteLoop: 'Infinite Loop', + }, + loopNumber: { + label: 'Loop Number', + requiredMessage: 'Please enter the number of loops', + }, + loopArray: { + label: 'Circular Array', + requiredMessage: 'Circular Array is required', + placeholder: 'Please select a circular array', + }, + loopSetting: 'Loop Settings', + loopDetail: 'Loop Details', + }, + loopStartNode: { + label: 'Loop Start', + loopIndex: 'Index', + loopItem: 'Loop Element', + }, + loopBodyNode: { + label: 'Loop Body', + text: 'Loop Body', + }, + loopContinueNode: { + label: 'Continue', + text: 'Used to terminate the current loop and proceed to the next one.', + isContinue: 'Continue', + }, + loopBreakNode: { + label: 'Break', + text: 'Terminate the current loop and exit the loop body', + isBreak: 'Break', + }, + variableSplittingNode: { + label: 'Variable Splitting', + text: 'By configuring JSON Path expressions, parse and split the input JSON format variable', + result: 'Result', + splitVariables: 'Split Variables', + inputVariables: 'Input Variable', + addVariables: 'Add Variables', + editVariables: 'Edit Variables', + variableListPlaceholder: 'Please add split variables', + expression: { + label: 'Expression', + placeholder: 'Please enter expression', + tooltip: 'Please use JSON Path expressions to split variables, e.g.: $.store.book', + }, + }, + parameterExtractionNode: { + label: 'Parameter Extraction', + text: 'Use AI models to extract structured parameters', + extractParameters: { + label: 'Extract Parameters', + variableListPlaceholder: 'Please add extraction parameters', + parameterType: 'Parameter Type', + }, + }, + }, + compare: { + is_null: 'Is null', + is_not_null: 'Is not null', + contain: 'Contains', + not_contain: 'Does not contain', + eq: 'Equal to', + ge: 'Greater than or equal to', + gt: 'Greater than', + le: 'Less than or equal to', + lt: 'Less than', + len_eq: 'Length equal to', + len_ge: 'Length greater than or equal to', + len_gt: 'Length greater than', + len_le: 'Length less than or equal to', + len_lt: 'Length less than', + }, + SystemPromptPlaceholder: 'System Prompt, can reference variables in the system, such as', + UserPromptPlaceholder: 'User Prompt, can reference variables in the system, such as', +} diff --git a/ui/src/locales/lang/en-US/views/knowledge.ts b/ui/src/locales/lang/en-US/views/knowledge.ts index b8307c421ac..d5736838a78 100644 --- a/ui/src/locales/lang/en-US/views/knowledge.ts +++ b/ui/src/locales/lang/en-US/views/knowledge.ts @@ -6,6 +6,9 @@ export default { searchBar: { placeholder: 'Search by name', }, + operation: { + publish: 'Publish', + }, setting: { vectorization: 'Vectorization', sync: 'Sync', diff --git a/ui/src/locales/lang/zh-CN/views/index.ts b/ui/src/locales/lang/zh-CN/views/index.ts index d5af697347c..954a5ccba46 100644 --- a/ui/src/locales/lang/zh-CN/views/index.ts +++ b/ui/src/locales/lang/zh-CN/views/index.ts @@ -16,6 +16,7 @@ import chatLog from './chat-log' import chatUser from './chat-user' import operateLog from './operate-log' import shared from './shared' +import knowledgeWorkflow from './knowledge-workflow' export default { login, model, @@ -34,5 +35,6 @@ export default { chatLog, chatUser, operateLog, - shared + shared, + knowledgeWorkflow, } diff --git a/ui/src/locales/lang/zh-CN/views/knowledge-workflow.ts b/ui/src/locales/lang/zh-CN/views/knowledge-workflow.ts new file mode 100644 index 00000000000..a712ba81e21 --- /dev/null +++ b/ui/src/locales/lang/zh-CN/views/knowledge-workflow.ts @@ -0,0 +1,492 @@ +export default { + node: '节点', + nodeName: '节点名称', + baseComponent: '基础组件', + nodeSetting: '节点设置', + workflow: '工作流', + searchBar: { + placeholder: '按名称搜索', + }, + info: { + previewVersion: '预览版本:', + saveTime: '保存时间:', + }, + setting: { + restoreVersion: '恢复版本', + restoreCurrentVersion: '恢复此版本', + addComponent: '添加组件', + releaseHistory: '发布历史', + autoSave: '自动保存', + latestRelease: '最近发布', + copyParam: '复制参数', + debug: '调试', + exit: '直接退出', + exitSave: '保存并退出', + }, + tip: { + noData: '没有找到相关结果', + nameMessage: '名字不能为空!', + onlyRight: '只允许从右边的锚点连出', + notRecyclable: '不可循环连线', + onlyLeft: '只允许连接左边的锚点', + applicationNodeError: '该应用不可用', + toolNodeError: '该工具不可用', + repeatedNodeError: '节点名称已存在!', + cannotCopy: '不能被复制', + copyError: '已复制节点', + paramErrorMessage: '参数已存在: ', + saveMessage: '当前的更改尚未保存,是否保存后退出?', + }, + delete: { + confirmTitle: '确定删除该节点?', + deleteMessage: '节点不允许删除', + }, + control: { + zoomOut: '缩小', + zoomIn: '放大', + fitView: '适应', + retract: '收起全部节点', + extend: '展开全部节点', + beautify: '一键美化', + }, + variable: { + global: '全局变量', + chat: '会话变量', + Referencing: '引用变量', + ReferencingRequired: '引用变量必填', + ReferencingError: '引用变量错误', + NoReferencing: '不存在的引用变量', + placeholder: '请选择变量', + inputPlaceholder: '请输入变量', + loop: '循环变量', + }, + condition: { + title: '执行条件', + front: '前置', + AND: '所有', + OR: '任一', + text: '连线节点执行完,执行当前节点', + }, + validate: { + startNodeRequired: '开始节点必填', + startNodeOnly: '开始节点只能有一个', + baseNodeRequired: '基本信息节点必填', + baseNodeOnly: '基本信息节点只能有一个', + notInWorkFlowNode: '未在流程中的节点', + noNextNode: '不存在的下一个节点', + nodeUnavailable: '节点不可用', + needConnect1: '节点的', + needConnect2: '分支需要连接', + cannotEndNode: '节点不能当做结束节点', + loopNodeBreakNodeRequired: '无限循环 必须存在 Break 节点', + }, + nodes: { + classify: { + aiCapability: 'AI能力', + businessLogic: '业务逻辑', + other: '其他', + dataProcessing: '数据处理', + }, + startNode: { + label: '开始', + question: '用户问题', + currentTime: '当前时间', + }, + baseNode: { + label: '基本信息', + appName: { + label: '应用名称', + }, + appDescription: { + label: '应用描述', + }, + fileUpload: { + label: '文件上传', + tooltip: '开启后,问答页面会显示上传文件的按钮。', + }, + FileUploadSetting: { + title: '文件上传设置', + maxFiles: '单次上传最多文件数', + fileLimit: '每个文件最大(MB)', + fileUploadType: { + label: '上传的文件类型', + documentText: '需要使用“文档内容提取”节点解析文档内容', + imageText: '需要使用“视觉模型”节点解析图片内容', + audioText: '需要使用“语音转文本”节点解析音频内容', + videoText: '需要使用“视频理解”节点解析视频内容', + otherText: '需要自行解析该类型文件', + }, + }, + }, + aiChatNode: { + label: 'AI 对话', + text: '与 AI 大模型进行对话', + answer: 'AI 回答内容', + returnContent: { + label: '返回内容', + tooltip: `关闭后该节点的内容则不输出给用户。 + 如果你想让用户看到该节点的输出内容,请打开开关。`, + }, + defaultPrompt: '已知信息', + think: '思考过程', + historyMessage: '历史聊天记录', + }, + searchKnowledgeNode: { + label: '知识库检索', + text: '关联知识库,查找与问题相关的分段', + paragraph_list: '检索结果的分段列表', + is_hit_handling_method_list: '满足直接回答的分段列表', + result: '检索结果', + directly_return: '满足直接回答的分段内容', + searchParam: '检索参数', + showKnowledge: { + label: '结果显示在知识来源中', + requiredMessage: '请设置参数', + }, + searchQuestion: { + label: '检索问题', + placeholder: '请选择检索问题', + requiredMessage: '请选择检索问题', + }, + }, + searchDocumentNode: { + label: '文档标签检索', + text: '从设定的检索范围中,根据文档标签检索出满足条件的文档', + selectKnowledge: '检索范围', + searchSetting: '检索设置', + custom: '手动', + customTooltip: '手动设置标签过滤条件', + auto: '自动', + autoTooltip: '根据检索问题自动匹配文档标签', + document_list: '文档列表', + knowledge_list: '知识库列表', + result: '检索结果', + searchParam: '检索参数', + select_variable: '选择变量', + valueMessage: `值或{name}`, + showKnowledge: { + label: '结果显示在知识来源中', + requiredMessage: '请设置参数', + }, + searchQuestion: { + label: '检索问题', + placeholder: '请选择检索问题', + requiredMessage: '请选择检索问题', + }, + }, + questionNode: { + label: '问题优化', + text: '根据历史聊天记录优化完善当前问题,更利于匹配知识库分段', + result: '问题优化结果', + systemDefault: `# 角色 +你是一位问题优化大师,擅长根据上下文精准揣测用户意图,并对用户提出的问题进行优化。 + +## 技能 +### 技能 1: 优化问题 +2. 接收用户输入的问题。 +3. 依据上下文仔细分析问题含义。 +4. 输出优化后的问题。 + +## 限制: +- 仅返回优化后的问题,不进行额外解释或说明。 +- 确保优化后的问题准确反映原始问题意图,不得改变原意。`, + }, + conditionNode: { + label: '判断器', + text: '根据不同条件执行不同的节点', + branch_name: '分支名称', + conditions: { + label: '条件', + info: '符合以下', + requiredMessage: '请选择条件', + }, + valueMessage: '请输入值', + addCondition: '添加条件', + addBranch: '添加分支', + }, + replyNode: { + label: '指定回复', + text: '指定回复内容,引用变量会转换为字符串进行输出', + replyContent: '回复内容', + }, + rerankerNode: { + label: '多路召回', + text: '使用重排模型对多个知识库的检索结果进行二次召回', + result_list: '重排结果列表', + result: '重排结果', + rerankerContent: { + label: '重排内容', + requiredMessage: '请选择重排内容', + }, + higher: '高于', + ScoreTooltip: 'Score越高相关性越强。', + max_paragraph_char_number: '最大引用字符数', + reranker_model: { + label: '重排模型', + placeholder: '请选择重排模型', + }, + }, + formNode: { + label: '表单收集', + text: '在问答过程中用于收集用户信息,可以根据收集到表单数据执行后续流程', + form_content_format1: '你好,请先填写下面表单内容:', + form_content_format2: '填写后请点击【提交】按钮进行提交。', + form_data: '表单全部内容', + formContent: { + label: '表单输出内容', + requiredMessage: '请表单输出内容', + tooltip: '设置执行该节点输出的内容,{ form } 为表单的占位符。', + }, + formAllContent: '表单全部内容', + formSetting: '表单配置', + }, + documentExtractNode: { + label: '文档内容提取', + text: '提取文档中的内容', + content: '文档内容', + }, + imageUnderstandNode: { + label: '图片理解', + text: '识别出图片中的对象、场景等信息回答用户问题', + answer: 'AI 回答内容', + model: { + label: '视觉模型', + requiredMessage: '请选择视觉模型', + }, + image: { + label: '选择图片', + requiredMessage: '请选择图片', + }, + }, + variableAggregationNode: { + label: '变量聚合', + text: '按聚合策略聚合每组的变量', + Strategy: '聚合策略', + placeholder: '返回每组的第一个非空值', + placeholder1: '返回每组变量的集合', + group: { + noneError: '名称不能为空', + dupError: '名称不能重复', + }, + addGroup: '添加分组', + editGroup: '编辑分组', + }, + variableAssignNode: { + label: '变量赋值', + text: '更新全局变量的值', + assign: '赋值', + }, + mcpNode: { + label: 'MCP 调用', + text: '通过SSE/Streamable HTTP方式执行MCP服务中的工具', + getToolsSuccess: '获取工具成功', + getTool: '获取工具', + toolParam: '工具参数', + mcpServerTip: '请输入JSON格式的MCP服务器配置', + mcpToolTip: '请选择工具', + configLabel: 'MCP Server Config (仅支持SSE/Streamable HTTP调用方式)', + reference: '引用MCP', + }, + imageGenerateNode: { + label: '图片生成', + text: '根据提供的文本内容生成图片', + answer: 'AI 回答内容', + model: { + label: '图片生成模型', + requiredMessage: '请选择图片生成模型', + }, + prompt: { + label: '提示词(正向)', + tooltip: '正向提示词,用来描述生成图像中期望包含的元素和视觉特点', + }, + negative_prompt: { + label: '提示词(负向)', + tooltip: '反向提示词,用来描述不希望在画面中看到的内容,可以对画面进行限制。', + placeholder: '请描述不想生成的图片内容,比如:颜色、血腥内容', + }, + }, + textToVideoGenerate: { + label: '文生视频', + text: '根据提供的文本内容生成视频', + answer: 'AI 回答内容', + model: { + label: '文生视频模型', + requiredMessage: '请选择文生视频模型', + }, + prompt: { + label: '提示词(正向)', + tooltip: '正向提示词,用来描述生成视频中期望包含的元素和视觉特点', + }, + negative_prompt: { + label: '提示词(负向)', + tooltip: '反向提示词,用来描述不希望在视频中看到的内容,可以对视频进行限制。', + placeholder: '请描述不想生成的视频内容,比如:颜色、血腥内容', + }, + }, + videoUnderstandNode: { + label: '视频理解', + text: '识别出视频中的对象、场景等信息回答用户问题', + answer: 'AI 回答内容', + model: { + label: '视觉模型', + requiredMessage: '请选择视觉模型', + }, + image: { + label: '选择视频', + requiredMessage: '请选择视频', + }, + }, + imageToVideoGenerate: { + label: '图生视频', + text: '根据提供的图片生成视频', + answer: 'AI 回答内容', + model: { + label: '图生视频模型', + requiredMessage: '请选择图生视频模型', + }, + prompt: { + label: '提示词(正向)', + tooltip: '正向提示词,用来描述生成视频中期望包含的元素和视觉特点', + }, + negative_prompt: { + label: '提示词(负向)', + tooltip: '反向提示词,用来描述不希望在视频中看到的内容,可以对视频进行限制。', + placeholder: '请描述不想生成的视频内容,比如:颜色、血腥内容', + }, + first_frame: { + label: '首帧图片', + requiredMessage: '请选择首帧图片', + }, + last_frame: { + label: '尾帧图片', + requiredMessage: '请选择尾帧图片', + }, + }, + speechToTextNode: { + label: '语音转文本', + text: '将音频通过语音识别模型转换为文本', + stt_model: { + label: '语音识别模型', + }, + audio: { + label: '选择语音文件', + placeholder: '请选择语音文件', + }, + }, + textToSpeechNode: { + label: '文本转语音', + text: '将文本通过语音合成模型转换为音频', + tts_model: { + label: '语音合成模型', + }, + content: { + label: '选择文本内容', + }, + }, + toolNode: { + label: '自定义工具', + text: '通过执行自定义脚本,实现数据处理', + }, + intentNode: { + label: '意图识别', + text: '将用户问题与用户预设的意图分类进行匹配', + other: '其他', + error2: '意图重复', + placeholder: '请选择分类项', + classify: { + label: '意图分类', + placeholder: '请输入', + }, + input: { + label: '输入', + }, + }, + applicationNode: { + label: '应用节点', + }, + loopNode: { + label: '循环节点', + text: '通过设置循环次数和逻辑,重复执行一系列任务', + loopType: { + label: '循环类型', + requiredMessage: '请选择循环类型', + arrayLoop: '数组循环', + numberLoop: '指定次数循环', + infiniteLoop: '无限循环', + }, + loopNumber: { + label: '循环次数', + requiredMessage: '循环次数必填', + }, + loopArray: { + label: '循环数组', + requiredMessage: '循环数组必填', + placeholder: '请选择循环数组', + }, + loopSetting: '循环设置', + loopDetail: '循环详情', + }, + loopStartNode: { + label: '循环开始', + loopIndex: '下标', + loopItem: '循环元素', + }, + loopBodyNode: { + label: '循环体', + text: '循环体', + }, + loopContinueNode: { + label: 'Continue', + text: '用于终止当前循环,执行下次循环', + isContinue: 'Continue', + }, + loopBreakNode: { + label: 'Break', + text: '终止当前循环,跳出循环体', + isBreak: 'Break', + }, + variableSplittingNode: { + label: '变量拆分', + text: '通过配置JSON Path 表达式,对输入的 JSON 格式变量进行解析和拆分', + splitVariables: '拆分变量', + inputVariables: '输入变量', + addVariables: '添加变量', + editVariables: '编辑变量', + variableListPlaceholder: '请添加拆分变量', + expression: { + label: '表达式', + placeholder: '请输入表达式', + tooltip: '请使用JSON Path 表达式拆分变量,例如:$.store.book', + }, + }, + parameterExtractionNode: { + label: '参数提取', + text: '利用 AI 模型提取结构化参数', + extractParameters: { + label: '提取参数', + variableListPlaceholder: '请添加提取参数', + parameterType: '参数类型', + }, + }, + }, + compare: { + is_null: '为空', + is_not_null: '不为空', + contain: '包含', + not_contain: '不包含', + eq: '等于', + ge: '大于等于', + gt: '大于', + le: '小于等于', + lt: '小于', + len_eq: '长度等于', + len_ge: '长度大于等于', + len_gt: '长度大于', + len_le: '长度小于等于', + len_lt: '长度小于', + is_true: '为真', + is_not_true: '不为真', + }, + SystemPromptPlaceholder: '系统提示词,可以引用系统中的变量:如', + UserPromptPlaceholder: '用户提示词,可以引用系统中的变量:如', +} diff --git a/ui/src/locales/lang/zh-CN/views/knowledge.ts b/ui/src/locales/lang/zh-CN/views/knowledge.ts index 681ebe5045d..63946ff9c83 100644 --- a/ui/src/locales/lang/zh-CN/views/knowledge.ts +++ b/ui/src/locales/lang/zh-CN/views/knowledge.ts @@ -6,6 +6,9 @@ export default { vectorization: '向量化', sync: '同步', }, + operation: { + publish: '发布', + }, tip: { professionalMessage: '社区版最多支持 50 个知识库,如需拥有更多知识库,请升级为专业版。', syncSuccess: '同步任务发送成功', diff --git a/ui/src/locales/lang/zh-Hant/views/index.ts b/ui/src/locales/lang/zh-Hant/views/index.ts index 5a5b899035e..7da9ed6dcf3 100644 --- a/ui/src/locales/lang/zh-Hant/views/index.ts +++ b/ui/src/locales/lang/zh-Hant/views/index.ts @@ -16,6 +16,7 @@ import applicationWorkflow from './application-workflow' import login from './login' import operateLog from './operate-log' import shared from './shared' +import knowledgeWorkflow from './knowledge-workflow' export default { application, applicationOverview, @@ -34,5 +35,6 @@ export default { role, workspace, chatUser, - shared + shared, + knowledgeWorkflow, } diff --git a/ui/src/locales/lang/zh-Hant/views/knowledge-workflow.ts b/ui/src/locales/lang/zh-Hant/views/knowledge-workflow.ts new file mode 100644 index 00000000000..5f77aa346b2 --- /dev/null +++ b/ui/src/locales/lang/zh-Hant/views/knowledge-workflow.ts @@ -0,0 +1,465 @@ +export default { + node: '節點', + nodeName: '節點名稱', + baseComponent: '基礎組件', + nodeSetting: '節點設置', + workflow: '工作流', + searchBar: { + placeholder: '按名稱搜索', + }, + info: { + previewVersion: '預覽版本:', + saveTime: '保存時間:', + }, + setting: { + restoreVersion: '恢復版本', + restoreCurrentVersion: '恢復此版本', + addComponent: '添加組件', + releaseHistory: '發布歷史', + autoSave: '自動保存', + latestRelease: '最近發布', + copyParam: '複製參數', + debug: '調試', + exit: '直接退出', + exitSave: '保存並退出', + }, + tip: { + noData: '沒有找到相關結果', + nameMessage: '名字不能為空!', + onlyRight: '只允許從右邊的錨點連出', + notRecyclable: '不可循環連線', + onlyLeft: '只允許連接左邊的錨點', + applicationNodeError: '該應用不可用', + toolNodeError: '該函數不可用', + repeatedNodeError: '節點名稱已存在!', + cannotCopy: '不能被複製', + copyError: '已複製節點', + paramErrorMessage: '參數已存在: ', + saveMessage: '當前修改未保存,是否保存後退出?', + }, + delete: { + confirmTitle: '確定刪除該節點?', + deleteMessage: '節點不允許刪除', + }, + control: { + zoomOut: '縮小', + zoomIn: '放大', + fitView: '適應', + retract: '收起全部節點', + extend: '展開全部節點', + beautify: '一鍵美化', + }, + variable: { + global: '全局變量', + Referencing: '引用變量', + ReferencingRequired: '引用變量必填', + ReferencingError: '引用變量錯誤', + NoReferencing: '不存在的引用變量', + placeholder: '請選擇變量', + inputPlaceholder: '請輸入變量', + loop: '循環變量', + }, + condition: { + title: '執行條件', + front: '前置', + AND: '所有', + OR: '任一', + text: '連線節點執行完,執行當前節點', + }, + validate: { + startNodeRequired: '開始節點必填', + startNodeOnly: '開始節點只能有一個', + baseNodeRequired: '基本信息節點必填', + baseNodeOnly: '基本信息節點只能有一個', + notInWorkFlowNode: '未在流程中的節點', + noNextNode: '不存在的下一個節點', + nodeUnavailable: '節點不可用', + needConnect1: '節點的', + needConnect2: '分支需要連接', + cannotEndNode: '節點不能當做結束節點', + loopNodeBreakNodeRequired: '無限循環必須存在Break節點', + }, + nodes: { + classify: { + aiCapability: 'AI能力', + businessLogic: '業務邏輯', + other: '其他', + dataProcessing: '數據處理', + }, + startNode: { + label: '開始', + question: '用戶問題', + currentTime: '當前時間', + }, + baseNode: { + label: '基本信息', + appName: { + label: '應用名稱', + }, + appDescription: { + label: '應用描述', + }, + fileUpload: { + label: '文件上傳', + tooltip: '開啟後,問答頁面會顯示上傳文件的按鈕。', + }, + FileUploadSetting: { + title: '文件上傳設置', + maxFiles: '單次上傳最多文件數', + fileLimit: '每個文件最大(MB)', + fileUploadType: { + label: '上傳的文件類型', + documentText: '需要使用「文檔內容提取」節點解析文檔內容', + imageText: '需要使用「圖片理解」節點解析圖片內容', + videoText: '需要使用「視頻理解」節點解析視頻內容', + audioText: '需要使用「語音轉文本」節點解析音頻內容', + }, + }, + }, + aiChatNode: { + label: 'AI 對話', + text: '與 AI 大模型進行對話', + answer: 'AI 回答內容', + returnContent: { + label: '返回內容', + tooltip: `關閉後該節點的內容則不輸出給用戶。 + 如果你想讓用戶看到該節點的輸出內容,請打開開關。`, + }, + defaultPrompt: '已知信息', + think: '思考過程', + historyMessage: '歷史聊天記錄', + }, + searchKnowledgeNode: { + label: '知識庫檢索', + text: '關聯知識庫,查找與問題相關的分段', + paragraph_list: '檢索結果的分段列表', + is_hit_handling_method_list: '滿足直接回答的分段列表', + result: '檢索結果', + directly_return: '滿足直接回答的分段內容', + searchParam: '檢索參數', + searchQuestion: { + label: '檢索問題', + placeholder: '請選擇檢索問題', + requiredMessage: '請選擇檢索問題', + }, + }, + searchDocumentNode: { + label: '文檔標籤檢索', + text: '從設定的檢索範圍中,根據文檔標籤檢索出符合條件的文檔', + selectKnowledge: '檢索範圍', + searchSetting: '檢索設定', + custom: '手動', + customTooltip: '手動設置標籤過濾條件', + auto: '自動', + autoTooltip: '根據檢索問題自動匹配文檔標簽', + document_list: '文件清單', + knowledge_list: '知識庫列表', + result: '檢索結果', + searchParam: '檢索參數', + select_variable: '選擇變數', + valueMessage: `值或{name}`, + showKnowledge: { + label: '結果顯示在知識來源', + requiredMessage: '請設定參數', + }, + searchQuestion: { + label: '檢索問題', + placeholder: '請選擇檢索問題', + requiredMessage: '請選擇檢索問題', + }, + }, + questionNode: { + label: '問題優化', + text: '根據歷史聊天記錄優化完善當前問題,更利於匹配知識庫分段', + result: '問題優化結果', + systemDefault: `# 角色 +妳是壹位問題優化大師,擅長根據上下文精準揣測用戶意圖,並對用戶提出的問題進行優化。 + +## 技能 +### 技能 1: 優化問題 +2. 接收用戶輸入的問題。 +3. 依據上下文仔細分析問題含義。 +4. 輸出優化後的問題。 + +## 限制: +- 僅返回優化後的問題,不進行額外解釋或說明。 +- 確保優化後的問題準確反映原始問題意圖,不得改變原意。`, + }, + conditionNode: { + label: '判斷器', + text: '根據不同條件執行不同的節點', + branch_name: '分支名稱', + conditions: { + label: '條件', + info: '符合以下', + requiredMessage: '請選擇條件', + }, + valueMessage: '請輸入值', + addCondition: '添加條件', + addBranch: '添加分支', + }, + replyNode: { + label: '指定回覆', + text: '指定回覆內容,引用變量會轉換為字符串進行輸出', + replyContent: '回覆內容', + }, + rerankerNode: { + label: '多路召回', + text: '使用重排模型對多個知識庫的檢索結果進行二次召回', + result_list: '重排結果列表', + result: '重排結果', + rerankerContent: { + label: '重排內容', + requiredMessage: '請選擇重排內容', + }, + higher: '高於', + ScoreTooltip: 'Score越高相關性越強。', + max_paragraph_char_number: '最大引用字符數', + reranker_model: { + label: '重排模型', + placeholder: '請選擇重排模型', + }, + }, + formNode: { + label: '表單收集', + text: '在問答過程中用於收集用戶信息,可以根據收集到表單數據執行後續流程', + form_content_format1: '你好,請先填寫下面表單內容:', + form_content_format2: '填寫後請點擊【提交】按鈕進行提交。', + form_data: '表單全部內容', + formContent: { + label: '表單輸出內容', + requiredMessage: '請表單輸出內容', + tooltip: '設置執行該節點輸出的內容,{ form } 為表單的佔位符。', + }, + formAllContent: '表單全部內容', + formSetting: '表單配置', + }, + documentExtractNode: { + label: '文檔內容提取', + text: '提取文檔中的內容', + content: '文檔內容', + }, + imageUnderstandNode: { + label: '圖片理解', + text: '識別出圖片中的物件、場景等信息回答用戶問題', + answer: 'AI 回答內容', + model: { + label: '視覺模型', + requiredMessage: '請選擇視覺模型', + }, + image: { + label: '選擇圖片', + requiredMessage: '請選擇圖片', + }, + }, + variableAssignNode: { + label: '變數賦值', + text: '更新全域變數的值', + assign: '賦值', + }, + variableAggregationNode: { + label: '變量聚合', + text: '按聚合策略聚合每組的變量', + Strategy: '聚合策略', + placeholder: '返回每組的第一個非空值', + placeholder1: '返回每組變量的集合', + group: { + noneError: '名稱不能為空', + dupError: '名稱不能重複', + }, + addGroup: '添加分組', + editGroup: '編輯分組', + }, + mcpNode: { + label: 'MCP 調用', + text: '通過SSE/Streamable HTTP方式執行MCP服務中的工具', + getToolsSuccess: '獲取工具成功', + getTool: '獲取工具', + toolParam: '工具參數', + mcpServerTip: '請輸入JSON格式的MCP服務器配置', + mcpToolTip: '請選擇工具', + configLabel: 'MCP Server Config (僅支持SSE/Streamable HTTP調用方式)', + reference: '引用MCP', + }, + imageGenerateNode: { + label: '圖片生成', + text: '根據提供的文本內容生成圖片', + answer: 'AI 回答內容', + model: { + label: '圖片生成模型', + requiredMessage: '請選擇圖片生成模型', + }, + prompt: { + label: '提示詞(正向)', + tooltip: '正向提示詞,用來描述生成圖像中期望包含的元素和視覺特點', + }, + negative_prompt: { + label: '提示詞(負向)', + tooltip: '反向提示詞,用來描述不希望在畫面中看到的內容,可以對畫面進行限制。', + placeholder: '請描述不想生成的圖片內容,比如:顏色、血腥內容', + }, + }, + textToVideoGenerate: { + label: '文生影片', + text: '根據提供的文字內容生成影片', + answer: 'AI 回答內容', + model: { + label: '文生影片模型', + requiredMessage: '請選擇文生影片模型', + }, + prompt: { + label: '提示詞(正向)', + tooltip: '正向提示詞,用來描述生成影片中期望包含的元素和視覺特點', + }, + negative_prompt: { + label: '提示詞(負向)', + tooltip: '反向提示詞,用來描述不希望在影片中看到的內容,可以對影片進行限制。', + placeholder: '請描述不想生成的影片內容,例如:顏色、血腥內容', + }, + }, + imageToVideoGenerate: { + label: '圖生影片', + text: '根據提供的圖片生成影片', + answer: 'AI 回答內容', + model: { + label: '圖生影片模型', + requiredMessage: '請選擇圖生影片模型', + }, + prompt: { + label: '提示詞(正向)', + tooltip: '正向提示詞,用來描述生成影片中期望包含的元素和視覺特點', + }, + negative_prompt: { + label: '提示詞(負向)', + tooltip: '反向提示詞,用來描述不希望在影片中看到的內容,可以對影片進行限制。', + placeholder: '請描述不想生成的影片內容,例如:顏色、血腥內容', + }, + first_frame: { + label: '首幀圖片', + requiredMessage: '請選擇首幀圖片', + }, + last_frame: { + label: '尾幀圖片', + requiredMessage: '請選擇尾幀圖片', + }, + }, + speechToTextNode: { + label: '語音轉文本', + text: '將音頻通過語音識別模型轉換為文本', + stt_model: { + label: '語音識別模型', + }, + audio: { + label: '選擇語音文件', + placeholder: '請選擇語音文件', + }, + }, + textToSpeechNode: { + label: '文本轉語音', + text: '將文本通過語音合成模型轉換為音頻', + tts_model: { + label: '語音合成模型', + }, + content: { + label: '選擇文本內容', + }, + }, + toolNode: { + label: '自定義工具', + text: '通過執行自定義腳本,實現數據處理', + }, + intentNode: { + label: '意圖識別', + text: '將用戶問題與用戶預設的意圖分類進行匹配', + other: '其他', + error2: '意圖重複', + placeholder: '請選擇分類項', + classify: { + label: '意圖分類', + placeholder: '請輸入', + }, + input: { + label: '輸入', + }, + }, + applicationNode: { + label: '應用節點', + }, + loopNode: { + label: '循環節點', + text: '通過設置循環次數和邏輯,重複執行一系列任務', + loopType: { + label: '循環類型', + requiredMessage: '請選擇循環類型', + arrayLoop: '數組循環', + numberLoop: '指定次數循環', + infiniteLoop: '無限循環', + }, + loopNumber: { + label: '循環次數', + requiredMessage: '請填寫循環次數', + }, + loopArray: { + label: '循環數組', + requiredMessage: '循環數組必填', + placeholder: '請選擇循環數組', + }, + loopSetting: '循環設置', + loopDetail: '循環詳情', + }, + loopStartNode: { + label: '循環開始', + loopIndex: '下標', + loopItem: '循環元素', + }, + loopBodyNode: { label: '循環體', text: '循環體' }, + loopContinueNode: { + label: 'Continue', + text: '用於終止當前循環,執行下次循環', + isContinue: 'Continue', + }, + loopBreakNode: { label: 'Break', text: '終止當前循環,跳出循環體', isBreak: 'Break' }, + variableSplittingNode: { + label: '變量拆分', + text: '通過配置 JSON Path 表達式,對輸入的 JSON 格式變量進行解析和拆分', + result: '結果', + splitVariables: '拆分變量', + inputVariables: '輸入變量', + addVariables: '添加變量', + editVariables: '編輯變量', + variableListPlaceholder: '請添加折開變數', + expression: { + label: '表達式', + placeholder: '請輸入表達式', + tooltip: '請使用JSON Path 表達式拆分變量,例如:$.store.book', + }, + }, + parameterExtractionNode: { + label: '參數提取', + text: '利用 AI 模型提取結構化參數', + extractParameters: { + label: '提取參數', + variableListPlaceholder: '請添加選取參數', + parameterType: '參數類型', + }, + }, + }, + compare: { + is_null: '為空', + is_not_null: '不為空', + contain: '包含', + not_contain: '不包含', + eq: '等於', + ge: '大於等於', + gt: '大於', + le: '小於等於', + lt: '小於', + len_eq: '長度等於', + len_ge: '長度大於等於', + len_gt: '長度大於', + len_le: '長度小於等於', + len_lt: '長度小於', + }, + SystemPromptPlaceholder: '系統提示詞,可以引用系統中的變量:如', + UserPromptPlaceholder: '用戶提示詞,可以引用系統中的變量:如', +} diff --git a/ui/src/locales/lang/zh-Hant/views/knowledge.ts b/ui/src/locales/lang/zh-Hant/views/knowledge.ts index f791c60fe76..cb75e2e2759 100644 --- a/ui/src/locales/lang/zh-Hant/views/knowledge.ts +++ b/ui/src/locales/lang/zh-Hant/views/knowledge.ts @@ -6,6 +6,9 @@ export default { searchBar: { placeholder: '按名稱搜尋', }, + operation: { + publish: '發佈', + }, setting: { vectorization: '向量化', sync: '同步', diff --git a/ui/src/permission/knowledge/system-share.ts b/ui/src/permission/knowledge/system-share.ts index 76beff8bc50..2546c0701e3 100644 --- a/ui/src/permission/knowledge/system-share.ts +++ b/ui/src/permission/knowledge/system-share.ts @@ -3,235 +3,61 @@ import { ComplexPermission } from '@/utils/permission/type' import { EditionConst, PermissionConst, RoleConst } from '@/utils/permission/data' const share = { is_share: () => false, - create: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_CREATE - ], - 'OR' - ), - sync: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_SYNC - ], - 'OR' - ), - vector: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_VECTOR - ], - 'OR' - ), - generate: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_GENERATE - ], - 'OR' - ), - edit: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_EDIT - ], - 'OR' - ), - export: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_EXPORT - ], - 'OR' - ), - delete: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DELETE - ], - 'OR' - ), - - doc_read: () => false, - doc_create: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_CREATE - ], - 'OR' - ), - doc_vector: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_VECTOR - ], - 'OR' - ), - doc_generate: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_GENERATE - ], - 'OR' - ), - doc_migrate: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_MIGRATE - ], - 'OR' - ), - doc_edit: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_EDIT - ], - 'OR' - ), - doc_sync: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_SYNC - ], - 'OR' - ), - doc_delete: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_DELETE - ], - 'OR' - ), - doc_export: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_EXPORT - ], - 'OR' - ), - doc_download: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_DOWNLOAD_SOURCE_FILE - ], - 'OR' - ), - doc_tag: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_TAG - ], - 'OR' - ), - doc_replace: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_REPLACE - ], - 'OR' - ), - problem_create: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_PROBLEM_CREATE - ], - 'OR' - ), - knowledge_chat_user_read: () => false, - knowledge_chat_user_edit: () => - hasPermission( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_EDIT - ], - 'OR' - ), - problem_read: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_PROBLEM_READ - ], - 'OR' - ), - problem_relate: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_PROBLEM_RELATE - ], - 'OR' - ), - problem_delete: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_PROBLEM_DELETE - ], - 'OR' - ), - problem_edit: () => - hasPermission ( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_PROBLEM_EDIT - ], - 'OR' - ), - tag_read: () => - hasPermission( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_TAG_READ - ], - 'OR', - ), - tag_create: () => - hasPermission( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_TAG_CREATE - ], - 'OR', - ), - tag_edit: () => - hasPermission( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_TAG_EDIT - ], - 'OR', - ), - tag_delete: () => + create: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_CREATE], 'OR'), + sync: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_SYNC], 'OR'), + vector: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_VECTOR], 'OR'), + generate: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_GENERATE], 'OR'), + edit: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_EDIT], 'OR'), + export: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_EXPORT], 'OR'), + delete: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DELETE], 'OR'), + + doc_read: () => false, + doc_create: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_CREATE], 'OR'), + doc_vector: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_VECTOR], 'OR'), + doc_generate: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_GENERATE], 'OR'), + doc_migrate: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_MIGRATE], 'OR'), + doc_edit: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_EDIT], 'OR'), + doc_sync: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_SYNC], 'OR'), + doc_delete: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_DELETE], 'OR'), + doc_export: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_EXPORT], 'OR'), + doc_download: () => hasPermission( - [ - RoleConst.ADMIN, - PermissionConst.SHARED_KNOWLEDGE_TAG_DELETE - ], + [RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_DOWNLOAD_SOURCE_FILE], 'OR', ), + doc_tag: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_TAG], 'OR'), + doc_replace: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_REPLACE], 'OR'), + problem_create: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_PROBLEM_CREATE], 'OR'), + knowledge_chat_user_read: () => false, + knowledge_chat_user_edit: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_EDIT], 'OR'), + problem_read: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_PROBLEM_READ], 'OR'), + problem_relate: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_PROBLEM_RELATE], 'OR'), + problem_delete: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_PROBLEM_DELETE], 'OR'), + problem_edit: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_PROBLEM_EDIT], 'OR'), + tag_read: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_TAG_READ], 'OR'), + tag_create: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_TAG_CREATE], 'OR'), + tag_edit: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_TAG_EDIT], 'OR'), + tag_delete: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_TAG_DELETE], 'OR'), - chat_user_edit: () =>false, + chat_user_edit: () => false, auth: () => false, folderRead: () => false, @@ -241,5 +67,6 @@ const share = { folderAuth: () => false, folderDelete: () => false, hit_test: () => false, + debug: (source_id: string) => true, } export default share diff --git a/ui/src/permission/knowledge/workspace-share.ts b/ui/src/permission/knowledge/workspace-share.ts index 65d9a0b0efe..6b82df5c74c 100644 --- a/ui/src/permission/knowledge/workspace-share.ts +++ b/ui/src/permission/knowledge/workspace-share.ts @@ -12,7 +12,7 @@ const workspaceShare = { delete: () => false, auth: () => false, - doc_read: () => false, + doc_read: () => false, doc_create: () => false, doc_vector: () => false, doc_generate: () => false, @@ -32,13 +32,13 @@ const workspaceShare = { tag_create: () => false, tag_delete: () => false, tag_edit: () => false, - + problem_read: () => false, problem_create: () => false, problem_relate: () => false, problem_delete: () => false, problem_edit: () => false, - chat_user_edit: () =>false, + chat_user_edit: () => false, folderRead: () => false, folderManage: () => false, @@ -47,6 +47,7 @@ const workspaceShare = { folderAuth: () => false, folderDelete: () => false, hit_test: () => false, + debug: (source_id: string) => true, } export default workspaceShare diff --git a/ui/src/permission/knowledge/workspace.ts b/ui/src/permission/knowledge/workspace.ts index a0e7307ab19..0c44a10a275 100644 --- a/ui/src/permission/knowledge/workspace.ts +++ b/ui/src/permission/knowledge/workspace.ts @@ -5,9 +5,14 @@ const workspace = { is_share: () => hasPermission( new ComplexPermission( - [RoleConst.USER.getWorkspaceRole,RoleConst.WORKSPACE_MANAGE.getWorkspaceRole], - [PermissionConst.KNOWLEDGE_READ.getWorkspacePermission,PermissionConst.KNOWLEDGE_READ.getWorkspacePermissionWorkspaceManageRole], - [EditionConst.IS_EE],'OR'), + [RoleConst.USER.getWorkspaceRole, RoleConst.WORKSPACE_MANAGE.getWorkspaceRole], + [ + PermissionConst.KNOWLEDGE_READ.getWorkspacePermission, + PermissionConst.KNOWLEDGE_READ.getWorkspacePermissionWorkspaceManageRole, + ], + [EditionConst.IS_EE], + 'OR', + ), 'OR', ), create: () => @@ -20,350 +25,540 @@ const workspace = { ], 'OR', ), - folderRead: (folder_id: string) => - hasPermission( - [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)],[],'AND'), - RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_FOLDER_READ.getKnowledgeWorkspaceResourcePermission(folder_id), - PermissionConst.KNOWLEDGE_READ.getWorkspacePermissionWorkspaceManageRole, - ], - 'OR' + folderRead: (folder_id: string) => + hasPermission( + [ + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)], + [], + 'AND', + ), + RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, + PermissionConst.KNOWLEDGE_FOLDER_READ.getKnowledgeWorkspaceResourcePermission(folder_id), + PermissionConst.KNOWLEDGE_READ.getWorkspacePermissionWorkspaceManageRole, + ], + 'OR', ), folderManage: () => true, - folderAuth: (folder_id: string) => - hasPermission( - [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)],[],'AND'), - RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), - PermissionConst.KNOWLEDGE_RESOURCE_AUTHORIZATION.getWorkspacePermissionWorkspaceManageRole, - ], - 'OR' - ), - folderCreate: (folder_id: string) => - hasPermission( - [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)],[],'AND'), - RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), - PermissionConst.KNOWLEDGE_CREATE.getWorkspacePermissionWorkspaceManageRole, - ], - 'OR' - ), - folderDelete: (folder_id: string) => - hasPermission( - [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)],[],'AND'), - RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), - PermissionConst.KNOWLEDGE_DELETE.getWorkspacePermissionWorkspaceManageRole, - ], - 'OR' - ), - folderEdit: (folder_id: string) => - hasPermission( - [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)],[],'AND'), - RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), - PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermissionWorkspaceManageRole, - ], - 'OR' - ), - sync: (source_id:string) => - hasPermission( - [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + folderAuth: (folder_id: string) => + hasPermission( + [ + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)], + [], + 'AND', + ), + RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, + PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), + PermissionConst.KNOWLEDGE_RESOURCE_AUTHORIZATION.getWorkspacePermissionWorkspaceManageRole, + ], + 'OR', + ), + folderCreate: (folder_id: string) => + hasPermission( + [ + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)], + [], + 'AND', + ), + RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, + PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), + PermissionConst.KNOWLEDGE_CREATE.getWorkspacePermissionWorkspaceManageRole, + ], + 'OR', + ), + folderDelete: (folder_id: string) => + hasPermission( + [ + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)], + [], + 'AND', + ), + RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, + PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), + PermissionConst.KNOWLEDGE_DELETE.getWorkspacePermissionWorkspaceManageRole, + ], + 'OR', + ), + folderEdit: (folder_id: string) => + hasPermission( + [ + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(folder_id)], + [], + 'AND', + ), + RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, + PermissionConst.KNOWLEDGE_FOLDER_EDIT.getKnowledgeWorkspaceResourcePermission(folder_id), + PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermissionWorkspaceManageRole, + ], + 'OR', + ), + sync: (source_id: string) => + hasPermission( + [ + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_SYNC.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_SYNC.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - vector: (source_id:string) => + vector: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_VECTOR.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_VECTOR.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - generate: (source_id:string) => + generate: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_GENERATE.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_GENERATE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - edit: (source_id:string) => + edit: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_EDIT.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - auth: (source_id:string) => + auth: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_RESOURCE_AUTHORIZATION.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_RESOURCE_AUTHORIZATION.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_RESOURCE_AUTHORIZATION.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - export: (source_id:string) => + export: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_EXPORT.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_EXPORT.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - delete: (source_id:string) => + delete: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_DELETE.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_DELETE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_read: () => false, - doc_create: (source_id:string) => + doc_read: () => false, + doc_create: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_CREATE.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_DOCUMENT_CREATE.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_DOCUMENT_CREATE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_vector: (source_id:string) => + doc_vector: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_VECTOR.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_DOCUMENT_VECTOR.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_DOCUMENT_VECTOR.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_generate: (source_id:string) => + doc_generate: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_GENERATE.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_DOCUMENT_GENERATE.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_DOCUMENT_GENERATE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_migrate: (source_id:string) => + doc_migrate: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_MIGRATE.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_DOCUMENT_MIGRATE.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_DOCUMENT_MIGRATE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_edit: (source_id:string) => + doc_edit: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_DOCUMENT_EDIT.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_DOCUMENT_EDIT.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_sync: (source_id:string) => + doc_sync: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_DOCUMENT_SYNC.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_DOCUMENT_SYNC.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_delete: (source_id:string) => + doc_delete: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_DELETE.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_DOCUMENT_DELETE.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_DOCUMENT_DELETE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_export: (source_id:string) => + doc_export: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_EXPORT.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_DOCUMENT_EXPORT.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_DOCUMENT_EXPORT.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_download: (source_id:string) => + doc_download: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_DOWNLOAD_SOURCE_FILE.getKnowledgeWorkspaceResourcePermission(source_id), - PermissionConst.KNOWLEDGE_DOCUMENT_DOWNLOAD_SOURCE_FILE.getWorkspacePermissionWorkspaceManageRole, + PermissionConst.KNOWLEDGE_DOCUMENT_DOWNLOAD_SOURCE_FILE.getKnowledgeWorkspaceResourcePermission( + source_id, + ), + PermissionConst.KNOWLEDGE_DOCUMENT_DOWNLOAD_SOURCE_FILE + .getWorkspacePermissionWorkspaceManageRole, ], 'OR', - ), - doc_tag: (source_id:string) => + ), + doc_tag: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_DOCUMENT_TAG.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_DOCUMENT_TAG.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - doc_replace: (source_id:string) => + doc_replace: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, - PermissionConst.KNOWLEDGE_DOCUMENT_REPLACE.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_DOCUMENT_REPLACE.getKnowledgeWorkspaceResourcePermission( + source_id, + ), PermissionConst.KNOWLEDGE_DOCUMENT_REPLACE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - knowledge_chat_user_read: (source_id:string) => false, - knowledge_chat_user_edit: (source_id:string) => + knowledge_chat_user_read: (source_id: string) => false, + knowledge_chat_user_edit: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_CHAT_USER_EDIT.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_CHAT_USER_EDIT.getWorkspacePermissionWorkspaceManageRole, - ] - ,'OR' + ], + 'OR', ), - problem_read: (source_id:string) => + problem_read: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_PROBLEM_READ.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_PROBLEM_READ.getWorkspacePermissionWorkspaceManageRole, ], 'OR', - ), - problem_create: (source_id:string) => + ), + problem_create: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_PROBLEM_CREATE.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_PROBLEM_CREATE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - problem_relate: (source_id:string) => + problem_relate: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_PROBLEM_RELATE.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_PROBLEM_RELATE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - problem_delete: (source_id:string) => + problem_delete: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_PROBLEM_DELETE.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_PROBLEM_DELETE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - problem_edit: (source_id:string) => + problem_edit: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_PROBLEM_EDIT.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_PROBLEM_EDIT.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - tag_read: (source_id:string) => + tag_read: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_TAG_READ.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_TAG_READ.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - tag_create: (source_id:string) => + tag_create: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_TAG_CREATE.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_TAG_CREATE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - tag_edit: (source_id:string) => + tag_edit: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_TAG_EDIT.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_TAG_EDIT.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - tag_delete: (source_id:string) => + tag_delete: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_TAG_DELETE.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_TAG_DELETE.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - chat_user_edit: (source_id:string) => + chat_user_edit: (source_id: string) => hasPermission( [ - new ComplexPermission([RoleConst.USER],[PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)],[],'AND'), + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, PermissionConst.KNOWLEDGE_CHAT_USER_EDIT.getKnowledgeWorkspaceResourcePermission(source_id), PermissionConst.KNOWLEDGE_CHAT_USER_EDIT.getWorkspacePermissionWorkspaceManageRole, ], 'OR', ), - hit_test: () => false, + debug: (source_id: string) => true, + hit_test: () => false, } export default workspace diff --git a/ui/src/router/modules/document.ts b/ui/src/router/modules/document.ts index b5c98c511b7..045e5150b0a 100644 --- a/ui/src/router/modules/document.ts +++ b/ui/src/router/modules/document.ts @@ -23,15 +23,28 @@ const DocumentRouter = { permission: [ () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return new ComplexPermission([RoleConst.USER], [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(to ? to.params.id : '',)], [], 'AND') } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return new ComplexPermission( + [RoleConst.USER], + [ + PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ), + ], + [], + 'AND', + ) + } }, () => { const to: any = get_next_route() if (to.params.folderId == 'shared') { return RoleConst.ADMIN - } else if (to.params.folderId == 'resource-management') { } - else { + } else if (to.params.folderId == 'resource-management') { + } else { return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() } }, @@ -39,8 +52,8 @@ const DocumentRouter = { const to: any = get_next_route() if (to.params.folderId == 'shared') { return PermissionConst.SHARED_KNOWLEDGE_DOCUMENT_READ - } else if (to.params.folderId == 'resource-management') { } - else { + } else if (to.params.folderId == 'resource-management') { + } else { return PermissionConst.KNOWLEDGE_DOCUMENT_READ.getKnowledgeWorkspaceResourcePermission( to ? to.params.id : '', ) @@ -48,15 +61,22 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { return PermissionConst.KNOWLEDGE_DOCUMENT_READ.getWorkspacePermissionWorkspaceManageRole() } }, () => { const to: any = get_next_route() if (to.params.folderId == 'share') { - return new ComplexPermission([RoleConst.EXTENDS_USER.getWorkspaceRole()], [PermissionConst.KNOWLEDGE_DOCUMENT_READ.getWorkspacePermission()], [], 'AND') + return new ComplexPermission( + [RoleConst.EXTENDS_USER.getWorkspaceRole()], + [PermissionConst.KNOWLEDGE_DOCUMENT_READ.getWorkspacePermission()], + [], + 'AND', + ) } }, () => { @@ -67,16 +87,36 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return RoleConst.ADMIN } + if (to.params.folderId == 'resource-management') { + return RoleConst.ADMIN + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return PermissionConst.RESOURCE_KNOWLEDGE_DOCUMENT_READ } + if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_DOCUMENT_READ + } }, ], }, component: () => import('@/views/document/index.vue'), }, + { + path: 'setting', + name: 'knowledge-setting', + meta: { + title: '知识库工作流', + icon: 'app-problems', + activeMenu: '/knowledge', + sameRoute: 'knowledge', + }, + redirect: (menu: any) => { + const from = 'workspace' + console.log(`/knowledge/${from}/${menu.params.id}/workflow`) + return `/knowledge/${from}/${menu.params.id}/workflow` + }, + component: () => import('@/views/knowledge/index.vue'), + }, { path: 'problem', name: 'Problem', @@ -91,18 +131,37 @@ const DocumentRouter = { permission: [ () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return new ComplexPermission([RoleConst.USER], [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(to ? to.params.id : '',)], [], 'AND') } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return new ComplexPermission( + [RoleConst.USER], + [ + PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ), + ], + [], + 'AND', + ) + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return PermissionConst.SHARED_KNOWLEDGE_PROBLEM_READ } else if (to.params.folderId == 'resource-management') { } - else { + if (to.params.folderId == 'shared') { + return PermissionConst.SHARED_KNOWLEDGE_PROBLEM_READ + } else if (to.params.folderId == 'resource-management') { + } else { return PermissionConst.KNOWLEDGE_PROBLEM_READ.getKnowledgeWorkspaceResourcePermission( to ? to.params.id : '', ) @@ -110,13 +169,22 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return PermissionConst.KNOWLEDGE_PROBLEM_READ.getWorkspacePermissionWorkspaceManageRole() } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return PermissionConst.KNOWLEDGE_PROBLEM_READ.getWorkspacePermissionWorkspaceManageRole() + } }, () => { const to: any = get_next_route() if (to.params.folderId == 'share') { - return new ComplexPermission([RoleConst.EXTENDS_USER.getWorkspaceRole()], [PermissionConst.KNOWLEDGE_PROBLEM_READ.getWorkspacePermission()], [], 'AND') + return new ComplexPermission( + [RoleConst.EXTENDS_USER.getWorkspaceRole()], + [PermissionConst.KNOWLEDGE_PROBLEM_READ.getWorkspacePermission()], + [], + 'AND', + ) } }, () => { @@ -127,11 +195,15 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return RoleConst.ADMIN } + if (to.params.folderId == 'resource-management') { + return RoleConst.ADMIN + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return PermissionConst.RESOURCE_KNOWLEDGE_PROBLEM_READ } + if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_PROBLEM_READ + } }, ], }, @@ -150,18 +222,37 @@ const DocumentRouter = { permission: [ () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return new ComplexPermission([RoleConst.USER], [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(to ? to.params.id : '',)], [], 'AND') } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return new ComplexPermission( + [RoleConst.USER], + [ + PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ), + ], + [], + 'AND', + ) + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return PermissionConst.SHARED_KNOWLEDGE_HIT_TEST_READ } else if (to.params.folderId == 'resource-management') { } - else { + if (to.params.folderId == 'shared') { + return PermissionConst.SHARED_KNOWLEDGE_HIT_TEST_READ + } else if (to.params.folderId == 'resource-management') { + } else { return PermissionConst.KNOWLEDGE_HIT_TEST_READ.getKnowledgeWorkspaceResourcePermission( to ? to.params.id : '', ) @@ -169,13 +260,22 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return PermissionConst.KNOWLEDGE_HIT_TEST_READ.getWorkspacePermissionWorkspaceManageRole() } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return PermissionConst.KNOWLEDGE_HIT_TEST_READ.getWorkspacePermissionWorkspaceManageRole() + } }, () => { const to: any = get_next_route() if (to.params.folderId == 'share') { - return new ComplexPermission([RoleConst.EXTENDS_USER.getWorkspaceRole()], [PermissionConst.KNOWLEDGE_HIT_TEST_READ.getWorkspacePermission()], [], 'AND') + return new ComplexPermission( + [RoleConst.EXTENDS_USER.getWorkspaceRole()], + [PermissionConst.KNOWLEDGE_HIT_TEST_READ.getWorkspacePermission()], + [], + 'AND', + ) } }, () => { @@ -186,11 +286,15 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return RoleConst.ADMIN } + if (to.params.folderId == 'resource-management') { + return RoleConst.ADMIN + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return PermissionConst.RESOURCE_KNOWLEDGE_HIT_TEST } + if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_HIT_TEST + } }, ], }, @@ -208,61 +312,96 @@ const DocumentRouter = { parentName: 'KnowledgeDetail', resourceType: SourceTypeEnum.KNOWLEDGE, group: 'KnowledgeDetail', - permission: [new ComplexPermission([RoleConst.ADMIN, - () => { - const to: any = get_next_route() - if (to.params.folderId == 'shared') { - return RoleConst.ADMIN - } else if (to.params.folderId == 'resource-management') { return RoleConst.ADMIN } - else { - return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() - } - },], [ + permission: [ + new ComplexPermission( + [ + RoleConst.ADMIN, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + return RoleConst.ADMIN + } else { + return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() + } + }, + ], + [ + () => { + const to: any = get_next_route() + if (to.params.folderId == 'shared') { + return PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_READ + } else if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_CHAT_USER_READ + } else { + return PermissionConst.KNOWLEDGE_CHAT_USER_READ.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ) + } + }, + () => { + const to: any = get_next_route() + if (to.params.folder_id == 'shared') { + return PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_READ + } else if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_CHAT_USER_READ + } else { + return PermissionConst.KNOWLEDGE_CHAT_USER_READ.getWorkspacePermissionWorkspaceManageRole() + } + }, + ], + [EditionConst.IS_EE, EditionConst.IS_PE], + 'OR', + ), () => { const to: any = get_next_route() if (to.params.folderId == 'shared') { - return PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_READ - } else if (to.params.folderId == 'resource-management') { return PermissionConst.RESOURCE_KNOWLEDGE_CHAT_USER_READ } - else { - return PermissionConst.KNOWLEDGE_CHAT_USER_READ.getKnowledgeWorkspaceResourcePermission( - to ? to.params.id : '', + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return new ComplexPermission( + [RoleConst.USER], + [ + PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ), + ], + [EditionConst.IS_EE, EditionConst.IS_PE], + 'AND', + ) + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'share') { + return new ComplexPermission( + [RoleConst.EXTENDS_USER.getWorkspaceRole()], + [PermissionConst.KNOWLEDGE_CHAT_USER_READ.getWorkspacePermission()], + [], + 'AND', ) } }, () => { const to: any = get_next_route() - if (to.params.folder_id == 'shared') { - return PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_READ - } else if (to.params.folderId == 'resource-management') { return PermissionConst.RESOURCE_KNOWLEDGE_CHAT_USER_READ } - else { return PermissionConst.KNOWLEDGE_CHAT_USER_READ.getWorkspacePermissionWorkspaceManageRole() } - }, - ], [EditionConst.IS_EE, EditionConst.IS_PE], 'OR'), - () => { - const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return new ComplexPermission([RoleConst.USER], [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(to ? to.params.id : '',)], [EditionConst.IS_EE, EditionConst.IS_PE], 'AND') } - }, - () => { - const to: any = get_next_route() - if (to.params.folderId == 'share') { - return new ComplexPermission([RoleConst.EXTENDS_USER.getWorkspaceRole()], [PermissionConst.KNOWLEDGE_CHAT_USER_READ.getWorkspacePermission()], [], 'AND') - } - }, - () => { - const to: any = get_next_route() - if (to.params.folderId == 'share') { - return RoleConst.USER.getWorkspaceRole() - } - }, - () => { - const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return RoleConst.ADMIN } - }, - () => { - const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return PermissionConst.RESOURCE_KNOWLEDGE_CHAT_USER_READ } - }, - ] + if (to.params.folderId == 'share') { + return RoleConst.USER.getWorkspaceRole() + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'resource-management') { + return RoleConst.ADMIN + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_CHAT_USER_READ + } + }, + ], }, component: () => import('@/views/chat-user/index.vue'), }, @@ -280,18 +419,37 @@ const DocumentRouter = { permission: [ () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return new ComplexPermission([RoleConst.USER], [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(to ? to.params.id : '',)], [], 'AND') } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return new ComplexPermission( + [RoleConst.USER], + [ + PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ), + ], + [], + 'AND', + ) + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return PermissionConst.SHARED_KNOWLEDGE_EDIT } else if (to.params.folderId == 'resource-management') { } - else { + if (to.params.folderId == 'shared') { + return PermissionConst.SHARED_KNOWLEDGE_EDIT + } else if (to.params.folderId == 'resource-management') { + } else { return PermissionConst.KNOWLEDGE_EDIT.getKnowledgeWorkspaceResourcePermission( to ? to.params.id : '', ) @@ -299,13 +457,22 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'shared') { return RoleConst.ADMIN } else if (to.params.folderId == 'resource-management') { } - else { return PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermissionWorkspaceManageRole() } + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermissionWorkspaceManageRole() + } }, () => { const to: any = get_next_route() if (to.params.folderId == 'share') { - return new ComplexPermission([RoleConst.EXTENDS_USER.getWorkspaceRole()], [PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermission()], [], 'AND') + return new ComplexPermission( + [RoleConst.EXTENDS_USER.getWorkspaceRole()], + [PermissionConst.KNOWLEDGE_EDIT.getWorkspacePermission()], + [], + 'AND', + ) } }, () => { @@ -316,11 +483,15 @@ const DocumentRouter = { }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return RoleConst.ADMIN } + if (to.params.folderId == 'resource-management') { + return RoleConst.ADMIN + } }, () => { const to: any = get_next_route() - if (to.params.folderId == 'resource-management') { return PermissionConst.RESOURCE_KNOWLEDGE_EDIT } + if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_EDIT + } }, ], }, diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index 5f9cc420bff..2bd42b2e0f8 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -36,6 +36,13 @@ export const routes: Array = [ meta: { activeMenu: '/application' }, component: () => import('@/views/application-workflow/index.vue'), }, + // 高级编排 + { + path: '/knowledge/:from/:id/workflow', + name: 'KnowledgeWorkflow', + meta: { activeMenu: '/knowledge' }, + component: () => import('@/views/knowledge-workflow/index.vue'), + }, // 对话 { path: '/chat/:accessToken', diff --git a/ui/src/views/application-workflow/index.vue b/ui/src/views/application-workflow/index.vue index b2b96d46b3d..f82b3d76ddd 100644 --- a/ui/src/views/application-workflow/index.vue +++ b/ui/src/views/application-workflow/index.vue @@ -159,7 +159,7 @@ import { ComplexPermission } from '@/utils/permission/type' import { EditionConst, PermissionConst, RoleConst } from '@/utils/permission/data' import permissionMap from '@/permission' import { loadSharedApi } from '@/utils/dynamics-api/shared-api' -provide('getApplicationDetail', () => detail) +provide('getResourceDetail', () => detail) const { theme } = useStore() const router = useRouter() const route = useRoute() diff --git a/ui/src/views/knowledge-workflow/component/DropdownMenu.vue b/ui/src/views/knowledge-workflow/component/DropdownMenu.vue new file mode 100644 index 00000000000..a0503ae3c43 --- /dev/null +++ b/ui/src/views/knowledge-workflow/component/DropdownMenu.vue @@ -0,0 +1,350 @@ + + + diff --git a/ui/src/views/knowledge-workflow/component/NodeContent.vue b/ui/src/views/knowledge-workflow/component/NodeContent.vue new file mode 100644 index 00000000000..1476242ac00 --- /dev/null +++ b/ui/src/views/knowledge-workflow/component/NodeContent.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/ui/src/views/knowledge-workflow/component/PublishHistory.vue b/ui/src/views/knowledge-workflow/component/PublishHistory.vue new file mode 100644 index 00000000000..707ea88f494 --- /dev/null +++ b/ui/src/views/knowledge-workflow/component/PublishHistory.vue @@ -0,0 +1,157 @@ + + + diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue new file mode 100644 index 00000000000..7874bbdb243 --- /dev/null +++ b/ui/src/views/knowledge-workflow/index.vue @@ -0,0 +1,605 @@ + + + diff --git a/ui/src/workflow/nodes/ai-chat-node/index.vue b/ui/src/workflow/nodes/ai-chat-node/index.vue index 2d1bf9d5174..a4896fe6059 100644 --- a/ui/src/workflow/nodes/ai-chat-node/index.vue +++ b/ui/src/workflow/nodes/ai-chat-node/index.vue @@ -335,7 +335,7 @@ import { useRoute } from 'vue-router' import { resetUrl } from '@/utils/common' import { relatedObject } from '@/utils/array.ts' -const getApplicationDetail = inject('getApplicationDetail') as any +const getResourceDetail = inject('getResourceDetail') as any const route = useRoute() const { @@ -431,14 +431,14 @@ const validate = () => { }) } -const application = getApplicationDetail() +const resource = getResourceDetail() function getSelectModel() { const obj = apiType.value === 'systemManage' ? { model_type: 'LLM', - workspace_id: application.value?.workspace_id, + workspace_id: resource.value?.workspace_id, } : { model_type: 'LLM', @@ -522,7 +522,7 @@ function getToolSelectOptions() { ? { scope: 'WORKSPACE', tool_type: 'CUSTOM', - workspace_id: application.value?.workspace_id, + workspace_id: resource.value?.workspace_id, } : { scope: 'WORKSPACE', @@ -545,7 +545,7 @@ function getMcpToolSelectOptions() { ? { scope: 'WORKSPACE', tool_type: 'MCP', - workspace_id: application.value?.workspace_id, + workspace_id: resource.value?.workspace_id, } : { scope: 'WORKSPACE', diff --git a/ui/src/workflow/nodes/base-node/index.vue b/ui/src/workflow/nodes/base-node/index.vue index 940eb3b070a..4448a65eccd 100644 --- a/ui/src/workflow/nodes/base-node/index.vue +++ b/ui/src/workflow/nodes/base-node/index.vue @@ -179,7 +179,7 @@ import FileUploadSettingDialog from '@/workflow/nodes/base-node/component/FileUp import ChatFieldTable from './component/ChatFieldTable.vue' import { useRoute } from 'vue-router' import { loadSharedApi } from '@/utils/dynamics-api/shared-api' -const getApplicationDetail = inject('getApplicationDetail') as any +const getResourceDetail = inject('getResourceDetail') as any const route = useRoute() const { @@ -261,13 +261,13 @@ const validate = () => { }) } -const application = getApplicationDetail() +const resource = getResourceDetail() function getSTTModel() { const obj = apiType.value === 'systemManage' ? { model_type: 'STT', - workspace_id: application.value?.workspace_id, + workspace_id: resource.value?.workspace_id, } : { model_type: 'STT', @@ -284,7 +284,7 @@ function getTTSModel() { apiType.value === 'systemManage' ? { model_type: 'TTS', - workspace_id: application.value?.workspace_id, + workspace_id: resource.value?.workspace_id, } : { model_type: 'TTS', diff --git a/ui/src/workflow/nodes/image-generate/index.vue b/ui/src/workflow/nodes/image-generate/index.vue index a9829b30b98..559771edd58 100644 --- a/ui/src/workflow/nodes/image-generate/index.vue +++ b/ui/src/workflow/nodes/image-generate/index.vue @@ -161,7 +161,7 @@ import AIModeParamSettingDialog from '@/views/application/component/AIModeParamS import { t } from '@/locales' import { useRoute } from 'vue-router' import { loadSharedApi } from '@/utils/dynamics-api/shared-api' -const getApplicationDetail = inject('getApplicationDetail') as any +const getResourceDetail = inject('getResourceDetail') as any const route = useRoute() const { @@ -226,13 +226,13 @@ const form_data = computed({ }, }) -const application = getApplicationDetail() +const resource = getResourceDetail() function getSelectModel() { const obj = apiType.value === 'systemManage' ? { model_type: 'TTI', - workspace_id: application.value?.workspace_id, + workspace_id: resource.value?.workspace_id, } : { model_type: 'TTI', diff --git a/ui/src/workflow/nodes/image-to-video/index.vue b/ui/src/workflow/nodes/image-to-video/index.vue index cb315f19efd..e216011d43e 100644 --- a/ui/src/workflow/nodes/image-to-video/index.vue +++ b/ui/src/workflow/nodes/image-to-video/index.vue @@ -16,7 +16,9 @@ prop="model_id" :rules="{ required: true, - message: $t('views.applicationWorkflow.nodes.imageToVideoGenerate.model.requiredMessage'), + message: $t( + 'views.applicationWorkflow.nodes.imageToVideoGenerate.model.requiredMessage', + ), trigger: 'change', }" > @@ -24,8 +26,7 @@
{{ - $t('views.applicationWorkflow.nodes.imageToVideoGenerate.model.label') + >{{ $t('views.applicationWorkflow.nodes.imageToVideoGenerate.model.label') }}*
@@ -69,14 +70,13 @@
{{ - $t('views.applicationWorkflow.nodes.imageToVideoGenerate.prompt.label') + >{{ $t('views.applicationWorkflow.nodes.imageToVideoGenerate.prompt.label') }}*
@@ -103,13 +103,15 @@
{{ - $t('views.applicationWorkflow.nodes.imageToVideoGenerate.negative_prompt.label') - }} + $t('views.applicationWorkflow.nodes.imageToVideoGenerate.negative_prompt.label') + }}
@@ -118,7 +120,9 @@ + >{{ $t('views.applicationWorkflow.nodes.imageToVideoGenerate.last_frame.label') }} +
{{ - $t('views.applicationWorkflow.nodes.aiChatNode.returnContent.label') - }} + $t('views.applicationWorkflow.nodes.aiChatNode.returnContent.label') + }}
- + - + + diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts index 8587cc2635a..798e5383d53 100644 --- a/ui/src/workflow/common/data.ts +++ b/ui/src/workflow/common/data.ts @@ -618,6 +618,45 @@ export const loopBreakNode = { }, } +export const knowledgeMenuNodes = [ + { + label: t('views.applicationWorkflow.nodes.classify.aiCapability'), + list: [ + aiChatNode, + intentNode, + textToSpeechNode, + speechToTextNode, + imageGenerateNode, + imageUnderstandNode, + textToVideoNode, + imageToVideoNode, + videoUnderstandNode, + questionNode, + ], + }, + { + label: t('views.knowledge.title'), + list: [documentExtractNode], + }, + { + label: t('views.applicationWorkflow.nodes.classify.businessLogic'), + list: [conditionNode, formNode, replyNode, loopNode], + }, + { + label: t('views.applicationWorkflow.nodes.classify.dataProcessing'), + list: [ + variableAssignNode, + variableAggregationNode, + variableSplittingNode, + parameterExtractionNode, + ], + }, + { + label: t('views.applicationWorkflow.nodes.classify.other'), + list: [mcpNode, toolNode], + }, +] + export const menuNodes = [ { label: t('views.applicationWorkflow.nodes.classify.aiCapability'), @@ -694,6 +733,44 @@ export const applicationLoopMenuNodes = [ list: [mcpNode, toolNode], }, ] +export const knowledgeLoopMenuNodes = [ + { + label: t('views.applicationWorkflow.nodes.classify.aiCapability'), + list: [ + aiChatNode, + intentNode, + textToSpeechNode, + speechToTextNode, + imageGenerateNode, + imageUnderstandNode, + textToVideoNode, + imageToVideoNode, + videoUnderstandNode, + questionNode, + ], + }, + { + label: t('views.knowledge.title'), + list: [rerankerNode, documentExtractNode], + }, + { + label: t('views.applicationWorkflow.nodes.classify.businessLogic'), + list: [conditionNode, formNode, replyNode, loopContinueNode, loopBreakNode], + }, + { + label: t('views.applicationWorkflow.nodes.classify.dataProcessing', '数据处理'), + list: [ + variableAssignNode, + variableSplittingNode, + parameterExtractionNode, + variableAggregationNode, + ], + }, + { + label: t('views.applicationWorkflow.nodes.classify.other'), + list: [mcpNode, toolNode], + }, +] export const getMenuNodes = (workflowMode: WorkflowMode) => { if (workflowMode == WorkflowMode.Application) { @@ -702,6 +779,12 @@ export const getMenuNodes = (workflowMode: WorkflowMode) => { if (workflowMode == WorkflowMode.ApplicationLoop) { return applicationLoopMenuNodes } + if (workflowMode == WorkflowMode.Knowledge) { + return knowledgeMenuNodes + } + if (workflowMode == WorkflowMode.KnowledgeLoop) { + return knowledgeLoopMenuNodes + } } /** diff --git a/ui/src/workflow/nodes/loop-body-node/LoopBodyContainer.vue b/ui/src/workflow/nodes/loop-body-node/LoopBodyContainer.vue index 6597f2e76ec..1b6a4c3e08a 100644 --- a/ui/src/workflow/nodes/loop-body-node/LoopBodyContainer.vue +++ b/ui/src/workflow/nodes/loop-body-node/LoopBodyContainer.vue @@ -110,16 +110,14 @@
diff --git a/ui/src/workflow/index.vue b/ui/src/workflow/index.vue index 73a17a4376b..a2e9f6566ed 100644 --- a/ui/src/workflow/index.vue +++ b/ui/src/workflow/index.vue @@ -78,7 +78,6 @@ const renderGraphData = (data?: any) => { strokeWidth: 1, }, }) - lf.value.graphModel.get = 'sdasdaad' lf.value.on('graph:rendered', () => { flowId.value = lf.value.graphModel.flowId }) diff --git a/ui/src/workflow/nodes/base-node/index.ts b/ui/src/workflow/nodes/base-node/index.ts index 69ede8e06af..fac9d3090af 100644 --- a/ui/src/workflow/nodes/base-node/index.ts +++ b/ui/src/workflow/nodes/base-node/index.ts @@ -15,8 +15,9 @@ class BaseModel extends AppNodeModel { return 600 } } + export default { - type: 'base-node', + type: 'knowledge-base-node', model: BaseModel, - view: BaseNode + view: BaseNode, } diff --git a/ui/src/workflow/nodes/knowledge-base-node/component/UserFieldFormDialog.vue b/ui/src/workflow/nodes/knowledge-base-node/component/UserFieldFormDialog.vue new file mode 100644 index 00000000000..0d5ff6d2c93 --- /dev/null +++ b/ui/src/workflow/nodes/knowledge-base-node/component/UserFieldFormDialog.vue @@ -0,0 +1,168 @@ + + + diff --git a/ui/src/workflow/nodes/knowledge-base-node/component/UserInputFieldTable.vue b/ui/src/workflow/nodes/knowledge-base-node/component/UserInputFieldTable.vue new file mode 100644 index 00000000000..d90049455a8 --- /dev/null +++ b/ui/src/workflow/nodes/knowledge-base-node/component/UserInputFieldTable.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/ui/src/workflow/nodes/knowledge-base-node/component/UserInputTitleDialog.vue b/ui/src/workflow/nodes/knowledge-base-node/component/UserInputTitleDialog.vue new file mode 100644 index 00000000000..08c2f48c32f --- /dev/null +++ b/ui/src/workflow/nodes/knowledge-base-node/component/UserInputTitleDialog.vue @@ -0,0 +1,83 @@ + + + diff --git a/ui/src/workflow/nodes/knowledge-base-node/index.ts b/ui/src/workflow/nodes/knowledge-base-node/index.ts new file mode 100644 index 00000000000..c51b52ea025 --- /dev/null +++ b/ui/src/workflow/nodes/knowledge-base-node/index.ts @@ -0,0 +1,22 @@ +import BaseNodeVue from './index.vue' +import { AppNode, AppNodeModel } from '@/workflow/common/app-node' + +class BaseNode extends AppNode { + constructor(props: any) { + super(props, BaseNodeVue) + } +} + +class BaseModel extends AppNodeModel { + constructor(data: any, graphModel: any) { + super(data, graphModel) + } + get_width() { + return 600 + } +} +export default { + type: 'knowledge-base-node', + model: BaseModel, + view: BaseNode, +} diff --git a/ui/src/workflow/nodes/knowledge-base-node/index.vue b/ui/src/workflow/nodes/knowledge-base-node/index.vue new file mode 100644 index 00000000000..8f986440bfc --- /dev/null +++ b/ui/src/workflow/nodes/knowledge-base-node/index.vue @@ -0,0 +1,30 @@ + + + From 3c31fbd52723a80074356de4eebed2d7b48a4193 Mon Sep 17 00:00:00 2001 From: CaptainB Date: Thu, 6 Nov 2025 16:07:31 +0800 Subject: [PATCH 05/75] feat: add KnowledgeWorkflowModelSerializer and Operate class for workflow management --- .../serializers/knowledge_workflow.py | 20 +++++- apps/knowledge/urls.py | 1 + apps/knowledge/views/knowledge_workflow.py | 63 ++++++++++++++++++- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index d41362c28ff..c849ac8ef87 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -15,6 +15,12 @@ from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer +class KnowledgeWorkflowModelSerializer(serializers.ModelSerializer): + class Meta: + model = KnowledgeWorkflow + fields = '__all__' + + class KnowledgeWorkflowSerializer(serializers.Serializer): class Create(serializers.Serializer): user_id = serializers.UUIDField(required=True, label=_('user id')) @@ -59,9 +65,21 @@ def save_workflow(self, instance: Dict): knowledge_id=knowledge_id, workspace_id=self.data.get('workspace_id'), work_flow=instance.get('work_flow', {}), - ) knowledge_workflow.save() return {**KnowledgeModelSerializer(knowledge).data, 'document_list': []} + + class Operate(serializers.Serializer): + user_id = serializers.UUIDField(required=True, label=_('user id')) + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) + + def edit(self, instance: Dict): + pass + + def one(self): + self.is_valid(raise_exception=True) + workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get('knowledge_id')).first() + return {**KnowledgeWorkflowModelSerializer(workflow).data} diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index 1ba3d59de85..1ff53a9cf9a 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -16,6 +16,7 @@ path('workspace//knowledge/tags', views.KnowledgeView.Tags.as_view()), path('workspace//knowledge/', views.KnowledgeView.Operate.as_view()), path('workspace//knowledge//sync', views.KnowledgeView.SyncWeb.as_view()), + path('workspace//knowledge//workfolw', views.KnowledgeWorkflowView.Operate.as_view()), path('workspace//knowledge//generate_related', views.KnowledgeView.GenerateRelated.as_view()), path('workspace//knowledge//embedding', views.KnowledgeView.Embedding.as_view()), path('workspace//knowledge//hit_test', views.KnowledgeView.HitTest.as_view()), diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index 1b8b47ddb93..539a1e9576b 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -7,9 +7,12 @@ from common.auth import TokenAuth from common.auth.authentication import has_permissions -from common.constants.permission_constants import PermissionConstants, RoleConstants +from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants +from common.log.log import log from common.result import result from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi +from knowledge.serializers.common import get_knowledge_operation_object +from knowledge.serializers.knowledge import KnowledgeSerializer from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer @@ -17,7 +20,7 @@ class KnowledgeWorkflowView(APIView): authentication_classes = [TokenAuth] @extend_schema( - methods=['GET'], + methods=['POST'], description=_('Create knowledge workflow'), summary=_('Create knowledge workflow'), operation_id=_('Create knowledge workflow'), # type: ignore @@ -34,6 +37,62 @@ def post(self, request: Request, workspace_id: str): data={'user_id': request.user.id, 'workspace_id': workspace_id} ).save_workflow(request.data)) + class Operate(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['PUT'], + description=_('Edit knowledge workflow'), + summary=_('Edit knowledge workflow'), + operation_id=_('Edit knowledge workflow'), # type: ignore + parameters=KnowledgeWorkflowApi.get_parameters(), + request=KnowledgeWorkflowApi.get_request(), + responses=KnowledgeWorkflowApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_EDIT.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_EDIT.get_workspace_permission_workspace_manage_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), + ViewPermission( + [RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND + ) + ) + @log( + menu='Knowledge Base', operate="Modify knowledge workflow", + get_operation_object=lambda r, keywords: get_knowledge_operation_object(keywords.get('knowledge_id')), + ) + def put(self, request: Request, workspace_id: str, knowledge_id: str): + return result.success(KnowledgeWorkflowSerializer.Operate( + data={'user_id': request.user.id, 'workspace_id': workspace_id, 'knowledge_id': knowledge_id} + ).edit(request.data)) + + @extend_schema( + methods=['GET'], + description=_('Get knowledge workflow'), + summary=_('Get knowledge workflow'), + operation_id=_('Get knowledge workflow'), # type: ignore + parameters=KnowledgeWorkflowApi.get_parameters(), + responses=KnowledgeWorkflowApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_READ.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_READ.get_workspace_permission_workspace_manage_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), + ViewPermission( + [RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND + ), + ) + def get(self, request: Request, workspace_id: str, knowledge_id: str): + return result.success(KnowledgeWorkflowSerializer.Operate( + data={'user_id': request.user.id, 'workspace_id': workspace_id, 'knowledge_id': knowledge_id} + ).one()) + class KnowledgeWorkflowVersionView(APIView): pass From 107fc4404996dcf685ca38cc4ac2a679afe5fc16 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Mon, 10 Nov 2025 14:05:20 +0800 Subject: [PATCH 06/75] fix: route --- ui/src/router/modules/document.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/src/router/modules/document.ts b/ui/src/router/modules/document.ts index 69e33c61af8..7778d9c5cc2 100644 --- a/ui/src/router/modules/document.ts +++ b/ui/src/router/modules/document.ts @@ -102,8 +102,8 @@ const DocumentRouter = { component: () => import('@/views/document/index.vue'), }, { - path: 'setting', - name: 'knowledge-setting', + path: 'knowledge-workflow-setting', + name: 'knowledgeWorkflowSetting', meta: { title: '知识库工作流', icon: 'app-problems', @@ -133,7 +133,7 @@ const DocumentRouter = { iconActive: 'QuestionFilled', title: 'views.problem.title', active: 'problem', - parentPath: '/knowledge/:id/:folderId', + parentPath: '/knowledge/:id/:folderId/:type', parentName: 'KnowledgeDetail', group: 'KnowledgeDetail', permission: [ @@ -224,7 +224,7 @@ const DocumentRouter = { icon: 'app-hit-test', title: 'views.application.hitTest.title', active: 'hit-test', - parentPath: '/knowledge/:id/:folderId', + parentPath: '/knowledge/:id/:folderId/:type', parentName: 'KnowledgeDetail', group: 'KnowledgeDetail', permission: [ @@ -316,7 +316,7 @@ const DocumentRouter = { iconActive: 'app-user-chat-active', title: 'views.chatUser.title', active: 'chat-user', - parentPath: '/knowledge/:id/:folderId', + parentPath: '/knowledge/:id/:folderId/:type', parentName: 'KnowledgeDetail', resourceType: SourceTypeEnum.KNOWLEDGE, group: 'KnowledgeDetail', @@ -421,7 +421,7 @@ const DocumentRouter = { iconActive: 'app-setting-active', title: 'common.setting', active: 'setting', - parentPath: '/knowledge/:id/:folderId', + parentPath: '/knowledge/:id/:folderId/:type', parentName: 'KnowledgeDetail', group: 'KnowledgeDetail', permission: [ From b886d8d4581b405100dfa836cab0be135dac31dd Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Tue, 11 Nov 2025 15:54:02 +0800 Subject: [PATCH 07/75] feat: knowledge workflow --- .../data_source_local_node/__init__.py | 8 ++ .../i_data_source_local_node.py | 38 +++++ .../data_source_local_node/impl/__init__.py | 8 ++ .../impl/base_data_source_local_node.py | 27 ++++ .../serializers/knowledge_workflow.py | 16 +++ apps/knowledge/views/knowledge_workflow.py | 8 +- ui/src/enums/application.ts | 4 + .../component/DropdownMenu.vue | 6 +- .../component/action/index.vue | 11 ++ ui/src/views/knowledge-workflow/index.vue | 5 +- ui/src/workflow/common/app-node.ts | 7 +- ui/src/workflow/common/data.ts | 26 +++- ui/src/workflow/common/validate.ts | 44 +++++- .../icons/data-source-local-node-icon.vue | 6 + .../nodes/data-source-local-node/index.ts | 12 ++ .../nodes/data-source-local-node/index.vue | 132 ++++++++++++++++++ .../nodes/data-source-web-node/index.ts | 12 ++ .../nodes/data-source-web-node/index.vue | 61 ++++++++ 18 files changed, 417 insertions(+), 14 deletions(-) create mode 100644 apps/application/flow/step_node/data_source_local_node/__init__.py create mode 100644 apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py create mode 100644 apps/application/flow/step_node/data_source_local_node/impl/__init__.py create mode 100644 apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py create mode 100644 ui/src/views/knowledge-workflow/component/action/index.vue create mode 100644 ui/src/workflow/icons/data-source-local-node-icon.vue create mode 100644 ui/src/workflow/nodes/data-source-local-node/index.ts create mode 100644 ui/src/workflow/nodes/data-source-local-node/index.vue create mode 100644 ui/src/workflow/nodes/data-source-web-node/index.ts create mode 100644 ui/src/workflow/nodes/data-source-web-node/index.vue diff --git a/apps/application/flow/step_node/data_source_local_node/__init__.py b/apps/application/flow/step_node/data_source_local_node/__init__.py new file mode 100644 index 00000000000..bbf804a7079 --- /dev/null +++ b/apps/application/flow/step_node/data_source_local_node/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: __init__.py.py + @date:2025/11/11 10:06 + @desc: +""" diff --git a/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py new file mode 100644 index 00000000000..ec06f36d570 --- /dev/null +++ b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py @@ -0,0 +1,38 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: i_data_source_local_node.py + @date:2025/11/11 10:06 + @desc: +""" +from abc import abstractmethod +from typing import Type + +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from application.flow.i_step_node import INode, NodeResult + + +class DataSourceLocalNodeParamsSerializer(serializers.Serializer): + file_format = serializers.ListField(child=serializers.CharField) + max_file_number = serializers.IntegerField(required=True, label=_("Number of uploaded files")) + file_max_size = serializers.IntegerField(required=True, label=_("Upload file size")) + + +class IDataSourceLocalNode(INode): + type = 'data-source-local-node' + + @abstractmethod + def get_form_class(self): + pass + + def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: + return DataSourceLocalNodeParamsSerializer + + def _run(self): + return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) + + def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult: + pass diff --git a/apps/application/flow/step_node/data_source_local_node/impl/__init__.py b/apps/application/flow/step_node/data_source_local_node/impl/__init__.py new file mode 100644 index 00000000000..6f830151971 --- /dev/null +++ b/apps/application/flow/step_node/data_source_local_node/impl/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: __init__.py.py + @date:2025/11/11 10:08 + @desc: +""" diff --git a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py new file mode 100644 index 00000000000..515c0134cc3 --- /dev/null +++ b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py @@ -0,0 +1,27 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: base_data_source_local_node.py + @date:2025/11/11 10:30 + @desc: +""" +from application.flow.i_step_node import NodeResult +from application.flow.step_node.data_source_local_node.i_data_source_local_node import IDataSourceLocalNode +from common import forms +from common.forms import BaseForm + + +class BaseDataSourceLocalNodeForm(BaseForm): + api_key = forms.PasswordInputField('API Key', required=True) + + +class BaseDataSourceLocalNode(IDataSourceLocalNode): + def save_context(self, details, workflow_manage): + pass + + def get_form_class(self): + return BaseDataSourceLocalNodeForm() + + def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult: + pass diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index c849ac8ef87..2473d3868ef 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -8,11 +8,13 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.step_node import get_node from common.exception.app_exception import AppApiException from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow from knowledge.serializers.knowledge import KnowledgeModelSerializer from system_manage.models import AuthTargetType from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer +from tools.models import Tool class KnowledgeWorkflowModelSerializer(serializers.ModelSerializer): @@ -22,6 +24,20 @@ class Meta: class KnowledgeWorkflowSerializer(serializers.Serializer): + class Form(serializers.Serializer): + type = serializers.CharField(required=True, label=_('type')) + id = serializers.CharField(required=True, label=_('type')) + + def get_form_list(self): + self.is_valid(raise_exception=True) + if self.data.get('type') == 'local': + node = get_node(self.data.get('id')) + return node.get_form_class()().to_form_list() + elif self.data.get('type') == 'tool': + tool = QuerySet(Tool).filter(id=self.data.get("id")).first() + # todo 调用工具数据源的函数获取表单列表 + return None + class Create(serializers.Serializer): user_id = serializers.UUIDField(required=True, label=_('user id')) workspace_id = serializers.CharField(required=True, label=_('workspace id')) diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index 539a1e9576b..15d7b11660a 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -12,10 +12,16 @@ from common.result import result from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi from knowledge.serializers.common import get_knowledge_operation_object -from knowledge.serializers.knowledge import KnowledgeSerializer from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer +class KnowledgeWorkflowFormView(APIView): + authentication_classes = [TokenAuth] + + def get(self): + return result.success(KnowledgeWorkflowSerializer.Form().get_form_list()) + + class KnowledgeWorkflowView(APIView): authentication_classes = [TokenAuth] diff --git a/ui/src/enums/application.ts b/ui/src/enums/application.ts index 9f7564c4053..873c77b6764 100644 --- a/ui/src/enums/application.ts +++ b/ui/src/enums/application.ts @@ -38,6 +38,10 @@ export enum WorkflowType { VariableAggregationNode = 'variable-aggregation-node', VideoUnderstandNode = 'video-understand-node', ParameterExtractionNode = 'parameter-extraction-node', + DataSourceLocalNode = 'data-source-local-node', +} +export enum WorkflowKind { + DataSource = 'data-source', } export enum WorkflowMode { // 应用工作流 diff --git a/ui/src/views/knowledge-workflow/component/DropdownMenu.vue b/ui/src/views/knowledge-workflow/component/DropdownMenu.vue index a0503ae3c43..6706a612f83 100644 --- a/ui/src/views/knowledge-workflow/component/DropdownMenu.vue +++ b/ui/src/views/knowledge-workflow/component/DropdownMenu.vue @@ -1,5 +1,9 @@ + + diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue index d0ff4c686d2..d808df5d273 100644 --- a/ui/src/views/knowledge-workflow/index.vue +++ b/ui/src/views/knowledge-workflow/index.vue @@ -301,7 +301,7 @@ const publish = () => { ?.validate() .then(() => { const workflow = getGraphData() - const workflowInstance = new WorkFlowInstance(workflow) + const workflowInstance = new WorkFlowInstance(workflow, WorkflowMode.Knowledge) try { workflowInstance.is_valid() } catch (e: any) { @@ -384,7 +384,7 @@ const clickShowDebug = () => { ?.validate() .then(() => { const graphData = getGraphData() - const workflow = new WorkFlowInstance(graphData) + const workflow = new WorkFlowInstance(graphData, WorkflowMode.Knowledge) try { workflow.is_valid() detail.value = { @@ -396,6 +396,7 @@ const clickShowDebug = () => { showDebug.value = true } catch (e: any) { + console.log(e) MsgError(e.toString()) } }) diff --git a/ui/src/workflow/common/app-node.ts b/ui/src/workflow/common/app-node.ts index 349de8b92b2..fc0f7bed561 100644 --- a/ui/src/workflow/common/app-node.ts +++ b/ui/src/workflow/common/app-node.ts @@ -1,3 +1,4 @@ +import { WorkflowKind } from './../../enums/application' import Components from '@/components' import ElementPlus from 'element-plus' import * as ElementPlusIcons from '@element-plus/icons-vue' @@ -414,9 +415,11 @@ class AppNodeModel extends HtmlResize.model { const { id, x, y, width } = this const showNode = this.properties.showNode === undefined ? true : this.properties.showNode const anchors: any = [] - if (![WorkflowType.Base as string, WorkflowType.KnowledgeBase as string].includes(this.type)) { - if (![WorkflowType.Start, WorkflowType.LoopStartNode.toString()].includes(this.type)) { + if ( + ![WorkflowType.Start, WorkflowType.LoopStartNode.toString()].includes(this.type) && + this.properties.kind != WorkflowKind.DataSource + ) { anchors.push({ x: x - width / 2 + 10, y: showNode ? y : y - 15, diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts index 1e7130a7c92..68b4734a4d7 100644 --- a/ui/src/workflow/common/data.ts +++ b/ui/src/workflow/common/data.ts @@ -1,3 +1,4 @@ +import { WorkflowKind } from './../../enums/application' import { WorkflowType, WorkflowMode } from '@/enums/application' import { t } from '@/locales' @@ -79,6 +80,25 @@ export const knowledgeBaseNode = { user_input_field_list: [], }, } +export const dataSourceLocalNode = { + id: WorkflowType.DataSourceLocalNode, + type: WorkflowType.DataSourceLocalNode, + x: 360, + y: 2761.3875, + text: t('views.applicationWorkflow.nodes.dataSourceLocalNode.text', '本地文件'), + label: t('views.applicationWorkflow.nodes.dataSourceLocalNode.label', '本地文件'), + properties: { + kind: WorkflowKind.DataSource, + height: 728.375, + stepName: t('views.applicationWorkflow.nodes.dataSourceLocalNode.label', '本地文件'), + input_field_list: [], + node_data: {}, + config: {}, + showNode: true, + user_input_config: {}, + user_input_field_list: [], + }, +} /** * 说明 * type 与 nodes 文件对应 @@ -641,6 +661,10 @@ export const loopBreakNode = { } export const knowledgeMenuNodes = [ + { + label: t('views.applicationWorkflow.nodes.classify.dataSource', '数据源'), + list: [dataSourceLocalNode], + }, { label: t('views.applicationWorkflow.nodes.classify.aiCapability'), list: [ @@ -868,7 +892,6 @@ export const compareList = [ { value: 'start_with', label: 'startWith' }, { value: 'end_with', label: 'endWith' }, ] - export const nodeDict: any = { [WorkflowType.AiChat]: aiChatNode, [WorkflowType.SearchKnowledge]: searchKnowledgeNode, @@ -903,6 +926,7 @@ export const nodeDict: any = { [WorkflowType.ParameterExtractionNode]: parameterExtractionNode, [WorkflowType.VariableAggregationNode]: variableAggregationNode, [WorkflowType.KnowledgeBase]: knowledgeBaseNode, + [WorkflowType.DataSourceLocalNode]: dataSourceLocalNode, } export function isWorkFlow(type: string | undefined) { diff --git a/ui/src/workflow/common/validate.ts b/ui/src/workflow/common/validate.ts index 50e7e94e948..913f6799e34 100644 --- a/ui/src/workflow/common/validate.ts +++ b/ui/src/workflow/common/validate.ts @@ -1,3 +1,4 @@ +import { WorkflowKind } from './../../enums/application' import { WorkflowType, WorkflowMode } from '@/enums/application' import { t } from '@/locales' @@ -43,7 +44,9 @@ const loop_end_nodes: Array = [ ] const end_nodes_dict = { [WorkflowMode.Application]: end_nodes, + [WorkflowMode.Knowledge]: end_nodes, [WorkflowMode.ApplicationLoop]: loop_end_nodes, + [WorkflowMode.KnowledgeLoop]: loop_end_nodes, } export class WorkFlowInstance { @@ -63,8 +66,10 @@ export class WorkFlowInstance { * 校验开始节点 */ private is_valid_start_node() { - const start_node_list = this.nodes.filter((item) => - [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id), + const start_node_list = this.nodes.filter( + (item) => + [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) || + item.properties.kind == WorkflowKind.DataSource, ) if (start_node_list.length == 0) { throw t('views.applicationWorkflow.validate.startNodeRequired') @@ -77,6 +82,10 @@ export class WorkFlowInstance { * 校验基本信息节点 */ private is_valid_base_node() { + console.log(this.workflowModel) + if (this.workflowModel == WorkflowMode.Knowledge) { + return + } const start_node_list = this.nodes.filter((item) => item.id === WorkflowType.Base) if (start_node_list.length == 0) { throw t('views.applicationWorkflow.validate.baseNodeRequired') @@ -106,8 +115,10 @@ export class WorkFlowInstance { * @returns */ get_start_node() { - const start_node_list = this.nodes.filter((item) => - [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id), + const start_node_list = this.nodes.filter( + (item) => + [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) || + item.properties.kind == WorkflowKind.DataSource, ) return start_node_list[0] } @@ -143,9 +154,26 @@ export class WorkFlowInstance { private is_valid_work_flow() { this.workFlowNodes = [] - this._is_valid_work_flow() + if (this.workflowModel == WorkflowMode.Knowledge) { + const start_node_list = this.nodes.filter( + (item) => + [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) || + item.properties.kind == WorkflowKind.DataSource, + ) + start_node_list.forEach((startNode) => { + this._is_valid_work_flow(startNode) + }) + } else { + this._is_valid_work_flow() + } + const notInWorkFlowNodes = this.nodes - .filter((node: any) => node.id !== WorkflowType.Start && node.id !== WorkflowType.Base) + .filter( + (node: any) => + node.id !== WorkflowType.Start && + node.id !== WorkflowType.Base && + node.id !== WorkflowType.KnowledgeBase, + ) .filter((node) => !this.workFlowNodes.includes(node)) if (notInWorkFlowNodes.length > 0) { throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${notInWorkFlowNodes.map((node) => node.properties.stepName).join(',')}` @@ -175,7 +203,9 @@ export class WorkFlowInstance { if ( node.type !== WorkflowType.Base && node.type !== WorkflowType.Start && - node.type !== WorkflowType.LoopStartNode + node.type !== WorkflowType.LoopStartNode && + node.type !== WorkflowType.KnowledgeBase && + node.properties.kind !== WorkflowKind.DataSource ) { if (!this.edges.some((edge) => edge.targetNodeId === node.id)) { throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${node.properties.stepName}` diff --git a/ui/src/workflow/icons/data-source-local-node-icon.vue b/ui/src/workflow/icons/data-source-local-node-icon.vue new file mode 100644 index 00000000000..f3457837ba9 --- /dev/null +++ b/ui/src/workflow/icons/data-source-local-node-icon.vue @@ -0,0 +1,6 @@ + + diff --git a/ui/src/workflow/nodes/data-source-local-node/index.ts b/ui/src/workflow/nodes/data-source-local-node/index.ts new file mode 100644 index 00000000000..c31422ad098 --- /dev/null +++ b/ui/src/workflow/nodes/data-source-local-node/index.ts @@ -0,0 +1,12 @@ +import DataSourceWebNodeVue from './index.vue' +import { AppNode, AppNodeModel } from '@/workflow/common/app-node' +class DataSourceWebNode extends AppNode { + constructor(props: any) { + super(props, DataSourceWebNodeVue) + } +} +export default { + type: 'data-source-local-node', + model: AppNodeModel, + view: DataSourceWebNode, +} diff --git a/ui/src/workflow/nodes/data-source-local-node/index.vue b/ui/src/workflow/nodes/data-source-local-node/index.vue new file mode 100644 index 00000000000..2ca58b13af6 --- /dev/null +++ b/ui/src/workflow/nodes/data-source-local-node/index.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/ui/src/workflow/nodes/data-source-web-node/index.ts b/ui/src/workflow/nodes/data-source-web-node/index.ts new file mode 100644 index 00000000000..0bc7c5283d4 --- /dev/null +++ b/ui/src/workflow/nodes/data-source-web-node/index.ts @@ -0,0 +1,12 @@ +import DataSourceWebNodeVue from './index.vue' +import { AppNode, AppNodeModel } from '@/workflow/common/app-node' +class DataSourceWebNode extends AppNode { + constructor(props: any) { + super(props, DataSourceWebNodeVue) + } +} +export default { + type: 'data-source-web-node', + model: AppNodeModel, + view: DataSourceWebNode, +} diff --git a/ui/src/workflow/nodes/data-source-web-node/index.vue b/ui/src/workflow/nodes/data-source-web-node/index.vue new file mode 100644 index 00000000000..f1d5d626000 --- /dev/null +++ b/ui/src/workflow/nodes/data-source-web-node/index.vue @@ -0,0 +1,61 @@ + + + + + From b82e12ac2e00931ac0d0c4212b21bb30ef1fbbc4 Mon Sep 17 00:00:00 2001 From: zhangzhanwei Date: Tue, 11 Nov 2025 16:15:16 +0800 Subject: [PATCH 08/75] feat: Knowledge workflow permission --- apps/common/constants/permission_constants.py | 32 ++++++++ ui/src/router/modules/document.ts | 78 ++++++++++++++++++- ui/src/utils/permission/data.ts | 10 +++ 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/apps/common/constants/permission_constants.py b/apps/common/constants/permission_constants.py index 67cb272a13e..58448a6a5d2 100644 --- a/apps/common/constants/permission_constants.py +++ b/apps/common/constants/permission_constants.py @@ -39,9 +39,12 @@ class Group(Enum): SYSTEM_RES_KNOWLEDGE = "SYSTEM_RESOURCE_KNOWLEDGE" KNOWLEDGE_HIT_TEST = "KNOWLEDGE_HIT_TEST" KNOWLEDGE_DOCUMENT = "KNOWLEDGE_DOCUMENT" + KNOWLEDGE_WORKFLOW = "KNOWLEDGE_WORKFLOW" KNOWLEDGE_TAG = "KNOWLEDGE_TAG" SYSTEM_KNOWLEDGE_DOCUMENT = "SYSTEM_KNOWLEDGE_DOCUMENT" + SYSTEM_KNOWLEDGE_WORKFLOW = "SYSTEM_KNOWLEDGE_WORKFLOW" SYSTEM_RES_KNOWLEDGE_DOCUMENT = "SYSTEM_RESOURCE_KNOWLEDGE_DOCUMENT" + SYSTEM_RES_KNOWLEDGE_WORKFLOW = "SYSTEM_RESOURCE_KNOWLEDGE_WORKFLOW" SYSTEM_RES_KNOWLEDGE_TAG = "SYSTEM_RES_KNOWLEDGE_TAG" SYSTEM_KNOWLEDGE_TAG = "SYSTEM_KNOWLEDGE_TAG" @@ -328,6 +331,7 @@ def get_workspace_role(self): Group.APPLICATION.value: _("Application"), Group.KNOWLEDGE.value: _("Knowledge"), Group.KNOWLEDGE_DOCUMENT.value: _("Document"), + Group.KNOWLEDGE_WORKFLOW.value: _("Workflow"), Group.KNOWLEDGE_TAG.value: _("Tag"), Group.KNOWLEDGE_PROBLEM.value: _("Problem"), Group.KNOWLEDGE_HIT_TEST.value: _("Hit-Test"), @@ -375,6 +379,7 @@ def get_workspace_role(self): Group.SYSTEM_MODEL.value: _("Model"), Group.SYSTEM_KNOWLEDGE.value: _("Knowledge"), Group.SYSTEM_KNOWLEDGE_DOCUMENT.value: _("Document"), + Group.SYSTEM_KNOWLEDGE_WORKFLOW.value: _("Workflow"), Group.SYSTEM_KNOWLEDGE_TAG.value: _("Tag"), Group.SYSTEM_KNOWLEDGE_PROBLEM.value: _("Problem"), Group.SYSTEM_KNOWLEDGE_HIT_TEST.value: _("Hit-Test"), @@ -383,6 +388,7 @@ def get_workspace_role(self): Group.SYSTEM_RES_MODEL.value: _("Model"), Group.SYSTEM_RES_KNOWLEDGE.value: _("Knowledge"), Group.SYSTEM_RES_KNOWLEDGE_DOCUMENT.value: _("Document"), + Group.SYSTEM_RES_KNOWLEDGE_WORKFLOW.value: _("Workflow"), Group.SYSTEM_RES_KNOWLEDGE_TAG.value: _("Tag"), Group.SYSTEM_RES_KNOWLEDGE_PROBLEM.value: _("Problem"), Group.SYSTEM_RES_KNOWLEDGE_HIT_TEST.value: _("Hit-Test"), @@ -616,6 +622,16 @@ class PermissionConstants(Enum): resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE], parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE] ) + KNOWLEDGE_WORKFLOW_READ = Permission( + group=Group.KNOWLEDGE_WORKFLOW, operate=Operate.READ, role_list=[RoleConstants.ADMIN, RoleConstants.USER], + resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_VIEW], + parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE] + ) + KNOWLEDGE_WORKFLOW_EDIT = Permission( + group=Group.KNOWLEDGE_WORKFLOW, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN, RoleConstants.USER], + resource_permission_group_list=[ResourcePermissionConst.KNOWLEDGE_MANGE], + parent_group=[WorkspaceGroup.KNOWLEDGE, UserGroup.KNOWLEDGE] + ) KNOWLEDGE_DOCUMENT_READ = Permission( group=Group.KNOWLEDGE_DOCUMENT, operate=Operate.READ, role_list=[RoleConstants.ADMIN, RoleConstants.USER], @@ -1209,6 +1225,14 @@ class PermissionConstants(Enum): group=Group.SYSTEM_KNOWLEDGE, operate=Operate.DELETE, role_list=[RoleConstants.ADMIN], parent_group=[SystemGroup.SHARED_KNOWLEDGE], is_ee=settings.edition == "EE" ) + SHARED_KNOWLEDGE_WORKFLOW_READ = Permission( + group=Group.SYSTEM_KNOWLEDGE_WORKFLOW, operate=Operate.READ, role_list=[RoleConstants.ADMIN], + parent_group=[SystemGroup.SHARED_KNOWLEDGE], is_ee=settings.edition == "EE" + ) + SHARED_KNOWLEDGE_WORKFLOW_EDIT = Permission( + group=Group.SYSTEM_KNOWLEDGE_WORKFLOW, operate=Operate.EDIT, role_list=[RoleConstants.ADMIN], + parent_group=[SystemGroup.SHARED_KNOWLEDGE], is_ee=settings.edition == "EE" + ) SHARED_KNOWLEDGE_DOCUMENT_READ = Permission( group=Group.SYSTEM_KNOWLEDGE_DOCUMENT, operate=Operate.READ, role_list=[RoleConstants.ADMIN], parent_group=[SystemGroup.SHARED_KNOWLEDGE], is_ee=settings.edition == "EE" @@ -1437,6 +1461,14 @@ class PermissionConstants(Enum): parent_group=[SystemGroup.RESOURCE_KNOWLEDGE], is_ee=settings.edition == "EE" ) # 文档 + RESOURCE_KNOWLEDGE_WORKFLOW_READ = Permission( + group=Group.SYSTEM_RES_KNOWLEDGE_WORKFLOW, operate=Operate.READ, role_list=[RoleConstants.ADMIN], + parent_group=[SystemGroup.RESOURCE_KNOWLEDGE], is_ee=settings.edition == "EE" + ) + RESOURCE_KNOWLEDGE_WORKFLOW_EDIT = Permission( + group=Group.SYSTEM_RES_KNOWLEDGE_WORKFLOW, operate=Operate.READ, role_list=[RoleConstants.ADMIN], + parent_group=[SystemGroup.RESOURCE_KNOWLEDGE], is_ee=settings.edition == "EE" + ) RESOURCE_KNOWLEDGE_DOCUMENT_READ = Permission( group=Group.SYSTEM_RES_KNOWLEDGE_DOCUMENT, operate=Operate.READ, role_list=[RoleConstants.ADMIN], parent_group=[SystemGroup.RESOURCE_KNOWLEDGE], is_ee=settings.edition == "EE" diff --git a/ui/src/router/modules/document.ts b/ui/src/router/modules/document.ts index 7778d9c5cc2..40d2a146bb0 100644 --- a/ui/src/router/modules/document.ts +++ b/ui/src/router/modules/document.ts @@ -112,11 +112,85 @@ const DocumentRouter = { permission: [ () => { const to: any = get_next_route() - if (to.params.type === '4') { + if (to.params.folderId == 'shared') { return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return new ComplexPermission( + [RoleConst.USER], + [ + PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ), + ], + [], + 'AND', + ) } }, - ], + () => { + const to: any = get_next_route() + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return RoleConst.WORKSPACE_MANAGE.getWorkspaceRole() + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'shared') { + return PermissionConst.SHARED_KNOWLEDGE_WORKFLOW_READ + } else if (to.params.folderId == 'resource-management') { + } else { + return PermissionConst.KNOWLEDGE_WORKFLOW_READ.getKnowledgeWorkspaceResourcePermission( + to ? to.params.id : '', + ) + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'shared') { + return RoleConst.ADMIN + } else if (to.params.folderId == 'resource-management') { + } else { + return PermissionConst.KNOWLEDGE_WORKFLOW_READ.getWorkspacePermissionWorkspaceManageRole() + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'share') { + return new ComplexPermission( + [RoleConst.EXTENDS_USER.getWorkspaceRole()], + [PermissionConst.KNOWLEDGE_WORKFLOW_READ.getWorkspacePermission()], + [], + 'AND', + ) + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'share') { + return RoleConst.USER.getWorkspaceRole() + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'resource-management') { + return RoleConst.ADMIN + } + }, + () => { + const to: any = get_next_route() + if (to.params.folderId == 'resource-management') { + return PermissionConst.RESOURCE_KNOWLEDGE_WORKFLOW_READ + } + }, + ].map(p => () => { + const to: any = get_next_route() + if (to.params.type !== '4') {return false} + return p() + }), }, redirect: (menu: any) => { const from = 'workspace' diff --git a/ui/src/utils/permission/data.ts b/ui/src/utils/permission/data.ts index fa79a84943b..3b2cd1af307 100644 --- a/ui/src/utils/permission/data.ts +++ b/ui/src/utils/permission/data.ts @@ -109,6 +109,9 @@ const PermissionConst = { KNOWLEDGE_EXPORT: new Permission('KNOWLEDGE:READ+EXPORT'), KNOWLEDGE_DELETE: new Permission('KNOWLEDGE:READ+DELETE'), KNOWLEDGE_GENERATE: new Permission('KNOWLEDGE:READ+GENERATE'), + + KNOWLEDGE_WORKFLOW_READ: new Permission('KNOWLEDGE_WORKFLOW:READ'), + KNOWLEDGE_WORKFLOW_EDIT: new Permission('KNOWLEDGE_WORKFLOW:READ+EDIT'), KNOWLEDGE_DOCUMENT_READ: new Permission('KNOWLEDGE_DOCUMENT:READ'), KNOWLEDGE_DOCUMENT_CREATE: new Permission('KNOWLEDGE_DOCUMENT:READ+CREATE'), @@ -190,6 +193,9 @@ const PermissionConst = { SHARED_KNOWLEDGE_EXPORT: new Permission('SYSTEM_KNOWLEDGE:READ+EXPORT'), SHARED_KNOWLEDGE_GENERATE: new Permission('SYSTEM_KNOWLEDGE:READ+GENERATE'), SHARED_KNOWLEDGE_DELETE: new Permission('SYSTEM_KNOWLEDGE:READ+DELETE'), + + SHARED_KNOWLEDGE_WORKFLOW_READ: new Permission('SYSTEM_KNOWLEDGE_WORKFLOW:READ'), + SHARED_KNOWLEDGE_WORKFLOW_EDIT: new Permission('SYSTEM_KNOWLEDGE_WORKFLOW:READ+EDIT'), SHARED_KNOWLEDGE_DOCUMENT_READ: new Permission('SYSTEM_KNOWLEDGE_DOCUMENT:READ'), SHARED_KNOWLEDGE_DOCUMENT_CREATE: new Permission('SYSTEM_KNOWLEDGE_DOCUMENT:READ+CREATE'), @@ -243,6 +249,9 @@ const PermissionConst = { RESOURCE_KNOWLEDGE_EXPORT: new Permission('SYSTEM_RESOURCE_KNOWLEDGE:READ+EXPORT'), RESOURCE_KNOWLEDGE_DELETE: new Permission('SYSTEM_RESOURCE_KNOWLEDGE:READ+DELETE'), RESOURCE_KNOWLEDGE_GENERATE: new Permission('SYSTEM_RESOURCE_KNOWLEDGE:READ+GENERATE'), + + RESOURCE_KNOWLEDGE_WORKFLOW_READ: new Permission('SYSTEM_RESOURCE_KNOWLEDGE_WORKFLOW:READ'), + RESOURCE_KNOWLEDGE_WORKFLOW_EDIT: new Permission('SYSTEM_RESOURCE_KNOWLEDGE_WORKFLOW:READ+EDIT'), RESOURCE_KNOWLEDGE_DOCUMENT_READ: new Permission('SYSTEM_RESOURCE_KNOWLEDGE_DOCUMENT:READ'), RESOURCE_KNOWLEDGE_DOCUMENT_CREATE: new Permission('SYSTEM_RESOURCE_KNOWLEDGE_DOCUMENT:READ+CREATE'), @@ -377,3 +386,4 @@ const EditionConst = { IS_CE: new Edition('X-PACK-CE'), } export {PermissionConst, RoleConst, EditionConst} + From 8d7734b9c6da998a0de487d01b5b91eca46cd43c Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Tue, 11 Nov 2025 17:41:52 +0800 Subject: [PATCH 09/75] feat: knowledge workflow --- apps/application/flow/step_node/__init__.py | 4 +- .../i_data_source_local_node.py | 5 +- .../impl/base_data_source_local_node.py | 3 +- .../serializers/knowledge_workflow.py | 2 +- apps/knowledge/urls.py | 1 + apps/knowledge/views/knowledge_workflow.py | 4 +- ui/src/api/knowledge/knowledge.ts | 9 +++ .../knowledge-workflow/component/Debug.vue | 29 ++++++++ .../component/action/index.vue | 73 ++++++++++++++++++- ui/src/views/knowledge-workflow/index.vue | 10 +-- ui/src/workflow/common/data.ts | 1 - 11 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 ui/src/views/knowledge-workflow/component/Debug.vue diff --git a/apps/application/flow/step_node/__init__.py b/apps/application/flow/step_node/__init__.py index ffb0c78288f..6a14e0fd6bb 100644 --- a/apps/application/flow/step_node/__init__.py +++ b/apps/application/flow/step_node/__init__.py @@ -9,6 +9,7 @@ from .ai_chat_step_node import * from .application_node import BaseApplicationNode from .condition_node import * +from .data_source_local_node.impl.base_data_source_local_node import BaseDataSourceLocalNode from .direct_reply_node import * from .document_extract_node import * from .form_node import * @@ -46,7 +47,8 @@ BaseVideoUnderstandNode, BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode, BaseLoopContinueNode, - BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode] + BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode, + BaseDataSourceLocalNode] def get_node(node_type): diff --git a/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py index ec06f36d570..beba58232bc 100644 --- a/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py @@ -16,7 +16,7 @@ class DataSourceLocalNodeParamsSerializer(serializers.Serializer): - file_format = serializers.ListField(child=serializers.CharField) + file_format = serializers.ListField(child=serializers.CharField(label=('')), label='') max_file_number = serializers.IntegerField(required=True, label=_("Number of uploaded files")) file_max_size = serializers.IntegerField(required=True, label=_("Upload file size")) @@ -24,8 +24,9 @@ class DataSourceLocalNodeParamsSerializer(serializers.Serializer): class IDataSourceLocalNode(INode): type = 'data-source-local-node' + @staticmethod @abstractmethod - def get_form_class(self): + def get_form_class(): pass def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: diff --git a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py index 515c0134cc3..0e501a11173 100644 --- a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py @@ -20,7 +20,8 @@ class BaseDataSourceLocalNode(IDataSourceLocalNode): def save_context(self, details, workflow_manage): pass - def get_form_class(self): + @staticmethod + def get_form_class(): return BaseDataSourceLocalNodeForm() def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult: diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index 2473d3868ef..73624b08c39 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -32,7 +32,7 @@ def get_form_list(self): self.is_valid(raise_exception=True) if self.data.get('type') == 'local': node = get_node(self.data.get('id')) - return node.get_form_class()().to_form_list() + return node.get_form_class().to_form_list() elif self.data.get('type') == 'tool': tool = QuerySet(Tool).filter(id=self.data.get("id")).first() # todo 调用工具数据源的函数获取表单列表 diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index 1ff53a9cf9a..a4e686445ab 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -69,5 +69,6 @@ path('workspace//knowledge//problem//', views.ProblemView.Page.as_view()), path('workspace//knowledge//document//', views.DocumentView.Page.as_view()), path('workspace//knowledge//', views.KnowledgeView.Page.as_view()), + path('workspace//knowledge//form_list//', views.KnowledgeWorkflowFormView.as_view()), ] diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index 15d7b11660a..4d6bfa9c048 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -18,8 +18,8 @@ class KnowledgeWorkflowFormView(APIView): authentication_classes = [TokenAuth] - def get(self): - return result.success(KnowledgeWorkflowSerializer.Form().get_form_list()) + def get(self, request: Request, workspace_id: str, knowledge_id: str, type: str, id: str): + return result.success(KnowledgeWorkflowSerializer.Form(data={'type': type, 'id': id}).get_form_list()) class KnowledgeWorkflowView(APIView): diff --git a/ui/src/api/knowledge/knowledge.ts b/ui/src/api/knowledge/knowledge.ts index d36f9b03741..7ebbc6f0c11 100644 --- a/ui/src/api/knowledge/knowledge.ts +++ b/ui/src/api/knowledge/knowledge.ts @@ -315,6 +315,14 @@ const delMulTag: ( ) => Promise> = (knowledge_id, tags, loading) => { return put(`${prefix.value}/${knowledge_id}/tags/batch_delete`, tags, null, loading) } +const getKnowledgeWorkflowFormList: ( + knowledge_id: string, + type: 'loacl' | 'tool', + id: string, + loading?: Ref, +) => Promise> = (knowledge_id: string, type: 'loacl' | 'tool', id: string, loading) => { + return get(`${prefix.value}/${knowledge_id}/form_list/${type}/${id}`, null, loading) +} export default { getKnowledgeList, @@ -340,4 +348,5 @@ export default { delTag, delMulTag, createWorkflowKnowledge, + getKnowledgeWorkflowFormList, } diff --git a/ui/src/views/knowledge-workflow/component/Debug.vue b/ui/src/views/knowledge-workflow/component/Debug.vue new file mode 100644 index 00000000000..bd6d69c4017 --- /dev/null +++ b/ui/src/views/knowledge-workflow/component/Debug.vue @@ -0,0 +1,29 @@ + + + diff --git a/ui/src/views/knowledge-workflow/component/action/index.vue b/ui/src/views/knowledge-workflow/component/action/index.vue index 66c7715905f..40059a3ce19 100644 --- a/ui/src/views/knowledge-workflow/component/action/index.vue +++ b/ui/src/views/knowledge-workflow/component/action/index.vue @@ -1,11 +1,78 @@ diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue index d808df5d273..9dfc74597aa 100644 --- a/ui/src/views/knowledge-workflow/index.vue +++ b/ui/src/views/knowledge-workflow/index.vue @@ -125,11 +125,9 @@
-
- -
+ { const isDefaultTheme = computed(() => { return theme.isDefaultTheme() }) +const DebugRef = ref>() let interval: any const workflowRef = ref() @@ -393,8 +393,8 @@ const clickShowDebug = () => { ...workflow.get_base_node()?.properties.node_data, work_flow: getGraphData(), } - - showDebug.value = true + console.log('sss', DebugRef.value) + DebugRef.value?.open(graphData) } catch (e: any) { console.log(e) MsgError(e.toString()) diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts index 68b4734a4d7..41975985c76 100644 --- a/ui/src/workflow/common/data.ts +++ b/ui/src/workflow/common/data.ts @@ -81,7 +81,6 @@ export const knowledgeBaseNode = { }, } export const dataSourceLocalNode = { - id: WorkflowType.DataSourceLocalNode, type: WorkflowType.DataSourceLocalNode, x: 360, y: 2761.3875, From 856c1eeb8ac2c73a16406c2d4268df9f96fe1cbc Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Tue, 11 Nov 2025 18:51:20 +0800 Subject: [PATCH 10/75] feat: knowledge workflow --- ui/src/views/knowledge-workflow/index.vue | 6 +- ui/src/workflow/common/validate.ts | 112 ++++++++++++++-------- 2 files changed, 74 insertions(+), 44 deletions(-) diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue index 9dfc74597aa..a0161deb6e6 100644 --- a/ui/src/views/knowledge-workflow/index.vue +++ b/ui/src/views/knowledge-workflow/index.vue @@ -149,7 +149,7 @@ import { MsgSuccess, MsgError, MsgConfirm } from '@/utils/message' import { datetimeFormat } from '@/utils/time' import { mapToUrlParams } from '@/utils/application' import useStore from '@/stores' -import { WorkFlowInstance } from '@/workflow/common/validate' +import { KnowledgeWorkFlowInstance } from '@/workflow/common/validate' import { hasPermission } from '@/utils/permission' import DebugVue from './component/Debug.vue' import { t } from '@/locales' @@ -301,7 +301,7 @@ const publish = () => { ?.validate() .then(() => { const workflow = getGraphData() - const workflowInstance = new WorkFlowInstance(workflow, WorkflowMode.Knowledge) + const workflowInstance = new KnowledgeWorkFlowInstance(workflow, WorkflowMode.Knowledge) try { workflowInstance.is_valid() } catch (e: any) { @@ -384,7 +384,7 @@ const clickShowDebug = () => { ?.validate() .then(() => { const graphData = getGraphData() - const workflow = new WorkFlowInstance(graphData, WorkflowMode.Knowledge) + const workflow = new KnowledgeWorkFlowInstance(graphData, WorkflowMode.Knowledge) try { workflow.is_valid() detail.value = { diff --git a/ui/src/workflow/common/validate.ts b/ui/src/workflow/common/validate.ts index 913f6799e34..b41044918e8 100644 --- a/ui/src/workflow/common/validate.ts +++ b/ui/src/workflow/common/validate.ts @@ -65,11 +65,9 @@ export class WorkFlowInstance { /** * 校验开始节点 */ - private is_valid_start_node() { - const start_node_list = this.nodes.filter( - (item) => - [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) || - item.properties.kind == WorkflowKind.DataSource, + is_valid_start_node() { + const start_node_list = this.nodes.filter((item) => + [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id), ) if (start_node_list.length == 0) { throw t('views.applicationWorkflow.validate.startNodeRequired') @@ -81,11 +79,7 @@ export class WorkFlowInstance { /** * 校验基本信息节点 */ - private is_valid_base_node() { - console.log(this.workflowModel) - if (this.workflowModel == WorkflowMode.Knowledge) { - return - } + is_valid_base_node() { const start_node_list = this.nodes.filter((item) => item.id === WorkflowType.Base) if (start_node_list.length == 0) { throw t('views.applicationWorkflow.validate.baseNodeRequired') @@ -115,10 +109,8 @@ export class WorkFlowInstance { * @returns */ get_start_node() { - const start_node_list = this.nodes.filter( - (item) => - [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) || - item.properties.kind == WorkflowKind.DataSource, + const start_node_list = this.nodes.filter((item) => + [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id), ) return start_node_list[0] } @@ -140,7 +132,7 @@ export class WorkFlowInstance { * 校验工作流 * @param up_node 上一个节点 */ - private _is_valid_work_flow(up_node?: any) { + _is_valid_work_flow(up_node?: any) { if (!up_node) { up_node = this.get_start_node() } @@ -152,28 +144,11 @@ export class WorkFlowInstance { } } - private is_valid_work_flow() { + is_valid_work_flow() { this.workFlowNodes = [] - if (this.workflowModel == WorkflowMode.Knowledge) { - const start_node_list = this.nodes.filter( - (item) => - [WorkflowType.Start, WorkflowType.LoopStartNode].includes(item.id) || - item.properties.kind == WorkflowKind.DataSource, - ) - start_node_list.forEach((startNode) => { - this._is_valid_work_flow(startNode) - }) - } else { - this._is_valid_work_flow() - } - + this._is_valid_work_flow() const notInWorkFlowNodes = this.nodes - .filter( - (node: any) => - node.id !== WorkflowType.Start && - node.id !== WorkflowType.Base && - node.id !== WorkflowType.KnowledgeBase, - ) + .filter((node: any) => node.id !== WorkflowType.Start && node.id !== WorkflowType.Base) .filter((node) => !this.workFlowNodes.includes(node)) if (notInWorkFlowNodes.length > 0) { throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${notInWorkFlowNodes.map((node) => node.properties.stepName).join(',')}` @@ -186,7 +161,7 @@ export class WorkFlowInstance { * @param node 节点 * @returns 节点列表 */ - private get_next_nodes(node: any) { + get_next_nodes(node: any) { const edge_list = this.edges.filter((edge) => edge.sourceNodeId == node.id) const node_list = edge_list .map((edge) => this.nodes.filter((node) => node.id == edge.targetNodeId)) @@ -198,14 +173,12 @@ export class WorkFlowInstance { return node_list } - private is_valid_nodes() { + is_valid_nodes() { for (const node of this.nodes) { if ( node.type !== WorkflowType.Base && node.type !== WorkflowType.Start && - node.type !== WorkflowType.LoopStartNode && - node.type !== WorkflowType.KnowledgeBase && - node.properties.kind !== WorkflowKind.DataSource + node.type !== WorkflowType.LoopStartNode ) { if (!this.edges.some((edge) => edge.targetNodeId === node.id)) { throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${node.properties.stepName}` @@ -218,7 +191,7 @@ export class WorkFlowInstance { * 校验节点 * @param node 节点 */ - private is_valid_node(node: any) { + is_valid_node(node: any) { if (node.properties.status && node.properties.status === 500) { throw `${node.properties.stepName} ${t('views.applicationWorkflow.validate.nodeUnavailable')}` } @@ -243,3 +216,60 @@ export class WorkFlowInstance { } } } + +export class KnowledgeWorkFlowInstance extends WorkFlowInstance { + is_valid_start_node() { + const start_node_list = this.nodes.filter( + (item) => item.properties.kind === WorkflowKind.DataSource, + ) + if (start_node_list.length == 0) { + throw t('views.applicationWorkflow.validate.startNodeRequired') + } + } + /** + * 校验基本信息节点 + */ + is_valid_base_node() { + const base_node_list = this.nodes.filter((item) => item.id === WorkflowType.KnowledgeBase) + if (base_node_list.length == 0) { + throw t('views.applicationWorkflow.validate.baseNodeRequired') + } else if (base_node_list.length > 1) { + throw t('views.applicationWorkflow.validate.baseNodeOnly') + } + } + + is_valid_work_flow() { + this.workFlowNodes = [] + const start_node_list = this.nodes.filter( + (item) => item.properties.kind === WorkflowKind.DataSource, + ) + start_node_list.forEach((n) => { + this._is_valid_work_flow(n) + }) + + const notInWorkFlowNodes = this.nodes + .filter( + (node: any) => + node.id !== WorkflowType.KnowledgeBase && + node.properties.kind !== WorkflowKind.DataSource, + ) + .filter((node) => !this.workFlowNodes.includes(node)) + if (notInWorkFlowNodes.length > 0) { + throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${notInWorkFlowNodes.map((node) => node.properties.stepName).join(',')}` + } + this.workFlowNodes = [] + } + + is_valid_nodes() { + for (const node of this.nodes) { + if ( + node.type !== WorkflowType.KnowledgeBase && + node.properties.kind !== WorkflowKind.DataSource + ) { + if (!this.edges.some((edge) => edge.targetNodeId === node.id)) { + throw `${t('views.applicationWorkflow.validate.notInWorkFlowNode')}:${node.properties.stepName}` + } + } + } + } +} From 4ee70651e7e1f165618e328c6c8370bdf3fc4d21 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Tue, 11 Nov 2025 19:00:39 +0800 Subject: [PATCH 11/75] feat: knowledge workflow --- .../knowledge-workflow/component/action/index.vue | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ui/src/views/knowledge-workflow/component/action/index.vue b/ui/src/views/knowledge-workflow/component/action/index.vue index 40059a3ce19..7aae4600d02 100644 --- a/ui/src/views/knowledge-workflow/component/action/index.vue +++ b/ui/src/views/knowledge-workflow/component/action/index.vue @@ -11,9 +11,17 @@ @@ -24,6 +32,7 @@ import { computed, ref } from 'vue' import { WorkflowKind, WorkflowMode, WorkflowType } from '@/enums/application' import DynamicsForm from '@/components/dynamics-form/index.vue' import type { FormField } from '@/components/dynamics-form/type' +import { iconComponent } from '@/workflow/icons/utils' import type { Dict } from '@/api/type/common' import type { FormRules } from 'element-plus' import { loadSharedApi } from '@/utils/dynamics-api/shared-api' From 8df567fb79b1bad5b95e998cfcf6a27b123e2098 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Wed, 12 Nov 2025 10:19:17 +0800 Subject: [PATCH 12/75] feat: knowledge workflow --- .../data_source_local_node/impl/base_data_source_local_node.py | 2 +- apps/knowledge/serializers/knowledge_workflow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py index 0e501a11173..7dd9b810b18 100644 --- a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py @@ -22,7 +22,7 @@ def save_context(self, details, workflow_manage): @staticmethod def get_form_class(): - return BaseDataSourceLocalNodeForm() + return BaseDataSourceLocalNodeForm def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult: pass diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index 73624b08c39..2473d3868ef 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -32,7 +32,7 @@ def get_form_list(self): self.is_valid(raise_exception=True) if self.data.get('type') == 'local': node = get_node(self.data.get('id')) - return node.get_form_class().to_form_list() + return node.get_form_class()().to_form_list() elif self.data.get('type') == 'tool': tool = QuerySet(Tool).filter(id=self.data.get("id")).first() # todo 调用工具数据源的函数获取表单列表 From 17c0056be19c51cf83cb1a560ab4223fb8b3e698 Mon Sep 17 00:00:00 2001 From: zhangzhanwei Date: Wed, 12 Nov 2025 14:56:21 +0800 Subject: [PATCH 13/75] feat: Data source web node --- apps/application/flow/step_node/__init__.py | 3 +- .../data_source_web_node/__init__.py | 8 +++++ .../i_data_source_web_node.py | 30 +++++++++++++++++++ .../data_source_web_node/impl/__init__.py | 8 +++++ .../impl/base_data_source_web_node.py | 29 ++++++++++++++++++ ui/src/enums/application.ts | 1 + .../lang/en-US/views/application-workflow.ts | 6 ++++ .../lang/zh-CN/views/application-workflow.ts | 6 ++++ .../zh-Hant/views/application-workflow.ts | 6 ++++ .../component/action/index.vue | 2 +- ui/src/workflow/common/data.ts | 28 ++++++++++++++++- .../icons/data-source-web-node-icon.vue | 6 ++++ .../nodes/data-source-web-node/index.vue | 26 +--------------- 13 files changed, 131 insertions(+), 28 deletions(-) create mode 100644 apps/application/flow/step_node/data_source_web_node/__init__.py create mode 100644 apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py create mode 100644 apps/application/flow/step_node/data_source_web_node/impl/__init__.py create mode 100644 apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py create mode 100644 ui/src/workflow/icons/data-source-web-node-icon.vue diff --git a/apps/application/flow/step_node/__init__.py b/apps/application/flow/step_node/__init__.py index 6a14e0fd6bb..382c5fedd18 100644 --- a/apps/application/flow/step_node/__init__.py +++ b/apps/application/flow/step_node/__init__.py @@ -10,6 +10,7 @@ from .application_node import BaseApplicationNode from .condition_node import * from .data_source_local_node.impl.base_data_source_local_node import BaseDataSourceLocalNode +from .data_source_web_node.impl.base_data_source_web_node import BaseDataSourceWebNode from .direct_reply_node import * from .document_extract_node import * from .form_node import * @@ -48,7 +49,7 @@ BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode, BaseLoopContinueNode, BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode, - BaseDataSourceLocalNode] + BaseDataSourceLocalNode,BaseDataSourceWebNode] def get_node(node_type): diff --git a/apps/application/flow/step_node/data_source_web_node/__init__.py b/apps/application/flow/step_node/data_source_web_node/__init__.py new file mode 100644 index 00000000000..461bab6fc12 --- /dev/null +++ b/apps/application/flow/step_node/data_source_web_node/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:niu + @file: __init__.py.py + @date:2025/11/12 13:43 + @desc: +""" diff --git a/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py b/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py new file mode 100644 index 00000000000..da97cfb9a47 --- /dev/null +++ b/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py @@ -0,0 +1,30 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:niu + @file: i_data_source_web_node.py + @date:2025/11/12 13:47 + @desc: +""" +from abc import abstractmethod + + +from application.flow.i_step_node import INode, NodeResult + + +class IDataSourceWebNode(INode): + + type = 'data-source-web-node' + + @staticmethod + @abstractmethod + def get_form_class(): + pass + + def _run(self): + return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) + + + def execute(self, **kwargs) -> NodeResult: + pass + diff --git a/apps/application/flow/step_node/data_source_web_node/impl/__init__.py b/apps/application/flow/step_node/data_source_web_node/impl/__init__.py new file mode 100644 index 00000000000..b7541b12df1 --- /dev/null +++ b/apps/application/flow/step_node/data_source_web_node/impl/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:niu + @file: __init__.py + @date:2025/11/12 13:44 + @desc: +""" diff --git a/apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py b/apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py new file mode 100644 index 00000000000..e015058e05b --- /dev/null +++ b/apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py @@ -0,0 +1,29 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:niu + @file: base_data_source_web_node.py + @date:2025/11/12 13:47 + @desc: +""" +from application.flow.i_step_node import NodeResult +from application.flow.step_node.data_source_web_node.i_data_source_web_node import IDataSourceWebNode +from common import forms +from common.forms import BaseForm + + +class BaseDataSourceWebNodeForm(BaseForm): + source_url = forms.TextInputField('source url', required=True) + selector = forms.TextInputField('knowledge selector', required=True) + + +class BaseDataSourceWebNode(IDataSourceWebNode): + def save_context(self, details, workflow_manage): + pass + + @staticmethod + def get_form_class(): + return BaseDataSourceWebNodeForm + + def execute(self, **kwargs) -> NodeResult: + pass \ No newline at end of file diff --git a/ui/src/enums/application.ts b/ui/src/enums/application.ts index 873c77b6764..933ab6763af 100644 --- a/ui/src/enums/application.ts +++ b/ui/src/enums/application.ts @@ -39,6 +39,7 @@ export enum WorkflowType { VideoUnderstandNode = 'video-understand-node', ParameterExtractionNode = 'parameter-extraction-node', DataSourceLocalNode = 'data-source-local-node', + DataSourceWebNode = 'data-source-web-node', } export enum WorkflowKind { DataSource = 'data-source', diff --git a/ui/src/locales/lang/en-US/views/application-workflow.ts b/ui/src/locales/lang/en-US/views/application-workflow.ts index 989d149161b..7ab3938ef11 100644 --- a/ui/src/locales/lang/en-US/views/application-workflow.ts +++ b/ui/src/locales/lang/en-US/views/application-workflow.ts @@ -79,6 +79,12 @@ export default { loopNodeBreakNodeRequired: 'Wireless loop must have a Break node', }, nodes: { + dataSourceWebNode: { + label: 'Web Site', + text: 'Web Site', + display: 'No data available', + field_label: 'Document list', + }, classify: { aiCapability: 'AI capability', businessLogic: 'Business logic', diff --git a/ui/src/locales/lang/zh-CN/views/application-workflow.ts b/ui/src/locales/lang/zh-CN/views/application-workflow.ts index d83a32bd462..f840d5ed679 100644 --- a/ui/src/locales/lang/zh-CN/views/application-workflow.ts +++ b/ui/src/locales/lang/zh-CN/views/application-workflow.ts @@ -81,6 +81,12 @@ export default { loopNodeBreakNodeRequired: '无限循环 必须存在 Break 节点', }, nodes: { + dataSourceWebNode: { + label: 'Web站点', + text: 'Web站点', + display: '暂无数据', + field_label: '文档列表', + }, classify: { aiCapability: 'AI能力', businessLogic: '业务逻辑', diff --git a/ui/src/locales/lang/zh-Hant/views/application-workflow.ts b/ui/src/locales/lang/zh-Hant/views/application-workflow.ts index 4c79f91d07d..f1ec72a1ccd 100644 --- a/ui/src/locales/lang/zh-Hant/views/application-workflow.ts +++ b/ui/src/locales/lang/zh-Hant/views/application-workflow.ts @@ -80,6 +80,12 @@ export default { loopNodeBreakNodeRequired: '無限循環必須存在Break節點', }, nodes: { + dataSourceWebNode: { + label: 'Web網站', + text: 'Web網站', + display: '暫無資料', + field_label: '文件列表', + }, classify: { aiCapability: 'AI能力', businessLogic: '業務邏輯', diff --git a/ui/src/views/knowledge-workflow/component/action/index.vue b/ui/src/views/knowledge-workflow/component/action/index.vue index 7aae4600d02..d846d4dd338 100644 --- a/ui/src/views/knowledge-workflow/component/action/index.vue +++ b/ui/src/views/knowledge-workflow/component/action/index.vue @@ -69,7 +69,7 @@ const { } = route as any const sourceChange = (node_id: string) => { const n = source_node_list.value.find((n: any) => n.id == node_id) - node_id = n ? ([WorkflowType.DataSourceLocalNode].includes(n.type) ? n.type : node_id) : node_id + node_id = n ? ([WorkflowType.DataSourceLocalNode,WorkflowType.DataSourceWebNode].includes(n.type) ? n.type : node_id) : node_id loadSharedApi({ type: 'knowledge', systemType: apiType.value }) .getKnowledgeWorkflowFormList(id, 'local', node_id) .then((ok) => { diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts index 41975985c76..b085f7ea86d 100644 --- a/ui/src/workflow/common/data.ts +++ b/ui/src/workflow/common/data.ts @@ -98,6 +98,31 @@ export const dataSourceLocalNode = { user_input_field_list: [], }, } + +export const dataSourceWebNode = { + id: WorkflowType.DataSourceWebNode, + type: WorkflowType.DataSourceWebNode, + x: 360, + y: 2761.3875, + text: t('views.applicationWorkflow.nodes.dataSourceWebNode.text', 'Web站点'), + label: t('views.applicationWorkflow.nodes.dataSourceWebNode.label', 'Web站点'), + properties: { + kind: WorkflowKind.DataSource, + height: 180, + stepName: t('views.applicationWorkflow.nodes.dataSourceWebNode.label', 'Web站点'), + config: { + fields: [ + { + label: t('views.applicationWorkflow.nodes.dataSourceWebNode.field_label'), + value: 'document_list', + }, + ], + }, + }, + +} + + /** * 说明 * type 与 nodes 文件对应 @@ -662,7 +687,7 @@ export const loopBreakNode = { export const knowledgeMenuNodes = [ { label: t('views.applicationWorkflow.nodes.classify.dataSource', '数据源'), - list: [dataSourceLocalNode], + list: [dataSourceLocalNode, dataSourceWebNode], }, { label: t('views.applicationWorkflow.nodes.classify.aiCapability'), @@ -926,6 +951,7 @@ export const nodeDict: any = { [WorkflowType.VariableAggregationNode]: variableAggregationNode, [WorkflowType.KnowledgeBase]: knowledgeBaseNode, [WorkflowType.DataSourceLocalNode]: dataSourceLocalNode, + [WorkflowType.DataSourceWebNode]: dataSourceWebNode, } export function isWorkFlow(type: string | undefined) { diff --git a/ui/src/workflow/icons/data-source-web-node-icon.vue b/ui/src/workflow/icons/data-source-web-node-icon.vue new file mode 100644 index 00000000000..db6c243f8a6 --- /dev/null +++ b/ui/src/workflow/icons/data-source-web-node-icon.vue @@ -0,0 +1,6 @@ + + diff --git a/ui/src/workflow/nodes/data-source-web-node/index.vue b/ui/src/workflow/nodes/data-source-web-node/index.vue index f1d5d626000..441b974f309 100644 --- a/ui/src/workflow/nodes/data-source-web-node/index.vue +++ b/ui/src/workflow/nodes/data-source-web-node/index.vue @@ -2,31 +2,7 @@
{{ $t('views.applicationWorkflow.nodeSetting') }}
- - - - - +

{{ $t('views.applicationWorkflow.nodes.dataSourceWebNode.display') }}

From 58446c896e32104ee39cc8a3e1c3de4074b0d626 Mon Sep 17 00:00:00 2001 From: zhangzhanwei Date: Wed, 12 Nov 2025 16:49:38 +0800 Subject: [PATCH 14/75] fix: Back route --- ui/src/permission/knowledge/system-manage.ts | 6 ++++++ ui/src/permission/knowledge/system-share.ts | 5 +++-- ui/src/permission/knowledge/workspace-share.ts | 2 +- ui/src/permission/knowledge/workspace.ts | 16 +++++++++++++++- ui/src/permission/model/system-manage.ts | 2 ++ ui/src/permission/model/system-share.ts | 1 + ui/src/permission/model/workspace.ts | 1 + ui/src/router/modules/document.ts | 4 ++-- ui/src/router/routes.ts | 2 +- ui/src/views/knowledge-workflow/index.vue | 6 +++--- 10 files changed, 35 insertions(+), 10 deletions(-) diff --git a/ui/src/permission/knowledge/system-manage.ts b/ui/src/permission/knowledge/system-manage.ts index 07208f5f029..be65ab207f8 100644 --- a/ui/src/permission/knowledge/system-manage.ts +++ b/ui/src/permission/knowledge/system-manage.ts @@ -185,6 +185,12 @@ const systemManage = { PermissionConst.RESOURCE_KNOWLEDGE_TAG_DELETE ],'OR' ), + bug: () => + hasPermission([ + RoleConst.ADMIN, + PermissionConst.RESOURCE_KNOWLEDGE_WORKFLOW_READ + ],'OR' + ), chat_user_edit: () =>false, diff --git a/ui/src/permission/knowledge/system-share.ts b/ui/src/permission/knowledge/system-share.ts index 2546c0701e3..646030b4815 100644 --- a/ui/src/permission/knowledge/system-share.ts +++ b/ui/src/permission/knowledge/system-share.ts @@ -56,7 +56,8 @@ const share = { tag_edit: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_TAG_EDIT], 'OR'), tag_delete: () => hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_TAG_DELETE], 'OR'), - + debug: () => + hasPermission([RoleConst.ADMIN, PermissionConst.SHARED_KNOWLEDGE_WORKFLOW_READ], 'OR'), chat_user_edit: () => false, auth: () => false, @@ -67,6 +68,6 @@ const share = { folderAuth: () => false, folderDelete: () => false, hit_test: () => false, - debug: (source_id: string) => true, + } export default share diff --git a/ui/src/permission/knowledge/workspace-share.ts b/ui/src/permission/knowledge/workspace-share.ts index 6b82df5c74c..ccb9c5808a2 100644 --- a/ui/src/permission/knowledge/workspace-share.ts +++ b/ui/src/permission/knowledge/workspace-share.ts @@ -47,7 +47,7 @@ const workspaceShare = { folderAuth: () => false, folderDelete: () => false, hit_test: () => false, - debug: (source_id: string) => true, + debug: () => true, } export default workspaceShare diff --git a/ui/src/permission/knowledge/workspace.ts b/ui/src/permission/knowledge/workspace.ts index 0c44a10a275..f381ca8b2d9 100644 --- a/ui/src/permission/knowledge/workspace.ts +++ b/ui/src/permission/knowledge/workspace.ts @@ -557,7 +557,21 @@ const workspace = { ], 'OR', ), - debug: (source_id: string) => true, + debug: (source_id: string) => + hasPermission( + [ + new ComplexPermission( + [RoleConst.USER], + [PermissionConst.KNOWLEDGE.getKnowledgeWorkspaceResourcePermission(source_id)], + [], + 'AND', + ), + RoleConst.WORKSPACE_MANAGE.getWorkspaceRole, + PermissionConst.KNOWLEDGE_WORKFLOW_READ.getKnowledgeWorkspaceResourcePermission(source_id), + PermissionConst.KNOWLEDGE_WORKFLOW_READ.getWorkspacePermissionWorkspaceManageRole, + ], + 'OR', + ), hit_test: () => false, } diff --git a/ui/src/permission/model/system-manage.ts b/ui/src/permission/model/system-manage.ts index bdd12a36865..9b247599fe2 100644 --- a/ui/src/permission/model/system-manage.ts +++ b/ui/src/permission/model/system-manage.ts @@ -29,6 +29,8 @@ const systemManage = { folderEdit: () => false, folderAuth: () => false, folderDelete: () => false, + debug: () => false, + } export default systemManage diff --git a/ui/src/permission/model/system-share.ts b/ui/src/permission/model/system-share.ts index 1e556d4ed33..b152b26d883 100644 --- a/ui/src/permission/model/system-share.ts +++ b/ui/src/permission/model/system-share.ts @@ -42,5 +42,6 @@ const share = { folderEdit: () => false, folderAuth: () => false, folderDelete: () => false, + debug: () => false, } export default share diff --git a/ui/src/permission/model/workspace.ts b/ui/src/permission/model/workspace.ts index ad3b6761050..0634154d6ec 100644 --- a/ui/src/permission/model/workspace.ts +++ b/ui/src/permission/model/workspace.ts @@ -93,6 +93,7 @@ const workspace = { ], 'OR' ), + debug: () => false, } export default workspace diff --git a/ui/src/router/modules/document.ts b/ui/src/router/modules/document.ts index 40d2a146bb0..7691a6a0d8e 100644 --- a/ui/src/router/modules/document.ts +++ b/ui/src/router/modules/document.ts @@ -194,8 +194,8 @@ const DocumentRouter = { }, redirect: (menu: any) => { const from = 'workspace' - console.log(`/knowledge/${from}/${menu.params.id}/workflow`) - return `/knowledge/${from}/${menu.params.id}/workflow` + console.log(`/knowledge/${from}/${menu.params.id}/${menu.params.folderId}/workflow`) + return `/knowledge/${from}/${menu.params.id}/${menu.params.folderId}/workflow` }, component: () => import('@/views/knowledge/index.vue'), }, diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index 2bd42b2e0f8..b0b3ca3e9df 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -38,7 +38,7 @@ export const routes: Array = [ }, // 高级编排 { - path: '/knowledge/:from/:id/workflow', + path: '/knowledge/:from/:id/:folderId/workflow', name: 'KnowledgeWorkflow', meta: { activeMenu: '/knowledge' }, component: () => import('@/views/knowledge-workflow/index.vue'), diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue index a0161deb6e6..4b0b65856bf 100644 --- a/ui/src/views/knowledge-workflow/index.vue +++ b/ui/src/views/knowledge-workflow/index.vue @@ -30,7 +30,7 @@ {{ $t('views.knowledgeWorkflow.setting.addComponent') }} - + {{ $t('views.knowledgeWorkflow.setting.debug') }} @@ -166,7 +166,7 @@ const { theme } = useStore() const router = useRouter() const route = useRoute() const { - params: { id, from }, + params: { id, from, folderId }, } = route as any const apiType = computed(() => { if (route.path.includes('resource-management')) { @@ -505,7 +505,7 @@ const get_resource_management_route = () => { } const get_route = () => { - return `/knowledge/${id}/${from}/document` + return `/knowledge/${id}/${folderId}/4/document` } /** From 6b61dac7bf46dfdd9ede3bddf9b855f5c5727b00 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Wed, 12 Nov 2025 17:00:39 +0800 Subject: [PATCH 15/75] feat: knowledge workflow --- .../i_data_source_local_node.py | 8 +- .../impl/base_data_source_local_node.py | 14 +- .../i_data_source_web_node.py | 2 +- .../impl/base_data_source_web_node.py | 6 +- apps/knowledge/serializers/knowledge.py | 14 +- .../serializers/knowledge_workflow.py | 3 +- apps/knowledge/views/knowledge_workflow.py | 5 +- ui/src/api/knowledge/knowledge.ts | 11 +- ui/src/components/dynamics-form/Demo.vue | 10 ++ .../items/upload/LocalFileUpload.vue | 149 ++++++++++++++++++ .../knowledge-workflow/component/Debug.vue | 4 +- .../component/action/index.vue | 4 +- .../nodes/data-source-local-node/index.vue | 32 ++-- 13 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue diff --git a/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py index beba58232bc..856369d54a3 100644 --- a/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py @@ -16,9 +16,9 @@ class DataSourceLocalNodeParamsSerializer(serializers.Serializer): - file_format = serializers.ListField(child=serializers.CharField(label=('')), label='') - max_file_number = serializers.IntegerField(required=True, label=_("Number of uploaded files")) - file_max_size = serializers.IntegerField(required=True, label=_("Upload file size")) + file_type_list = serializers.ListField(child=serializers.CharField(label=('')), label='') + file_size_limit = serializers.IntegerField(required=True, label=_("Number of uploaded files")) + file_count_limit = serializers.IntegerField(required=True, label=_("Upload file size")) class IDataSourceLocalNode(INode): @@ -26,7 +26,7 @@ class IDataSourceLocalNode(INode): @staticmethod @abstractmethod - def get_form_class(): + def get_form_list(node): pass def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: diff --git a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py index 7dd9b810b18..d64fdd86103 100644 --- a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py @@ -21,8 +21,18 @@ def save_context(self, details, workflow_manage): pass @staticmethod - def get_form_class(): - return BaseDataSourceLocalNodeForm + def get_form_list(node): + node_data = node.get('properties').get('node_data') + return [{ + 'field': 'file_list', + 'input_type': 'LocalFileUpload', + 'attrs': { + 'file_count_limit': node_data.get('file_count_limit') or 10, + 'file_size_limit': node_data.get('file_size_limit') or 100, + 'file_type_list': node_data.get('file_type_list'), + }, + 'label': '', + }] def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult: pass diff --git a/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py b/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py index da97cfb9a47..f5e210a8edf 100644 --- a/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py +++ b/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py @@ -18,7 +18,7 @@ class IDataSourceWebNode(INode): @staticmethod @abstractmethod - def get_form_class(): + def get_form_list(node): pass def _run(self): diff --git a/apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py b/apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py index e015058e05b..518e2061e62 100644 --- a/apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py +++ b/apps/application/flow/step_node/data_source_web_node/impl/base_data_source_web_node.py @@ -22,8 +22,8 @@ def save_context(self, details, workflow_manage): pass @staticmethod - def get_form_class(): - return BaseDataSourceWebNodeForm + def get_form_list(node): + return BaseDataSourceWebNodeForm().to_form_list() def execute(self, **kwargs) -> NodeResult: - pass \ No newline at end of file + pass diff --git a/apps/knowledge/serializers/knowledge.py b/apps/knowledge/serializers/knowledge.py index d8e347dc5c2..420823db627 100644 --- a/apps/knowledge/serializers/knowledge.py +++ b/apps/knowledge/serializers/knowledge.py @@ -31,7 +31,7 @@ from common.utils.logger import maxkb_logger from common.utils.split_model import get_split_model from knowledge.models import Knowledge, KnowledgeScope, KnowledgeType, Document, Paragraph, Problem, \ - ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder, File, Tag + ProblemParagraphMapping, TaskType, State, SearchMode, KnowledgeFolder, File, Tag, KnowledgeWorkflow from knowledge.serializers.common import ProblemParagraphManage, drop_knowledge_index, \ get_embedding_model_id_by_knowledge_id, MetaSerializer, \ GenerateRelatedSerializer, get_embedding_model_by_knowledge_id, list_paragraph, write_image, zip_dir @@ -350,7 +350,7 @@ def one(self): workflow = k.work_flow return { **knowledge_dict, - 'workflow': workflow, + 'work_flow': workflow, 'meta': json.loads(knowledge_dict.get('meta', '{}')), 'application_id_list': list(filter( lambda application_id: all_application_list.__contains__(application_id), @@ -413,7 +413,15 @@ def edit(self, instance: Dict, select_one=True): application_id=application_id, knowledge_id=self.data.get('knowledge_id') ) for application_id in application_id_list ]) if len(application_id_list) > 0 else None - + if instance.get("work_flow"): + QuerySet(KnowledgeWorkflow).update_or_create(knowledge_id=self.data.get("knowledge_id"), + create_defaults={'id': uuid.uuid7(), + 'knowledge_id': self.data.get("knowledge_id"), + "workspace_id": self.data.get('workspace_id'), + 'work_flow': instance.get('work_flow', {}), }, + defaults={ + 'work_flow': instance.get('work_flow') + }) knowledge.save() if select_one: return self.one() diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index 2473d3868ef..f449cc59d91 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -27,12 +27,13 @@ class KnowledgeWorkflowSerializer(serializers.Serializer): class Form(serializers.Serializer): type = serializers.CharField(required=True, label=_('type')) id = serializers.CharField(required=True, label=_('type')) + node = serializers.DictField(required=True, label="") def get_form_list(self): self.is_valid(raise_exception=True) if self.data.get('type') == 'local': node = get_node(self.data.get('id')) - return node.get_form_class()().to_form_list() + return node.get_form_list(self.data.get("node")) elif self.data.get('type') == 'tool': tool = QuerySet(Tool).filter(id=self.data.get("id")).first() # todo 调用工具数据源的函数获取表单列表 diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index 4d6bfa9c048..7f902b135ed 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -18,8 +18,9 @@ class KnowledgeWorkflowFormView(APIView): authentication_classes = [TokenAuth] - def get(self, request: Request, workspace_id: str, knowledge_id: str, type: str, id: str): - return result.success(KnowledgeWorkflowSerializer.Form(data={'type': type, 'id': id}).get_form_list()) + def post(self, request: Request, workspace_id: str, knowledge_id: str, type: str, id: str): + return result.success(KnowledgeWorkflowSerializer.Form( + data={'type': type, 'id': id, 'node': request.data.get('node')}).get_form_list()) class KnowledgeWorkflowView(APIView): diff --git a/ui/src/api/knowledge/knowledge.ts b/ui/src/api/knowledge/knowledge.ts index 7ebbc6f0c11..4704afe9cad 100644 --- a/ui/src/api/knowledge/knowledge.ts +++ b/ui/src/api/knowledge/knowledge.ts @@ -319,9 +319,16 @@ const getKnowledgeWorkflowFormList: ( knowledge_id: string, type: 'loacl' | 'tool', id: string, + node: any, loading?: Ref, -) => Promise> = (knowledge_id: string, type: 'loacl' | 'tool', id: string, loading) => { - return get(`${prefix.value}/${knowledge_id}/form_list/${type}/${id}`, null, loading) +) => Promise> = ( + knowledge_id: string, + type: 'loacl' | 'tool', + id: string, + node, + loading, +) => { + return post(`${prefix.value}/${knowledge_id}/form_list/${type}/${id}`, { node }, {}, loading) } export default { diff --git a/ui/src/components/dynamics-form/Demo.vue b/ui/src/components/dynamics-form/Demo.vue index 0075f9f0116..5e631f3de7f 100644 --- a/ui/src/components/dynamics-form/Demo.vue +++ b/ui/src/components/dynamics-form/Demo.vue @@ -22,6 +22,16 @@ import { ref } from 'vue' import type { Dict } from '@/api/type/common' const damo_data: Array = [ + { + field: 'aa', + input_type: 'LocalFileUpload', + attrs: { + file_count_limit: 10, + file_size_limit: 10, + file_type_list: ['TXT'], + }, + label: '', + }, { field: 'name', input_type: 'PasswordInput', diff --git a/ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue b/ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue new file mode 100644 index 00000000000..2e92fa9ec86 --- /dev/null +++ b/ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue @@ -0,0 +1,149 @@ + + + diff --git a/ui/src/views/knowledge-workflow/component/Debug.vue b/ui/src/views/knowledge-workflow/component/Debug.vue index bd6d69c4017..a3c1c395d82 100644 --- a/ui/src/views/knowledge-workflow/component/Debug.vue +++ b/ui/src/views/knowledge-workflow/component/Debug.vue @@ -1,7 +1,8 @@ From 7eddb4bd8335339798c5dbcae44e6f0ab272cc10 Mon Sep 17 00:00:00 2001 From: CaptainB Date: Mon, 17 Nov 2025 15:36:58 +0800 Subject: [PATCH 19/75] feat: add Data Source tool functionality and localization --- apps/tools/serializers/tool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/tools/serializers/tool.py b/apps/tools/serializers/tool.py index 7a09853c0b8..64fab365617 100644 --- a/apps/tools/serializers/tool.py +++ b/apps/tools/serializers/tool.py @@ -610,6 +610,7 @@ def import_(self, scope=ToolScope.WORKSPACE): workspace_id=self.data.get('workspace_id'), input_field_list=tool.get('input_field_list'), init_field_list=tool.get('init_field_list', []), + tool_type=tool.get('tool_type'), folder_id=folder_id, scope=scope, is_active=False From bc1a4809e01ee1890b439ed1c7c6f739652bc1ed Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Mon, 17 Nov 2025 19:08:43 +0800 Subject: [PATCH 20/75] feat: knowledge workflow --- apps/application/flow/common.py | 27 ++++++++++---- apps/application/flow/i_step_node.py | 16 ++++++++- .../flow/knowledge_workflow_manage.py | 35 +++++++++++++++++++ apps/application/flow/loop_workflow_manage.py | 8 ++--- apps/application/flow/step_node/__init__.py | 13 ++++--- .../ai_chat_step_node/i_chat_node.py | 14 ++++++-- .../application_node/i_application_node.py | 2 ++ .../condition_node/i_condition_node.py | 3 ++ .../i_data_source_local_node.py | 5 ++- .../impl/base_data_source_local_node.py | 5 +-- .../i_data_source_web_node.py | 6 ++-- .../direct_reply_node/i_reply_node.py | 3 ++ .../i_document_extract_node.py | 4 ++- .../flow/step_node/form_node/i_form_node.py | 2 ++ .../i_image_generate_node.py | 2 ++ .../i_image_to_video_node.py | 2 ++ .../i_image_understand_node.py | 2 ++ .../step_node/intent_node/i_intent_node.py | 11 +++--- .../loop_break_node/i_loop_break_node.py | 2 ++ .../i_loop_continue_node.py | 4 ++- .../flow/step_node/loop_node/i_loop_node.py | 2 ++ .../loop_start_node/i_loop_start_node.py | 5 +-- .../flow/step_node/mcp_node/i_mcp_node.py | 2 ++ .../i_parameter_extraction_node.py | 3 ++ .../question_node/i_question_node.py | 2 ++ .../reranker_node/i_reranker_node.py | 4 ++- .../i_search_document_node.py | 2 ++ .../i_search_knowledge_node.py | 2 ++ .../i_speech_to_text_node.py | 4 ++- .../flow/step_node/start_node/i_start_node.py | 3 +- .../i_text_to_speech_node.py | 2 ++ .../i_text_to_video_node.py | 2 ++ .../tool_lib_node/i_tool_lib_node.py | 2 ++ .../flow/step_node/tool_node/i_tool_node.py | 7 ++-- .../i_variable_aggregation_node.py | 11 +++--- .../i_variable_assign_node.py | 2 ++ .../i_variable_splitting_node.py | 2 ++ .../i_video_understand_node.py | 5 +-- apps/application/flow/workflow_manage.py | 28 ++++++++------- .../serializers/knowledge_workflow.py | 22 +++++++++++- apps/knowledge/urls.py | 1 + apps/knowledge/views/knowledge_workflow.py | 10 +++++- ui/src/api/knowledge/knowledge.ts | 10 +++++- .../items/upload/LocalFileUpload.vue | 17 ++++++--- .../knowledge-workflow/component/Debug.vue | 6 ++-- .../component/action/DataSource.vue | 8 ++--- .../component/action/index.vue | 22 ++++++++++-- ui/src/views/knowledge-workflow/index.vue | 3 +- 48 files changed, 275 insertions(+), 80 deletions(-) create mode 100644 apps/application/flow/knowledge_workflow_manage.py diff --git a/apps/application/flow/common.py b/apps/application/flow/common.py index 2ba94487591..1b8d674f0e0 100644 --- a/apps/application/flow/common.py +++ b/apps/application/flow/common.py @@ -6,7 +6,7 @@ @date:2024/12/11 17:57 @desc: """ - +from enum import Enum from typing import List, Dict from django.db.models import QuerySet @@ -90,6 +90,16 @@ def __init__(self, edge, node): self.node = node +class WorkflowMode(Enum): + APPLICATION = "application" + + APPLICATION_LOOP = "application-loop" + + KNOWLEDGE = "knowledge" + + KNOWLEDGE_LOOP = "knowledge-loop" + + class Workflow: """ 节点列表 @@ -112,7 +122,10 @@ class Workflow: """ next_node_map: Dict[str, List[EdgeNode]] - def __init__(self, nodes: List[Node], edges: List[Edge]): + workflow_mode: WorkflowMode + + def __init__(self, nodes: List[Node], edges: List[Edge], + workflow_mode: WorkflowMode = WorkflowMode.APPLICATION.value): self.nodes = nodes self.edges = edges self.node_map = {node.id: node for node in nodes} @@ -125,6 +138,7 @@ def __init__(self, nodes: List[Node], edges: List[Edge]): self.next_node_map = {key: [EdgeNode(edge, self.node_map.get(edge.targetNodeId)) for edge in edges] for key, edges in group_by(edges, key=lambda edge: edge.sourceNodeId).items()} + self.workflow_mode = workflow_mode def get_node(self, node_id): """ @@ -167,13 +181,13 @@ def get_next_nodes(self, node_id) -> List[Node]: return [en.node for en in self.next_node_map.get(node_id, [])] @staticmethod - def new_instance(flow_obj: Dict): + def new_instance(flow_obj: Dict, workflow_mode: WorkflowMode = WorkflowMode.APPLICATION.value): nodes = flow_obj.get('nodes') edges = flow_obj.get('edges') nodes = [Node(node.get('id'), node.get('type'), **node) for node in nodes] edges = [Edge(edge.get('id'), edge.get('type'), **edge) for edge in edges] - return Workflow(nodes, edges) + return Workflow(nodes, edges, workflow_mode) def get_start_node(self): return self.get_node('start-node') @@ -190,10 +204,9 @@ def is_valid(self): self.is_valid_base_node() self.is_valid_work_flow() - @staticmethod - def is_valid_node_params(node: Node): + def is_valid_node_params(self, node: Node): from application.flow.step_node import get_node - get_node(node.type)(node, None, None) + get_node(node.type, self.workflow_mode)(node, None, None) def is_valid_node(self, node: Node): self.is_valid_node_params(node) diff --git a/apps/application/flow/i_step_node.py b/apps/application/flow/i_step_node.py index 286b2b49591..3ee7400b206 100644 --- a/apps/application/flow/i_step_node.py +++ b/apps/application/flow/i_step_node.py @@ -96,6 +96,14 @@ def handler(self, workflow): application_public_access_client.save() +class KnowledgeWorkflowPostHandler(WorkFlowPostHandler): + def __init__(self, chat_info): + super().__init__(chat_info) + + def handler(self, workflow): + pass + + class NodeResult: def __init__(self, node_variable: Dict, workflow_variable: Dict, _write_context=write_context, _is_interrupt=is_interrupt): @@ -152,6 +160,12 @@ class FlowParamsSerializer(serializers.Serializer): debug = serializers.BooleanField(required=True, label="是否debug") +class KnowledgeFlowParamsSerializer(serializers.Serializer): + knowledge_id = serializers.CharField(required=True, label="知识库id") + data_source = serializers.DictField(required=True, label="数据源") + knowledge_base = serializers.DictField(required=False, label="知识库设置") + + class INode: view_type = 'many_view' @@ -221,7 +235,7 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: pass def get_flow_params_serializer_class(self) -> Type[serializers.Serializer]: - return FlowParamsSerializer + return self.workflow_manage.get_params_serializer_class() def get_write_error_context(self, e): self.status = 500 diff --git a/apps/application/flow/knowledge_workflow_manage.py b/apps/application/flow/knowledge_workflow_manage.py new file mode 100644 index 00000000000..184ae3d1041 --- /dev/null +++ b/apps/application/flow/knowledge_workflow_manage.py @@ -0,0 +1,35 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: Knowledge_workflow_manage.py + @date:2025/11/13 19:02 + @desc: +""" + +from application.flow.common import Workflow +from application.flow.i_step_node import WorkFlowPostHandler, KnowledgeFlowParamsSerializer +from application.flow.workflow_manage import WorkflowManage +from common.handle.base_to_response import BaseToResponse +from common.handle.impl.response.system_to_response import SystemToResponse + + +class KnowledgeWorkflowManage(WorkflowManage): + + def __init__(self, flow: Workflow, + params, + work_flow_post_handler: WorkFlowPostHandler, + base_to_response: BaseToResponse = SystemToResponse(), + start_node_id=None, + start_node_data=None, chat_record=None, child_node=None): + super().__init__(flow, params, work_flow_post_handler, base_to_response, None, None, None, + None, + None, None, start_node_id, start_node_data, chat_record, child_node) + + def get_params_serializer_class(self): + return KnowledgeFlowParamsSerializer + + def get_start_node(self): + start_node_list = [node for node in self.flow.nodes if + self.params.get('data_source', {}).get('node_id') == node.id] + return start_node_list[0] diff --git a/apps/application/flow/loop_workflow_manage.py b/apps/application/flow/loop_workflow_manage.py index cba38d320d2..7fc73c5f82d 100644 --- a/apps/application/flow/loop_workflow_manage.py +++ b/apps/application/flow/loop_workflow_manage.py @@ -105,10 +105,10 @@ def get_node_cls_by_id(self, node_id, up_node_id_list=None, get_node_params=lambda node: node.properties.get('node_data')): for node in self.flow.nodes: if node.id == node_id: - node_instance = get_node(node.type)(node, - self.params, self, up_node_id_list, - get_node_params, - salt=self.get_index()) + node_instance = get_node(node.type, self.flow.workflow_mode)(node, + self.params, self, up_node_id_list, + get_node_params, + salt=self.get_index()) return node_instance return None diff --git a/apps/application/flow/step_node/__init__.py b/apps/application/flow/step_node/__init__.py index dbca4e20ce9..b089f15ed86 100644 --- a/apps/application/flow/step_node/__init__.py +++ b/apps/application/flow/step_node/__init__.py @@ -50,11 +50,14 @@ BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode, BaseLoopContinueNode, BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode, +<<<<<<< Updated upstream BaseDataSourceLocalNode,BaseDataSourceWebNode,BaseKnowledgeWriteNode] +======= + BaseDataSourceLocalNode, BaseDataSourceWebNode] +node_map = {n.type: {w: n for w in n.support} for n in node_list} +>>>>>>> Stashed changes -def get_node(node_type): - find_list = [node for node in node_list if node.type == node_type] - if len(find_list) > 0: - return find_list[0] - return None + +def get_node(node_type, workflow_model): + return node_map.get(node_type).get(workflow_model) diff --git a/apps/application/flow/step_node/ai_chat_step_node/i_chat_node.py b/apps/application/flow/step_node/ai_chat_step_node/i_chat_node.py index 21e23f167c3..a7566bdac74 100644 --- a/apps/application/flow/step_node/ai_chat_step_node/i_chat_node.py +++ b/apps/application/flow/step_node/ai_chat_step_node/i_chat_node.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -34,7 +35,8 @@ class ChatNodeSerializer(serializers.Serializer): mcp_enable = serializers.BooleanField(required=False, label=_("Whether to enable MCP")) mcp_servers = serializers.JSONField(required=False, label=_("MCP Server")) mcp_tool_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("MCP Tool ID")) - mcp_tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True, label=_("MCP Tool IDs"), ) + mcp_tool_ids = serializers.ListField(child=serializers.UUIDField(), required=False, allow_empty=True, + label=_("MCP Tool IDs"), ) mcp_source = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("MCP Source")) tool_enable = serializers.BooleanField(required=False, default=False, label=_("Whether to enable tools")) @@ -42,14 +44,22 @@ class ChatNodeSerializer(serializers.Serializer): label=_("Tool IDs"), ) mcp_output_enable = serializers.BooleanField(required=False, default=True, label=_("Whether to enable MCP output")) + class IChatNode(INode): type = 'ai-chat-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP, + WorkflowMode.KNOWLEDGE] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ChatNodeSerializer def _run(self): - return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) + if [WorkflowMode.KNOWLEDGE, WorkflowMode.APPLICATION_LOOP].__contains__( + self.workflow_manage.flow.workflow_mode): + return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data, + **{'history_chat_record': [], 'stream': True, 'chat_id': None, 'chat_record_id': None}) + else: + return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id, chat_record_id, diff --git a/apps/application/flow/step_node/application_node/i_application_node.py b/apps/application/flow/step_node/application_node/i_application_node.py index 5a4ea6e5135..b41151ee341 100644 --- a/apps/application/flow/step_node/application_node/i_application_node.py +++ b/apps/application/flow/step_node/application_node/i_application_node.py @@ -3,6 +3,7 @@ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from django.utils.translation import gettext_lazy as _ @@ -25,6 +26,7 @@ class ApplicationNodeSerializer(serializers.Serializer): class IApplicationNode(INode): type = 'application-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ApplicationNodeSerializer diff --git a/apps/application/flow/step_node/condition_node/i_condition_node.py b/apps/application/flow/step_node/condition_node/i_condition_node.py index 1bd541b4cf1..305b260efa2 100644 --- a/apps/application/flow/step_node/condition_node/i_condition_node.py +++ b/apps/application/flow/step_node/condition_node/i_condition_node.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode @@ -36,3 +37,5 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ConditionNodeParamsSerializer type = 'condition-node' + + support = [WorkflowMode.APPLICATION_LOOP] diff --git a/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py index 856369d54a3..e6b39f686fa 100644 --- a/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/i_data_source_local_node.py @@ -12,6 +12,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -35,5 +36,7 @@ def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: def _run(self): return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) - def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult: + def execute(self, file_type_list, file_size_limit, file_count_limit, **kwargs) -> NodeResult: pass + + support = [WorkflowMode.KNOWLEDGE] diff --git a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py index d64fdd86103..24da1589852 100644 --- a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py @@ -34,5 +34,6 @@ def get_form_list(node): 'label': '', }] - def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult: - pass + def execute(self, file_type_list, file_size_limit, file_count_limit, **kwargs) -> NodeResult: + return NodeResult({'file_list': self.workflow_manage.params.get('data_source', {}).get('file_list')}, + self.workflow_manage.params.get('knowledge_base') or {}) diff --git a/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py b/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py index f5e210a8edf..205304d9d20 100644 --- a/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py +++ b/apps/application/flow/step_node/data_source_web_node/i_data_source_web_node.py @@ -8,13 +8,13 @@ """ from abc import abstractmethod - +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult class IDataSourceWebNode(INode): - type = 'data-source-web-node' + support = [WorkflowMode.KNOWLEDGE] @staticmethod @abstractmethod @@ -24,7 +24,5 @@ def get_form_list(node): def _run(self): return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) - def execute(self, **kwargs) -> NodeResult: pass - diff --git a/apps/application/flow/step_node/direct_reply_node/i_reply_node.py b/apps/application/flow/step_node/direct_reply_node/i_reply_node.py index acb9262bd06..4e530cb5c09 100644 --- a/apps/application/flow/step_node/direct_reply_node/i_reply_node.py +++ b/apps/application/flow/step_node/direct_reply_node/i_reply_node.py @@ -10,6 +10,7 @@ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from common.exception.app_exception import AppApiException @@ -38,6 +39,8 @@ def is_valid(self, *, raise_exception=False): class IReplyNode(INode): type = 'reply-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP, + WorkflowMode.KNOWLEDGE] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ReplyNodeParamsSerializer diff --git a/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py b/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py index e907220ca1d..1b8b9c38171 100644 --- a/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py +++ b/apps/application/flow/step_node/document_extract_node/i_document_extract_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -14,7 +15,8 @@ class DocumentExtractNodeSerializer(serializers.Serializer): class IDocumentExtractNode(INode): type = 'document-extract-node' - + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE_LOOP, + WorkflowMode.KNOWLEDGE] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return DocumentExtractNodeSerializer diff --git a/apps/application/flow/step_node/form_node/i_form_node.py b/apps/application/flow/step_node/form_node/i_form_node.py index 552434dbff8..a47a75ddcac 100644 --- a/apps/application/flow/step_node/form_node/i_form_node.py +++ b/apps/application/flow/step_node/form_node/i_form_node.py @@ -10,6 +10,7 @@ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from django.utils.translation import gettext_lazy as _ @@ -24,6 +25,7 @@ class FormNodeParamsSerializer(serializers.Serializer): class IFormNode(INode): type = 'form-node' view_type = 'single_view' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return FormNodeParamsSerializer diff --git a/apps/application/flow/step_node/image_generate_step_node/i_image_generate_node.py b/apps/application/flow/step_node/image_generate_step_node/i_image_generate_node.py index 5ea2afad2dd..58eb191b46c 100644 --- a/apps/application/flow/step_node/image_generate_step_node/i_image_generate_node.py +++ b/apps/application/flow/step_node/image_generate_step_node/i_image_generate_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -31,6 +32,7 @@ class ImageGenerateNodeSerializer(serializers.Serializer): class IImageGenerateNode(INode): type = 'image-generate-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ImageGenerateNodeSerializer diff --git a/apps/application/flow/step_node/image_to_video_step_node/i_image_to_video_node.py b/apps/application/flow/step_node/image_to_video_step_node/i_image_to_video_node.py index 5c408f6d42b..eddeac4269e 100644 --- a/apps/application/flow/step_node/image_to_video_step_node/i_image_to_video_node.py +++ b/apps/application/flow/step_node/image_to_video_step_node/i_image_to_video_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -34,6 +35,7 @@ class ImageToVideoNodeSerializer(serializers.Serializer): class IImageToVideoNode(INode): type = 'image-to-video-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ImageToVideoNodeSerializer diff --git a/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py b/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py index 1803fbea327..c7dd99ccb2a 100644 --- a/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py +++ b/apps/application/flow/step_node/image_understand_step_node/i_image_understand_node.py @@ -4,6 +4,7 @@ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from django.utils.translation import gettext_lazy as _ @@ -30,6 +31,7 @@ class ImageUnderstandNodeSerializer(serializers.Serializer): class IImageUnderstandNode(INode): type = 'image-understand-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ImageUnderstandNodeSerializer diff --git a/apps/application/flow/step_node/intent_node/i_intent_node.py b/apps/application/flow/step_node/intent_node/i_intent_node.py index 0c48be9b963..487df0bdd6d 100644 --- a/apps/application/flow/step_node/intent_node/i_intent_node.py +++ b/apps/application/flow/step_node/intent_node/i_intent_node.py @@ -5,11 +5,11 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult class IntentBranchSerializer(serializers.Serializer): - id = serializers.CharField(required=True, label=_("Branch id")) content = serializers.CharField(required=True, label=_("content")) isOther = serializers.BooleanField(required=True, label=_("Branch Type")) @@ -24,8 +24,11 @@ class IntentNodeSerializer(serializers.Serializer): label=_("Model parameter settings")) branch = IntentBranchSerializer(many=True) + class IIntentNode(INode): type = 'intent-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] + def save_context(self, details, workflow_manage): pass @@ -38,9 +41,9 @@ def _run(self): self.node_params_serializer.data.get('content_list')[1:], ) - return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data, user_input=str(question)) - + return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data, + user_input=str(question)) def execute(self, model_id, dialogue_number, history_chat_record, user_input, branch, model_params_setting=None, **kwargs) -> NodeResult: - pass \ No newline at end of file + pass diff --git a/apps/application/flow/step_node/loop_break_node/i_loop_break_node.py b/apps/application/flow/step_node/loop_break_node/i_loop_break_node.py index d42d05a114b..81792a014fa 100644 --- a/apps/application/flow/step_node/loop_break_node/i_loop_break_node.py +++ b/apps/application/flow/step_node/loop_break_node/i_loop_break_node.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode from application.flow.i_step_node import NodeResult @@ -28,6 +29,7 @@ class LoopBreakNodeSerializer(serializers.Serializer): class ILoopBreakNode(INode): type = 'loop-break-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return LoopBreakNodeSerializer diff --git a/apps/application/flow/step_node/loop_continue_node/i_loop_continue_node.py b/apps/application/flow/step_node/loop_continue_node/i_loop_continue_node.py index 07ee1252f1b..4dfe9426f1f 100644 --- a/apps/application/flow/step_node/loop_continue_node/i_loop_continue_node.py +++ b/apps/application/flow/step_node/loop_continue_node/i_loop_continue_node.py @@ -8,10 +8,11 @@ """ from typing import Type +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult -from django.utils.translation import gettext_lazy as _ class ConditionSerializer(serializers.Serializer): @@ -27,6 +28,7 @@ class LoopContinueNodeSerializer(serializers.Serializer): class ILoopContinueNode(INode): type = 'loop-continue-node' + support = [WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return LoopContinueNodeSerializer diff --git a/apps/application/flow/step_node/loop_node/i_loop_node.py b/apps/application/flow/step_node/loop_node/i_loop_node.py index 6b217651328..dacb57c2cf5 100644 --- a/apps/application/flow/step_node/loop_node/i_loop_node.py +++ b/apps/application/flow/step_node/loop_node/i_loop_node.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from common.exception.app_exception import AppApiException @@ -40,6 +41,7 @@ def is_valid(self, *, raise_exception=False): class ILoopNode(INode): type = 'loop-node' + support = [WorkflowMode.APPLICATION] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return ILoopNodeSerializer diff --git a/apps/application/flow/step_node/loop_start_node/i_loop_start_node.py b/apps/application/flow/step_node/loop_start_node/i_loop_start_node.py index 21a059b7695..1995aa6fdd7 100644 --- a/apps/application/flow/step_node/loop_start_node/i_loop_start_node.py +++ b/apps/application/flow/step_node/loop_start_node/i_loop_start_node.py @@ -6,15 +6,16 @@ @date:2024/6/3 16:54 @desc: """ - +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult class ILoopStarNode(INode): type = 'loop-start-node' + support = [WorkflowMode.APPLICATION_LOOP] def _run(self): return self.execute(**self.flow_params_serializer.data) - def execute(self, **kwargs) -> NodeResult: + def execute(self, **kwargs) -> NodeResult: pass diff --git a/apps/application/flow/step_node/mcp_node/i_mcp_node.py b/apps/application/flow/step_node/mcp_node/i_mcp_node.py index cdd3f7e6ae0..0d8a1933484 100644 --- a/apps/application/flow/step_node/mcp_node/i_mcp_node.py +++ b/apps/application/flow/step_node/mcp_node/i_mcp_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -19,6 +20,7 @@ class McpNodeSerializer(serializers.Serializer): class IMcpNode(INode): type = 'mcp-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return McpNodeSerializer diff --git a/apps/application/flow/step_node/parameter_extraction_node/i_parameter_extraction_node.py b/apps/application/flow/step_node/parameter_extraction_node/i_parameter_extraction_node.py index 6ff6700575b..a0a9bc5cb59 100644 --- a/apps/application/flow/step_node/parameter_extraction_node/i_parameter_extraction_node.py +++ b/apps/application/flow/step_node/parameter_extraction_node/i_parameter_extraction_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -23,6 +24,8 @@ class VariableSplittingNodeParamsSerializer(serializers.Serializer): class IParameterExtractionNode(INode): type = 'parameter-extraction-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP, WorkflowMode.KNOWLEDGE, + WorkflowMode.KNOWLEDGE_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return VariableSplittingNodeParamsSerializer diff --git a/apps/application/flow/step_node/question_node/i_question_node.py b/apps/application/flow/step_node/question_node/i_question_node.py index 74153bbfb84..0835292d38f 100644 --- a/apps/application/flow/step_node/question_node/i_question_node.py +++ b/apps/application/flow/step_node/question_node/i_question_node.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -31,6 +32,7 @@ class QuestionNodeSerializer(serializers.Serializer): class IQuestionNode(INode): type = 'question-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return QuestionNodeSerializer diff --git a/apps/application/flow/step_node/reranker_node/i_reranker_node.py b/apps/application/flow/step_node/reranker_node/i_reranker_node.py index d0164393d2d..0421c55b00a 100644 --- a/apps/application/flow/step_node/reranker_node/i_reranker_node.py +++ b/apps/application/flow/step_node/reranker_node/i_reranker_node.py @@ -10,6 +10,7 @@ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from django.utils.translation import gettext_lazy as _ @@ -41,6 +42,7 @@ def is_valid(self, *, raise_exception=False): class IRerankerNode(INode): type = 'reranker-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return RerankerStepNodeSerializer @@ -57,6 +59,6 @@ def _run(self): reranker_list=reranker_list) - def execute(self, question, reranker_setting, reranker_list, reranker_model_id,show_knowledge, + def execute(self, question, reranker_setting, reranker_list, reranker_model_id, show_knowledge, **kwargs) -> NodeResult: pass diff --git a/apps/application/flow/step_node/search_document_node/i_search_document_node.py b/apps/application/flow/step_node/search_document_node/i_search_document_node.py index 65eb87d96d5..2d55ef64bae 100644 --- a/apps/application/flow/step_node/search_document_node/i_search_document_node.py +++ b/apps/application/flow/step_node/search_document_node/i_search_document_node.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -42,6 +43,7 @@ def is_valid(self, *, raise_exception=False): class ISearchDocumentStepNode(INode): type = 'search-document-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return SearchDocumentStepNodeSerializer diff --git a/apps/application/flow/step_node/search_knowledge_node/i_search_knowledge_node.py b/apps/application/flow/step_node/search_knowledge_node/i_search_knowledge_node.py index 7f9311e2d24..17da82a4a27 100644 --- a/apps/application/flow/step_node/search_knowledge_node/i_search_knowledge_node.py +++ b/apps/application/flow/step_node/search_knowledge_node/i_search_knowledge_node.py @@ -13,6 +13,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from common.utils.common import flat_map @@ -67,6 +68,7 @@ def get_paragraph_list(chat_record, node_id): class ISearchKnowledgeStepNode(INode): type = 'search-knowledge-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return SearchDatasetStepNodeSerializer diff --git a/apps/application/flow/step_node/speech_to_text_step_node/i_speech_to_text_node.py b/apps/application/flow/step_node/speech_to_text_step_node/i_speech_to_text_node.py index 719e4201e88..ba87a862b45 100644 --- a/apps/application/flow/step_node/speech_to_text_step_node/i_speech_to_text_node.py +++ b/apps/application/flow/step_node/speech_to_text_step_node/i_speech_to_text_node.py @@ -2,10 +2,11 @@ from typing import Type +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult -from django.utils.translation import gettext_lazy as _ class SpeechToTextNodeSerializer(serializers.Serializer): @@ -22,6 +23,7 @@ class SpeechToTextNodeSerializer(serializers.Serializer): class ISpeechToTextNode(INode): type = 'speech-to-text-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return SpeechToTextNodeSerializer diff --git a/apps/application/flow/step_node/start_node/i_start_node.py b/apps/application/flow/step_node/start_node/i_start_node.py index 41d73f21811..40caf0199bf 100644 --- a/apps/application/flow/step_node/start_node/i_start_node.py +++ b/apps/application/flow/step_node/start_node/i_start_node.py @@ -6,12 +6,13 @@ @date:2024/6/3 16:54 @desc: """ - +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult class IStarNode(INode): type = 'start-node' + support = [WorkflowMode.APPLICATION] def _run(self): return self.execute(**self.flow_params_serializer.data) diff --git a/apps/application/flow/step_node/text_to_speech_step_node/i_text_to_speech_node.py b/apps/application/flow/step_node/text_to_speech_step_node/i_text_to_speech_node.py index 539c8dbb536..1751a123cee 100644 --- a/apps/application/flow/step_node/text_to_speech_step_node/i_text_to_speech_node.py +++ b/apps/application/flow/step_node/text_to_speech_step_node/i_text_to_speech_node.py @@ -4,6 +4,7 @@ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from django.utils.translation import gettext_lazy as _ @@ -22,6 +23,7 @@ class TextToSpeechNodeSerializer(serializers.Serializer): class ITextToSpeechNode(INode): type = 'text-to-speech-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return TextToSpeechNodeSerializer diff --git a/apps/application/flow/step_node/text_to_video_step_node/i_text_to_video_node.py b/apps/application/flow/step_node/text_to_video_step_node/i_text_to_video_node.py index c91d70f5905..39cb4393ddc 100644 --- a/apps/application/flow/step_node/text_to_video_step_node/i_text_to_video_node.py +++ b/apps/application/flow/step_node/text_to_video_step_node/i_text_to_video_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -31,6 +32,7 @@ class TextToVideoNodeSerializer(serializers.Serializer): class ITextToVideoNode(INode): type = 'text-to-video-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return TextToVideoNodeSerializer diff --git a/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py b/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py index 43a93cc3f05..07408f1fb96 100644 --- a/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py +++ b/apps/application/flow/step_node/tool_lib_node/i_tool_lib_node.py @@ -13,6 +13,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from common.field.common import ObjectField from tools.models.tool import Tool @@ -40,6 +41,7 @@ def is_valid(self, *, raise_exception=False): class IToolLibNode(INode): type = 'tool-lib-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return FunctionLibNodeParamsSerializer diff --git a/apps/application/flow/step_node/tool_node/i_tool_node.py b/apps/application/flow/step_node/tool_node/i_tool_node.py index 2180efb7772..286e4426685 100644 --- a/apps/application/flow/step_node/tool_node/i_tool_node.py +++ b/apps/application/flow/step_node/tool_node/i_tool_node.py @@ -10,15 +10,15 @@ from typing import Type from django.core import validators +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from rest_framework.utils.formatting import lazy_format +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult from common.exception.app_exception import AppApiException from common.field.common import ObjectField -from django.utils.translation import gettext_lazy as _ -from rest_framework.utils.formatting import lazy_format - class InputField(serializers.Serializer): name = serializers.CharField(required=True, label=_('Variable Name')) @@ -53,6 +53,7 @@ def is_valid(self, *, raise_exception=False): class IToolNode(INode): type = 'tool-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return FunctionNodeParamsSerializer diff --git a/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py b/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py index d5a2f332a2e..b1de910c44d 100644 --- a/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py +++ b/apps/application/flow/step_node/variable_aggregation_node/i_variable_aggregation_node.py @@ -5,11 +5,10 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult - - class VariableListSerializer(serializers.Serializer): v_id = serializers.CharField(required=True, label=_("Variable id")) variable = serializers.ListField(required=True, label=_("Variable")) @@ -29,15 +28,13 @@ class VariableAggregationNodeSerializer(serializers.Serializer): class IVariableAggregation(INode): type = 'variable-aggregation-node' - + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return VariableAggregationNodeSerializer def _run(self): - return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) + return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) - def execute(self,strategy,group_list,**kwargs) -> NodeResult: + def execute(self, strategy, group_list, **kwargs) -> NodeResult: pass - - diff --git a/apps/application/flow/step_node/variable_assign_node/i_variable_assign_node.py b/apps/application/flow/step_node/variable_assign_node/i_variable_assign_node.py index 1eb21266c83..87b8449d1cd 100644 --- a/apps/application/flow/step_node/variable_assign_node/i_variable_assign_node.py +++ b/apps/application/flow/step_node/variable_assign_node/i_variable_assign_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -15,6 +16,7 @@ class VariableAssignNodeParamsSerializer(serializers.Serializer): class IVariableAssignNode(INode): type = 'variable-assign-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return VariableAssignNodeParamsSerializer diff --git a/apps/application/flow/step_node/variable_splitting_node/i_variable_splitting_node.py b/apps/application/flow/step_node/variable_splitting_node/i_variable_splitting_node.py index 52cff8eb203..033a7477c45 100644 --- a/apps/application/flow/step_node/variable_splitting_node/i_variable_splitting_node.py +++ b/apps/application/flow/step_node/variable_splitting_node/i_variable_splitting_node.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult @@ -18,6 +19,7 @@ class VariableSplittingNodeParamsSerializer(serializers.Serializer): class IVariableSplittingNode(INode): type = 'variable-splitting-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return VariableSplittingNodeParamsSerializer diff --git a/apps/application/flow/step_node/video_understand_step_node/i_video_understand_node.py b/apps/application/flow/step_node/video_understand_step_node/i_video_understand_node.py index 3266a8e0beb..c6a4b8db4d6 100644 --- a/apps/application/flow/step_node/video_understand_step_node/i_video_understand_node.py +++ b/apps/application/flow/step_node/video_understand_step_node/i_video_understand_node.py @@ -2,12 +2,12 @@ from typing import Type +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult -from django.utils.translation import gettext_lazy as _ - class VideoUnderstandNodeSerializer(serializers.Serializer): model_id = serializers.CharField(required=True, label=_("Model id")) @@ -30,6 +30,7 @@ class VideoUnderstandNodeSerializer(serializers.Serializer): class IVideoUnderstandNode(INode): type = 'video-understand-node' + support = [WorkflowMode.APPLICATION, WorkflowMode.APPLICATION_LOOP] def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return VideoUnderstandNodeSerializer diff --git a/apps/application/flow/workflow_manage.py b/apps/application/flow/workflow_manage.py index d35173ac92c..78a458b64c7 100644 --- a/apps/application/flow/workflow_manage.py +++ b/apps/application/flow/workflow_manage.py @@ -22,7 +22,7 @@ from application.flow import tools from application.flow.common import Workflow -from application.flow.i_step_node import INode, WorkFlowPostHandler, NodeResult +from application.flow.i_step_node import INode, WorkFlowPostHandler, NodeResult, FlowParamsSerializer from application.flow.step_node import get_node from common.handle.base_to_response import BaseToResponse from common.handle.impl.response.system_to_response import SystemToResponse @@ -302,8 +302,8 @@ def await_result(self): answer_tokens = sum([row.get('answer_tokens') for row in details.values() if 'answer_tokens' in row and row.get('answer_tokens') is not None]) self.work_flow_post_handler.handler(self) - yield self.base_to_response.to_stream_chunk_response(self.params['chat_id'], - self.params['chat_record_id'], + yield self.base_to_response.to_stream_chunk_response(self.params.get('chat_id'), + self.params.get('chat_record_id'), '', [], '', True, message_tokens, answer_tokens, {}) @@ -316,7 +316,7 @@ def run_chain_manage(self, current_node, node_result_future, language='zh'): translation.activate(language) if current_node is None: start_node = self.get_start_node() - current_node = get_node(start_node.type)(start_node, self.params, self) + current_node = get_node(start_node.type, self.flow.workflow_mode)(start_node, self.params, self) self.node_chunk_manage.add_node_chunk(current_node.node_chunk) # 添加节点 self.append_node(current_node) @@ -402,8 +402,8 @@ def hand_event_node_result(self, current_node, node_result_future): node_type = r.get("node_type") view_type = r.get('view_type') reasoning_content = r.get('reasoning_content') - chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'], - self.params['chat_record_id'], + chunk = self.base_to_response.to_stream_chunk_response(self.params.get('chat_id'), + self.params.get('chat_record_id'), current_node.id, current_node.up_node_id_list, content, False, 0, 0, @@ -417,8 +417,8 @@ def hand_event_node_result(self, current_node, node_result_future): 'node_status': "SUCCESS"}) current_node.node_chunk.add_chunk(chunk) chunk = (self.base_to_response - .to_stream_chunk_response(self.params['chat_id'], - self.params['chat_record_id'], + .to_stream_chunk_response(self.params.get('chat_id'), + self.params.get('chat_record_id'), current_node.id, current_node.up_node_id_list, '', False, 0, 0, {'node_is_end': True, @@ -436,8 +436,8 @@ def hand_event_node_result(self, current_node, node_result_future): except Exception as e: # 添加节点 traceback.print_exc() - chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'], - self.params['chat_record_id'], + chunk = self.base_to_response.to_stream_chunk_response(self.params.get('chat_id'), + self.params.get('chat_record_id'), current_node.id, current_node.up_node_id_list, 'Exception:' + str(e), False, 0, 0, @@ -698,8 +698,9 @@ def get_node_cls_by_id(self, node_id, up_node_id_list=None, get_node_params=lambda node: node.properties.get('node_data')): for node in self.flow.nodes: if node.id == node_id: - node_instance = get_node(node.type)(node, - self.params, self, up_node_id_list, get_node_params) + node_instance = get_node(node.type, self.flow.workflow_mode)(node, + self.params, self, up_node_id_list, + get_node_params) return node_instance return None @@ -712,3 +713,6 @@ def get_node_by_id(self, node_id): def get_node_reference(self, reference_address: Dict): node = self.get_node_by_id(reference_address.get('node_id')) return node.context[reference_address.get('node_field')] + + def get_params_serializer_class(self): + return FlowParamsSerializer diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index f449cc59d91..9748f975750 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -8,6 +8,9 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import Workflow, WorkflowMode +from application.flow.i_step_node import KnowledgeWorkflowPostHandler +from application.flow.knowledge_workflow_manage import KnowledgeWorkflowManage from application.flow.step_node import get_node from common.exception.app_exception import AppApiException from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow @@ -23,6 +26,23 @@ class Meta: fields = '__all__' +class KnowledgeWorkflowActionSerializer(serializers.Serializer): + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) + + def action(self, instance: Dict, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + knowledge_workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id")).first() + work_flow_manage = KnowledgeWorkflowManage( + Workflow.new_instance(knowledge_workflow.work_flow, WorkflowMode.KNOWLEDGE), + {'knowledge_id': self.data.get("knowledge_id"), 'stream': True, + **instance}, + KnowledgeWorkflowPostHandler(None)) + r = work_flow_manage.run() + return r + + class KnowledgeWorkflowSerializer(serializers.Serializer): class Form(serializers.Serializer): type = serializers.CharField(required=True, label=_('type')) @@ -32,7 +52,7 @@ class Form(serializers.Serializer): def get_form_list(self): self.is_valid(raise_exception=True) if self.data.get('type') == 'local': - node = get_node(self.data.get('id')) + node = get_node(self.data.get('id'), WorkflowMode.KNOWLEDGE) return node.get_form_list(self.data.get("node")) elif self.data.get('type') == 'tool': tool = QuerySet(Tool).filter(id=self.data.get("id")).first() diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index a4e686445ab..331539ed8bc 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -70,5 +70,6 @@ path('workspace//knowledge//document//', views.DocumentView.Page.as_view()), path('workspace//knowledge//', views.KnowledgeView.Page.as_view()), path('workspace//knowledge//form_list//', views.KnowledgeWorkflowFormView.as_view()), + path('workspace//knowledge//action', views.KnowledgeWorkflowActionView.as_view()) ] diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index 7f902b135ed..c4b0fd1f651 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -12,7 +12,7 @@ from common.result import result from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi from knowledge.serializers.common import get_knowledge_operation_object -from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer +from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer, KnowledgeWorkflowActionSerializer class KnowledgeWorkflowFormView(APIView): @@ -23,6 +23,14 @@ def post(self, request: Request, workspace_id: str, knowledge_id: str, type: str data={'type': type, 'id': id, 'node': request.data.get('node')}).get_form_list()) +class KnowledgeWorkflowActionView(APIView): + authentication_classes = [TokenAuth] + + def post(self, request: Request, workspace_id: str, knowledge_id: str): + return KnowledgeWorkflowActionSerializer( + data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).action(request.data, True) + + class KnowledgeWorkflowView(APIView): authentication_classes = [TokenAuth] diff --git a/ui/src/api/knowledge/knowledge.ts b/ui/src/api/knowledge/knowledge.ts index 4704afe9cad..d7436e3f830 100644 --- a/ui/src/api/knowledge/knowledge.ts +++ b/ui/src/api/knowledge/knowledge.ts @@ -1,7 +1,7 @@ import { Result } from '@/request/Result' import { get, post, del, put, exportFile, exportExcel } from '@/request/index' import { type Ref } from 'vue' -import type { pageRequest } from '@/api/type/common' +import type { Dict, pageRequest } from '@/api/type/common' import type { knowledgeData } from '@/api/type/knowledge' import useStore from '@/stores' @@ -330,6 +330,13 @@ const getKnowledgeWorkflowFormList: ( ) => { return post(`${prefix.value}/${knowledge_id}/form_list/${type}/${id}`, { node }, {}, loading) } +const workflowAction: ( + knowledge_id: string, + instance: Dict, + loading?: Ref, +) => Promise> = (knowledge_id: string, instance, loading) => { + return post(`${prefix.value}/${knowledge_id}/action`, instance, {}, loading) +} export default { getKnowledgeList, @@ -356,4 +363,5 @@ export default { delMulTag, createWorkflowKnowledge, getKnowledgeWorkflowFormList, + workflowAction, } diff --git a/ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue b/ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue index 2e92fa9ec86..124a03efbc6 100644 --- a/ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue +++ b/ui/src/components/dynamics-form/items/upload/LocalFileUpload.vue @@ -1,5 +1,5 @@ diff --git a/ui/src/views/knowledge-workflow/component/action/DataSource.vue b/ui/src/views/knowledge-workflow/component/action/DataSource.vue index ae4bcfd4453..674fc0a2507 100644 --- a/ui/src/views/knowledge-workflow/component/action/DataSource.vue +++ b/ui/src/views/knowledge-workflow/component/action/DataSource.vue @@ -9,8 +9,8 @@ require-asterisk-position="right" > diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue index 91130dc6702..86172110b64 100644 --- a/ui/src/views/knowledge-workflow/index.vue +++ b/ui/src/views/knowledge-workflow/index.vue @@ -393,8 +393,7 @@ const clickShowDebug = () => { ...workflow.get_base_node()?.properties.node_data, work_flow: getGraphData(), } - console.log('sss', DebugRef.value) - DebugRef.value?.open(graphData) + DebugRef.value?.open(graphData, id) } catch (e: any) { console.log(e) MsgError(e.toString()) From 30902f85cb2056ae72996662ec1d4eec60e582b9 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Tue, 18 Nov 2025 10:58:11 +0800 Subject: [PATCH 21/75] feat: knowledge workflow --- apps/application/flow/step_node/__init__.py | 6 +----- .../knowledge_write_node/i_knowledge_write_node.py | 7 ++----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/apps/application/flow/step_node/__init__.py b/apps/application/flow/step_node/__init__.py index b089f15ed86..c323a46c935 100644 --- a/apps/application/flow/step_node/__init__.py +++ b/apps/application/flow/step_node/__init__.py @@ -50,13 +50,9 @@ BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode, BaseLoopContinueNode, BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode, -<<<<<<< Updated upstream - BaseDataSourceLocalNode,BaseDataSourceWebNode,BaseKnowledgeWriteNode] -======= - BaseDataSourceLocalNode, BaseDataSourceWebNode] + BaseDataSourceLocalNode, BaseDataSourceWebNode, BaseKnowledgeWriteNode] node_map = {n.type: {w: n for w in n.support} for n in node_list} ->>>>>>> Stashed changes def get_node(node_type, workflow_model): diff --git a/apps/application/flow/step_node/knowledge_write_node/i_knowledge_write_node.py b/apps/application/flow/step_node/knowledge_write_node/i_knowledge_write_node.py index eadb0becbc8..fec3cebadb0 100644 --- a/apps/application/flow/step_node/knowledge_write_node/i_knowledge_write_node.py +++ b/apps/application/flow/step_node/knowledge_write_node/i_knowledge_write_node.py @@ -11,23 +11,20 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from application.flow.common import WorkflowMode from application.flow.i_step_node import INode, NodeResult - class KnowledgeWriteNodeParamSerializer(serializers.Serializer): paragraph_list = serializers.ListField(required=True, label=_("Paragraph list")) chunk_length = serializers.CharField(required=True, label=_("Child chunk length")) - - class IKnowledgeWriteNode(INode): def get_node_params_serializer_class(self) -> Type[serializers.Serializer]: return KnowledgeWriteNodeParamSerializer - def _run(self): return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data) @@ -35,4 +32,4 @@ def execute(self, paragraph_list, chunk_length, **kwargs) -> NodeResult: pass type = 'knowledge-write-node' - + support = [WorkflowMode.KNOWLEDGE] From 3e47117f46f6f50f52bfbddd38574da78bb67c68 Mon Sep 17 00:00:00 2001 From: CaptainB Date: Tue, 18 Nov 2025 11:04:22 +0800 Subject: [PATCH 22/75] fix: simplify export tool permission check in ToolListContainer.vue --- ui/src/views/tool/component/ToolListContainer.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/views/tool/component/ToolListContainer.vue b/ui/src/views/tool/component/ToolListContainer.vue index 39ce26e88c7..57defeb346d 100644 --- a/ui/src/views/tool/component/ToolListContainer.vue +++ b/ui/src/views/tool/component/ToolListContainer.vue @@ -232,8 +232,7 @@ {{ $t('common.export') }} From 754cb89ca4d035ded57f5c616d5e80759fd23358 Mon Sep 17 00:00:00 2001 From: CaptainB Date: Tue, 18 Nov 2025 11:09:33 +0800 Subject: [PATCH 23/75] fix: simplify export condition in ToolResourceIndex.vue --- ui/src/views/system-resource-management/ToolResourceIndex.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/views/system-resource-management/ToolResourceIndex.vue b/ui/src/views/system-resource-management/ToolResourceIndex.vue index a309445640a..12b08c938e6 100644 --- a/ui/src/views/system-resource-management/ToolResourceIndex.vue +++ b/ui/src/views/system-resource-management/ToolResourceIndex.vue @@ -311,7 +311,7 @@ From 8baa8e81cfee86449261a3378e6fb2c5e5cab052 Mon Sep 17 00:00:00 2001 From: CaptainB Date: Tue, 18 Nov 2025 11:20:33 +0800 Subject: [PATCH 24/75] fix: simplify condition for copying tool in ToolListContainer --- ui/src/locales/lang/en-US/views/tool.ts | 2 + ui/src/locales/lang/zh-CN/views/tool.ts | 2 + ui/src/locales/lang/zh-Hant/views/tool.ts | 2 + .../tool/component/ToolListContainer.vue | 39 ++++++++++++++++++- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/ui/src/locales/lang/en-US/views/tool.ts b/ui/src/locales/lang/en-US/views/tool.ts index 074d494d5a0..a5c30bcae1a 100644 --- a/ui/src/locales/lang/en-US/views/tool.ts +++ b/ui/src/locales/lang/en-US/views/tool.ts @@ -9,6 +9,8 @@ export default { editDataSourceTool: 'Edit Data Source', editMcpTool: 'Edit MCP', copyTool: 'Copy Tool', + copyMcpTool: 'Copy MCP', + copyDataSource: 'Copy Data Source', importTool: 'Import Tool', settingTool: 'Set Tool', mcpConfig: 'MCP Service Config', diff --git a/ui/src/locales/lang/zh-CN/views/tool.ts b/ui/src/locales/lang/zh-CN/views/tool.ts index d597c93a8d8..ee1f3606efc 100644 --- a/ui/src/locales/lang/zh-CN/views/tool.ts +++ b/ui/src/locales/lang/zh-CN/views/tool.ts @@ -9,6 +9,8 @@ export default { editDataSourceTool: '编辑数据源', editMcpTool: '编辑MCP', copyTool: '复制工具', + copyMcpTool: '复制MCP', + copyDataSource: '复制数据源', importTool: '导入工具', settingTool: '设置工具', mcpConfig: 'MCP服务配置', diff --git a/ui/src/locales/lang/zh-Hant/views/tool.ts b/ui/src/locales/lang/zh-Hant/views/tool.ts index 923e043173f..be29d4bbb96 100644 --- a/ui/src/locales/lang/zh-Hant/views/tool.ts +++ b/ui/src/locales/lang/zh-Hant/views/tool.ts @@ -9,6 +9,8 @@ export default { editDataSourceTool: '編輯資料來源', editMcpTool: '編輯MCP', copyTool: '複製工具', + copyMcpTool: '複製MCP', + copyDataSource: '複製資料來源', importTool: '匯入工具', settingTool: '設定工具', mcpConfig: 'MCP服務配置', diff --git a/ui/src/views/tool/component/ToolListContainer.vue b/ui/src/views/tool/component/ToolListContainer.vue index 57defeb346d..0d14b5a302b 100644 --- a/ui/src/views/tool/component/ToolListContainer.vue +++ b/ui/src/views/tool/component/ToolListContainer.vue @@ -204,8 +204,7 @@ {{ $t('common.copy') }} @@ -540,6 +539,18 @@ async function changeState(row: any) { } async function copyTool(row: any) { + // mcp工具 + if (row?.tool_type === 'MCP') { + bus.emit('select_node', row.folder_id) + await copyMcpTool(row) + return + } + // 数据源工具 + if (row?.tool_type === 'DATA_SOURCE') { + bus.emit('select_node', row.folder_id) + await copyDataSource(row) + return + } ToolDrawertitle.value = t('views.tool.copyTool') const res = await loadSharedApi({ type: 'tool', systemType: apiType.value }).getToolById( row.id, @@ -551,6 +562,30 @@ async function copyTool(row: any) { ToolFormDrawerRef.value.open(obj) } +async function copyMcpTool(row: any) { + McpToolDrawertitle.value = t('views.tool.copyMcpTool') + const res = await loadSharedApi({ type: 'tool', systemType: apiType.value }).getToolById( + row.id, + changeStateloading, + ) + const obj = cloneDeep(res.data) + delete obj['id'] + obj['name'] = obj['name'] + ` ${t('common.copyTitle')}` + McpToolFormDrawerRef.value.open(obj) +} + +async function copyDataSource(row: any) { + DataSourceToolDrawertitle.value = t('views.tool.copyDataSource') + const res = await loadSharedApi({ type: 'tool', systemType: apiType.value }).getToolById( + row.id, + changeStateloading, + ) + const obj = cloneDeep(res.data) + delete obj['id'] + obj['name'] = obj['name'] + ` ${t('common.copyTitle')}` + DataSourceToolFormDrawerRef.value.open(obj) +} + function exportTool(row: any) { loadSharedApi({ type: 'tool', systemType: apiType.value }) .exportTool(row.id, row.name, loading) From e758f015f34743027f486d9ce3965c558cebb4b1 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Wed, 19 Nov 2025 16:32:11 +0800 Subject: [PATCH 25/75] feat: knowledge workflow --- apps/application/flow/i_step_node.py | 13 +++-- .../flow/knowledge_workflow_manage.py | 51 +++++++++++++++++++ .../impl/base_data_source_local_node.py | 12 +++++ .../migrations/0005_knowledgeaction.py | 32 ++++++++++++ apps/knowledge/models/knowledge_action.py | 49 ++++++++++++++++++ .../serializers/knowledge_workflow.py | 26 ++++++++-- apps/knowledge/urls.py | 3 +- apps/knowledge/views/knowledge_workflow.py | 12 ++++- ui/src/api/knowledge/knowledge.ts | 9 +++- .../component/action/Result.vue | 38 ++++++++++++++ .../component/action/index.vue | 18 +++++-- .../component/UserInputFieldTable.vue | 2 + 12 files changed, 250 insertions(+), 15 deletions(-) create mode 100644 apps/knowledge/migrations/0005_knowledgeaction.py create mode 100644 apps/knowledge/models/knowledge_action.py create mode 100644 ui/src/views/knowledge-workflow/component/action/Result.vue diff --git a/apps/application/flow/i_step_node.py b/apps/application/flow/i_step_node.py index 3ee7400b206..1eee330db83 100644 --- a/apps/application/flow/i_step_node.py +++ b/apps/application/flow/i_step_node.py @@ -21,6 +21,7 @@ from application.models import ApplicationChatUserStats from application.models import ChatRecord, ChatUserType from common.field.common import InstanceField +from knowledge.models.knowledge_action import KnowledgeAction, State chat_cache = cache @@ -97,11 +98,13 @@ def handler(self, workflow): class KnowledgeWorkflowPostHandler(WorkFlowPostHandler): - def __init__(self, chat_info): + def __init__(self, chat_info, knowledge_action_id): super().__init__(chat_info) + self.knowledge_action_id = knowledge_action_id def handler(self, workflow): - pass + QuerySet(KnowledgeAction).filter(id=self.knowledge_action_id).update( + state=State.SUCCESS) class NodeResult: @@ -161,7 +164,8 @@ class FlowParamsSerializer(serializers.Serializer): class KnowledgeFlowParamsSerializer(serializers.Serializer): - knowledge_id = serializers.CharField(required=True, label="知识库id") + knowledge_id = serializers.UUIDField(required=True, label="知识库id") + knowledge_action_id = serializers.UUIDField(required=True, label="知识库任务执行器id") data_source = serializers.DictField(required=True, label="数据源") knowledge_base = serializers.DictField(required=False, label="知识库设置") @@ -241,7 +245,8 @@ def get_write_error_context(self, e): self.status = 500 self.answer_text = str(e) self.err_message = str(e) - self.context['run_time'] = time.time() - self.context['start_time'] + current_time = time.time() + self.context['run_time'] = current_time - (self.context.get('start_time') or current_time) def write_error_context(answer, status=200): pass diff --git a/apps/application/flow/knowledge_workflow_manage.py b/apps/application/flow/knowledge_workflow_manage.py index 184ae3d1041..bba879aebe8 100644 --- a/apps/application/flow/knowledge_workflow_manage.py +++ b/apps/application/flow/knowledge_workflow_manage.py @@ -6,12 +6,20 @@ @date:2025/11/13 19:02 @desc: """ +import traceback +from concurrent.futures import ThreadPoolExecutor + +from django.db.models import QuerySet +from django.utils.translation import get_language from application.flow.common import Workflow from application.flow.i_step_node import WorkFlowPostHandler, KnowledgeFlowParamsSerializer from application.flow.workflow_manage import WorkflowManage from common.handle.base_to_response import BaseToResponse from common.handle.impl.response.system_to_response import SystemToResponse +from knowledge.models.knowledge_action import KnowledgeAction, State + +executor = ThreadPoolExecutor(max_workers=200) class KnowledgeWorkflowManage(WorkflowManage): @@ -33,3 +41,46 @@ def get_start_node(self): start_node_list = [node for node in self.flow.nodes if self.params.get('data_source', {}).get('node_id') == node.id] return start_node_list[0] + + def run(self): + executor.submit(self._run) + + def _run(self): + QuerySet(KnowledgeAction).filter(id=self.params.get('knowledge_action_id')).update( + state=State.STARTED) + language = get_language() + self.run_chain_async(self.start_node, None, language) + while self.is_run(): + pass + self.work_flow_post_handler.handler(self) + + def run_chain(self, current_node, node_result_future=None): + if node_result_future is None: + node_result_future = self.run_node_future(current_node) + try: + result = self.hand_node_result(current_node, node_result_future) + return result + except Exception as e: + traceback.print_exc() + return None + + def hand_node_result(self, current_node, node_result_future): + try: + current_result = node_result_future.result() + result = current_result.write_context(current_node, self) + if result is not None: + # 阻塞获取结果 + list(result) + return current_result + except Exception as e: + traceback.print_exc() + self.status = 500 + current_node.get_write_error_context(e) + self.answer += str(e) + QuerySet(KnowledgeAction).filter(id=self.params.get('knowledge_action_id')).update( + details=self.get_runtime_details(), + state=State.FAILURE) + finally: + current_node.node_chunk.end() + QuerySet(KnowledgeAction).filter(id=self.params.get('knowledge_action_id')).update( + details=self.get_runtime_details()) diff --git a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py index 24da1589852..b4326350108 100644 --- a/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py +++ b/apps/application/flow/step_node/data_source_local_node/impl/base_data_source_local_node.py @@ -37,3 +37,15 @@ def get_form_list(node): def execute(self, file_type_list, file_size_limit, file_count_limit, **kwargs) -> NodeResult: return NodeResult({'file_list': self.workflow_manage.params.get('data_source', {}).get('file_list')}, self.workflow_manage.params.get('knowledge_base') or {}) + + def get_details(self, index: int, **kwargs): + return { + 'name': self.node.properties.get('stepName'), + "index": index, + 'run_time': self.context.get('run_time'), + 'type': self.node.type, + 'file_list': self.context.get('file_list'), + 'knowledge_base': self.workflow_params.get('knowledge_base'), + 'status': self.status, + 'err_message': self.err_message + } diff --git a/apps/knowledge/migrations/0005_knowledgeaction.py b/apps/knowledge/migrations/0005_knowledgeaction.py new file mode 100644 index 00000000000..3633a8d238e --- /dev/null +++ b/apps/knowledge/migrations/0005_knowledgeaction.py @@ -0,0 +1,32 @@ +# Generated by Django 5.2.8 on 2025-11-19 06:06 + +import common.encoder.encoder +import django.db.models.deletion +import uuid_utils.compat +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('knowledge', '0004_alter_document_type_alter_knowledge_type_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='KnowledgeAction', + fields=[ + ('create_time', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, db_index=True, verbose_name='修改时间')), + ('id', models.UUIDField(default=uuid_utils.compat.uuid7, editable=False, primary_key=True, serialize=False, verbose_name='主键id')), + ('state', models.CharField(choices=[('PENDING', 'Pending'), ('STARTED', 'Started'), ('SUCCESS', 'Success'), ('FAILURE', 'Failure'), ('REVOKE', 'Revoke'), ('REVOKED', 'Revoked')], default='STARTED', max_length=20, verbose_name='状态')), + ('details', models.JSONField(default=dict, encoder=common.encoder.encoder.SystemEncoder, verbose_name='执行详情')), + ('run_time', models.FloatField(default=0, verbose_name='运行时长')), + ('meta', models.JSONField(default=dict, verbose_name='元数据')), + ('knowledge', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, to='knowledge.knowledge', verbose_name='知识库')), + ], + options={ + 'db_table': 'knowledge_action', + }, + ), + ] diff --git a/apps/knowledge/models/knowledge_action.py b/apps/knowledge/models/knowledge_action.py new file mode 100644 index 00000000000..0825415b39e --- /dev/null +++ b/apps/knowledge/models/knowledge_action.py @@ -0,0 +1,49 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: knowledge_action.py + @date:2025/11/18 17:59 + @desc: +""" +import uuid_utils.compat as uuid + +from django.db import models + +from common.encoder.encoder import SystemEncoder +from common.mixins.app_model_mixin import AppModelMixin +from knowledge.models import Knowledge + + +class State(models.TextChoices): + # 等待 + PENDING = 'PENDING' + # 执行中 + STARTED = 'STARTED' + # 成功 + SUCCESS = 'SUCCESS' + # 失败 + FAILURE = 'FAILURE' + # 取消任务 + REVOKE = 'REVOKE' + # 取消成功 + REVOKED = 'REVOKED' + + +class KnowledgeAction(AppModelMixin): + id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id") + + knowledge = models.ForeignKey(Knowledge, on_delete=models.DO_NOTHING, verbose_name="知识库", db_constraint=False) + + state = models.CharField(verbose_name='状态', max_length=20, + choices=State.choices, + default=State.STARTED) + + details = models.JSONField(verbose_name="执行详情", default=dict, encoder=SystemEncoder) + + run_time = models.FloatField(verbose_name="运行时长", default=0) + + meta = models.JSONField(verbose_name="元数据", default=dict) + + class Meta: + db_table = "knowledge_action" diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index 9748f975750..04ac6fa82c9 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -14,6 +14,7 @@ from application.flow.step_node import get_node from common.exception.app_exception import AppApiException from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow +from knowledge.models.knowledge_action import KnowledgeAction, State from knowledge.serializers.knowledge import KnowledgeModelSerializer from system_manage.models import AuthTargetType from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer @@ -34,13 +35,30 @@ def action(self, instance: Dict, with_valid=True): if with_valid: self.is_valid(raise_exception=True) knowledge_workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id")).first() + knowledge_action_id = uuid.uuid7() + KnowledgeAction(id=knowledge_action_id, knowledge_id=self.data.get("knowledge_id"), state=State.STARTED).save() work_flow_manage = KnowledgeWorkflowManage( Workflow.new_instance(knowledge_workflow.work_flow, WorkflowMode.KNOWLEDGE), - {'knowledge_id': self.data.get("knowledge_id"), 'stream': True, + {'knowledge_id': self.data.get("knowledge_id"), 'knowledge_action_id': knowledge_action_id, 'stream': True, **instance}, - KnowledgeWorkflowPostHandler(None)) - r = work_flow_manage.run() - return r + KnowledgeWorkflowPostHandler(None, knowledge_action_id)) + work_flow_manage.run() + return {'id': knowledge_action_id, 'knowledge_id': self.data.get("knowledge_id"), 'state': State.STARTED, + 'details': {}} + + class Operate(serializers.Serializer): + workspace_id = serializers.CharField(required=True, label=_('workspace id')) + knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) + id = serializers.UUIDField(required=True, label=_('knowledge action id')) + + def one(self, is_valid=True): + if is_valid: + self.is_valid(raise_exception=True) + knowledge_action_id = self.data.get("id") + knowledge_action = QuerySet(KnowledgeAction).filter(id=knowledge_action_id).first() + return {'id': knowledge_action_id, 'knowledge_id': knowledge_action.knowledge_id, + 'state': knowledge_action.state, + 'details': knowledge_action.details} class KnowledgeWorkflowSerializer(serializers.Serializer): diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index 331539ed8bc..5cbec1b78f1 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -70,6 +70,7 @@ path('workspace//knowledge//document//', views.DocumentView.Page.as_view()), path('workspace//knowledge//', views.KnowledgeView.Page.as_view()), path('workspace//knowledge//form_list//', views.KnowledgeWorkflowFormView.as_view()), - path('workspace//knowledge//action', views.KnowledgeWorkflowActionView.as_view()) + path('workspace//knowledge//action', views.KnowledgeWorkflowActionView.as_view()), + path('workspace//knowledge//action/', views.KnowledgeWorkflowActionView.Operate.as_view()) ] diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index c4b0fd1f651..f0b0d0c26cd 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -27,8 +27,16 @@ class KnowledgeWorkflowActionView(APIView): authentication_classes = [TokenAuth] def post(self, request: Request, workspace_id: str, knowledge_id: str): - return KnowledgeWorkflowActionSerializer( - data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).action(request.data, True) + return result.success(KnowledgeWorkflowActionSerializer( + data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).action(request.data, True)) + + class Operate(APIView): + authentication_classes = [TokenAuth] + + def get(self, request, workspace_id: str, knowledge_id: str, knowledge_action_id: str): + return result.success(KnowledgeWorkflowActionSerializer.Operate( + data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id, 'id': knowledge_action_id}) + .one()) class KnowledgeWorkflowView(APIView): diff --git a/ui/src/api/knowledge/knowledge.ts b/ui/src/api/knowledge/knowledge.ts index d7436e3f830..22ad4995b59 100644 --- a/ui/src/api/knowledge/knowledge.ts +++ b/ui/src/api/knowledge/knowledge.ts @@ -337,7 +337,13 @@ const workflowAction: ( ) => Promise> = (knowledge_id: string, instance, loading) => { return post(`${prefix.value}/${knowledge_id}/action`, instance, {}, loading) } - +const getWorkflowAction: ( + knowledge_id: string, + knowledge_action_id: string, + loading?: Ref, +) => Promise> = (knowledge_id: string, knowledge_action_id, loading) => { + return get(`${prefix.value}/${knowledge_id}/action/${knowledge_action_id}`, {}, loading) +} export default { getKnowledgeList, getKnowledgeListPage, @@ -364,4 +370,5 @@ export default { createWorkflowKnowledge, getKnowledgeWorkflowFormList, workflowAction, + getWorkflowAction, } diff --git a/ui/src/views/knowledge-workflow/component/action/Result.vue b/ui/src/views/knowledge-workflow/component/action/Result.vue new file mode 100644 index 00000000000..9653dbebb4b --- /dev/null +++ b/ui/src/views/knowledge-workflow/component/action/Result.vue @@ -0,0 +1,38 @@ + + + diff --git a/ui/src/views/knowledge-workflow/component/action/index.vue b/ui/src/views/knowledge-workflow/component/action/index.vue index feb019d8b2f..64ea50b1622 100644 --- a/ui/src/views/knowledge-workflow/component/action/index.vue +++ b/ui/src/views/knowledge-workflow/component/action/index.vue @@ -1,7 +1,13 @@