From d571c768ea1e6b18d7f8e6e33e74ce78126b4b32 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Tue, 11 Nov 2025 14:05:02 +0800 Subject: [PATCH] V4.14.1 feature (#5880) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: app split (#5858) * feat: app split script * fix: app split * chore: app split script try-catch * adjust dashborad page (#5872) * create page * create page * create button * router name * bot * template * create page * mobile * toolfolder * fix * fix * fix build * split tool select * img * doc * rename enum * fix page adjust (#5883) * fix page adjust * fix ad store * fix: initv4141 (#5886) * fix: create app * doc * hide api * doc * feat: payment pause interactive (#5892) * fix: copy clbs (#5889) * fix: copy clbs * fix: copy clbs * fix: http protocol handling in baseURL (#5890) * fix: http protocol handling in baseURL * ui fix * auto saved version * fix * auto save * fix: model permission modal (#5895) * folder * fix: del app * navbar * fix: plugin file selector (#5893) * fix: plugin file selector * fix: plugin file selector * workflow tool inputform * pick --------- Co-authored-by: archer <545436317@qq.com> * fix: workflow tool time * doc * fix workorder button (#5896) * add inform track (#5897) * remove invalid track * comment * feat: marketplace refresh api (#5884) * marketplace refresh * fix: helper bot menu button (#5898) --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: heheer Co-authored-by: 伍闲犬 --- .claude/design/i18n优化实施计划.md | 8 +- .claude/design/路由性能诊断报告.md | 8 +- .../core/app/workflow/inteactive/SKILL.md | 534 ++++++++++ .claude/skills/create-skill-file/SKILL.md | 475 +++++++++ .../create-skill-file/examples/bad-example.md | 867 +++++++++++++++++ .../examples/good-example.md | 908 ++++++++++++++++++ .../templates/basic-skill-template.md | 95 ++ .../templates/workflow-skill-template.md | 402 ++++++++ .../docs/introduction/development/sealos.mdx | 2 +- document/content/docs/upgrading/4-14/4141.mdx | 25 + document/data/doc-last-modified.json | 4 +- packages/global/common/file/tools.ts | 6 +- .../global/common/middle/tracks/constants.ts | 5 +- .../global/common/system/config/constants.ts | 6 +- .../global/common/system/types/index.d.ts | 1 + packages/global/core/app/constants.ts | 21 +- packages/global/core/app/utils.ts | 2 +- packages/global/core/app/version.d.ts | 1 + .../global/core/workflow/runtime/utils.ts | 4 + .../template/system/interactive/type.d.ts | 13 +- packages/global/core/workflow/type/index.d.ts | 4 +- .../service/common/file/image/controller.ts | 91 +- packages/service/common/s3/buckets/base.ts | 19 +- packages/service/common/s3/sources/avatar.ts | 17 + .../service/common/s3/sources/chat/index.ts | 4 + packages/service/core/app/controller.ts | 9 +- packages/service/core/app/http.ts | 5 +- packages/service/core/app/tool/controller.ts | 2 +- .../core/app/tool/workflowTool/utils.ts | 13 + packages/service/core/app/version/schema.ts | 3 +- packages/service/core/chat/saveChat.ts | 2 + packages/service/core/chat/utils.ts | 55 +- .../service/core/workflow/dispatch/index.ts | 53 +- .../core/workflow/dispatch/plugin/runInput.ts | 16 +- .../service/support/permission/teamLimit.ts | 11 +- packages/service/support/user/audit/util.ts | 10 +- .../web/components/common/Icon/constants.ts | 7 +- .../common/Icon/icons/common/refresh.svg | 3 + .../common/Icon/icons/common/rocket.svg | 3 + .../common/Icon/icons/core/app/agent.svg | 39 + .../common/Icon/icons/core/app/aiAgent.svg | 45 + .../common/Icon/icons/core/app/create.svg | 14 + .../common/Icon/icons/core/app/simpleBot.svg | 31 +- .../common/Textarea/CodeEditor/Editor.tsx | 3 +- .../core/plugin/tool/TagFilterBox.tsx | 2 +- packages/web/i18n/en/account.json | 1 + packages/web/i18n/en/app.json | 34 +- packages/web/i18n/en/chat.json | 5 + packages/web/i18n/en/common.json | 8 +- packages/web/i18n/en/user.json | 5 +- packages/web/i18n/zh-CN/account.json | 1 + packages/web/i18n/zh-CN/app.json | 35 +- packages/web/i18n/zh-CN/chat.json | 5 + packages/web/i18n/zh-CN/common.json | 11 +- packages/web/i18n/zh-CN/user.json | 2 + packages/web/i18n/zh-Hant/account.json | 1 + packages/web/i18n/zh-Hant/app.json | 31 +- packages/web/i18n/zh-Hant/chat.json | 5 + packages/web/i18n/zh-Hant/common.json | 8 +- packages/web/i18n/zh-Hant/user.json | 5 +- projects/app/public/imgs/app/agentPreview.svg | 1 + projects/app/public/imgs/app/createButton.png | Bin 0 -> 203772 bytes .../public/imgs/app/httpToolSetPreview.svg | 56 ++ .../app/public/imgs/app/mcpToolsPreview.svg | 55 ++ .../app/public/imgs/app/moreTemplateBg.svg | 1 + .../app/public/imgs/app/pluginPreview.svg | 1 + .../public/imgs/app/simpleAgentPreview.svg | 78 ++ .../app/public/imgs/app/simpleDeprecated.svg | 164 ++++ .../app/public/imgs/app/workflowPreview.svg | 1 + projects/app/public/imgs/bot.svg | 52 + projects/app/public/imgs/botClosed.svg | 32 + projects/app/public/imgs/botTextCN.svg | 17 + projects/app/public/imgs/botTextEn.svg | 17 + .../app/src/components/Layout/HelperBot.tsx | 205 ++++ .../src/components/Layout/WorkorderButton.tsx | 143 --- projects/app/src/components/Layout/index.tsx | 4 +- projects/app/src/components/Layout/navbar.tsx | 33 +- .../app/src/components/Layout/navbarPhone.tsx | 14 +- .../common/Textarea/MyTextarea/index.tsx | 73 +- .../common/secret/HeaderAuthForm.tsx | 16 +- .../components/core/ai/ModelTable/index.tsx | 10 +- .../core/app/formRender/FileSelector.tsx | 343 +++---- .../components/core/app/formRender/index.tsx | 6 +- .../ChatContainer/ChatBox/Input/ChatInput.tsx | 7 +- .../ChatBox/Input/InputGuideBox.tsx | 3 +- .../ChatBox/Input/VoiceInput.tsx | 5 +- .../chat/ChatContainer/ChatBox/Provider.tsx | 21 +- .../ChatBox/components/ChatItem.tsx | 7 +- .../ChatBox/components/FeedbackModal.tsx | 4 +- .../ChatBox/components/QuoteList.tsx | 4 +- .../core/chat/ChatContainer/ChatBox/index.tsx | 37 +- .../core/chat/ChatContainer/ChatBox/utils.ts | 39 +- .../PluginRunBox/components/RenderInput.tsx | 74 +- .../ChatContainer/PluginRunBox/context.tsx | 34 +- .../context/workflowAuthContext.tsx | 38 + .../core/chat/components/AIResponseBox.tsx | 38 +- .../support/user/inform/SystemMsgModal.tsx | 6 + .../account/AccountContainer.tsx | 4 +- .../pageComponents/account/TeamSelector.tsx | 4 +- .../app/src/pageComponents/app/constants.ts | 50 +- .../pageComponents/app/create/AppTypeCard.tsx | 49 + .../app/detail/HTTPTools/Header.tsx | 4 +- .../detail/HTTPTools/SchemaConfigModal.tsx | 13 +- .../app/detail/Logs/DetailLogsModal.tsx | 2 +- .../app/detail/MCPTools/Header.tsx | 2 +- .../app/detail/Plugin/Header.tsx | 2 +- .../app/detail/PublishHistoriesSlider.tsx | 10 +- .../pageComponents/app/detail/RouteTab.tsx | 2 +- .../app/detail/SimpleApp/Header.tsx | 2 +- .../SimpleApp/components/ToolSelectModal.tsx | 61 +- .../app/detail/Workflow/Header.tsx | 2 +- .../WorkflowComponents/Flow/ChatTest.tsx | 2 +- .../Flow/components/NodeTemplates/header.tsx | 101 +- .../Flow/components/NodeTemplates/list.tsx | 7 +- .../NodeTemplates/useNodeTemplates.tsx | 20 +- .../nodes/NodePluginIO/InputTypeConfig.tsx | 7 +- .../context/workflowDebugContext.tsx | 2 +- .../src/pageComponents/app/detail/context.tsx | 4 +- .../pageComponents/app/detail/useChatTest.tsx | 2 +- .../src/pageComponents/chat/ChatHeader.tsx | 8 +- .../AddFavouriteAppModal.tsx | 7 +- .../pageComponents/chat/ChatTeamApp/List.tsx | 4 +- .../chat/ChatTeamApp/TypeTag.tsx | 14 +- .../pageComponents/chat/ChatTeamApp/index.tsx | 31 +- .../chat/ChatWindow/AppChatWindow.tsx | 1 - .../app/src/pageComponents/chat/ToolMenu.tsx | 25 +- .../pageComponents/chat/WorkorderEntrance.tsx | 24 + .../pageComponents/dashboard/Container.tsx | 234 +++-- .../dashboard/TeamPlanStatusCard.tsx | 204 ++++ .../{apps => agent}/JsonImportModal.tsx | 6 +- .../pageComponents/dashboard/agent/List.tsx | 700 ++++++++++++++ .../dashboard/agent/TemplateCreatePanel.tsx | 249 +++++ .../dashboard/{apps => agent}/TypeTag.tsx | 48 +- .../dashboard/{apps => agent}/context.tsx | 52 +- .../dashboard/apps/CreateModal.tsx | 330 ------- .../dashboard/apps/HttpToolsCreateModal.tsx | 192 ---- .../pageComponents/dashboard/apps/List.tsx | 463 --------- .../dashboard/apps/MCPToolsEditModal.tsx | 267 ----- .../login/LoginForm/FormLayout.tsx | 2 +- .../src/pageComponents/login/RegisterForm.tsx | 16 - projects/app/src/pages/404.tsx | 2 +- projects/app/src/pages/_error.tsx | 2 +- projects/app/src/pages/account/info/index.tsx | 6 +- .../app/src/pages/account/model/index.tsx | 2 +- projects/app/src/pages/api/admin/initv4141.ts | 211 ++++ projects/app/src/pages/api/core/app/copy.ts | 8 +- projects/app/src/pages/api/core/app/create.ts | 23 +- .../src/pages/api/core/app/folder/create.ts | 13 +- projects/app/src/pages/api/core/app/list.ts | 2 +- .../src/pages/api/core/app/mcpTools/create.ts | 6 +- .../api/core/app/mcpTools/getChildren.ts | 2 +- .../src/pages/api/core/app/template/list.ts | 42 +- projects/app/src/pages/api/core/app/update.ts | 2 +- .../src/pages/api/core/app/version/list.ts | 2 +- .../src/pages/api/core/app/version/publish.ts | 36 +- .../app/src/pages/api/core/chat/chatTest.ts | 4 +- .../api/core/chat/getPaginationRecords.ts | 6 +- .../core/plugin/admin/tool/app/systemApps.ts | 2 +- .../user/team/plan/getTeamPlanStatus.ts | 7 +- .../app/src/pages/api/v1/chat/completions.ts | 4 +- .../app/src/pages/api/v2/chat/completions.ts | 4 +- projects/app/src/pages/app/detail/index.tsx | 4 +- projects/app/src/pages/chat/share.tsx | 4 + .../pages/dashboard/{apps => agent}/index.tsx | 226 ++--- .../app/src/pages/dashboard/create/index.tsx | 602 ++++++++++++ .../tool => dashboard/systemTool}/index.tsx | 230 +++-- .../pages/dashboard/templateMarket/index.tsx | 14 +- .../app/src/pages/dashboard/tool/index.tsx | 262 +++++ projects/app/src/pages/index.tsx | 2 +- projects/app/src/pages/login/fastlogin.tsx | 2 +- projects/app/src/pages/login/index.tsx | 8 +- projects/app/src/pages/login/provider.tsx | 8 +- projects/app/src/pages/price/index.tsx | 57 +- projects/app/src/service/support/mcp/utils.ts | 6 +- projects/app/src/web/common/api/fetch.ts | 2 +- .../app/src/web/common/middle/tracks/utils.ts | 18 + projects/app/src/web/common/system/api.ts | 5 + .../src/web/common/system/useSystemStore.ts | 17 +- projects/app/src/web/core/app/api/template.ts | 4 +- projects/app/src/web/core/app/api/tool.ts | 10 +- projects/app/src/web/core/app/templates.ts | 15 +- .../web/core/chat/context/chatItemContext.tsx | 2 +- .../core/chat/context/chatRecordContext.tsx | 5 +- projects/app/src/web/support/user/auth.ts | 13 + .../src/pages/api/admin/refresh.ts | 23 + projects/marketplace/src/service/auth.ts | 1 + projects/marketplace/src/service/tool/data.ts | 5 + test/cases/service/support/mcp/utils.test.ts | 2 +- 188 files changed, 8246 insertions(+), 2560 deletions(-) create mode 100644 .claude/skills/core/app/workflow/inteactive/SKILL.md create mode 100644 .claude/skills/create-skill-file/SKILL.md create mode 100644 .claude/skills/create-skill-file/examples/bad-example.md create mode 100644 .claude/skills/create-skill-file/examples/good-example.md create mode 100644 .claude/skills/create-skill-file/templates/basic-skill-template.md create mode 100644 .claude/skills/create-skill-file/templates/workflow-skill-template.md create mode 100644 packages/web/components/common/Icon/icons/common/refresh.svg create mode 100644 packages/web/components/common/Icon/icons/common/rocket.svg create mode 100644 packages/web/components/common/Icon/icons/core/app/agent.svg create mode 100644 packages/web/components/common/Icon/icons/core/app/aiAgent.svg create mode 100644 packages/web/components/common/Icon/icons/core/app/create.svg create mode 100644 projects/app/public/imgs/app/agentPreview.svg create mode 100644 projects/app/public/imgs/app/createButton.png create mode 100644 projects/app/public/imgs/app/httpToolSetPreview.svg create mode 100644 projects/app/public/imgs/app/mcpToolsPreview.svg create mode 100644 projects/app/public/imgs/app/moreTemplateBg.svg create mode 100644 projects/app/public/imgs/app/pluginPreview.svg create mode 100644 projects/app/public/imgs/app/simpleAgentPreview.svg create mode 100644 projects/app/public/imgs/app/simpleDeprecated.svg create mode 100644 projects/app/public/imgs/app/workflowPreview.svg create mode 100644 projects/app/public/imgs/bot.svg create mode 100644 projects/app/public/imgs/botClosed.svg create mode 100644 projects/app/public/imgs/botTextCN.svg create mode 100644 projects/app/public/imgs/botTextEn.svg create mode 100644 projects/app/src/components/Layout/HelperBot.tsx delete mode 100644 projects/app/src/components/Layout/WorkorderButton.tsx create mode 100644 projects/app/src/components/core/chat/ChatContainer/context/workflowAuthContext.tsx create mode 100644 projects/app/src/pageComponents/app/create/AppTypeCard.tsx create mode 100644 projects/app/src/pageComponents/chat/WorkorderEntrance.tsx create mode 100644 projects/app/src/pageComponents/dashboard/TeamPlanStatusCard.tsx rename projects/app/src/pageComponents/dashboard/{apps => agent}/JsonImportModal.tsx (96%) create mode 100644 projects/app/src/pageComponents/dashboard/agent/List.tsx create mode 100644 projects/app/src/pageComponents/dashboard/agent/TemplateCreatePanel.tsx rename projects/app/src/pageComponents/dashboard/{apps => agent}/TypeTag.tsx (52%) rename projects/app/src/pageComponents/dashboard/{apps => agent}/context.tsx (82%) delete mode 100644 projects/app/src/pageComponents/dashboard/apps/CreateModal.tsx delete mode 100644 projects/app/src/pageComponents/dashboard/apps/HttpToolsCreateModal.tsx delete mode 100644 projects/app/src/pageComponents/dashboard/apps/List.tsx delete mode 100644 projects/app/src/pageComponents/dashboard/apps/MCPToolsEditModal.tsx create mode 100644 projects/app/src/pages/api/admin/initv4141.ts rename projects/app/src/pages/dashboard/{apps => agent}/index.tsx (52%) create mode 100644 projects/app/src/pages/dashboard/create/index.tsx rename projects/app/src/pages/{plugin/tool => dashboard/systemTool}/index.tsx (69%) create mode 100644 projects/app/src/pages/dashboard/tool/index.tsx create mode 100644 projects/marketplace/src/pages/api/admin/refresh.ts create mode 100644 projects/marketplace/src/service/auth.ts diff --git a/.claude/design/i18n优化实施计划.md b/.claude/design/i18n优化实施计划.md index 4d9c0d0b7..230f3b97a 100644 --- a/.claude/design/i18n优化实施计划.md +++ b/.claude/design/i18n优化实施计划.md @@ -133,7 +133,7 @@ export default i18n; export const pageNamespaces: Record = { // 应用相关 '/app/detail': ['app', 'chat', 'workflow', 'publish', 'file'], - '/dashboard/apps': ['app'], + '/dashboard/agent': ['app'], // 数据集相关 '/dataset/list': ['dataset'], @@ -583,7 +583,7 @@ P0 - 高频访问页面 (优先迁移): - /app/detail (应用编辑页) - /dataset/list (数据集列表) - /dataset/detail (数据集详情) - - /dashboard/apps (应用列表) + - /dashboard/agent (应用列表) P1 - 中频访问页面: - /account/setting (账户设置) @@ -810,7 +810,7 @@ import { test, expect } from '@playwright/test'; test.describe('i18n Performance', () => { test('should load translations within 200ms', async ({ page }) => { - await page.goto('http://localhost:3000/dashboard/apps'); + await page.goto('http://localhost:3000/dashboard/agent'); const startTime = Date.now(); @@ -826,7 +826,7 @@ test.describe('i18n Performance', () => { }); test('should switch routes without translation flash', async ({ page }) => { - await page.goto('http://localhost:3000/dashboard/apps'); + await page.goto('http://localhost:3000/dashboard/agent'); await page.waitForLoadState('networkidle'); // 点击链接切换路由 diff --git a/.claude/design/路由性能诊断报告.md b/.claude/design/路由性能诊断报告.md index 9634cbf94..335b9de39 100644 --- a/.claude/design/路由性能诊断报告.md +++ b/.claude/design/路由性能诊断报告.md @@ -82,7 +82,7 @@ export async function getServerSideProps(content: any) { }; } -// projects/app/src/pages/dashboard/apps/index.tsx:344 +// projects/app/src/pages/dashboard/agent/index.tsx:344 export async function getServerSideProps(content: any) { return { props: { @@ -317,7 +317,7 @@ const { loading: loadingApp, runAsync: reloadApp } = useRequest2( refreshDeps: [appId], errorToast: t('common:core.app.error.Get app failed'), onError(err: any) { - router.replace('/dashboard/apps'); + router.replace('/dashboard/agent'); }, onSuccess(res) { setAppDetail(res); @@ -585,7 +585,7 @@ import i18n from './client'; const pageNamespaces = { '/app/detail': ['app', 'chat', 'workflow'], '/dataset/list': ['dataset'], - '/dashboard/apps': ['app'], + '/dashboard/agent': ['app'], // ... 更多映射 }; @@ -1321,7 +1321,7 @@ import { test, expect } from '@playwright/test'; test.describe('Route Switching Performance', () => { test('should switch routes within 500ms', async ({ page }) => { - await page.goto('http://localhost:3000/dashboard/apps'); + await page.goto('http://localhost:3000/dashboard/agent'); // 等待页面完全加载 await page.waitForLoadState('networkidle'); diff --git a/.claude/skills/core/app/workflow/inteactive/SKILL.md b/.claude/skills/core/app/workflow/inteactive/SKILL.md new file mode 100644 index 000000000..6652163ba --- /dev/null +++ b/.claude/skills/core/app/workflow/inteactive/SKILL.md @@ -0,0 +1,534 @@ +--- +name: workflow-interactive-dev +description: 用于开发 FastGPT 工作流中的交互响应。详细说明了交互节点的架构、开发流程和需要修改的文件。 +--- + +# 交互节点开发指南 + +## 概述 + +FastGPT 工作流支持多种交互节点类型,允许在工作流执行过程中暂停并等待用户输入。本指南详细说明了如何开发新的交互节点。 + +## 现有交互节点类型 + +当前系统支持以下交互节点类型: + +1. **userSelect** - 用户选择节点(单选) +2. **formInput** - 表单输入节点(多字段表单) +3. **childrenInteractive** - 子工作流交互 +4. **loopInteractive** - 循环交互 +5. **paymentPause** - 欠费暂停交互 + +## 交互节点架构 + +### 核心类型定义 + +交互节点的类型定义位于 `packages/global/core/workflow/template/system/interactive/type.d.ts` + +```typescript +// 基础交互结构 +type InteractiveBasicType = { + entryNodeIds: string[]; // 入口节点ID列表 + memoryEdges: RuntimeEdgeItemType[]; // 需要记忆的边 + nodeOutputs: NodeOutputItemType[]; // 节点输出 + skipNodeQueue?: Array; // 跳过的节点队列 + usageId?: string; // 用量记录ID +}; + +// 具体交互节点类型 +type YourInteractiveNode = InteractiveNodeType & { + type: 'yourNodeType'; + params: { + // 节点特定参数 + }; +}; +``` + +### 工作流执行机制 + +交互节点在工作流执行中的特殊处理(位于 `packages/service/core/workflow/dispatch/index.ts:1012-1019`): + +```typescript +// 部分交互节点不会自动重置 isEntry 标志(因为需要根据 isEntry 字段来判断是首次进入还是流程进入) +runtimeNodes.forEach((item) => { + if ( + item.flowNodeType !== FlowNodeTypeEnum.userSelect && + item.flowNodeType !== FlowNodeTypeEnum.formInput && + item.flowNodeType !== FlowNodeTypeEnum.agent + ) { + item.isEntry = false; + } +}); +``` + +## 开发新交互响应的步骤 + +### 步骤 1: 定义节点类型 + +**文件**: `packages/global/core/workflow/template/system/interactive/type.d.ts` + +```typescript +export type YourInputItemType = { + // 定义输入项的结构 + key: string; + label: string; + value: any; + // ... 其他字段 +}; + +type YourInteractiveNode = InteractiveNodeType & { + type: 'yourNodeType'; + params: { + description: string; + yourInputField: YourInputItemType[]; + submitted?: boolean; // 可选:是否已提交 + }; +}; + +// 添加到联合类型 +export type InteractiveNodeResponseType = + | UserSelectInteractive + | UserInputInteractive + | YourInteractiveNode // 新增 + | ChildrenInteractive + | LoopInteractive + | PaymentPauseInteractive; +``` + +### 步骤 2: 定义节点枚举(可选) + +**文件**: `packages/global/core/workflow/node/constant.ts` + +如果不需要添加新的节点类型,则不需要修改这个文件。 + +```typescript +export enum FlowNodeTypeEnum { + // ... 现有类型 + yourNodeType = 'yourNodeType', // 新增节点类型 +} +``` + +### 步骤 3: 创建节点模板(可选) + +**文件**: `packages/global/core/workflow/template/system/interactive/yourNode.ts` + +```typescript +import { i18nT } from '../../../../../../web/i18n/utils'; +import { + FlowNodeTemplateTypeEnum, + NodeInputKeyEnum, + NodeOutputKeyEnum, + WorkflowIOValueTypeEnum +} from '../../../constants'; +import { + FlowNodeInputTypeEnum, + FlowNodeOutputTypeEnum, + FlowNodeTypeEnum +} from '../../../node/constant'; +import { type FlowNodeTemplateType } from '../../../type/node'; + +export const YourNode: FlowNodeTemplateType = { + id: FlowNodeTypeEnum.yourNodeType, + templateType: FlowNodeTemplateTypeEnum.interactive, + flowNodeType: FlowNodeTypeEnum.yourNodeType, + showSourceHandle: true, // 是否显示源连接点 + showTargetHandle: true, // 是否显示目标连接点 + avatar: 'core/workflow/template/yourNode', + name: i18nT('app:workflow.your_node'), + intro: i18nT('app:workflow.your_node_tip'), + isTool: true, // 标记为工具节点 + inputs: [ + { + key: NodeInputKeyEnum.description, + renderTypeList: [FlowNodeInputTypeEnum.textarea], + valueType: WorkflowIOValueTypeEnum.string, + label: i18nT('app:workflow.node_description'), + placeholder: i18nT('app:workflow.your_node_placeholder') + }, + { + key: NodeInputKeyEnum.yourInputField, + renderTypeList: [FlowNodeInputTypeEnum.custom], + valueType: WorkflowIOValueTypeEnum.any, + label: '', + value: [] // 默认值 + } + ], + outputs: [ + { + id: NodeOutputKeyEnum.yourResult, + key: NodeOutputKeyEnum.yourResult, + required: true, + label: i18nT('workflow:your_result'), + valueType: WorkflowIOValueTypeEnum.object, + type: FlowNodeOutputTypeEnum.static + } + ] +}; +``` + +### 步骤 4: 创建节点执行逻辑或在需要处理交互逻辑的节点上增加新逻辑 + +**文件**: `packages/service/core/workflow/dispatch/interactive/yourNode.ts` + +```typescript +import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; +import type { + DispatchNodeResultType, + ModuleDispatchProps +} from '@fastgpt/global/core/workflow/runtime/type'; +import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; +import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; +import type { YourInputItemType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; +import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt'; + +type Props = ModuleDispatchProps<{ + [NodeInputKeyEnum.description]: string; + [NodeInputKeyEnum.yourInputField]: YourInputItemType[]; +}>; + +type YourNodeResponse = DispatchNodeResultType<{ + [NodeOutputKeyEnum.yourResult]?: Record; +}>; + +export const dispatchYourNode = async (props: Props): Promise => { + const { + histories, + node, + params: { description, yourInputField }, + query, + lastInteractive + } = props; + const { isEntry } = node; + + // 第一阶段:非入口节点或不是对应的交互类型,返回交互请求 + if (!isEntry || lastInteractive?.type !== 'yourNodeType') { + return { + [DispatchNodeResponseKeyEnum.interactive]: { + type: 'yourNodeType', + params: { + description, + yourInputField + } + } + }; + } + + // 第二阶段:处理用户提交的数据 + node.isEntry = false; // 重要:重置入口标志 + + const { text } = chatValue2RuntimePrompt(query); + const userInputVal = (() => { + try { + return JSON.parse(text); // 根据实际格式解析 + } catch (error) { + return {}; + } + })(); + + return { + data: { + [NodeOutputKeyEnum.yourResult]: userInputVal + }, + // 移除当前交互的历史记录(最后2条) + [DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2), + [DispatchNodeResponseKeyEnum.toolResponses]: userInputVal, + [DispatchNodeResponseKeyEnum.nodeResponse]: { + yourResult: userInputVal + } + }; +}; +``` + +### 步骤 5: 注册节点回调 + +**文件**: `packages/service/core/workflow/dispatch/constants.ts` + +```typescript +import { dispatchYourNode } from './interactive/yourNode'; + +export const callbackMap: Record = { + // ... 现有节点 + [FlowNodeTypeEnum.yourNodeType]: dispatchYourNode, +}; +``` + +### 步骤 6: 创建前端渲染组件 + +#### 6.1 聊天界面交互组件 + +**文件**: `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx` + +```typescript +export const YourNodeComponent = React.memo(function YourNodeComponent({ + interactiveParams: { description, yourInputField, submitted }, + defaultValues = {}, + SubmitButton +}: { + interactiveParams: YourInteractiveNode['params']; + defaultValues?: Record; + SubmitButton: (e: { onSubmit: UseFormHandleSubmit> }) => React.JSX.Element; +}) { + const { handleSubmit, control } = useForm({ + defaultValues + }); + + return ( + + + + {yourInputField.map((input) => ( + + {/* 渲染你的输入组件 */} + ( + + )} + /> + + ))} + + + {!submitted && ( + + + + )} + + ); +}); +``` + +#### 6.2 工作流编辑器节点组件 + +**文件**: `projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeYourNode.tsx` + +```typescript +import React, { useMemo } from 'react'; +import { type NodeProps } from 'reactflow'; +import { Box, Button } from '@chakra-ui/react'; +import NodeCard from './render/NodeCard'; +import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d'; +import Container from '../components/Container'; +import RenderInput from './render/RenderInput'; +import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; +import { useTranslation } from 'next-i18next'; +import { type FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io.d'; +import { useContextSelector } from 'use-context-selector'; +import IOTitle from '../components/IOTitle'; +import RenderOutput from './render/RenderOutput'; +import { WorkflowActionsContext } from '../../context/workflowActionsContext'; + +const NodeYourNode = ({ data, selected }: NodeProps) => { + const { t } = useTranslation(); + const { nodeId, inputs, outputs } = data; + const onChangeNode = useContextSelector(WorkflowActionsContext, (v) => v.onChangeNode); + + const CustomComponent = useMemo( + () => ({ + [NodeInputKeyEnum.yourInputField]: (v: FlowNodeInputItemType) => { + // 自定义渲染逻辑 + return ( + + {/* 你的自定义UI */} + + ); + } + }), + [nodeId, onChangeNode, t] + ); + + return ( + + + + + + + + + + ); +}; + +export default React.memo(NodeYourNode); +``` + +### 步骤 7: 注册节点组件 + +需要在节点注册表中添加你的节点组件(具体位置根据项目配置而定)。 + +### 步骤 8: 添加国际化 + +**文件**: `packages/web/i18n/zh-CN/app.json` 和其他语言文件 + +```json +{ + "workflow": { + "your_node": "你的节点名称", + "your_node_tip": "节点功能说明", + "your_node_placeholder": "提示文本" + } +} +``` + +### 步骤9 调整保存对话记录逻辑 + +**文件**: `FastGPT/packages/service/core/chat/saveChat.ts` + +修改 `updateInteractiveChat` 方法,支持新的交互 + +### 步骤10 根据历史记录获取/设置交互状态 + +**文件**: `FastGPT/projects/app/src/components/core/chat/ChatContainer/ChatBox/utils.ts` +**文件**: `FastGPT/packages/global/core/workflow/runtime/utils.ts` + +调整`setInteractiveResultToHistories`, `getInteractiveByHistories` 和 `getLastInteractiveValue`方法。 + +## 关键注意事项 + +### 1. isEntry 标志管理 + +交互节点需要保持 `isEntry` 标志在工作流恢复时有效: + +```typescript +// 在 packages/service/core/workflow/dispatch/index.ts 中 +// 确保你的节点类型被添加到白名单 +if ( + item.flowNodeType !== FlowNodeTypeEnum.userSelect && + item.flowNodeType !== FlowNodeTypeEnum.formInput && + item.flowNodeType !== FlowNodeTypeEnum.yourNodeType // 新增 +) { + item.isEntry = false; +} +``` + +### 2. 交互响应流程 + +交互节点有两个执行阶段: + +1. **第一次执行**: 返回 `interactive` 响应,暂停工作流 +2. **第二次执行**: 接收用户输入,继续工作流 + +```typescript +// 第一阶段 +if (!isEntry || lastInteractive?.type !== 'yourNodeType') { + return { + [DispatchNodeResponseKeyEnum.interactive]: { + type: 'yourNodeType', + params: { /* ... */ } + } + }; +} + +// 第二阶段 +node.isEntry = false; // 重要!重置标志 +// 处理用户输入... +``` + +### 3. 历史记录管理 + +交互节点需要正确处理历史记录: + +```typescript +return { + // 移除交互对话的历史记录(用户问题 + 系统响应) + [DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2), + // ... 其他返回值 +}; +``` + +### 4. Skip 节点队列 + +交互节点触发时,系统会保存 `skipNodeQueue` 以便恢复时跳过已处理的节点。 + +### 5. 工具调用支持 + +如果节点需要在工具调用中使用,设置 `isTool: true`。 + +## 测试清单 + +开发完成后,请测试以下场景: + +- [ ] 节点在工作流编辑器中正常显示 +- [ ] 节点配置保存和加载正确 +- [ ] 交互请求正确发送到前端 +- [ ] 前端组件正确渲染交互界面 +- [ ] 用户输入正确传回后端 +- [ ] 工作流正确恢复并继续执行 +- [ ] 历史记录正确更新 +- [ ] 节点输出正确连接到后续节点 +- [ ] 错误情况处理正确 +- [ ] 多语言支持完整 + +## 参考实现 + +可以参考以下现有实现: + +1. **简单单选**: `userSelect` 节点 + - 类型定义: `packages/global/core/workflow/template/system/interactive/type.d.ts:48-55` + - 执行逻辑: `packages/service/core/workflow/dispatch/interactive/userSelect.ts` + - 前端组件: `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx:29-63` + +2. **复杂表单**: `formInput` 节点 + - 类型定义: `packages/global/core/workflow/template/system/interactive/type.d.ts:57-82` + - 执行逻辑: `packages/service/core/workflow/dispatch/interactive/formInput.ts` + - 前端组件: `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx:65-126` + +## 常见问题 + +### Q: 交互节点执行了两次? +A: 这是正常的。第一次返回交互请求,第二次处理用户输入。确保在第二次执行时设置 `node.isEntry = false`。 + +### Q: 工作流恢复后没有继续执行? +A: 检查你的节点类型是否在 `isEntry` 白名单中(dispatch/index.ts:1013-1018)。 + +### Q: 用户输入格式不对? +A: 检查 `chatValue2RuntimePrompt` 的返回值,根据你的数据格式进行解析。 + +### Q: 如何支持多个交互节点串联? +A: 每个交互节点都会暂停工作流,用户完成后会自动继续到下一个节点。 + +## 文件清单总结 + +开发新交互节点需要修改/创建以下文件: + +### 后端核心文件 +1. `packages/global/core/workflow/template/system/interactive/type.d.ts` - 类型定义 +2. `packages/global/core/workflow/node/constant.ts` - 节点枚举 +3. `packages/global/core/workflow/template/system/interactive/yourNode.ts` - 节点模板 +4. `packages/service/core/workflow/dispatch/interactive/yourNode.ts` - 执行逻辑 +5. `packages/service/core/workflow/dispatch/constants.ts` - 回调注册 +6. `packages/service/core/workflow/dispatch/index.ts` - isEntry 白名单 + +### 前端组件文件 +7. `projects/app/src/components/core/chat/components/Interactive/InteractiveComponents.tsx` - 聊天交互组件 +8. `projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeYourNode.tsx` - 工作流编辑器组件 + +### 国际化文件 +9. `packages/web/i18n/zh-CN/app.json` - 中文翻译 +10. `packages/web/i18n/en/app.json` - 英文翻译 +11. `packages/web/i18n/zh-Hant/app.json` - 繁体中文翻译 + +## 附录:关键输入输出键定义 + +如果需要新的输入输出键,在以下文件中定义: + +**文件**: `packages/global/core/workflow/constants.ts` + +```typescript +export enum NodeInputKeyEnum { + // ... 现有键 + yourInputKey = 'yourInputKey', +} + +export enum NodeOutputKeyEnum { + // ... 现有键 + yourOutputKey = 'yourOutputKey', +} +``` diff --git a/.claude/skills/create-skill-file/SKILL.md b/.claude/skills/create-skill-file/SKILL.md new file mode 100644 index 000000000..e089adc08 --- /dev/null +++ b/.claude/skills/create-skill-file/SKILL.md @@ -0,0 +1,475 @@ +--- +name: create-skill-file +description: Guides Claude in creating well-structured SKILL.md files following best practices. Provides clear guidelines for naming, structure, and content organization to make skills easy to discover and execute. +--- + +# Claude Agent Skill 编写规范 + +> 如何创建高质量的 SKILL.md 文件 + +## 目录 + +- [快速开始](#快速开始) +- [核心原则](#核心原则) +- [文件结构规范](#文件结构规范) +- [命名和描述规范](#命名和描述规范) +- [内容编写指南](#内容编写指南) +- [质量检查清单](#质量检查清单) + +--- + +## 快速开始 + +### 3步创建 Skill + +**第1步: 创建目录** + +```bash +mkdir -p .claude/skill/your-skill-name +cd .claude/skill/your-skill-name +``` + +**第2步: 创建 SKILL.md** + +```markdown +--- +name: your-skill-name +description: Brief description with trigger keywords and scenarios +--- + +# Your Skill Title + +## When to Use This Skill + +- User asks to [specific scenario] +- User mentions "[keyword]" + +## How It Works + +1. Step 1: [Action] +2. Step 2: [Action] + +## Examples + +**Input**: User request +**Output**: Expected result +``` + +**第3步: 测试** +- 在对话中使用 description 中的关键词触发 +- 观察 Claude 是否正确执行 +- 根据效果调整 + +--- + +## 核心原则 + +### 1. 保持简洁 + +只添加 Claude **不知道**的新知识: +- ✅ 项目特定的工作流程 +- ✅ 特殊的命名规范或格式要求 +- ✅ 自定义工具和脚本的使用方法 +- ❌ 通用编程知识 +- ❌ 显而易见的步骤 + +**示例对比**: + +```markdown +# ❌ 过度详细 +1. 创建 Python 文件 +2. 导入必要的库 +3. 定义函数 +4. 编写主程序逻辑 + +# ✅ 简洁有效 +使用 `scripts/api_client.py` 调用内部 API。 +请求头必须包含 `X-Internal-Token`(从环境变量 `INTERNAL_API_KEY` 获取)。 +``` + +### 2. 设定合适的自由度 + +| 自由度 | 适用场景 | 编写方式 | +|--------|---------|---------| +| **高** | 需要创造性、多种解决方案 | 提供指导原则,不限定具体步骤 | +| **中** | 有推荐模式但允许变化 | 提供参数化示例和默认流程 | +| **低** | 容易出错、需严格执行 | 提供详细的分步指令或脚本 | + +**判断标准**: +- 任务是否有明确的"正确答案"? → 低自由度 +- 是否需要适应不同场景? → 高自由度 +- 错误的代价有多大? → 代价高则用低自由度 + +### 3. 渐进式披露 + +将复杂内容分层组织: + +``` +SKILL.md (主文档, 200-500行) +├── reference.md (详细文档) +├── examples.md (完整示例) +└── scripts/ (可执行脚本) +``` + +**规则**: +- SKILL.md 超过 500行 → 拆分子文件 +- 子文件超过 100行 → 添加目录 +- 引用深度 ≤ 1层 + +--- + +## 文件结构规范 + +### YAML Frontmatter + +```yaml +--- +name: skill-name-here +description: Clear description of what this skill does and when to activate it +--- +``` + +**字段规范**: + +| 字段 | 要求 | 说明 | +|------|------|------| +| `name` | 小写字母、数字、短横线,≤64字符 | 必须与目录名一致 | +| `description` | 纯文本,≤1024字符 | 用于检索和激活 | + +**命名禁忌**: +- ❌ XML 标签、保留字(`anthropic`, `claude`) +- ❌ 模糊词汇(`helper`, `utility`, `manager`) +- ❌ 空格或下划线(用短横线 `-`) + +**Description 技巧**: + +```yaml +# ❌ 过于泛化 +description: Helps with code tasks + +# ✅ 具体且包含关键词 +description: Processes CSV files and generates Excel reports with charts. Use when user asks to convert data formats or create visual reports. + +# ✅ 说明触发场景 +description: Analyzes Python code for security vulnerabilities using bandit. Activates when user mentions "security audit" or "vulnerability scan". +``` + +### 目录组织 + +**基础结构**(简单 Skill): +``` +skill-name/ +└── SKILL.md +``` + +**标准结构**(推荐): +``` +skill-name/ +├── SKILL.md +├── templates/ +│ └── template.md +└── scripts/ + └── script.py +``` + +--- + +## 命名和描述规范 + +### Skill 命名 + +**推荐格式**: 动名词形式 (verb-ing + noun) + +``` +✅ 好的命名: +- processing-csv-files +- generating-api-docs +- managing-database-migrations + +❌ 不好的命名: +- csv (过于简短) +- data_processor (使用下划线) +- helper (过于模糊) +``` + +### Description 编写 + +**必须使用第三人称**: + +```yaml +# ❌ 错误 +description: I help you process PDFs + +# ✅ 正确 +description: Processes PDF documents and extracts structured data +``` + +**4C 原则**: +- **Clear** (清晰): 避免术语和模糊词汇 +- **Concise** (简洁): 1-2句话说明核心功能 +- **Contextual** (上下文): 说明适用场景 +- **Complete** (完整): 功能 + 触发条件 + +--- + +## 内容编写指南 + +### "When to Use" 章节 + +明确说明触发场景: + +```markdown +## When to Use This Skill + +- User asks to analyze Python code for type errors +- User mentions "mypy" or "type checking" +- User is working in a Python project with type hints +- User needs to add type annotations +``` + +**模式**: +- 直接请求: "User asks to X" +- 关键词: "User mentions 'keyword'" +- 上下文: "User is working with X" +- 任务类型: "User needs to X" + +### 工作流设计 + +**简单线性流程**: + +```markdown +## How It Works + +1. Scan the project for all `.py` files +2. Run `mypy --strict` on each file +3. Parse error output and categorize by severity +4. Generate summary report with fix suggestions +``` + +**条件分支流程**: + +```markdown +## Workflow + +1. **Check project type** + - If Django → Use `django-stubs` config + - If Flask → Use `flask-stubs` config + - Otherwise → Use default mypy config + +2. **Run type checking** + - If errors found → Proceed to step 3 + - If no errors → Report success and exit +``` + +**Checklist 模式**(验证型任务): + +```markdown +## Pre-deployment Checklist + +Execute in order. Stop if any step fails. + +- [ ] Run tests: `npm test` (must pass) +- [ ] Build: `npm run build` (no errors) +- [ ] Check deps: `npm audit` (no critical vulnerabilities) +``` + +### 示例和模板 + +**输入-输出示例**: + +```markdown +## Examples + +### Example 1: Basic Check + +**User Request**: "Check my code for type errors" + +**Action**: +1. Scan for `.py` files +2. Run `mypy` on all files + +**Output**: + + Found 3 type errors in 2 files: + src/main.py:15: error: Missing return type + src/utils.py:42: error: Incompatible types + +``` + +### 脚本集成 + +**何时使用脚本**: +- 简单命令 → 直接在 SKILL.md 中说明 +- 复杂流程 → 提供独立脚本 + +**脚本编写规范**: + +```python +#!/usr/bin/env python3 +""" +Brief description of what this script does. + +Usage: + python script.py [--option value] +""" + +import argparse + +DEFAULT_VALUE = 80 # Use constants, not magic numbers + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", help="Directory to process") + parser.add_argument("--threshold", type=int, default=DEFAULT_VALUE) + + args = parser.parse_args() + + # Validate inputs + if not Path(args.directory).is_dir(): + print(f"Error: {args.directory} not found") + return 1 + + # Execute + result = process(args.directory, args.threshold) + + # Report + print(f"Processed {result['count']} files") + return 0 + +if __name__ == "__main__": + exit(main()) +``` + +**关键规范**: +- ✅ Shebang 行和 docstring +- ✅ 类型注解和常量 +- ✅ 参数验证和错误处理 +- ✅ 清晰的返回值(0=成功, 1=失败) + +### 最佳实践 + +**Do**: +- ✅ 提供可执行的命令和脚本 +- ✅ 包含输入-输出示例 +- ✅ 说明验证标准和成功条件 +- ✅ 包含 Do/Don't 清单 + +**Don't**: +- ❌ 包含 Claude 已知的通用知识 +- ❌ 使用抽象描述而非具体步骤 +- ❌ 遗漏错误处理指导 +- ❌ 示例使用伪代码而非真实代码 + +--- + +## 质量检查清单 + +### 核心质量 + +- [ ] `name` 符合命名规范(小写、短横线、≤64字符) +- [ ] `description` 包含触发关键词和场景(≤1024字符) +- [ ] 名称与目录名一致 +- [ ] 只包含 Claude 不知道的信息 +- [ ] 没有冗余或重复内容 + +### 功能完整性 + +- [ ] 有"When to Use"章节,列出 3-5 个触发场景 +- [ ] 有清晰的执行流程或步骤 +- [ ] 至少 2-3 个完整示例 +- [ ] 包含输入和预期输出 +- [ ] 错误处理有指导 + +### 结构规范 + +- [ ] 章节组织清晰 +- [ ] 超过 200行有目录导航 +- [ ] 引用层级 ≤ 1层 +- [ ] 所有路径使用正斜杠 `/` +- [ ] 术语使用一致 + +### 脚本和模板 + +- [ ] 脚本包含使用说明和参数文档 +- [ ] 脚本有错误处理 +- [ ] 避免魔法数字,使用配置 +- [ ] 模板格式清晰易用 + +### 最终检查 + +- [ ] 通读全文,确保流畅易读 +- [ ] 使用实际场景测试触发 +- [ ] 长度适中(200-500行,或已拆分) + +--- + +## 常见问题 + +**Q: Skill 多长才合适?** +- 最小: 50-100行 +- 理想: 200-500行 +- 最大: 500行(超过则拆分) + +**Q: 如何让 Skill 更容易激活?** +- 在 `description` 中使用用户会说的关键词 +- 说明具体场景("when user asks to X") +- 提及相关工具名称 + +**Q: 多个 Skill 功能重叠怎么办?** +- 使用更具体的 `description` 区分 +- 在"When to Use"中说明关系 +- 考虑合并为一个 Skill + +**Q: Skill 需要维护吗?** +- 每季度审查一次,更新过时信息 +- 根据使用反馈迭代 +- 工具或 API 变更时及时更新 + +--- + +## 快速参考 + +### Frontmatter 模板 + +```yaml +--- +name: skill-name +description: Brief description with trigger keywords +--- +``` + +### 基础结构模板 + +```markdown +# Skill Title + +## When to Use This Skill +- Scenario 1 +- Scenario 2 + +## How It Works +1. Step 1 +2. Step 2 + +## Examples +### Example 1 +... + +## References +- [Link](url) +``` + +--- + +## 相关资源 + +- [Claude Agent Skills 官方文档](https://docs.claude.com/en/docs/agents-and-tools/agent-skills) +- [Best Practices Checklist](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practices) +- [模板文件](templates/) - 开箱即用的模板 + - [基础 skill 的模板](templates/basic-skill-template.md) + - [工作流 skill 的模板](templates/workflow-skill-template.md) +- [示例库](examples/) - 完整的 Skill 示例 + - [优秀示例](examples/good-example.md) + - [常见错误示例](examples/bad-example.md) + +--- diff --git a/.claude/skills/create-skill-file/examples/bad-example.md b/.claude/skills/create-skill-file/examples/bad-example.md new file mode 100644 index 000000000..c0ae20b1b --- /dev/null +++ b/.claude/skills/create-skill-file/examples/bad-example.md @@ -0,0 +1,867 @@ +# 不好的 Skill 示例与改进建议 + +本文档展示常见的 Skill 编写错误,并提供改进建议。 + +--- + +## 示例 1: 过于模糊的 Skill + +### ❌ 不好的版本 + +```markdown +--- +name: helper +description: Helps with code +--- + +# Code Helper + +This skill helps you with coding tasks. + +## Usage + +Use this when you need help with code. + +## How It Works + +1. Analyzes your code +2. Provides suggestions +3. Helps improve it +``` + +### 问题分析 + +| 问题 | 说明 | 影响 | +|------|------|------| +| **模糊的名称** | "helper" 太泛化,没有说明具体做什么 | Claude 不知道何时激活 | +| **无关键词** | description 缺少具体触发词 | 用户很难激活这个 Skill | +| **无具体场景** | 没说明适用什么类型的代码 | 适用范围不清 | +| **抽象的步骤** | "Provides suggestions" 太模糊 | Claude 不知道具体做什么 | +| **无示例** | 没有实际例子 | 用户和 Claude 都不清楚预期输出 | + +### ✅ 改进版本 + +```markdown +--- +name: python-code-refactoring +description: Refactors Python code to improve readability and maintainability using standard patterns. Activates when user asks to clean up code, improve structure, or mentions refactoring. Focuses on function extraction, variable naming, and removing code smells. +--- + +# Python Code Refactoring Skill + +Improves Python code quality through systematic refactoring. + +## When to Use This Skill + +- User asks to "refactor this code", "clean up this function", or "improve readability" +- User mentions "code smell", "technical debt", or "maintainability" +- User is working with Python code that has: + - Long functions (>50 lines) + - Nested conditionals (>3 levels) + - Repeated code patterns + - Unclear variable names + +## How It Works + +### 1. Identify Refactoring Opportunities + +Scan code for common issues: +- Functions longer than 50 lines +- Repeated code blocks (DRY violations) +- Magic numbers without constants +- Unclear variable names (x, temp, data) +- Deep nesting (>3 levels) + +### 2. Prioritize Changes + +Focus on high-impact refactorings: +- **High**: Extract complex nested logic to functions +- **Medium**: Rename unclear variables +- **Low**: Minor style improvements + +### 3. Apply Refactorings + +**Extract Method**: +```python +# Before +def process_order(order): + # 50 lines of validation logic + # 30 lines of payment processing + # 20 lines of email notification + +# After +def process_order(order): + validate_order(order) + process_payment(order) + send_confirmation_email(order) +``` + +**Replace Magic Numbers**: +```python +# Before +if user.age < 18: + return False + +# After +MINIMUM_AGE = 18 +if user.age < MINIMUM_AGE: + return False +``` + +**Simplify Conditionals**: +```python +# Before +if user.role == 'admin': + return True +elif user.role == 'moderator': + return True +elif user.role == 'editor': + return True +else: + return False + +# After +PRIVILEGED_ROLES = {'admin', 'moderator', 'editor'} +return user.role in PRIVILEGED_ROLES +``` + +### 4. Verify Improvements + +After refactoring: +- Run existing tests (all must pass) +- Check code length reduced +- Verify improved readability + +## Example + +**User Request**: "Refactor this function, it's too long" + +```python +def process_user_registration(data): + if not data.get('email'): + return {'error': 'Email required'} + if '@' not in data['email']: + return {'error': 'Invalid email'} + if not data.get('password'): + return {'error': 'Password required'} + if len(data['password']) < 8: + return {'error': 'Password too short'} + if not any(c.isupper() for c in data['password']): + return {'error': 'Password needs uppercase'} + existing = db.query(User).filter_by(email=data['email']).first() + if existing: + return {'error': 'Email already registered'} + salt = bcrypt.gensalt() + hashed = bcrypt.hashpw(data['password'].encode(), salt) + user = User(email=data['email'], password_hash=hashed) + db.add(user) + db.commit() + token = jwt.encode({'user_id': user.id}, SECRET_KEY) + send_email(data['email'], 'Welcome!', 'Thanks for registering') + return {'success': True, 'token': token} +``` + +**Refactored**: + +```python +def process_user_registration(data): + """Register new user with validation and email confirmation.""" + # Validation + validation_error = validate_registration_data(data) + if validation_error: + return {'error': validation_error} + + # Check uniqueness + if user_exists(data['email']): + return {'error': 'Email already registered'} + + # Create user + user = create_user(data['email'], data['password']) + + # Generate token + token = generate_auth_token(user.id) + + # Send welcome email + send_welcome_email(user.email) + + return {'success': True, 'token': token} + + +def validate_registration_data(data): + """Validate registration data, return error message or None.""" + if not data.get('email'): + return 'Email required' + if '@' not in data['email']: + return 'Invalid email' + if not data.get('password'): + return 'Password required' + return validate_password_strength(data['password']) + + +def validate_password_strength(password): + """Check password meets security requirements.""" + MIN_PASSWORD_LENGTH = 8 + if len(password) < MIN_PASSWORD_LENGTH: + return f'Password must be at least {MIN_PASSWORD_LENGTH} characters' + if not any(c.isupper() for c in password): + return 'Password must contain uppercase letter' + return None + + +def user_exists(email): + """Check if user with given email already exists.""" + return db.query(User).filter_by(email=email).first() is not None + + +def create_user(email, password): + """Create and save new user with hashed password.""" + salt = bcrypt.gensalt() + hashed = bcrypt.hashpw(password.encode(), salt) + user = User(email=email, password_hash=hashed) + db.add(user) + db.commit() + return user + + +def generate_auth_token(user_id): + """Generate JWT authentication token.""" + return jwt.encode({'user_id': user_id}, SECRET_KEY) + + +def send_welcome_email(email): + """Send welcome email to new user.""" + send_email(email, 'Welcome!', 'Thanks for registering') +``` + +**Improvements**: +- ✅ Main function reduced from 20 lines to 15 lines +- ✅ Each function has single responsibility +- ✅ Magic number (8) extracted to constant +- ✅ All functions documented with docstrings +- ✅ Easier to test individual functions +- ✅ Easier to modify validation rules + +## Best Practices + +- ✅ Extract functions with clear names +- ✅ Use constants instead of magic numbers +- ✅ Keep functions under 30 lines +- ✅ Maximum nesting depth of 2-3 levels +- ✅ Write docstrings for extracted functions +``` + +### 改进要点 + +1. ✅ 具体的名称: `python-code-refactoring` 而非 `helper` +2. ✅ 详细的 description: 包含触发词和适用场景 +3. ✅ 明确的触发条件: 列出具体的使用场景 +4. ✅ 可执行的步骤: 每个步骤都有具体操作 +5. ✅ 实际代码示例: 展示完整的重构过程 +6. ✅ 具体的改进指标: 列出可验证的改进效果 + +--- + +## 示例 2: 过度冗长的 Skill + +### ❌ 不好的版本 + +```markdown +--- +name: python-basics +description: Teaches Python programming basics +--- + +# Python Basics + +This skill helps you learn Python programming. + +## Variables + +In Python, you can create variables like this: + +```python +x = 5 +y = "hello" +z = 3.14 +``` + +Python supports different data types: +- Integers (int): whole numbers like 1, 2, 3 +- Floats (float): decimal numbers like 3.14, 2.5 +- Strings (str): text like "hello", 'world' +- Booleans (bool): True or False + +## Conditional Statements + +You can use if statements to make decisions: + +```python +if x > 0: + print("Positive") +elif x < 0: + print("Negative") +else: + print("Zero") +``` + +The if statement checks a condition. If True, it runs the indented code. +The elif means "else if" and provides an alternative condition. +The else runs if none of the above conditions are True. + +## Loops + +Python has two main types of loops: + +### For Loops + +For loops iterate over a sequence: + +```python +for i in range(5): + print(i) +``` + +This prints numbers 0 through 4. The range() function generates numbers. + +### While Loops + +While loops continue while a condition is True: + +```python +i = 0 +while i < 5: + print(i) + i += 1 +``` + +This does the same thing as the for loop above. + +## Functions + +Functions are reusable blocks of code: + +```python +def greet(name): + return f"Hello, {name}!" +``` + +The def keyword defines a function. The function name is greet. +It takes one parameter called name. The return statement sends back a value. + +## Lists + +Lists store multiple items: + +```python +fruits = ["apple", "banana", "orange"] +``` + +You can access items by index: + +```python +first_fruit = fruits[0] # "apple" +``` + +... [continues for 50 more sections about Python basics] +``` + +### 问题分析 + +| 问题 | 说明 | 影响 | +|------|------|------| +| **包含通用知识** | Python 基础知识 Claude 已经知道 | 浪费 token,增加检索成本 | +| **教程式内容** | 像教程而非工作指南 | Claude 不需要学习,需要的是工作指导 | +| **过度详细** | 解释显而易见的概念 | 信息过载,难以找到关键信息 | +| **缺少项目特定信息** | 没有项目相关的规范或约定 | 无法提供项目特定价值 | + +### ✅ 改进版本 + +```markdown +--- +name: project-python-conventions +description: Enforces Python coding conventions specific to this project. Activates when user writes Python code or asks about code style. Covers naming, imports, error handling, and project-specific patterns. +--- + +# Project Python Conventions + +Project-specific Python coding standards and patterns. + +## When to Use This Skill + +- User is writing or reviewing Python code +- User asks about code style or conventions +- User needs guidance on project patterns + +## Import Organization + +Follow this order: + +```python +# 1. Standard library +import os +import sys +from typing import Optional, List + +# 2. Third-party packages +import numpy as np +from fastapi import FastAPI + +# 3. Local application imports +from core.models import User +from utils.helpers import format_date +``` + +## Naming Conventions + +### Project-Specific Rules + +| Type | Pattern | Example | +|------|---------|---------| +| API endpoints | `/api/v1/{resource}` | `/api/v1/users` | +| Database tables | `{resource}_table` | `users_table` | +| Environment variables | `APP_{NAME}` | `APP_DATABASE_URL` | +| Config files | `{env}.config.py` | `prod.config.py` | + +### Forbidden Patterns + +```python +# ❌ Don't use single-letter variables (except i, j, k in loops) +d = get_data() + +# ✅ Do use descriptive names +user_data = get_data() + +# ❌ Don't use abbreviations +usr_mgr = UserManager() + +# ✅ Do use full words +user_manager = UserManager() +``` + +## Error Handling Pattern + +Use project's custom exceptions: + +```python +from core.exceptions import UserNotFoundError, ValidationError + +def get_user(user_id: int) -> User: + """ + Retrieve user by ID. + + Raises: + UserNotFoundError: If user doesn't exist + ValidationError: If user_id is invalid + """ + if not isinstance(user_id, int) or user_id <= 0: + raise ValidationError(f"Invalid user_id: {user_id}") + + user = db.query(User).get(user_id) + if user is None: + raise UserNotFoundError(f"User {user_id} not found") + + return user +``` + +**Never** use bare `except:` - always catch specific exceptions. + +## Database Queries + +Always use the project's query helper: + +```python +# ❌ Don't use raw SQLAlchemy queries +users = db.query(User).filter(User.age > 18).all() + +# ✅ Do use query helper +from core.database import QueryBuilder + +users = QueryBuilder(User).where('age', '>', 18).get() +``` + +## API Response Format + +All API endpoints must return this format: + +```python +{ + "success": True, + "data": { + # ... response data + }, + "error": None, + "meta": { + "timestamp": "2025-01-31T12:00:00Z", + "version": "1.0" + } +} +``` + +Use the response helper: + +```python +from core.responses import success_response, error_response + +@app.get("/users/{id}") +async def get_user(id: int): + try: + user = get_user_data(id) + return success_response(user) + except UserNotFoundError as e: + return error_response(str(e), status_code=404) +``` + +## Testing Patterns + +### Test File Location + +``` +project/ +├── src/ +│ └── services/ +│ └── user_service.py +└── tests/ + └── services/ + └── test_user_service.py +``` + +### Test Naming + +```python +# Format: test_{function_name}_{scenario}_{expected_result} + +def test_get_user_valid_id_returns_user(): + """Test getting user with valid ID returns User object.""" + pass + +def test_get_user_invalid_id_raises_validation_error(): + """Test getting user with invalid ID raises ValidationError.""" + pass + +def test_get_user_nonexistent_id_raises_not_found_error(): + """Test getting non-existent user raises UserNotFoundError.""" + pass +``` + +## References + +- [Full Style Guide](docs/STYLE_GUIDE.md) +- [API Standards](docs/API_STANDARDS.md) +- [Database Conventions](docs/DATABASE.md) +``` + +### 改进要点 + +1. ✅ 只包含项目特定信息: 不教 Python 基础 +2. ✅ 简洁明了: 200 行 vs 原来的 500+ 行 +3. ✅ 实用的规则: 直接可应用的约定 +4. ✅ 清晰的示例: Do/Don't 对比 +5. ✅ 引用详细文档: 用链接而非全部内容 + +--- + +## 示例 3: 缺少上下文的 Skill + +### ❌ 不好的版本 + +```markdown +--- +name: deployment +description: Deploys code +--- + +# Deployment + +## Steps + +1. Build the code +2. Run tests +3. Deploy to server +4. Verify deployment +``` + +### 问题分析 + +| 问题 | 说明 | 影响 | +|------|------|------| +| **无具体命令** | 没说明如何 build, test, deploy | Claude 无法执行 | +| **无环境区分** | 开发、测试、生产部署可能不同 | 可能部署到错误环境 | +| **无错误处理** | 没说明出错时怎么办 | 失败时不知如何恢复 | +| **无验证标准** | "Verify" 太模糊 | 不知道检查什么 | + +### ✅ 改进版本 + +```markdown +--- +name: deploy-to-production +description: Deploys application to production environment on AWS. Activates when user asks to deploy to prod or mentions production deployment. Includes pre-flight checks, blue-green deployment, and rollback procedures. +--- + +# Production Deployment + +Safely deploy application to production with zero downtime. + +## When to Use This Skill + +- User asks to "deploy to production" or "push to prod" +- User mentions "production deployment", "go live" +- User needs to rollback a deployment + +## Prerequisites + +Before deployment, verify: + +```bash +# 1. On main branch +git branch --show-current # Must be "main" + +# 2. All tests pass +npm test # Exit code must be 0 + +# 3. Build succeeds +npm run build # Must complete without errors + +# 4. No uncommitted changes +git status # Must show "nothing to commit" + +# 5. Latest code pulled +git pull origin main # Must be up to date +``` + +If any prerequisite fails, **stop** and fix the issue. + +## Deployment Process + +### Step 1: Pre-flight Checks + +```bash +# Run deployment readiness script +./scripts/preflight-check.sh + +# Expected output: +# ✓ Tests passed +# ✓ Build succeeded +# ✓ Environment variables configured +# ✓ Database migrations ready +# ✓ Ready to deploy +``` + +### Step 2: Database Migrations (if needed) + +```bash +# Connect to production database +aws rds describe-db-instances --db-instance-identifier prod-db + +# Backup before migration +./scripts/backup-database.sh prod + +# Run migrations +NODE_ENV=production npm run migrate + +# Verify migration succeeded +npm run migrate:status +``` + +### Step 3: Blue-Green Deployment + +```bash +# Deploy to green environment (inactive) +aws elasticbeanstalk create-environment \ + --application-name myapp \ + --environment-name myapp-prod-green \ + --solution-stack-name "64bit Amazon Linux 2 v5.x.x running Node.js 18" + +# Wait for green environment to be healthy +aws elasticbeanstalk wait environment-updated \ + --environment-name myapp-prod-green + +# Check green environment health +curl https://myapp-prod-green.aws.com/health +# Expected: {"status": "healthy"} +``` + +### Step 4: Smoke Tests + +```bash +# Run smoke tests against green environment +BASE_URL=https://myapp-prod-green.aws.com npm run test:smoke + +# Tests must include: +# - Health check endpoint +# - Authentication flow +# - Critical API endpoints +# - Database connectivity +``` + +### Step 5: Switch Traffic + +```bash +# Swap URLs (blue becomes green, green becomes blue) +aws elasticbeanstalk swap-environment-cnames \ + --source-environment-name myapp-prod-blue \ + --destination-environment-name myapp-prod-green + +# Wait 5 minutes for DNS propagation +echo "Waiting for DNS propagation..." +sleep 300 + +# Verify production URL serves new version +curl https://myapp.com/version +# Expected: {"version": "1.2.3"} (new version) +``` + +### Step 6: Monitor + +```bash +# Monitor error rates for 15 minutes +aws cloudwatch get-metric-statistics \ + --namespace AWS/ELB \ + --metric-name HTTPCode_Backend_5XX \ + --start-time $(date -u -d '15 minutes ago' +%Y-%m-%dT%H:%M:%S) \ + --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ + --period 300 \ + --statistics Sum + +# Error rate must be < 1% +``` + +If error rate exceeds 1%: +- **Rollback immediately** (see Rollback section) +- Investigate issue +- Fix and redeploy + +### Step 7: Cleanup + +```bash +# After 24 hours, if no issues: +# Terminate old blue environment +aws elasticbeanstalk terminate-environment \ + --environment-name myapp-prod-blue +``` + +## Rollback Procedure + +If deployment fails: + +```bash +# 1. Swap back to previous version +aws elasticbeanstalk swap-environment-cnames \ + --source-environment-name myapp-prod-green \ + --destination-environment-name myapp-prod-blue + +# 2. Verify old version is serving +curl https://myapp.com/version +# Expected: {"version": "1.2.2"} (old version) + +# 3. Rollback database migrations (if ran) +NODE_ENV=production npm run migrate:rollback + +# 4. Notify team +./scripts/notify-rollback.sh "Deployment rolled back due to [reason]" +``` + +## Example Deployment + +**User Request**: "Deploy v1.2.3 to production" + +**Execution Log**: + +``` +[14:00:00] Starting deployment of v1.2.3 to production +[14:00:05] ✓ Pre-flight checks passed +[14:00:10] ✓ Database backup completed +[14:00:30] ✓ Database migrations applied (3 migrations) +[14:01:00] → Creating green environment +[14:05:00] ✓ Green environment healthy +[14:05:30] ✓ Smoke tests passed (12/12) +[14:06:00] → Switching traffic to green environment +[14:11:00] ✓ DNS propagated +[14:11:05] ✓ Production serving v1.2.3 +[14:11:10] → Monitoring for 15 minutes +[14:26:10] ✓ Error rate: 0.05% (within threshold) +[14:26:15] ✓ Deployment successful +[14:26:20] → Old environment will be terminated in 24h + +Deployment completed successfully in 26 minutes +``` + +## References + +- [AWS Deployment Guide](docs/AWS_DEPLOYMENT.md) +- [Runbook](docs/RUNBOOK.md) +- [On-Call Procedures](docs/ONCALL.md) +``` + +### 改进要点 + +1. ✅ 具体命令: 每个步骤都有可执行的命令 +2. ✅ 环境明确: 专注于生产环境部署 +3. ✅ 验证标准: 说明检查什么和预期结果 +4. ✅ 错误处理: 包含完整的回滚流程 +5. ✅ 实际输出: 展示命令的预期输出 +6. ✅ 监控指标: 定义具体的成功标准 + +--- + +## 常见错误总结 + +### 1. 命名和描述问题 + +| 错误 | 示例 | 改进 | +|------|------|------| +| 过于泛化 | `name: helper` | `name: python-type-hints` | +| 缺少关键词 | `description: Helps with code` | `description: Adds type hints to Python using mypy` | +| 使用第一人称 | `description: I help you...` | `description: Adds type hints...` | + +### 2. 内容问题 + +| 错误 | 说明 | 改进 | +|------|------|------| +| 包含通用知识 | 教 Python 基础语法 | 只包含项目特定规范 | +| 过于抽象 | "分析代码并提供建议" | "检查函数长度、变量命名、重复代码" | +| 缺少示例 | 只有文字描述 | 包含输入-输出示例 | + +### 3. 结构问题 + +| 错误 | 说明 | 改进 | +|------|------|------| +| 无层次结构 | 所有内容混在一起 | 使用标题、列表、代码块组织 | +| 缺少"When to Use" | 不知道何时激活 | 列出 3-5 个触发场景 | +| 无验证步骤 | 不知道如何确认成功 | 说明检查项和预期结果 | + +### 4. 自由度问题 + +| 错误 | 说明 | 改进 | +|------|------|------| +| 创意任务低自由度 | 为架构设计提供分步指令 | 提供指导原则和考虑因素 | +| 危险任务高自由度 | 生产部署没有具体步骤 | 提供详细的检查清单 | +| 不匹配任务类型 | 代码生成用教程式内容 | 提供模板和实际示例 | + +--- + +## 快速检查清单 + +在发布 Skill 之前,问自己: + +### 基础检查 + +- [ ] Name 是否具体且描述性强? +- [ ] Description 包含触发关键词和场景? +- [ ] 有明确的"When to Use"章节? +- [ ] 内容只包含 Claude 不知道的信息? + +### 内容检查 + +- [ ] 是否有实际的代码示例? +- [ ] 步骤是否具体可执行? +- [ ] 是否说明了如何验证成功? +- [ ] 是否包含错误处理指导? + +### 结构检查 + +- [ ] 内容组织清晰(使用标题、列表)? +- [ ] 自由度设定合适(匹配任务类型)? +- [ ] 长度合适(200-500行,或拆分子文件)? +- [ ] 包含 Do/Don't 最佳实践? + +如果有任何一项答"否",参考本文档的改进建议进行修改。 \ No newline at end of file diff --git a/.claude/skills/create-skill-file/examples/good-example.md b/.claude/skills/create-skill-file/examples/good-example.md new file mode 100644 index 000000000..e3016a1f2 --- /dev/null +++ b/.claude/skills/create-skill-file/examples/good-example.md @@ -0,0 +1,908 @@ +# 好的 Skill 示例 + +本文档展示几个编写良好的 SKILL.md 示例,说明最佳实践的实际应用。 + +--- + +## 示例 1: 数据库迁移 Skill (高质量基础 Skill) + +```markdown +--- +name: database-migration +description: Manages database schema migrations using Alembic for SQLAlchemy projects. Activates when user asks to create migrations, upgrade/downgrade database, or mentions Alembic. Handles both development and production scenarios with safety checks. +--- + +# Database Migration Skill + +Automates database schema migration management using Alembic. + +## When to Use This Skill + +- User asks to "create migration", "update database schema", or "rollback migration" +- User mentions "Alembic", "database migration", or "schema change" +- User is working in a Python project with SQLAlchemy models +- User needs to apply or revert database changes + +## Quick Start + +Create a new migration: +```bash +alembic revision --autogenerate -m "Description of changes" +``` + +Apply migrations: +```bash +alembic upgrade head +``` + +## How It Works + +### Creating Migrations + +1. **Detect model changes** + - Scan SQLAlchemy models in `models/` directory + - Compare with current database schema + - Identify additions, modifications, deletions + +2. **Generate migration script** + - Run `alembic revision --autogenerate` + - Review generated script for accuracy + - Edit if necessary (Alembic can't auto-detect everything) + +3. **Verify migration** + - Check upgrade() function is correct + - Ensure downgrade() function reverses changes + - Test on development database first + +### Applying Migrations + +1. **Safety checks** + - Backup database (production only) + - Verify no pending migrations + - Check database connectivity + +2. **Execute migration** + - Run `alembic upgrade head` + - Monitor for errors + - Verify schema matches expected state + +3. **Post-migration validation** + - Run application tests + - Check data integrity + - Confirm application starts successfully + +## Examples + +### Example 1: Add New Column + +**User Request**: "Add an email column to the users table" + +**Step 1**: Update the model +```python +# models/user.py +class User(Base): + __tablename__ = 'users' + id = Column(Integer, primary_key=True) + username = Column(String(50), nullable=False) + email = Column(String(120), nullable=True) # ← New field +``` + +**Step 2**: Generate migration +```bash +alembic revision --autogenerate -m "Add email column to users table" +``` + +**Generated migration** (alembic/versions/abc123_add_email.py): +```python +def upgrade(): + op.add_column('users', sa.Column('email', sa.String(120), nullable=True)) + +def downgrade(): + op.drop_column('users', 'email') +``` + +**Step 3**: Review and apply +```bash +# Review the migration file +cat alembic/versions/abc123_add_email.py + +# Apply migration +alembic upgrade head +``` + +**Output**: +``` +INFO [alembic.runtime.migration] Running upgrade xyz789 -> abc123, Add email column to users table +``` + +### Example 2: Complex Migration with Data Changes + +**User Request**: "Split the 'name' column into 'first_name' and 'last_name'" + +**Step 1**: Create empty migration (can't auto-generate data changes) +```bash +alembic revision -m "Split name into first_name and last_name" +``` + +**Step 2**: Write custom migration +```python +def upgrade(): + # Add new columns + op.add_column('users', sa.Column('first_name', sa.String(50))) + op.add_column('users', sa.Column('last_name', sa.String(50))) + + # Migrate existing data + connection = op.get_bind() + users = connection.execute("SELECT id, name FROM users") + for user_id, name in users: + parts = name.split(' ', 1) + first = parts[0] + last = parts[1] if len(parts) > 1 else '' + connection.execute( + "UPDATE users SET first_name = %s, last_name = %s WHERE id = %s", + (first, last, user_id) + ) + + # Make new columns non-nullable and drop old column + op.alter_column('users', 'first_name', nullable=False) + op.alter_column('users', 'last_name', nullable=False) + op.drop_column('users', 'name') + +def downgrade(): + # Add back name column + op.add_column('users', sa.Column('name', sa.String(100))) + + # Restore data + connection = op.get_bind() + users = connection.execute("SELECT id, first_name, last_name FROM users") + for user_id, first, last in users: + full_name = f"{first} {last}".strip() + connection.execute( + "UPDATE users SET name = %s WHERE id = %s", + (full_name, user_id) + ) + + op.alter_column('users', 'name', nullable=False) + op.drop_column('users', 'first_name') + op.drop_column('users', 'last_name') +``` + +**Step 3**: Test thoroughly +```bash +# Apply migration +alembic upgrade head + +# Verify data +python -c "from models import User; print(User.query.first().first_name)" + +# Test rollback +alembic downgrade -1 +python -c "from models import User; print(User.query.first().name)" + +# Reapply +alembic upgrade head +``` + +## Best Practices + +### Do + +- ✅ Always review auto-generated migrations before applying +- ✅ Test migrations on development database first +- ✅ Write reversible downgrade() functions +- ✅ Backup production databases before major migrations +- ✅ Use meaningful migration messages + +### Don't + +- ❌ Trust auto-generated migrations blindly +- ❌ Skip downgrade() implementation +- ❌ Apply untested migrations to production +- ❌ Modify existing migration files after they're committed +- ❌ Use raw SQL without bind parameters + +## Troubleshooting + +### "Target database is not up to date" + +**Problem**: Someone else applied migrations you don't have locally + +**Solution**: +```bash +git pull # Get latest migrations +alembic upgrade head # Apply them locally +``` + +### "Can't locate revision identified by 'xyz'" + +**Problem**: Migration file deleted or branch conflict + +**Solution**: +1. Check if migration file exists in `alembic/versions/` +2. If missing, restore from git history +3. If branch conflict, merge migration branches: + ```bash + alembic merge -m "Merge migration branches" head1 head2 + ``` + +### Migration fails mid-execution + +**Problem**: Error occurred during migration + +**Solution**: +1. Check error message for specifics +2. Manually fix database to consistent state if needed +3. Update migration script to fix the issue +4. Mark migration as completed or retry: + ```bash + # Mark as done without running + alembic stamp head + + # Or fix and retry + alembic upgrade head + ``` + +## Configuration + +### Project Structure +``` +project/ +├── alembic/ +│ ├── versions/ # Migration scripts +│ ├── env.py # Alembic environment +│ └── script.py.mako # Migration template +├── alembic.ini # Alembic configuration +└── models/ # SQLAlchemy models + ├── __init__.py + ├── user.py + └── post.py +``` + +### alembic.ini Configuration +```ini +[alembic] +script_location = alembic +sqlalchemy.url = driver://user:pass@localhost/dbname + +[loggers] +keys = root,sqlalchemy,alembic + +[logger_alembic] +level = INFO +handlers = console +qualname = alembic +``` + +## References + +- [Alembic Documentation](https://alembic.sqlalchemy.org/) +- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/) +- [Project Migration Guidelines](docs/database-migrations.md) +``` + +### 为什么这是好的 Skill? + +1. ✅ **清晰的 description**: 包含触发关键词 ("Alembic", "create migrations") 和场景 ("SQLAlchemy projects") +2. ✅ **具体的触发条件**: "When to Use" 列出 4 个明确场景 +3. ✅ **分步工作流**: 每个操作都有清晰的 1-2-3 步骤 +4. ✅ **实际示例**: 包含简单和复杂两个示例,有完整代码 +5. ✅ **最佳实践**: Do/Don't 清单易于遵循 +6. ✅ **故障排除**: 覆盖 3 个常见问题及解决方案 +7. ✅ **项目特定信息**: 包含配置和目录结构 + +--- + +## 示例 2: API 文档生成 Skill (优秀的工作流 Skill) + +```markdown +--- +name: api-documentation-generation +description: Generates OpenAPI/Swagger documentation from FastAPI or Flask applications. Activates when user asks to create API docs, generate OpenAPI spec, or needs to document REST endpoints. Supports automatic extraction and custom annotations. +--- + +# API Documentation Generation Skill + +Automates creation of comprehensive API documentation from Python web applications. + +## When to Use This Skill + +- User asks to "generate API docs" or "create OpenAPI spec" +- User mentions "Swagger", "OpenAPI", "API documentation" +- User has a FastAPI or Flask application +- User needs to document REST API endpoints + +## Workflow + +### Phase 1: Discovery + +1. **Identify framework** + - Check for FastAPI: `from fastapi import FastAPI` in codebase + - Check for Flask: `from flask import Flask` in codebase + - Check for Flask-RESTful: `from flask_restful import Resource` + +2. **Locate API definitions** + - Scan for route decorators: `@app.get()`, `@app.post()`, `@app.route()` + - Find API routers and blueprints + - Identify request/response models + +3. **Extract metadata** + - Endpoint paths and HTTP methods + - Request parameters (path, query, body) + - Response schemas and status codes + - Authentication requirements + +### Phase 2: Enhancement + +1. **Review docstrings** + - Check if endpoints have docstrings + - Verify docstrings follow format (summary, description, params, returns) + - Flag missing documentation + +2. **Add missing docs** (if user approves) + - Generate docstrings based on type hints + - Infer descriptions from parameter names + - Add example requests/responses + +3. **Validate schemas** + - Ensure Pydantic models are well-documented + - Check for missing field descriptions + - Verify example values are provided + +### Phase 3: Generation + +1. **Generate OpenAPI spec** + ```bash + # For FastAPI + python -c "from main import app; import json; print(json.dumps(app.openapi()))" > openapi.json + + # For Flask with flasgger + python scripts/generate_swagger.py > swagger.json + ``` + +2. **Create Swagger UI** + - Copy Swagger UI static files to `docs/api/` + - Configure to load generated spec + - Test in browser: `http://localhost:8000/docs` + +3. **Generate Markdown docs** + - Use `openapi-to-md` to create human-readable docs + - Organize by tags/resource groups + - Add navigation and table of contents + +### Phase 4: Validation + +1. **Check completeness** + - All endpoints documented? + - All parameters described? + - Example requests provided? + +2. **Validate spec** + ```bash + openapi-spec-validator openapi.json + ``` + +3. **Test interactive docs** + - Try sample requests in Swagger UI + - Verify authentication flows work + - Check response schemas match actual responses + +## Examples + +### Example 1: FastAPI Application + +**User Request**: "Generate API documentation for my FastAPI app" + +**Discovery**: +``` +Found FastAPI application in: main.py +Endpoints discovered: +- GET /users (List users) +- POST /users (Create user) +- GET /users/{id} (Get user by ID) +- PUT /users/{id} (Update user) +- DELETE /users/{id} (Delete user) +``` + +**Enhancement**: +``` +Checking documentation coverage... +✓ GET /users - Well documented +✗ POST /users - Missing response examples +✗ GET /users/{id} - No description +✗ PUT /users/{id} - No description +✗ DELETE /users/{id} - No description + +Would you like me to add missing documentation? +``` + +**User**: Yes + +**Adding docs**: +```python +@app.get("/users/{id}", summary="Get user by ID") +async def get_user(id: int): + """ + Retrieve a single user by their unique ID. + + Args: + id: The unique identifier of the user + + Returns: + User object with id, name, email, and created_at fields + + Raises: + 404: User not found + """ + # ... implementation +``` + +**Generation**: +```bash +Generating OpenAPI specification... +✓ openapi.json created (15.2 KB) +✓ Swagger UI deployed to docs/api/ +✓ Markdown docs created in docs/api/endpoints/ + +View documentation at: http://localhost:8000/docs +``` + +### Example 2: Flask Application with Custom Schemas + +**User Request**: "Document my Flask API and include request/response examples" + +**Discovery**: +``` +Found Flask application with Flask-RESTful +Endpoints: +- GET /api/products +- POST /api/products +- GET /api/products/ +- PATCH /api/products/ +- DELETE /api/products/ +``` + +**Custom Examples Added**: +```python +class ProductList(Resource): + def get(self): + """ + Get all products + + Example Response: + ```json + { + "products": [ + { + "id": 1, + "name": "Widget", + "price": 29.99, + "stock": 100 + } + ], + "total": 1 + } + ``` + """ + pass + + def post(self): + """ + Create a new product + + Example Request: + ```json + { + "name": "New Widget", + "price": 39.99, + "stock": 50 + } + ``` + + Example Response: + ```json + { + "id": 2, + "name": "New Widget", + "price": 39.99, + "stock": 50, + "created_at": "2025-01-31T12:00:00Z" + } + ``` + """ + pass +``` + +**Result**: +``` +Generated documentation: +- openapi.json (with examples) +- Swagger UI at /api/docs +- Postman collection at docs/api/postman_collection.json +- Markdown API reference at docs/api/README.md + +All endpoints now include: +✓ Request examples +✓ Response examples +✓ Error codes +✓ Authentication requirements +``` + +## Configuration + +### FastAPI Projects + +No additional configuration needed! FastAPI auto-generates OpenAPI docs. + +Access at: +- Swagger UI: `http://localhost:8000/docs` +- ReDoc: `http://localhost:8000/redoc` +- OpenAPI JSON: `http://localhost:8000/openapi.json` + +### Flask Projects + +Install flasgger: +```bash +pip install flasgger +``` + +Configure in app: +```python +from flasgger import Swagger + +app = Flask(__name__) +swagger = Swagger(app, template={ + "info": { + "title": "My API", + "description": "API for managing resources", + "version": "1.0.0" + } +}) +``` + +## Best Practices + +- ✅ Use type hints - enables automatic schema generation +- ✅ Write descriptive docstrings for all endpoints +- ✅ Provide example requests and responses +- ✅ Document error codes and edge cases +- ✅ Keep docs in sync with code (auto-generate when possible) + +## Tools Used + +- **FastAPI**: Built-in OpenAPI support +- **flasgger**: Swagger for Flask +- **openapi-spec-validator**: Validates OpenAPI specs +- **openapi-to-md**: Converts OpenAPI to Markdown + +## References + +- [OpenAPI Specification](https://spec.openapis.org/oas/latest.html) +- [FastAPI Documentation](https://fastapi.tiangolo.com/) +- [Swagger Documentation](https://swagger.io/docs/) +``` + +### 为什么这是优秀的工作流 Skill? + +1. ✅ **清晰的工作流阶段**: 4 个阶段 (Discovery, Enhancement, Generation, Validation) +2. ✅ **决策点**: Phase 2 询问用户是否添加缺失文档 +3. ✅ **实际输出示例**: 展示了命令输出和生成的代码 +4. ✅ **多框架支持**: 处理 FastAPI 和 Flask 两种情况 +5. ✅ **工具集成**: 列出所需工具及其用途 +6. ✅ **可执行命令**: 提供完整的命令示例 +7. ✅ **验证步骤**: Phase 4 确保生成的文档质量 + +--- + +## 示例 3: 代码审查 Skill (高自由度 Skill) + +```markdown +--- +name: code-review +description: Performs comprehensive code reviews focusing on best practices, security, performance, and maintainability. Activates when user asks to review code, check pull request, or mentions code quality. Provides actionable feedback with severity ratings. +--- + +# Code Review Skill + +Conducts thorough code reviews with focus on quality, security, and best practices. + +## When to Use This Skill + +- User asks to "review my code" or "check this PR" +- User mentions "code review", "code quality", or "best practices" +- User wants feedback on specific code changes +- User needs security or performance analysis + +## Review Criteria + +Code is evaluated across 5 dimensions: + +### 1. Correctness +- Logic errors and bugs +- Edge case handling +- Error handling and validation +- Type safety + +### 2. Security +- SQL injection vulnerabilities +- XSS vulnerabilities +- Authentication/authorization issues +- Sensitive data exposure +- Dependency vulnerabilities + +### 3. Performance +- Algorithm efficiency +- Database query optimization +- Memory leaks +- Unnecessary computations +- Caching opportunities + +### 4. Maintainability +- Code clarity and readability +- Function/class size +- Code duplication +- Naming conventions +- Documentation + +### 5. Best Practices +- Language-specific idioms +- Design patterns +- SOLID principles +- Testing coverage +- Error handling patterns + +## Review Process + +1. **Understand context** + - What does this code do? + - What problem does it solve? + - Are there any constraints or requirements? + +2. **Identify issues** + - Scan for common anti-patterns + - Check against language best practices + - Look for security vulnerabilities + - Assess performance implications + +3. **Prioritize feedback** + - **Critical**: Security issues, data loss risks, crashes + - **High**: Bugs, major performance issues + - **Medium**: Code smells, maintainability concerns + - **Low**: Style preferences, minor optimizations + +4. **Provide suggestions** + - Explain the issue clearly + - Show better alternative (code example) + - Explain why the alternative is better + +## Example Review + +### Code Submitted + +```python +def get_user_data(user_id): + conn = sqlite3.connect('users.db') + cursor = conn.cursor() + query = "SELECT * FROM users WHERE id = " + str(user_id) + cursor.execute(query) + result = cursor.fetchone() + return result +``` + +### Review Feedback + +**❌ CRITICAL: SQL Injection Vulnerability** + +The code concatenates user input directly into SQL query, allowing SQL injection attacks. + +**Bad**: +```python +query = "SELECT * FROM users WHERE id = " + str(user_id) +``` + +**Good**: +```python +query = "SELECT * FROM users WHERE id = ?" +cursor.execute(query, (user_id,)) +``` + +**Why**: Parameterized queries prevent SQL injection by properly escaping user input. + +--- + +**⚠️ HIGH: Resource Leak** + +Database connection is never closed, leading to resource exhaustion under load. + +**Bad**: +```python +conn = sqlite3.connect('users.db') +# ... use connection +# connection never closed +``` + +**Good**: +```python +with sqlite3.connect('users.db') as conn: + cursor = conn.cursor() + # ... use connection + # automatically closed when exiting with block +``` + +--- + +**ℹ️ MEDIUM: Overly Broad SELECT** + +Using `SELECT *` fetches unnecessary data and is fragile to schema changes. + +**Bad**: +```python +query = "SELECT * FROM users WHERE id = ?" +``` + +**Good**: +```python +query = "SELECT id, name, email FROM users WHERE id = ?" +``` + +**Why**: Explicit column selection is more efficient and makes intent clear. + +--- + +**📝 LOW: Missing Error Handling** + +Function doesn't handle case where user doesn't exist. + +**Suggestion**: +```python +def get_user_data(user_id: int) -> Optional[dict]: + """ + Retrieve user data by ID. + + Args: + user_id: The unique identifier of the user + + Returns: + Dictionary with user data, or None if user not found + """ + with sqlite3.connect('users.db') as conn: + cursor = conn.cursor() + query = "SELECT id, name, email FROM users WHERE id = ?" + cursor.execute(query, (user_id,)) + result = cursor.fetchone() + + if result is None: + return None + + return { + 'id': result[0], + 'name': result[1], + 'email': result[2] + } +``` + +### Revised Code + +After applying all suggestions: + +```python +from typing import Optional +import sqlite3 + +def get_user_data(user_id: int) -> Optional[dict]: + """ + Retrieve user data by ID. + + Args: + user_id: The unique identifier of the user + + Returns: + Dictionary with user data, or None if user not found + + Raises: + ValueError: If user_id is not a positive integer + """ + if not isinstance(user_id, int) or user_id <= 0: + raise ValueError(f"Invalid user_id: {user_id}") + + with sqlite3.connect('users.db') as conn: + conn.row_factory = sqlite3.Row # Enable dict-like access + cursor = conn.cursor() + + query = "SELECT id, name, email, created_at FROM users WHERE id = ?" + cursor.execute(query, (user_id,)) + result = cursor.fetchone() + + if result is None: + return None + + return dict(result) # Convert Row to dict +``` + +### Summary + +**Issues Found**: 4 +- 1 Critical (SQL Injection) +- 1 High (Resource Leak) +- 1 Medium (Inefficient Query) +- 1 Low (Missing Error Handling) + +**All Issues Addressed**: ✓ + +## Best Practices + +### When Reviewing + +- 🎯 Focus on impact - prioritize critical issues +- 📝 Be specific - provide code examples +- 🎓 Be educational - explain why, not just what +- 🤝 Be constructive - suggest improvements, don't just criticize +- ⚖️ Be balanced - acknowledge good practices too + +### What to Look For + +**Python-specific**: +- Use of `with` for resource management +- Type hints on function signatures +- Proper exception handling +- List comprehensions vs loops +- Dictionary vs if-elif chains + +**General**: +- DRY principle violations +- Magic numbers +- Long functions (>50 lines) +- Deep nesting (>3 levels) +- Missing tests for critical paths + +## Automated Tools + +Complement manual review with automated tools: + +```bash +# Linting +pylint mycode.py +flake8 mycode.py + +# Type checking +mypy mycode.py + +# Security scanning +bandit -r . +safety check + +# Code complexity +radon cc mycode.py -a +``` + +## References + +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) +- [Python Best Practices](https://docs.python-guide.org/) +- [Clean Code Principles](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) +``` + +### 为什么这是高自由度 Skill? + +1. ✅ **指导原则而非严格步骤**: 提供评审维度,不限定具体流程 +2. ✅ **情境适应**: 根据代码类型和问题严重性调整重点 +3. ✅ **教育性**: 解释"为什么",帮助 Claude 做出判断 +4. ✅ **优先级框架**: 定义严重性级别,让 Claude 自行判断 +5. ✅ **完整示例**: 展示从问题识别到解决的完整流程 +6. ✅ **工具集成**: 提到自动化工具,但不强制使用 + +--- + +## 总结: 好 Skill 的共同特征 + +| 特征 | 说明 | 示例位置 | +|------|------|---------| +| **清晰触发** | description 包含关键词和场景 | 所有 frontmatter | +| **结构化内容** | 使用标题、列表、代码块组织信息 | 所有示例 | +| **实际示例** | 真实代码,不是伪代码 | Example sections | +| **决策指导** | 告诉 Claude 何时做什么 | 工作流 Skill 的 Phase 2 | +| **可执行命令** | 提供完整的命令,不是抽象描述 | 迁移 Skill 的命令 | +| **错误处理** | 包含故障排除章节 | 所有 Troubleshooting | +| **最佳实践** | Do/Don't 清单 | 所有 Best Practices | +| **工具引用** | 说明使用哪些工具及如何使用 | API 文档 Skill | +| **验证步骤** | 说明如何确认操作成功 | 迁移 Skill 的验证 | +| **合适的自由度** | 根据任务特性选择指导程度 | 代码审查 Skill | diff --git a/.claude/skills/create-skill-file/templates/basic-skill-template.md b/.claude/skills/create-skill-file/templates/basic-skill-template.md new file mode 100644 index 000000000..8ff71762d --- /dev/null +++ b/.claude/skills/create-skill-file/templates/basic-skill-template.md @@ -0,0 +1,95 @@ +--- +name: your-skill-name +description: Brief description of what this skill does and when to activate it. Include trigger keywords and scenarios where this skill should be used. +--- + +# Your Skill Title + +> Brief one-line summary of what this skill accomplishes + +## When to Use This Skill + +- User asks to [specific action or task] +- User mentions keywords like "[keyword1]", "[keyword2]", or "[keyword3]" +- User is working with [specific technology/framework/tool] +- User needs to [specific outcome or goal] + +## Quick Start + +```bash +# Basic usage example +command-to-run --option value +``` + +## How It Works + +1. **Step 1**: Brief description of first step + - Detail about what happens + - Any prerequisites or conditions + +2. **Step 2**: Brief description of second step + - Key actions taken + - Expected outputs + +3. **Step 3**: Brief description of final step + - Validation or verification + - Success criteria + +## Examples + +### Example 1: Basic Usage + +**User Request**: "Example of what user might say" + +**Action**: What Claude does in response + +**Output**: +``` +Expected output or result +``` + +### Example 2: Advanced Usage + +**User Request**: "More complex user request" + +**Action**: +1. First action taken +2. Second action taken +3. Final action + +**Output**: +``` +Expected output showing more complex results +``` + +## Best Practices + +- ✅ Do this for best results +- ✅ Follow this pattern +- ❌ Avoid this common mistake +- ❌ Don't do this + +## Troubleshooting + +### Common Issue 1 + +**Problem**: Description of the problem + +**Solution**: How to fix it + +### Common Issue 2 + +**Problem**: Description of another problem + +**Solution**: Steps to resolve + +## References + +- [Related Documentation](link-to-docs) +- [Official Guide](link-to-guide) +- [Additional Resources](link-to-resources) + +--- + +**Version**: 1.0 +**Last Updated**: YYYY-MM-DD \ No newline at end of file diff --git a/.claude/skills/create-skill-file/templates/workflow-skill-template.md b/.claude/skills/create-skill-file/templates/workflow-skill-template.md new file mode 100644 index 000000000..6fd6522ba --- /dev/null +++ b/.claude/skills/create-skill-file/templates/workflow-skill-template.md @@ -0,0 +1,402 @@ +--- +name: your-workflow-skill +description: Guides Claude through a multi-step workflow for [specific task]. Activates when user needs to [trigger scenario] or mentions [key terms]. +--- + +# Your Workflow Skill Title + +> Automates a complex multi-step process with decision points and validation + +## When to Use This Skill + +- User needs to execute a multi-step workflow +- User asks to "[workflow trigger phrase]" +- User is working on [specific type of project or task] +- Task requires validation and error handling at each step + +## Workflow Overview + +``` +┌─────────────┐ +│ Start │ +└──────┬──────┘ + │ + ▼ +┌─────────────────┐ +│ Preparation │ +│ & Validation │ +└────────┬────────┘ + │ + ┌────▼────┐ + │ Step 1 │ + └────┬────┘ + │ + ┌────▼────┐ + │ Step 2 │──┐ + └────┬────┘ │ (Loop if needed) + │ │ + └───────┘ + │ + ┌────▼────┐ + │ Step 3 │ + └────┬────┘ + │ + ▼ + ┌─────────────┐ + │ Complete │ + │ & Report │ + └─────────────┘ +``` + +## Detailed Workflow + +### Preparation Phase + +Before starting the main workflow: + +- [ ] Check prerequisite 1 +- [ ] Validate prerequisite 2 +- [ ] Ensure prerequisite 3 is met + +If any prerequisite fails: +- Stop execution +- Report which prerequisite failed +- Provide remediation steps + +### Step 1: [Step Name] + +**Purpose**: What this step accomplishes + +**Actions**: +1. Action 1 +2. Action 2 +3. Action 3 + +**Validation**: +- Check condition 1 +- Verify condition 2 + +**On Success**: → Proceed to Step 2 +**On Failure**: → [Error handling procedure] + +### Step 2: [Step Name] + +**Purpose**: What this step accomplishes + +**Actions**: +1. Action 1 +2. Action 2 + +**Decision Point**: +- If condition A: → Action X +- If condition B: → Action Y +- Otherwise: → Default action + +**Validation**: +- Verify expected output +- Check for errors + +**On Success**: → Proceed to Step 3 +**On Failure**: → [Error handling procedure] + +### Step 3: [Step Name] + +**Purpose**: Final actions and cleanup + +**Actions**: +1. Finalize changes +2. Run validation tests +3. Generate summary report + +**Success Criteria**: +- All tests pass +- No errors in logs +- Expected artifacts created + +## Examples + +### Example 1: Standard Workflow Execution + +**User Request**: "Run the [workflow name]" + +**Execution**: + +**Preparation Phase** ✓ +``` +✓ Prerequisite 1 met +✓ Prerequisite 2 validated +✓ Ready to begin +``` + +**Step 1: [Step Name]** ✓ +``` +→ Action 1 completed +→ Action 2 completed +→ Validation passed +``` + +**Step 2: [Step Name]** ✓ +``` +→ Decision: Condition A detected +→ Executing Action X +→ Validation passed +``` + +**Step 3: [Step Name]** ✓ +``` +→ Finalization complete +→ All tests passed +→ Summary generated +``` + +**Result**: Workflow completed successfully + +### Example 2: Workflow with Error Recovery + +**User Request**: "Execute [workflow name]" + +**Execution**: + +**Step 1** ✓ +``` +→ Completed successfully +``` + +**Step 2** ⚠️ +``` +→ Action 1 completed +→ Action 2 failed: [Error message] +``` + +**Error Recovery**: +1. Identified root cause: [Explanation] +2. Applied fix: [Fix description] +3. Retrying Step 2... + +**Step 2 (Retry)** ✓ +``` +→ Completed after fix +``` + +**Step 3** ✓ +``` +→ Completed successfully +``` + +**Result**: Workflow completed with 1 retry + +## Error Handling + +### Error Categories + +| Category | Action | +|----------|--------| +| **Recoverable** | Attempt automatic fix, retry up to 3 times | +| **User Input Needed** | Pause workflow, ask user for guidance | +| **Critical** | Stop workflow, rollback changes if possible | + +### Common Errors + +**Error 1: [Error Name]** +- **Cause**: What causes this error +- **Detection**: How to identify it +- **Recovery**: Steps to fix + 1. Recovery action 1 + 2. Recovery action 2 + 3. Retry from failed step + +**Error 2: [Error Name]** +- **Cause**: What causes this error +- **Detection**: How to identify it +- **Recovery**: Manual intervention required + - Ask user: "[Question to ask]" + - Wait for user input + - Apply user's guidance + - Resume workflow + +## Rollback Procedure + +If the workflow fails critically: + +1. **Identify last successful step** + - Step 1: ✓ Completed + - Step 2: ❌ Failed at action 3 + +2. **Undo changes from failed step** + - Revert action 1 + - Revert action 2 + - Clean up partial state + +3. **Verify system state** + - Confirm rollback successful + - Check for side effects + +4. **Report to user** + ``` + Workflow failed at Step 2, action 3 + Reason: [Error message] + All changes have been rolled back + System is back to pre-workflow state + ``` + +## Workflow Variations + +### Variation 1: Quick Mode + +**When to use**: User needs faster execution, can accept lower validation + +**Changes**: +- Skip optional validations +- Use cached data where available +- Reduce logging verbosity + +**Trade-offs**: +- ⚡ 50% faster +- ⚠️ Less detailed error messages + +### Variation 2: Strict Mode + +**When to use**: Production deployments, critical changes + +**Changes**: +- Enable all validations +- Require explicit user confirmation at each step +- Generate detailed audit logs + +**Trade-offs**: +- 🛡️ Maximum safety +- 🐢 Slower execution + +## Monitoring and Logging + +Throughout the workflow: + +``` +[TIMESTAMP] [STEP] [STATUS] Message + +[2025-01-31 14:30:01] [PREP] [INFO] Starting preparation phase +[2025-01-31 14:30:02] [PREP] [OK] All prerequisites met +[2025-01-31 14:30:03] [STEP1] [INFO] Beginning Step 1 +[2025-01-31 14:30:05] [STEP1] [OK] Step 1 completed successfully +[2025-01-31 14:30:06] [STEP2] [INFO] Beginning Step 2 +[2025-01-31 14:30:08] [STEP2] [WARN] Condition B detected, using fallback +[2025-01-31 14:30:10] [STEP2] [OK] Step 2 completed with warnings +[2025-01-31 14:30:11] [STEP3] [INFO] Beginning Step 3 +[2025-01-31 14:30:15] [STEP3] [OK] Step 3 completed successfully +[2025-01-31 14:30:16] [COMPLETE] [OK] Workflow finished successfully +``` + +## Post-Workflow Report + +After completion, generate a summary: + +```markdown +# Workflow Execution Report + +**Workflow**: [Workflow Name] +**Started**: 2025-01-31 14:30:01 +**Completed**: 2025-01-31 14:30:16 +**Duration**: 15 seconds +**Status**: ✓ Success + +## Steps Executed + +1. ✓ Preparation Phase (1s) +2. ✓ Step 1: [Step Name] (2s) +3. ✓ Step 2: [Step Name] (4s) - 1 warning +4. ✓ Step 3: [Step Name] (4s) + +## Warnings + +- Step 2: Condition B detected, used fallback action + +## Artifacts Generated + +- `/path/to/output1.txt` +- `/path/to/output2.json` +- `/path/to/report.html` + +## Next Steps + +- Review generated artifacts +- Deploy to production (if applicable) +- Archive logs to `/logs/workflow-20250131-143001.log` +``` + +## Best Practices + +### Do + +- ✅ Validate inputs before starting workflow +- ✅ Provide clear progress updates at each step +- ✅ Log all decisions and actions +- ✅ Handle errors gracefully with recovery options +- ✅ Generate summary report at completion + +### Don't + +- ❌ Skip validation steps to save time +- ❌ Continue after critical errors +- ❌ Assume prerequisites are met without checking +- ❌ Lose partial progress on failure +- ❌ Leave system in inconsistent state + +## Advanced Features + +### Parallel Execution + +Some steps can run in parallel: + +``` +Step 1 ─┬─→ Step 2A ─┐ + │ ├─→ Step 3 + └─→ Step 2B ─┘ +``` + +**Requirements**: +- Steps 2A and 2B must be independent +- Both must complete before Step 3 + +**Implementation**: +1. Start Step 2A in background +2. Start Step 2B in background +3. Wait for both to complete +4. Verify both succeeded +5. Proceed to Step 3 + +### Conditional Branching + +``` +Step 1 → Decision + ├─→ [Condition A] → Path A → Step 3 + ├─→ [Condition B] → Path B → Step 3 + └─→ [Default] → Path C → Step 3 +``` + +## Testing This Workflow + +To test the workflow without side effects: + +1. Use `--dry-run` flag to simulate execution +2. Check that all steps are logged correctly +3. Verify error handling with intentional failures +4. Confirm rollback procedure works + +Example: +```bash +workflow-runner --dry-run --inject-error step2 +``` + +Expected output: +``` +[DRY RUN] Step 1: Would execute [actions] +[DRY RUN] Step 2: Injected error as requested +[DRY RUN] Error Recovery: Would attempt fix +[DRY RUN] Rollback: Would undo Step 1 changes +``` + +--- + +**Version**: 1.0 +**Last Updated**: YYYY-MM-DD +**Maintainer**: Team Name \ No newline at end of file diff --git a/document/content/docs/introduction/development/sealos.mdx b/document/content/docs/introduction/development/sealos.mdx index f65f219ca..dbaa4fe01 100644 --- a/document/content/docs/introduction/development/sealos.mdx +++ b/document/content/docs/introduction/development/sealos.mdx @@ -155,7 +155,7 @@ FastGPT 商业版共包含了2个应用(fastgpt, fastgpt-plus)和2个数据 SYSTEM_NAME=FastGPT SYSTEM_DESCRIPTION= SYSTEM_FAVICON=/favicon.ico -HOME_URL=/dashboard/apps +HOME_URL=/dashboard/agent ``` SYSTEM_FAVICON 可以是一个网络地址 diff --git a/document/content/docs/upgrading/4-14/4141.mdx b/document/content/docs/upgrading/4-14/4141.mdx index dce288413..a492311b8 100644 --- a/document/content/docs/upgrading/4-14/4141.mdx +++ b/document/content/docs/upgrading/4-14/4141.mdx @@ -3,15 +3,34 @@ title: 'V4.14.1(进行中)' description: 'FastGPT V4.14.1 更新说明' --- +## 更新指南 +### 1. 更新镜像: + + +### 2. 执行升级脚本 + +仅需使用过自定义系统工具的商业版用户操作。 +从任意终端,发起 1 个 HTTP 请求。其中 `{{rootkey}}` 替换成环境变量里的 `rootkey`;`{{host}}` 替换成**FastGPT 域名**。 + +```bash +curl --location --request POST 'https://{{host}}/api/admin/initv4141' \ +--header 'rootkey: {{rootkey}}' \ +--header 'Content-Type: application/json' +``` + +复制一份原应用目录给工具使用。 ## 🚀 新增内容 +1. 新工作台交互。 +2. 工作流运行欠费后提供继续运行按键,无需从头开始。 ## ⚙️ 优化 1. 在同一轮对话中,MCP Client 会持久化实例,不会销毁。 2. 模型重载时候,不会把全局模型配置清空再添加,从而导致重载阶段模型调用错误。 +3. 自动保存,增加一条团队云端保存记录。 ## 🐛 修复 @@ -19,3 +38,9 @@ description: 'FastGPT V4.14.1 更新说明' 2. 富文本编辑器 tab 空格未对齐。 3. 嵌套运行 Agent 时候,跳过节点队列未初始化,导致无法正常运行。 4. 判断器右侧是 number 引用时,会出现报错。 +5. 工作流工具入参为文件选择时,未出现选择框。 +6. HTTP 插件无法正确处理 http 协议(非 https)接口请求。 +7. 文本类型的全局变量,默认值编辑框 UI。 +8. 代码节点行数超过 100 行时显示重叠。 +9. 删除应用,未把目录内的删除。 +10. 浏览器未传递实时日期至服务器。 diff --git a/document/data/doc-last-modified.json b/document/data/doc-last-modified.json index d87252c43..963db47bc 100644 --- a/document/data/doc-last-modified.json +++ b/document/data/doc-last-modified.json @@ -39,7 +39,7 @@ "document/content/docs/introduction/development/proxy/http_proxy.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/development/proxy/nginx.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/development/quick-start.mdx": "2025-10-21T11:58:25+08:00", - "document/content/docs/introduction/development/sealos.mdx": "2025-09-29T11:52:39+08:00", + "document/content/docs/introduction/development/sealos.mdx": "2025-11-07T12:06:28+08:00", "document/content/docs/introduction/development/signoz.mdx": "2025-09-17T22:29:56+08:00", "document/content/docs/introduction/guide/DialogBoxes/htmlRendering.mdx": "2025-07-23T21:35:03+08:00", "document/content/docs/introduction/guide/DialogBoxes/quoteList.mdx": "2025-07-23T21:35:03+08:00", @@ -115,7 +115,7 @@ "document/content/docs/upgrading/4-13/4131.mdx": "2025-09-30T15:47:06+08:00", "document/content/docs/upgrading/4-13/4132.mdx": "2025-10-21T11:46:53+08:00", "document/content/docs/upgrading/4-14/4140.mdx": "2025-11-06T15:43:00+08:00", - "document/content/docs/upgrading/4-14/4141.mdx": "2025-11-07T10:56:20+08:00", + "document/content/docs/upgrading/4-14/4141.mdx": "2025-11-10T22:52:46+08:00", "document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00", "document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00", diff --git a/packages/global/common/file/tools.ts b/packages/global/common/file/tools.ts index 585ea4af0..fbaa9a8ef 100644 --- a/packages/global/common/file/tools.ts +++ b/packages/global/common/file/tools.ts @@ -59,7 +59,11 @@ export const parseUrlToFileType = (url: string): UserChatItemFileItemType | unde const parseUrl = new URL(url, 'http://localhost:3000'); // Get filename from URL - const filename = parseUrl.searchParams.get('filename') || parseUrl.pathname.split('/').pop(); + const filename = (() => { + // Here is a S3 Object Key + if (url.startsWith('chat/')) return url.split('/').pop()?.split('-')[1]; + return parseUrl.searchParams.get('filename') || parseUrl.pathname.split('/').pop(); + })(); const extension = filename?.split('.').pop()?.toLowerCase() || ''; // If it's a document type, return as file, otherwise treat as image diff --git a/packages/global/common/middle/tracks/constants.ts b/packages/global/common/middle/tracks/constants.ts index d93d42684..c7e6e76a3 100644 --- a/packages/global/common/middle/tracks/constants.ts +++ b/packages/global/common/middle/tracks/constants.ts @@ -6,5 +6,8 @@ export enum TrackEnum { createDataset = 'createDataset', appNodes = 'appNodes', runSystemTool = 'runSystemTool', - datasetSearch = 'datasetSearch' + datasetSearch = 'datasetSearch', + readSystemAnnouncement = 'readSystemAnnouncement', + clickOperationalAd = 'clickOperationalAd', + closeOperationalAd = 'closeOperationalAd' } diff --git a/packages/global/common/system/config/constants.ts b/packages/global/common/system/config/constants.ts index 0be8d6346..a47f11040 100644 --- a/packages/global/common/system/config/constants.ts +++ b/packages/global/common/system/config/constants.ts @@ -2,7 +2,8 @@ export enum SystemConfigsTypeEnum { fastgpt = 'fastgpt', fastgptPro = 'fastgptPro', systemMsgModal = 'systemMsgModal', - license = 'license' + license = 'license', + operationalAd = 'operationalAd' } export const SystemConfigsTypeMap = { @@ -17,5 +18,8 @@ export const SystemConfigsTypeMap = { }, [SystemConfigsTypeEnum.license]: { label: 'license' + }, + [SystemConfigsTypeEnum.operationalAd]: { + label: 'operationalAd' } }; diff --git a/packages/global/common/system/types/index.d.ts b/packages/global/common/system/types/index.d.ts index 379623b8c..78831b4ff 100644 --- a/packages/global/common/system/types/index.d.ts +++ b/packages/global/common/system/types/index.d.ts @@ -53,6 +53,7 @@ export type FastGPTFeConfigsType = { googleClientVerKey?: string; mcpServerProxyEndpoint?: string; chineseRedirectUrl?: string; + botIframeUrl?: string; show_emptyChat?: boolean; show_appStore?: boolean; diff --git a/packages/global/core/app/constants.ts b/packages/global/core/app/constants.ts index a4bfe86a2..5129049eb 100644 --- a/packages/global/core/app/constants.ts +++ b/packages/global/core/app/constants.ts @@ -8,19 +8,32 @@ import { export enum AppTypeEnum { folder = 'folder', + toolFolder = 'toolFolder', simple = 'simple', + agent = 'agent', workflow = 'advanced', - plugin = 'plugin', - toolSet = 'toolSet', // 'mcp' + workflowTool = 'plugin', + mcpToolSet = 'toolSet', // 'mcp' httpToolSet = 'httpToolSet', - tool = 'tool', hidden = 'hidden', // deprecated + tool = 'tool', httpPlugin = 'httpPlugin' } -export const AppFolderTypeList = [AppTypeEnum.folder, AppTypeEnum.httpPlugin]; +export const AppFolderTypeList = [ + AppTypeEnum.folder, + AppTypeEnum.toolFolder, + AppTypeEnum.httpPlugin +]; + +export const ToolTypeList = [ + AppTypeEnum.mcpToolSet, + AppTypeEnum.httpToolSet, + AppTypeEnum.workflowTool +]; +export const AppTypeList = [AppTypeEnum.simple, AppTypeEnum.agent, AppTypeEnum.workflow]; export const defaultTTSConfig: AppTTSConfigType = { type: 'web' }; diff --git a/packages/global/core/app/utils.ts b/packages/global/core/app/utils.ts index 0f8cc4222..39b52ef00 100644 --- a/packages/global/core/app/utils.ts +++ b/packages/global/core/app/utils.ts @@ -185,7 +185,7 @@ export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFor return AppTypeEnum.workflow; } if (config.nodes.some((node) => node.flowNodeType === 'pluginInput')) { - return AppTypeEnum.plugin; + return AppTypeEnum.workflowTool; } return ''; }; diff --git a/packages/global/core/app/version.d.ts b/packages/global/core/app/version.d.ts index 4c24004bb..669ed98eb 100644 --- a/packages/global/core/app/version.d.ts +++ b/packages/global/core/app/version.d.ts @@ -12,6 +12,7 @@ export type AppVersionSchemaType = { edges: AppSchema['edges']; chatConfig: AppSchema['chatConfig']; isPublish?: boolean; + isAutoSave?: boolean; versionName: string; tmbId: string; }; diff --git a/packages/global/core/workflow/runtime/utils.ts b/packages/global/core/workflow/runtime/utils.ts index 7de6c9c09..f79770486 100644 --- a/packages/global/core/workflow/runtime/utils.ts +++ b/packages/global/core/workflow/runtime/utils.ts @@ -200,6 +200,10 @@ export const getLastInteractiveValue = ( if (lastValue.interactive.type === 'userInput' && !lastValue.interactive.params.submitted) { return lastValue.interactive; } + + if (lastValue.interactive.type === 'paymentPause' && !lastValue.interactive.params.continue) { + return lastValue.interactive; + } } return; diff --git a/packages/global/core/workflow/template/system/interactive/type.d.ts b/packages/global/core/workflow/template/system/interactive/type.d.ts index 9fe0586ce..e4346a5f8 100644 --- a/packages/global/core/workflow/template/system/interactive/type.d.ts +++ b/packages/global/core/workflow/template/system/interactive/type.d.ts @@ -31,6 +31,7 @@ type ChildrenInteractive = InteractiveNodeType & { }; }; +// Loop bode type LoopInteractive = InteractiveNodeType & { type: 'loopInteractive'; params: { @@ -80,10 +81,20 @@ type UserInputInteractive = InteractiveNodeType & { }; }; +// 欠费暂停交互 +export type PaymentPauseInteractive = InteractiveNodeType & { + type: 'paymentPause'; + params: { + description?: string; + continue?: boolean; + }; +}; + export type InteractiveNodeResponseType = | UserSelectInteractive | UserInputInteractive | ChildrenInteractive - | LoopInteractive; + | LoopInteractive + | PaymentPauseInteractive; export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType; diff --git a/packages/global/core/workflow/type/index.d.ts b/packages/global/core/workflow/type/index.d.ts index 0938f02ce..ff8fb0793 100644 --- a/packages/global/core/workflow/type/index.d.ts +++ b/packages/global/core/workflow/type/index.d.ts @@ -50,7 +50,7 @@ export type WorkflowTemplateType = { // template market export type TemplateMarketItemType = WorkflowTemplateType & { tags: string[]; - type: AppTypeEnum.simple | AppTypeEnum.workflow | AppTypeEnum.plugin; + type: AppTypeEnum.simple | AppTypeEnum.workflow | AppTypeEnum.workflowTool; }; // template market list export type TemplateMarketListItemType = { @@ -59,6 +59,6 @@ export type TemplateMarketListItemType = { intro?: string; author?: string; tags: string[]; - type: AppTypeEnum.simple | AppTypeEnum.workflow | AppTypeEnum.plugin; + type: AppTypeEnum.simple | AppTypeEnum.workflow | AppTypeEnum.workflowTool; avatar: string; }; diff --git a/packages/service/common/file/image/controller.ts b/packages/service/common/file/image/controller.ts index 755005222..700d41631 100644 --- a/packages/service/common/file/image/controller.ts +++ b/packages/service/common/file/image/controller.ts @@ -8,6 +8,10 @@ import { addHours } from 'date-fns'; import { imageFileType } from '@fastgpt/global/common/file/constants'; import { retryFn } from '@fastgpt/global/common/system/utils'; import { UserError } from '@fastgpt/global/common/error/utils'; +import { S3Sources } from '../../s3/type'; +import { getS3AvatarSource } from '../../s3/sources/avatar'; +import path from 'path'; +import { getNanoid } from '@fastgpt/global/common/string/tools'; export const maxImgSize = 1024 * 1024 * 12; const base64MimeRegex = /data:image\/([^\)]+);base64/; @@ -56,60 +60,67 @@ export async function uploadMongoImg({ return `${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`; } -export const copyImage = async ({ + +export const copyAvatarImage = async ({ teamId, imageUrl, + ttl, session }: { teamId: string; imageUrl: string; + ttl: boolean; session?: ClientSession; }) => { - const imageId = getIdFromPath(imageUrl); - if (!imageId) return imageUrl; + if (!imageUrl) return; - const image = await MongoImage.findOne( - { - _id: imageId, - teamId - }, - undefined, - { - session - } - ); - if (!image) return imageUrl; + // S3 + if (imageUrl.startsWith(`${imageBaseUrl}/${S3Sources.avatar}`)) { + const extendName = path.extname(imageUrl); + const key = await getS3AvatarSource().copyAvatar({ + sourceKey: imageUrl.slice(imageBaseUrl.length), + targetKey: `${S3Sources.avatar}/${teamId}/${getNanoid(6)}${extendName}`, + ttl + }); + return key; + } - const [newImage] = await MongoImage.create( - [ - { - teamId, - binary: image.binary, - metadata: image.metadata - } - ], - { - session, - ordered: true - } - ); - - return `${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(newImage._id)}.${image.metadata?.mime?.split('/')[1]}`; -}; - -const getIdFromPath = (path?: string) => { - if (!path) return; - - const paths = path.split('/'); + const paths = imageUrl.split('/'); const name = paths[paths.length - 1]; - - if (!name) return; - const id = name.split('.')[0]; - if (!id || !Types.ObjectId.isValid(id)) return; - return id; + // Mongo + if (id && Types.ObjectId.isValid(id)) { + const image = await MongoImage.findOne( + { + _id: id, + teamId + }, + undefined, + { + session + } + ); + if (!image) return imageUrl; + const [newImage] = await MongoImage.create( + [ + { + teamId, + binary: image.binary, + metadata: image.metadata + } + ], + { + session, + ordered: true + } + ); + return `${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(newImage._id)}.${image.metadata?.mime?.split('/')[1]}`; + } + + return imageUrl; }; + export const removeImageByPath = (path?: string, session?: ClientSession) => { if (!path) return; diff --git a/packages/service/common/s3/buckets/base.ts b/packages/service/common/s3/buckets/base.ts index 4d1183584..a17c3b3b8 100644 --- a/packages/service/common/s3/buckets/base.ts +++ b/packages/service/common/s3/buckets/base.ts @@ -79,7 +79,24 @@ export class S3BaseBucket { return this.delete(src); } - copy(src: string, dst: string, options?: CopyConditions): ReturnType { + async copy({ + src, + dst, + ttl, + options + }: { + src: string; + dst: string; + ttl: boolean; + options?: CopyConditions; + }): ReturnType { + if (ttl) { + await MongoS3TTL.create({ + minioKey: dst, + bucketName: this.name, + expiredTime: addHours(new Date(), 24) + }); + } return this.client.copyObject(this.name, src, dst, options); } diff --git a/packages/service/common/s3/sources/avatar.ts b/packages/service/common/s3/sources/avatar.ts index d8c3efb56..0fbcd738d 100644 --- a/packages/service/common/s3/sources/avatar.ts +++ b/packages/service/common/s3/sources/avatar.ts @@ -66,6 +66,23 @@ class S3AvatarSource { await this.deleteAvatar(oldAvatar, session); } } + + async copyAvatar({ + sourceKey, + targetKey, + ttl + }: { + sourceKey: string; + targetKey: string; + ttl: boolean; + }) { + await this.bucket.copy({ + src: sourceKey, + dst: targetKey, + ttl + }); + return targetKey; + } } export function getS3AvatarSource() { diff --git a/packages/service/common/s3/sources/chat/index.ts b/packages/service/common/s3/sources/chat/index.ts index cbe0124a2..0aaad6526 100644 --- a/packages/service/common/s3/sources/chat/index.ts +++ b/packages/service/common/s3/sources/chat/index.ts @@ -22,6 +22,10 @@ class S3ChatSource { return (this.instance ??= new S3ChatSource()); } + static isChatFileKey(key?: string): key is `${typeof S3Sources.chat}/${string}` { + return key?.startsWith(`${S3Sources.chat}/`) ?? false; + } + async createGetChatFileURL(params: { key: string; expiredHours?: number; external: boolean }) { const { key, expiredHours = 1, external = false } = params; // 默认一个小时 diff --git a/packages/service/core/app/controller.ts b/packages/service/core/app/controller.ts index 9039fda63..bd8ec2238 100644 --- a/packages/service/core/app/controller.ts +++ b/packages/service/core/app/controller.ts @@ -4,7 +4,7 @@ import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; +import { AppFolderTypeList } from '@fastgpt/global/core/app/constants'; import { MongoApp } from './schema'; import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { encryptSecretValue, storeSecretValue } from '../../common/secret/utils'; @@ -153,7 +153,7 @@ export const onDelOneApp = async ({ }); const deletedAppIds = apps - .filter((app) => app.type !== AppTypeEnum.folder) + .filter((app) => !AppFolderTypeList.includes(app.type)) .map((app) => String(app._id)); // Remove eval job @@ -217,6 +217,7 @@ export const onDelOneApp = async ({ { session } ); + // Delete avatar await removeImageByPath(app.avatar, session); }; @@ -239,10 +240,10 @@ export const onDelOneApp = async ({ for await (const app of apps) { if (session) { await del(app, session); - return deletedAppIds; } await mongoSessionRun((session) => del(app, session)); - return deletedAppIds; } + + return deletedAppIds; }; diff --git a/packages/service/core/app/http.ts b/packages/service/core/app/http.ts index 3177ff8cf..cae7736e6 100644 --- a/packages/service/core/app/http.ts +++ b/packages/service/core/app/http.ts @@ -148,7 +148,10 @@ export const runHTTPTool = async ({ const { data } = await axios({ method: method.toUpperCase(), - baseURL: baseUrl.startsWith('https://') ? baseUrl : `https://${baseUrl}`, + baseURL: + baseUrl.startsWith('http://') || baseUrl.startsWith('https://') + ? baseUrl + : `https://${baseUrl}`, url: toolPath, headers, data: body, diff --git a/packages/service/core/app/tool/controller.ts b/packages/service/core/app/tool/controller.ts index 8010de959..d2ed14e9d 100644 --- a/packages/service/core/app/tool/controller.ts +++ b/packages/service/core/app/tool/controller.ts @@ -238,7 +238,7 @@ export async function getChildAppPreviewNode({ }) : true; - if (item.type === AppTypeEnum.toolSet) { + if (item.type === AppTypeEnum.mcpToolSet) { const children = await getMCPChildren(item); version.nodes[0].toolConfig = { mcpToolSet: { diff --git a/packages/service/core/app/tool/workflowTool/utils.ts b/packages/service/core/app/tool/workflowTool/utils.ts index 8b5825fb1..57955655d 100644 --- a/packages/service/core/app/tool/workflowTool/utils.ts +++ b/packages/service/core/app/tool/workflowTool/utils.ts @@ -71,6 +71,19 @@ export const serverGetWorkflowToolRunUserQuery = ({ if (input.renderTypeList.includes(FlowNodeInputTypeEnum.password)) { value = encryptSecretValue(value); + } else if ( + input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) && + Array.isArray(value) + ) { + value = value.map((item) => { + return { + id: item.id, + key: item.key, + name: item.name, + type: item.type, + url: item.key ? undefined : item.url + }; + }); } return { diff --git a/packages/service/core/app/version/schema.ts b/packages/service/core/app/version/schema.ts index b483dcc08..052fad999 100644 --- a/packages/service/core/app/version/schema.ts +++ b/packages/service/core/app/version/schema.ts @@ -34,6 +34,7 @@ const AppVersionSchema = new Schema( type: chatConfigType }, isPublish: Boolean, + isAutoSave: Boolean, versionName: String }, { @@ -41,7 +42,7 @@ const AppVersionSchema = new Schema( } ); -AppVersionSchema.index({ appId: 1, _id: -1 }); +AppVersionSchema.index({ appId: 1, time: -1 }); export const MongoAppVersion = getMongoModel( AppVersionCollectionName, diff --git a/packages/service/core/chat/saveChat.ts b/packages/service/core/chat/saveChat.ts index 2b0793e83..ad5f35ca0 100644 --- a/packages/service/core/chat/saveChat.ts +++ b/packages/service/core/chat/saveChat.ts @@ -389,6 +389,8 @@ export const updateInteractiveChat = async (props: Props) => { : item; }); finalInteractive.params.submitted = true; + } else if (finalInteractive.type === 'paymentPause') { + chatItem.value.pop(); } if (aiResponse.customFeedbacks) { diff --git a/packages/service/core/chat/utils.ts b/packages/service/core/chat/utils.ts index 4c3bb7ee9..1991e1163 100644 --- a/packages/service/core/chat/utils.ts +++ b/packages/service/core/chat/utils.ts @@ -1,11 +1,14 @@ -import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; +import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; import type { ChatItemType } from '@fastgpt/global/core/chat/type'; import { getS3ChatSource } from '../../common/s3/sources/chat'; +import type { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; +import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; -export const addPreviewUrlToChatItems = async (histories: ChatItemType[]) => { - // Presign file urls - const s3ChatSource = getS3ChatSource(); - for await (const item of histories) { +export const addPreviewUrlToChatItems = async ( + histories: ChatItemType[], + type: 'chatFlow' | 'workflowTool' +) => { + async function addToChatflow(item: ChatItemType) { for await (const value of item.value) { if (value.type === ChatItemValueTypeEnum.file && value.file && value.file.key) { value.file.url = await s3ChatSource.createGetChatFileURL({ @@ -15,4 +18,46 @@ export const addPreviewUrlToChatItems = async (histories: ChatItemType[]) => { } } } + async function addToWorkflowTool(item: ChatItemType) { + if (item.obj !== ChatRoleEnum.Human || !Array.isArray(item.value)) return; + + for (let j = 0; j < item.value.length; j++) { + const value = item.value[j]; + if (value.type !== ChatItemValueTypeEnum.text) continue; + const inputValueString = value.text?.content || ''; + const parsedInputValue = JSON.parse(inputValueString) as FlowNodeInputItemType[]; + + for (const input of parsedInputValue) { + if ( + input.renderTypeList[0] !== FlowNodeInputTypeEnum.fileSelect || + !Array.isArray(input.value) + ) + continue; + + for (const file of input.value) { + if (!file.key) continue; + const url = await getS3ChatSource().createGetChatFileURL({ + key: file.key, + external: true + }); + file.url = url; + } + } + + item.value[j].text = { + ...value.text, + content: JSON.stringify(parsedInputValue) + }; + } + } + + // Presign file urls + const s3ChatSource = getS3ChatSource(); + for await (const item of histories) { + if (type === 'chatFlow') { + await addToChatflow(item); + } else if (type === 'workflowTool') { + await addToWorkflowTool(item); + } + } }; diff --git a/packages/service/core/workflow/dispatch/index.ts b/packages/service/core/workflow/dispatch/index.ts index 32f10a513..51a5c0344 100644 --- a/packages/service/core/workflow/dispatch/index.ts +++ b/packages/service/core/workflow/dispatch/index.ts @@ -55,6 +55,8 @@ import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; import { getS3ChatSource } from '../../../common/s3/sources/chat'; import { addPreviewUrlToChatItems } from '../../chat/utils'; import type { MCPClient } from '../../app/mcp'; +import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; +import { i18nT } from '../../../../web/i18n/utils'; type Props = Omit & { runtimeNodes: RuntimeNodeItemType[]; @@ -132,7 +134,7 @@ export async function dispatchWorkFlow({ } // Add preview url to chat items - await addPreviewUrlToChatItems(histories); + await addPreviewUrlToChatItems(histories, 'chatFlow'); for (const item of query) { if (item.type !== ChatItemValueTypeEnum.file || !item.file?.key) continue; item.file.url = await getS3ChatSource().createGetChatFileURL({ @@ -620,6 +622,23 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise { + try { + await checkTeamAIPoints(data.runningUserInfo.teamId); + } catch (error) { + // Next time you enter the system, you will still start from the current node(Current check team blance node). + if (error === TeamErrEnum.aiPointsNotEnough) { + return { + [DispatchNodeResponseKeyEnum.interactive]: { + type: 'paymentPause', + params: { + description: i18nT('chat:balance_not_enough_pause') + } + } + }; + } + } + } /* Check node run/skip or wait */ private async checkNodeCanRun( node: RuntimeNodeItemType, @@ -652,6 +671,7 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise { + const nodeRunResult = await (async () => { if (status === 'run') { + const blanceCheckResult = await this.checkTeamBlance(); + if (blanceCheckResult) { + return { + node, + runStatus: 'pause' as const, + result: blanceCheckResult + }; + } + // All source edges status to waiting runtimeEdges.forEach((item) => { if (item.target === node.nodeId) { @@ -870,10 +900,21 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise item.type === ChatFileTypeEnum.file || item.type === ChatFileTypeEnum.image ) ) { + // 为文件对象重新签发 URL(如果有 key 但没有 url) + for (let i = 0; i < val.length; i++) { + const fileItem = val[i]; + if (fileItem.key && !fileItem.url) { + val[i].url = await getS3ChatSource().createGetChatFileURL({ + key: fileItem.key, + external: true, + expiredHours: 1 + }); + } + } params[key] = val.map((item) => item.url); } } diff --git a/packages/service/support/permission/teamLimit.ts b/packages/service/support/permission/teamLimit.ts index 3ae9287b1..988dba322 100644 --- a/packages/service/support/permission/teamLimit.ts +++ b/packages/service/support/permission/teamLimit.ts @@ -49,8 +49,8 @@ export const checkTeamAppLimit = async (teamId: string, amount = 1) => { $in: [ AppTypeEnum.simple, AppTypeEnum.workflow, - AppTypeEnum.plugin, - AppTypeEnum.toolSet, + AppTypeEnum.workflowTool, + AppTypeEnum.mcpToolSet, AppTypeEnum.httpToolSet ] } @@ -65,7 +65,12 @@ export const checkTeamAppLimit = async (teamId: string, amount = 1) => { if (global?.licenseData?.maxApps && typeof global?.licenseData?.maxApps === 'number') { const totalApps = await MongoApp.countDocuments({ type: { - $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.toolSet] + $in: [ + AppTypeEnum.simple, + AppTypeEnum.workflow, + AppTypeEnum.workflowTool, + AppTypeEnum.mcpToolSet + ] } }); if (totalApps >= global.licenseData.maxApps) { diff --git a/packages/service/support/user/audit/util.ts b/packages/service/support/user/audit/util.ts index cefeb9fb7..53be0c1a0 100644 --- a/packages/service/support/user/audit/util.ts +++ b/packages/service/support/user/audit/util.ts @@ -12,13 +12,13 @@ import { retryFn } from '@fastgpt/global/common/system/utils'; export function getI18nAppType(type: AppTypeEnum): string { if (type === AppTypeEnum.folder) return i18nT('account_team:type.Folder'); - if (type === AppTypeEnum.simple) return i18nT('account_team:type.Simple bot'); + if (type === AppTypeEnum.simple) return i18nT('app:type.Chat_Agent'); if (type === AppTypeEnum.workflow) return i18nT('account_team:type.Workflow bot'); - if (type === AppTypeEnum.plugin) return i18nT('account_team:type.Plugin'); + if (type === AppTypeEnum.workflowTool) return i18nT('app:toolType_workflow'); if (type === AppTypeEnum.httpPlugin) return i18nT('account_team:type.Http plugin'); - if (type === AppTypeEnum.httpToolSet) return i18nT('account_team:type.Http tool set'); - if (type === AppTypeEnum.toolSet) return i18nT('account_team:type.Tool set'); - if (type === AppTypeEnum.tool) return i18nT('account_team:type.Tool'); + if (type === AppTypeEnum.httpToolSet) return i18nT('app:toolType_http'); + if (type === AppTypeEnum.mcpToolSet) return i18nT('app:toolType_mcp'); + if (type === AppTypeEnum.tool) return i18nT('app:toolType_mcp'); return i18nT('common:UnKnow'); } diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 8ea76053b..dbc136653 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -93,12 +93,14 @@ export const iconPaths = { 'common/playFill': () => import('./icons/common/playFill.svg'), 'common/playLight': () => import('./icons/common/playLight.svg'), 'common/publishFill': () => import('./icons/common/publishFill.svg'), + 'common/refresh': () => import('./icons/common/refresh.svg'), 'common/refreshLight': () => import('./icons/common/refreshLight.svg'), 'common/resultLight': () => import('./icons/common/resultLight.svg'), 'common/retryLight': () => import('./icons/common/retryLight.svg'), 'common/rightArrow': () => import('./icons/common/rightArrow.svg'), 'common/rightArrowFill': () => import('./icons/common/rightArrowFill.svg'), 'common/rightArrowLight': () => import('./icons/common/rightArrowLight.svg'), + 'common/rocket': () => import('./icons/common/rocket.svg'), 'common/routePushLight': () => import('./icons/common/routePushLight.svg'), 'common/running': () => import('./icons/common/running.svg'), 'common/saveFill': () => import('./icons/common/saveFill.svg'), @@ -115,7 +117,7 @@ export const iconPaths = { 'common/text/t': () => import('./icons/common/text/t.svg'), 'common/thirdParty': () => import('./icons/common/thirdParty.svg'), 'common/tickFill': () => import('./icons/common/tickFill.svg'), - 'common/plugin': () => import('./icons/common/toolkit.svg'), + 'common/toolkit': () => import('./icons/common/toolkit.svg'), 'common/trash': () => import('./icons/common/trash.svg'), 'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'), 'common/uploadFileFill': () => import('./icons/common/uploadFileFill.svg'), @@ -131,10 +133,13 @@ export const iconPaths = { 'common/wecom': () => import('./icons/common/wecom.svg'), configmap: () => import('./icons/configmap.svg'), copy: () => import('./icons/copy.svg'), + 'core/app/agent': () => import('./icons/core/app/agent.svg'), + 'core/app/aiAgent': () => import('./icons/core/app/aiAgent.svg'), 'core/app/aiFill': () => import('./icons/core/app/aiFill.svg'), 'core/app/aiLight': () => import('./icons/core/app/aiLight.svg'), 'core/app/aiLightSmall': () => import('./icons/core/app/aiLightSmall.svg'), 'core/app/appApiLight': () => import('./icons/core/app/appApiLight.svg'), + 'core/app/create': () => import('./icons/core/app/create.svg'), 'core/app/customFeedback': () => import('./icons/core/app/customFeedback.svg'), 'core/app/headphones': () => import('./icons/core/app/headphones.svg'), 'core/app/inputGuides': () => import('./icons/core/app/inputGuides.svg'), diff --git a/packages/web/components/common/Icon/icons/common/refresh.svg b/packages/web/components/common/Icon/icons/common/refresh.svg new file mode 100644 index 000000000..b8e5a56c9 --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/refresh.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/common/rocket.svg b/packages/web/components/common/Icon/icons/common/rocket.svg new file mode 100644 index 000000000..f51810302 --- /dev/null +++ b/packages/web/components/common/Icon/icons/common/rocket.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/core/app/agent.svg b/packages/web/components/common/Icon/icons/core/app/agent.svg new file mode 100644 index 000000000..f4c6ae349 --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/app/agent.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/core/app/aiAgent.svg b/packages/web/components/common/Icon/icons/core/app/aiAgent.svg new file mode 100644 index 000000000..4b24c2b26 --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/app/aiAgent.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/web/components/common/Icon/icons/core/app/create.svg b/packages/web/components/common/Icon/icons/core/app/create.svg new file mode 100644 index 000000000..b42e23d4d --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/app/create.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/core/app/simpleBot.svg b/packages/web/components/common/Icon/icons/core/app/simpleBot.svg index 1a0f2e0b3..2271088f2 100644 --- a/packages/web/components/common/Icon/icons/core/app/simpleBot.svg +++ b/packages/web/components/common/Icon/icons/core/app/simpleBot.svg @@ -1,19 +1,12 @@ - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + diff --git a/packages/web/components/common/Textarea/CodeEditor/Editor.tsx b/packages/web/components/common/Textarea/CodeEditor/Editor.tsx index 0efdef919..d954a3c7e 100644 --- a/packages/web/components/common/Textarea/CodeEditor/Editor.tsx +++ b/packages/web/components/common/Textarea/CodeEditor/Editor.tsx @@ -38,7 +38,7 @@ const options = { horizontalScrollbarSize: 8, alwaysConsumeMouseWheel: false }, - lineNumbersMinChars: 0, + lineNumbersMinChars: 4, fontSize: 14, scrollBeyondLastLine: false, folding: true, @@ -129,7 +129,6 @@ const MyEditor = ({ py={1} height={height} position={'relative'} - pl={2} {...props} > {t('common:All')} - + {tags.map((tag) => { diff --git a/packages/web/i18n/en/account.json b/packages/web/i18n/en/account.json index 870cd61b7..d982c3a78 100644 --- a/packages/web/i18n/en/account.json +++ b/packages/web/i18n/en/account.json @@ -12,6 +12,7 @@ "custom_model": "custom model", "default_model": "Default model", "default_model_config": "Default model configuration", + "language": "Language and time zone", "logout": "Sign out", "model.active": "Active", "model.alias": "Alias", diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index a6f92a989..945c20f56 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -51,18 +51,21 @@ "app.version_past": "Previously Published", "app.version_publish_tips": "This version will be saved to the team cloud, synchronized with the entire team, and update the app version on all release channels.", "app_detail": "Application Details", + "app_name_placeholder": "Enter app name", "apply_code": "Apply", "apply_code_failed": "Failed to apply code", "auto_execute": "Automatic execution", "auto_execute_default_prompt_placeholder": "Default questions sent when executing automatically", "auto_execute_tip": "After turning it on, the workflow will be automatically triggered when the user enters the conversation interface. \nExecution order: 1. Dialogue starter; 2. Global variables; 3. Automatic execution.", "auto_save": "Auto save", + "change_app_type": "Change App Type", "chat_debug": "Chat Preview", "chat_logs": "Logs", "chat_logs_tips": "Logs will record the online, shared, and API (requires chatId) conversation records of this app.", "click_to_config": "Go to configuration", "code_applied_successfully": "Code applied successfully", "code_function_describe": "Describe your code function, enter \"/\" to select the variable", + "collapse_tool_create": "close", "config_ai_model_params": "Click to configure AI model related properties", "config_file_upload": "Click to Configure File Upload Rules", "config_question_guide": "Configuration guess you want to ask", @@ -78,16 +81,20 @@ "core.module.template.System Tools": "System Tools", "core.workflow.Copilot": "AI Generation", "create_by_curl": "By CURL", - "create_by_template": "By template", + "create_by_template": "Create by template", "create_copy_success": "Duplicate Created Successfully", "create_empty_app": "Create Default App", "create_empty_plugin": "Create Default Plugin", "create_empty_workflow": "Create Default Workflow", + "create_from_template": "Create from template", "create_http_toolset": "Create HTTP Toolset", + "create_your_first_agent": "Create your first Agent", + "create_your_first_tool": "Create your first tool", "cron.every_day": "Run Daily", "cron.every_month": "Run Monthly", "cron.every_week": "Run Weekly", "cron.interval": "Run at Intervals", + "current_package": "Current package: ", "custom_plugin": "Custom Plugin", "custom_plugin_associated_plugin_label": "Associated Plugin", "custom_plugin_associated_plugin_placeholder": "Enter plugin name or appId to search", @@ -130,6 +137,7 @@ "edit_info": "Edit", "empty_tool_tips": "Please add tools on the left side", "execute_time": "Execution Time", + "expand_tool_create": "Expand MCP/Http create", "export_config_successful": "Configuration copied, some sensitive information automatically filtered. Please check for any remaining sensitive data.", "export_configs": "Export", "fastgpt_marketplace": "FastGPT plug-in market", @@ -142,6 +150,8 @@ "find_more_tools": "Explore more", "go_to_chat": "Go to Conversation", "go_to_run": "Go to Execution", + "has_no_create_per": "You do not have permission to create in the current directory.", + "hide_templates": "Collapse", "http_toolset_add_tips": "Click the \"Add\" button to add tools", "http_toolset_config_tips": "Click \"Start Configuration\" to add tools", "image": "picture", @@ -259,6 +269,7 @@ "plugin_cost_by_token": "Charged based on token usage", "plugin_cost_folder_tip": "This tool set contains subordinate tools, and the call points are determined based on the actual calling tool", "plugin_cost_per_times": "{{cost}} points/time", + "plugin_db": "Plugins", "plugin_dispatch": "Plugin Invocation", "plugin_dispatch_tip": "Adds extra capabilities to the model. The specific plugins to be invoked will be autonomously decided by the model.\nIf a plugin is selected, the Dataset invocation will automatically be treated as a special plugin.", "plugin_offline_tips": "Your system does not have direct access to plugin market data,\n\nPlease manually copy the URL and go to get more plugins", @@ -274,16 +285,22 @@ "publish_success": "Publish Successful", "question_guide_tip": "After the conversation, 3 guiding questions will be generated for you.", "reasoning_response": "Output thinking", + "recharge": "Go to recharge", "reference_variable": "Reference variables", + "refresh_templates": "Refresh", + "remaining_points": "Reaming points: ", "request_headers": "Request header", "response_format": "Response format", "save_team_app_log_keys": "Save as team configuration", "saved_success": "Saved successfully! \nTo use this version externally, click Save and Publish", + "search_agent": "Search Agent", "search_app": "Search apps", "search_tool": "Search Tools", "secret_get_course": "Course", + "select_app_type": "Select App Type", "setting_app": "Workflow", "setting_plugin": "Workflow", + "show_templates": "Expand", "show_top_p_tip": "An alternative method of temperature sampling, called Nucleus sampling, the model considers the results of tokens with TOP_P probability mass quality. \nTherefore, 0.1 means that only tokens containing the highest probability quality are considered. \nThe default is 1.", "simple_tool_tips": "This plugin contains special inputs and is not currently supported for invocation by simple applications.", "source_updateTime": "Update time", @@ -326,6 +343,9 @@ "time_type": "Time Type", "time_zone": "Time Zone", "too_to_active": "Active", + "toolType_http": "HTTP tools", + "toolType_mcp": "MCP tools", + "toolType_workflow": "Workflow tools", "tool_active_manual_config_desc": "The temporary key is saved in this application and is only for use by this application.", "tool_active_system_config_desc": "Use the system configured key", "tool_active_system_config_price_desc": "Additional payment for key price ({{price}} points/time)", @@ -339,7 +359,6 @@ "tool_soon_offset_tips": "This tool will be offline in the future. For the sake of your business stability, please replace it as soon as possible.", "tool_tip": "When executed as a tool, is this field used as a tool response result?", "tool_type_tools": "tool", - "plugin_db": "Plugins", "toolkit_activation_label": "Key Activation", "toolkit_activation_not_required": "No Activation", "toolkit_activation_required": "Activation Required", @@ -411,6 +430,7 @@ "tts_browser": "Browser's own (free)", "tts_close": "Close", "type.All": "All", + "type.Chat_Agent": "Dialogue Agent", "type.Create http plugin tip": "Batch create plugins through OpenAPI Schema, compatible with GPTs format.", "type.Create mcp tools tip": "Automatically parse and batch create callable MCP tools by entering the MCP address", "type.Create one plugin tip": "Customizable input and output workflows, usually used to encapsulate reusable workflows.", @@ -441,8 +461,17 @@ "type.error.Workflow data is empty": "No workflow data was obtained", "type.error.workflowresponseempty": "Response content is empty", "type.hidden": "Hide app", + "type_http_tool_set_intro": "Batch import API tool", + "type_mcp_intro": "Connect to MCP service", "type_not_recognized": "App type not recognized", + "type_plugin_description": "Output the specified results with one click.", + "type_plugin_intro": "Commonly used to encapsulate workflow", + "type_simple_description": "The most basic conversation application, no complicated configuration is required, and it can be created and used quickly.", + "type_simple_intro": "Quickly create conversation agents", + "type_workflow_description": "Memory-enabled complex multi-turn conversational workflows.", + "type_workflow_intro": "Drag and drop arrangement and multiple rounds of dialogue", "un_auth": "No permission", + "unnamed_app": "Unnamed", "upload_file_exists_filtered": "Duplicate files have been automatically filtered.", "upload_file_extension_type_canSelectAudio": "Audio", "upload_file_extension_type_canSelectCustomFileExtension": "Custom file extension type", @@ -455,6 +484,7 @@ "upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation", "upload_method": "Upload method", "url_upload": "File link", + "used_points": "Used point: ", "variable.internal_type_desc": "Use only inside the workflow and will not appear in the dialog box", "variable.select type_desc": "The input box will be displayed in the site conversation and run preview, and this variable will not be displayed in the sharing link.", "variable.textarea_type_desc": "Allows users to input up to 4000 characters in the dialogue box.", diff --git a/packages/web/i18n/en/chat.json b/packages/web/i18n/en/chat.json index 2e708049a..357f99659 100644 --- a/packages/web/i18n/en/chat.json +++ b/packages/web/i18n/en/chat.json @@ -4,6 +4,7 @@ "LLM_model_response_empty": "The model flow response is empty, please check whether the model flow output is normal.", "ai_reasoning": "Thinking process", "back_to_text": "Text input", + "balance_not_enough_pause": "Workflow paused due to insufficient AI points", "chat.quote.No Data": "The file cannot be found", "chat.quote.deleted": "This data has been deleted ~", "chat.waiting_for_response": "Please wait for the conversation to complete", @@ -11,6 +12,7 @@ "chat_input_guide_lexicon_is_empty": "Lexicon not configured yet", "chat_test_app": "Debug-{{name}}", "citations": "{{num}} References", + "clear_input_value": "Clear input", "click_contextual_preview": "Click to see contextual preview", "click_to_add_url": "Click to add link", "completion_finish_close": "Disconnection", @@ -24,10 +26,12 @@ "config_input_guide": "Set Up Input Guide", "config_input_guide_lexicon": "Set Up Lexicon", "config_input_guide_lexicon_title": "Set Up Lexicon", + "confirm_clear_input_value": "Are you sure to clear the form content? \nDefault values ​​will be restored!", "confirm_to_clear_share_chat_history": "Are you sure you want to clear all chat history?", "content_empty": "No Content", "contextual": "{{num}} Contexts", "contextual_preview": "Contextual Preview {{num}} Items", + "continue_run": "continue running", "core.chat.moveCancel": "Swipe to Cancel", "core.chat.shortSpeak": "Speaking Time is Too Short", "csv_input_lexicon_tip": "Only CSV batch import is supported, click to download the template", @@ -161,6 +165,7 @@ "source_cronJob": "Scheduled execution", "start_chat": "Start", "stream_output": "Stream Output", + "task_has_continued": "Task has continued running", "unsupported_file_type": "Unsupported file types", "upload": "Upload", "variable_invisable_in_share": "External variables are not visible in login-free links", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 1c91d903b..6061b08a3 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -13,6 +13,7 @@ "Confirm": "Confirm", "Continue_Adding": "Continue adding", "Copy": "Copy", + "Create": "create", "Creating": "Creating", "Delete": "Delete", "Detail": "Detail", @@ -99,7 +100,9 @@ "all_quotes": "All quotes", "all_result": "Full Results", "app_evaluation": "App Evaluation(Beta)", + "app_icon_and_name": "icon", "app_not_version": "This application has not been published, please publish it first", + "app_type": "Application Type", "auth_config": "Authentication", "auth_type": "Authentication type", "auth_type.Custom": "Customize", @@ -754,6 +757,7 @@ "core.workflow.tool.Select Tool": "Select Tool", "core.workflow.variable": "Variable", "create": "Create", + "create_app": "Create an application", "create_failed": "Create failed", "create_success": "Created Successfully", "create_time": "Creation Time", @@ -919,8 +923,8 @@ "name": "name", "name_is_empty": "Name Cannot Be Empty", "navbar.Account": "Account", - "navbar.Chat": "Chat", - "navbar.Config": "administrator", + "navbar.Chat": "portal", + "navbar.Config": "admini", "navbar.Datasets": "Dataset", "navbar.Studio": "Studio", "navbar.Toolkit": "Toolkit", diff --git a/packages/web/i18n/en/user.json b/packages/web/i18n/en/user.json index 6b488c9d2..f92b17567 100644 --- a/packages/web/i18n/en/user.json +++ b/packages/web/i18n/en/user.json @@ -17,6 +17,7 @@ "bill.valid_time": "Effective Time", "bill.you_can_convert": "You can convert", "bill.yuan": "Yuan", + "current_package": "Current package: ", "delete.admin_failed": "Failed to Delete Admin", "delete.admin_success": "Admin Deleted Successfully", "delete.failed": "Delete failed", @@ -87,6 +88,7 @@ "team.add_writer": "Add writable members", "team.avatar_and_name": "avatar", "team.belong_to_group": "Member group", + "team.collaborator.added": "Added", "team.group.avatar": "Group avatar", "team.group.create": "Create group", "team.group.create_failed": "Failed to create group", @@ -110,6 +112,5 @@ "team.manage_collaborators": "Manage Collaborators", "team.no_collaborators": "No Collaborators", "team.org.org": "Organization", - "team.write_role_member": "Write Permission", - "team.collaborator.added": "Added" + "team.write_role_member": "Write Permission" } diff --git a/packages/web/i18n/zh-CN/account.json b/packages/web/i18n/zh-CN/account.json index 45fc7379a..43ba6e269 100644 --- a/packages/web/i18n/zh-CN/account.json +++ b/packages/web/i18n/zh-CN/account.json @@ -12,6 +12,7 @@ "custom_model": "自定义模型", "default_model": "预设模型", "default_model_config": "默认模型配置", + "language": "语言与时区", "logout": "登出", "model.active": "启用", "model.alias": "别名", diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index efe4b2768..bea725aa9 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -53,18 +53,21 @@ "app.version_past": "发布过", "app.version_publish_tips": "该版本将被保存至团队云端,同步给整个团队,同时更新所有发布渠道的应用版本", "app_detail": "应用详情", + "app_name_placeholder": "请输入应用名称", "apply_code": "应用", "apply_code_failed": "应用代码失败", "auto_execute": "自动执行", "auto_execute_default_prompt_placeholder": "自动执行时,发送的默认问题", "auto_execute_tip": "开启后,用户进入对话界面将自动触发工作流。执行顺序:1、对话开场白;2、全局变量;3、自动执行。", "auto_save": "自动保存", + "change_app_type": "更改应用类型", "chat_debug": "调试预览", "chat_logs": "对话日志", "chat_logs_tips": "日志会记录该应用的在线、分享和 API(需填写 chatId)对话记录", "click_to_config": "去配置", "code_applied_successfully": "代码应用成功", "code_function_describe": "描述你的代码功能,输入“/”可选择变量", + "collapse_tool_create": "收起", "config_ai_model_params": "点击配置 AI 模型相关属性", "config_file_upload": "点击配置文件上传规则", "config_question_guide": "配置猜你想问", @@ -80,16 +83,20 @@ "core.module.template.System Tools": "系统工具", "core.workflow.Copilot": "AI 生成", "create_by_curl": "从 CURL 创建", - "create_by_template": "从模板选择", + "create_by_template": "从模板创建", "create_copy_success": "创建副本成功", "create_empty_app": "创建空白应用", "create_empty_plugin": "创建空白插件", "create_empty_workflow": "创建空白工作流", + "create_from_template": "从模板新建", "create_http_toolset": "创建 HTTP 工具集", + "create_your_first_agent": "创建你的第一个 Agent", + "create_your_first_tool": "创建你的第一个工具", "cron.every_day": "每天执行", "cron.every_month": "每月执行", "cron.every_week": "每周执行", "cron.interval": "间隔执行", + "current_package": "当前套餐:", "custom_plugin": "自定义插件", "custom_plugin_associated_plugin_label": "关联插件", "custom_plugin_associated_plugin_placeholder": "输入插件名或 appId 查找插件", @@ -134,6 +141,7 @@ "edit_param": "编辑参数", "empty_tool_tips": "请在左侧添加工具", "execute_time": "执行时间", + "expand_tool_create": "展开MCP、Http创建", "export_config_successful": "已复制配置,自动过滤部分敏感信息,请注意检查是否仍有敏感数据", "export_configs": "导出配置", "fastgpt_marketplace": "FastGPT 插件市场", @@ -146,6 +154,8 @@ "find_more_tools": "探索更多", "go_to_chat": "去对话", "go_to_run": "去运行", + "has_no_create_per": "当前目录您无新建权限", + "hide_templates": "隐藏模板", "http_toolset_add_tips": "点击添加按钮来添加工具", "http_toolset_config_tips": "点击开始配置来添加工具", "image": "图片", @@ -268,6 +278,7 @@ "permission.des.write": "可查看和编辑应用", "permission.name.read": "仅对话", "permission.name.readChatLog": "查看对话日志", + "plugin": "资源库", "plugin.Instructions": "使用说明", "plugin_cost_by_token": "依据 token 消耗计费", "plugin_cost_folder_tip": "该工具集包含下属工具,调用积分依据实际调用工具决定", @@ -287,16 +298,22 @@ "publish_success": "发布成功", "question_guide_tip": "对话结束后,会为你生成 3 个引导性问题。", "reasoning_response": "输出思考", + "recharge": "去充值", "reference_variable": "引用变量", + "refresh_templates": "换一批", + "remaining_points": "剩余积分:", "request_headers": "请求头", "response_format": "回复格式", "save_team_app_log_keys": "保存为团队配置", "saved_success": "保存成功!如需在外部使用该版本,请点击“保存并发布”", + "search_agent": "搜索 Agent", "search_app": "搜索应用", "search_tool": "搜索工具", "secret_get_course": "教程", + "select_app_type": "选择应用类型", "setting_app": "应用配置", "setting_plugin": "插件配置", + "show_templates": "显示模板", "show_top_p_tip": "用温度采样的替代方法,称为Nucleus采样,该模型考虑了具有TOP_P概率质量质量的令牌的结果。因此,0.1表示仅考虑包含最高概率质量的令牌。默认为 1。", "simple_tool_tips": "该插件含有特殊输入,暂不支持被简易应用调用", "source_updateTime": "更新时间", @@ -343,6 +360,9 @@ "time_type": "时间类型", "time_zone": "时区", "too_to_active": "去激活", + "toolType_http": "HTTP 工具", + "toolType_mcp": "MCP 工具", + "toolType_workflow": "工作流工具", "tool_active_manual_config_desc": "临时密钥保存在本应用中,仅供该应用使用", "tool_active_system_config_desc": "使用系统已配置好的密钥", "tool_active_system_config_price_desc": "需额外支付密钥价格( {{price}} 积分/次)", @@ -356,7 +376,6 @@ "tool_soon_offset_tips": "该工具将在后续下线,为了您的业务稳定,请尽快替换", "tool_tip": "作为工具执行时,该字段是否作为工具响应结果", "tool_type_tools": "工具", - "plugin": "资源库", "toolkit_activation_label": "密钥激活", "toolkit_activation_not_required": "免激活", "toolkit_activation_required": "需要激活", @@ -428,6 +447,7 @@ "tts_browser": "浏览器自带(免费)", "tts_close": "关闭", "type.All": "全部", + "type.Chat_Agent": "对话 Agent", "type.Create http plugin tip": "通过 OpenAPI Schema 批量创建工具(兼容GPTs)、通过curl或手动创建工具", "type.Create http toolset tip": "通过 OpenAPI Schema 批量创建工具(兼容GPTs)", "type.Create mcp tools tip": "通过输入 MCP 地址,自动解析并批量创建可调用的 MCP 工具", @@ -459,8 +479,18 @@ "type.error.Workflow data is empty": "没有获取到工作流数据", "type.error.workflowresponseempty": "响应内容为空", "type.hidden": "隐藏应用", + "type_http_tool_set_intro": "批量导入 API 工具", + "type_mcp_intro": "连接 MCP 服务", "type_not_recognized": "未识别到应用类型", + "type_plugin_description": "一键输出指定结果。", + "type_plugin_intro": "常用于封装工作流", + "type_simple_description": "最基础的对话应用,无需复杂配置,快速创建和使用。", + "type_simple_intro": "快速创建对话 Agent", + "type_workflow_description": "支持记忆的复杂多轮对话工作流。", + "type_workflow_intro": "拖拽编排与多轮对话", "un_auth": "无权限", + "unnamed_app": "未命名", + "upgrade": "去升级", "upload_file_exists_filtered": "已自动过滤重复文件", "upload_file_extension_type_canSelectAudio": "音频", "upload_file_extension_type_canSelectCustomFileExtension": "自定义文件扩展类型", @@ -473,6 +503,7 @@ "upload_file_max_amount_tip": "单轮对话中最大上传文件数量", "upload_method": "上传方式", "url_upload": "文件链接", + "used_points": "积分用量:", "variable.internal_type_desc": "仅在工作流内部使用,不会出现在对话框中", "variable.select type_desc": "会在站内对话和运行预览中显示输入框,在分享链接中不会显示此变量", "variable.textarea_type_desc": "允许用户最多输入4000字的对话框。", diff --git a/packages/web/i18n/zh-CN/chat.json b/packages/web/i18n/zh-CN/chat.json index 3123d3576..3117883f7 100644 --- a/packages/web/i18n/zh-CN/chat.json +++ b/packages/web/i18n/zh-CN/chat.json @@ -4,6 +4,7 @@ "LLM_model_response_empty": "模型流响应为空,请检查模型流输出是否正常", "ai_reasoning": "思考过程", "back_to_text": "返回输入", + "balance_not_enough_pause": "由于 AI 积分不足,暂停运行工作流", "chat.quote.No Data": "找不到该文件", "chat.quote.deleted": "该数据已被删除~", "chat.waiting_for_response": "请等待对话完成", @@ -11,6 +12,7 @@ "chat_input_guide_lexicon_is_empty": "还没有配置词库", "chat_test_app": "调试-{{name}}", "citations": "{{num}}条引用", + "clear_input_value": "清空输入", "click_contextual_preview": "点击查看上下文预览", "click_to_add_url": "点击添加链接", "completion_finish_close": "连接断开", @@ -24,10 +26,12 @@ "config_input_guide": "配置输入引导", "config_input_guide_lexicon": "配置词库", "config_input_guide_lexicon_title": "配置词库", + "confirm_clear_input_value": "确认清空表单内容?将会恢复默认值!", "confirm_to_clear_share_chat_history": "确认清空所有聊天记录?", "content_empty": "内容为空", "contextual": "{{num}}条上下文", "contextual_preview": "上下文预览 {{num}} 条", + "continue_run": "继续运行", "core.chat.moveCancel": "上滑取消", "core.chat.shortSpeak": "说话时间太短", "csv_input_lexicon_tip": "仅支持 CSV 批量导入,点击下载模板", @@ -164,6 +168,7 @@ "source_cronJob": "定时执行", "start_chat": "开始对话", "stream_output": "流输出", + "task_has_continued": "任务已继续运行", "unsupported_file_type": "不支持的文件类型", "upload": "上传", "variable_invisable_in_share": "外部变量在免登录链接中不可见", diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 049389a6e..bfed3a4a3 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -13,6 +13,7 @@ "Confirm": "确认", "Continue_Adding": "继续添加", "Copy": "复制", + "Create": "创建", "Creating": "创建中", "Delete": "删除", "Detail": "详情", @@ -98,8 +99,10 @@ "add_success": "添加成功", "all_quotes": "全部引用", "all_result": "完整结果", - "app_evaluation": "应用评测(Beta)", + "app_evaluation": "Agent 评测(Beta)", + "app_icon_and_name": "图标 & 名称", "app_not_version": " 该应用未发布过,请先发布应用", + "app_type": "应用类型", "auth_config": "鉴权配置", "auth_type": "鉴权类型", "auth_type.Custom": "自定义", @@ -671,6 +674,7 @@ "core.module.template.ai_chat_intro": "AI 大模型对话", "core.module.template.all_team_app": "全部", "core.module.template.config_params": "可以配置应用的系统参数", + "core.module.template.empty_agent": "空白 Agent", "core.module.template.empty_plugin": "空白插件", "core.module.template.empty_workflow": "空白工作流", "core.module.template.self_input": "插件输入", @@ -756,6 +760,7 @@ "core.workflow.tool.Select Tool": "选择工具", "core.workflow.variable": "变量", "create": "去创建", + "create_app": "创建应用", "create_failed": "创建失败", "create_success": "创建成功", "create_time": "创建时间", @@ -921,12 +926,12 @@ "name": "名称", "name_is_empty": "名称不能为空", "navbar.Account": "账号", - "navbar.Chat": "聊天", + "navbar.Chat": "门户", "navbar.Config": "管理员", "navbar.Datasets": "知识库", "navbar.Studio": "工作台", "navbar.Toolkit": "工具箱", - "navbar.Tools": "工具", + "navbar.Tools": "我的工具", "navbar.plugin": "插件库", "new_create": "新建", "next_step": "下一步", diff --git a/packages/web/i18n/zh-CN/user.json b/packages/web/i18n/zh-CN/user.json index 62039b19b..8a1dd8017 100644 --- a/packages/web/i18n/zh-CN/user.json +++ b/packages/web/i18n/zh-CN/user.json @@ -17,6 +17,8 @@ "bill.valid_time": "生效时间", "bill.you_can_convert": "您可兑换", "bill.yuan": "元", + "common.current_package": "当前套餐", + "current_package": "当前套餐:", "delete.admin_failed": "删除管理员失败", "delete.admin_success": "删除管理员成功", "delete.failed": "删除失败", diff --git a/packages/web/i18n/zh-Hant/account.json b/packages/web/i18n/zh-Hant/account.json index d530ac216..f8d7f0c52 100644 --- a/packages/web/i18n/zh-Hant/account.json +++ b/packages/web/i18n/zh-Hant/account.json @@ -12,6 +12,7 @@ "custom_model": "自訂模型", "default_model": "預設模型", "default_model_config": "預設模型設定", + "language": "語言與時區", "logout": "登出", "model.active": "啟用", "model.alias": "別名", diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index 7df4bb65e..856db8f1b 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -51,18 +51,21 @@ "app.version_past": "已發布過", "app.version_publish_tips": "此版本將儲存至團隊雲端,同步給整個團隊,同時更新所有發布通道的應用程式版本", "app_detail": "應用程式詳細資訊", + "app_name_placeholder": "請輸入應用名稱", "apply_code": "應用", "apply_code_failed": "應用代碼失敗", "auto_execute": "自動執行", "auto_execute_default_prompt_placeholder": "自動執行時,傳送的預設問題", "auto_execute_tip": "開啟後,使用者進入對話式介面將自動觸發工作流程。\n執行順序:1、對話開場白;2、全域變數;3、自動執行。", "auto_save": "自動儲存", + "change_app_type": "更改應用程式類型", "chat_debug": "聊天預覽", "chat_logs": "對話紀錄", "chat_logs_tips": "紀錄會記錄此應用程式的線上、分享和 API(需填寫 chatId)對話紀錄", "click_to_config": "去配置", "code_applied_successfully": "代碼應用成功", "code_function_describe": "描述你的代碼功能,輸入“/”可選擇變量", + "collapse_tool_create": "收起", "config_ai_model_params": "點選設定 AI 模型相關屬性", "config_file_upload": "點選設定檔案上傳規則", "config_question_guide": "設定猜你想問", @@ -83,11 +86,14 @@ "create_empty_app": "建立空白應用程式", "create_empty_plugin": "建立空白外掛", "create_empty_workflow": "建立空白工作流程", + "create_from_template": "從模板新建", "create_http_toolset": "創建 HTTP 工具集", + "create_your_first_agent": "創建你的第一個 Agent", "cron.every_day": "每天執行", "cron.every_month": "每月執行", "cron.every_week": "每週執行", "cron.interval": "間隔執行", + "current_package": "當前套餐:", "custom_plugin": "自訂外掛", "custom_plugin_associated_plugin_label": "關聯外掛", "custom_plugin_associated_plugin_placeholder": "輸入外掛名稱或 appId 尋找外掛", @@ -130,6 +136,7 @@ "edit_info": "編輯資訊", "empty_tool_tips": "請在左側添加工具", "execute_time": "執行時間", + "expand_tool_create": "展開 MCP、Http 創建", "export_config_successful": "已複製設定,自動過濾部分敏感資訊,請注意檢查是否仍有敏感資料", "export_configs": "匯出設定", "fastgpt_marketplace": "FastGPT 插件市場", @@ -142,6 +149,8 @@ "find_more_tools": "探索更多", "go_to_chat": "前往對話", "go_to_run": "前往執行", + "has_no_create_per": "當前目錄您無新建權限", + "hide_templates": "隱藏模板", "http_toolset_add_tips": "點擊添加按鈕來添加工具", "http_toolset_config_tips": "點擊開始配置來添加工具", "image": "圖片", @@ -259,6 +268,7 @@ "plugin_cost_by_token": "根據 token 消耗計費", "plugin_cost_folder_tip": "該工具集包含下屬工具,調用積分依據實際調用工具決定", "plugin_cost_per_times": "{{cost}} 積分/次", + "plugin_db": "資源庫", "plugin_dispatch": "外掛呼叫", "plugin_dispatch_tip": "賦予模型取得外部資料的能力,具體呼叫哪些外掛,將由模型自主決定,所有外掛都將以非串流模式執行。\n若選擇了外掛,知識庫呼叫將自動作為一個特殊的外掛。", "plugin_offline_tips": "您的系統無法直接訪問插件市場數據,\n請手動複製網址並前往,以獲得更多插件", @@ -274,16 +284,22 @@ "publish_success": "發布成功", "question_guide_tip": "對話結束後,會為你產生 3 個引導性問題。", "reasoning_response": "輸出思考", + "recharge": "去充值", "reference_variable": "引用變量", + "refresh_templates": "換一批", + "remaining_points": "剩餘積分:", "request_headers": "請求頭", "response_format": "回覆格式", "save_team_app_log_keys": "保存為團隊配置", "saved_success": "儲存成功!\n如需在外部使用該版本,請點選“儲存並發布”", + "search_agent": "搜索 Agent", "search_app": "搜尋應用程式", "search_tool": "搜索工具", "secret_get_course": "教程", + "select_app_type": "選擇應用程式類型", "setting_app": "應用程式設定", "setting_plugin": "外掛設定", + "show_templates": "顯示模板", "show_top_p_tip": "用溫度取樣的替代方法,稱為 Nucleus 取樣,該模型考慮了具有 TOP_P 機率質量質量的令牌的結果。\n因此,0.1 表示僅考慮包含最高機率質量的令牌。\n預設為 1。", "simple_tool_tips": "該外掛含有特殊輸入,暫不支援被簡易應用呼叫", "source_updateTime": "更新時間", @@ -326,6 +342,9 @@ "time_type": "時間類型", "time_zone": "時區", "too_to_active": "去激活", + "toolType_http": "HTTP 工具", + "toolType_mcp": "MCP 工具", + "toolType_workflow": "工作流工具", "tool_active_manual_config_desc": "臨時密鑰保存在本應用中,僅供該應用使用", "tool_active_system_config_desc": "使用系統已配置好的密鑰", "tool_active_system_config_price_desc": "需額外支付密鑰價格( {{price}} 積分/次)", @@ -339,7 +358,6 @@ "tool_soon_offset_tips": "該工具將在後續下線,為了您的業務穩定,請盡快替換", "tool_tip": "作為工具執行時,該字段是否作為工具響應結果", "tool_type_tools": "工具", - "plugin_db": "資源庫", "toolkit_activation_label": "密鑰激活", "toolkit_activation_not_required": "免激活", "toolkit_activation_required": "需要激活", @@ -440,8 +458,18 @@ "type.error.Workflow data is empty": "沒有獲取到工作流數據", "type.error.workflowresponseempty": "響應內容為空", "type.hidden": "隱藏應用", + "type_http_tool_set_intro": "批量導入 API 工具", + "type_mcp_intro": "連接 MCP 服務", "type_not_recognized": "未識別到應用程式類型", + "type_plugin_description": "一鍵輸出指定結果。", + "type_plugin_intro": "常用於封裝工作流", + "type_simple_description": "最基礎的對話應用,無需複雜配置,快速創建和使用。", + "type_simple_intro": "快速創建對話 Agent", + "type_workflow_description": "支持記憶的複雜多輪對話工作流。", + "type_workflow_intro": "拖拽編排與多輪對話", "un_auth": "無權限", + "unnamed_app": "未命名", + "upgrade": "去升級", "upload_file_exists_filtered": "已自動過濾重複檔案", "upload_file_extension_type_canSelectAudio": "音頻", "upload_file_extension_type_canSelectCustomFileExtension": "自定義文件擴展類型", @@ -454,6 +482,7 @@ "upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量", "upload_method": "上傳方式", "url_upload": "文件鏈接", + "used_points": "積分用量:", "variable.internal_type_desc": "僅在工作流內部使用,不會出現在對話框中", "variable.select type_desc": "會在站內對話和運行預覽中顯示輸入框,在分享鏈接中不會顯示此變量", "variable.textarea_type_desc": "允許使用者最多輸入 4000 字的對話框。", diff --git a/packages/web/i18n/zh-Hant/chat.json b/packages/web/i18n/zh-Hant/chat.json index ff04f633d..950ce7bee 100644 --- a/packages/web/i18n/zh-Hant/chat.json +++ b/packages/web/i18n/zh-Hant/chat.json @@ -4,6 +4,7 @@ "LLM_model_response_empty": "模型流程回應為空,請檢查模型流程輸出是否正常", "ai_reasoning": "思考過程", "back_to_text": "返回輸入", + "balance_not_enough_pause": "由於 AI 積分不足,暫停運行工作流", "chat.quote.No Data": "找不到該文件", "chat.quote.deleted": "該資料已被刪除~", "chat.waiting_for_response": "請等待對話完成", @@ -11,6 +12,7 @@ "chat_input_guide_lexicon_is_empty": "尚未設定詞彙庫", "chat_test_app": "除錯-{{name}}", "citations": "{{num}} 筆引用", + "clear_input_value": "清空輸入", "click_contextual_preview": "點選檢視上下文預覽", "click_to_add_url": "點擊添加鏈接", "completion_finish_close": "連接斷開", @@ -24,10 +26,12 @@ "config_input_guide": "設定輸入導引", "config_input_guide_lexicon": "設定詞彙庫", "config_input_guide_lexicon_title": "設定詞彙庫", + "confirm_clear_input_value": "確認清空表單內容?\n將會恢復默認值!", "confirm_to_clear_share_chat_history": "確認清空所有聊天記錄?", "content_empty": "無內容", "contextual": "{{num}} 筆上下文", "contextual_preview": "上下文預覽 {{num}} 筆", + "continue_run": "繼續運行", "core.chat.moveCancel": "上滑取消", "core.chat.shortSpeak": "說話時間太短", "csv_input_lexicon_tip": "僅支援 CSV 批次匯入,點選下載範本", @@ -161,6 +165,7 @@ "source_cronJob": "定時執行", "start_chat": "開始對話", "stream_output": "串流輸出", + "task_has_continued": "任務已繼續運行", "unsupported_file_type": "不支援的檔案類型", "upload": "上傳", "variable_invisable_in_share": "外部變量在免登錄鏈接中不可見", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index e6df24186..7b96ecea5 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -13,6 +13,7 @@ "Confirm": "確認", "Continue_Adding": "繼續新增", "Copy": "複製", + "Create": "創建", "Creating": "建立中", "Delete": "刪除", "Detail": "詳細資料", @@ -99,7 +100,9 @@ "all_quotes": "全部引用", "all_result": "完整結果", "app_evaluation": "應用評測(Beta)", + "app_icon_and_name": "圖示", "app_not_version": "該應用未發布過,請先發布應用", + "app_type": "應用類型", "auth_config": "鑑權配置", "auth_type": "鑑權類型", "auth_type.Custom": "自定義", @@ -753,6 +756,7 @@ "core.workflow.tool.Select Tool": "選擇工具", "core.workflow.variable": "變數", "create": "建立", + "create_app": "創建應用", "create_failed": "建立失敗", "create_success": "建立成功", "create_time": "建立時間", @@ -918,11 +922,11 @@ "name": "名稱", "name_is_empty": "名稱不能為空", "navbar.Account": "帳戶", - "navbar.Chat": "對話", + "navbar.Chat": "門戶", "navbar.Datasets": "知識庫", "navbar.Studio": "工作區", "navbar.Toolkit": "工具箱", - "navbar.Tools": "工具", + "navbar.Tools": "我的工具", "navbar.plugin": "插件庫", "new_create": "建立新項目", "next_step": "下一步", diff --git a/packages/web/i18n/zh-Hant/user.json b/packages/web/i18n/zh-Hant/user.json index c08cb9a43..89931a03f 100644 --- a/packages/web/i18n/zh-Hant/user.json +++ b/packages/web/i18n/zh-Hant/user.json @@ -17,6 +17,7 @@ "bill.valid_time": "生效時間", "bill.you_can_convert": "您可兌換", "bill.yuan": "元", + "current_package": "當前套餐:", "delete.admin_failed": "刪除管理員失敗", "delete.admin_success": "刪除管理員成功", "delete.failed": "刪除失敗", @@ -87,6 +88,7 @@ "team.add_writer": "新增可寫入成員", "team.avatar_and_name": "頭像與名稱", "team.belong_to_group": "所屬成員群組", + "team.collaborator.added": "已新增", "team.group.avatar": "群組頭像", "team.group.create": "建立群組", "team.group.create_failed": "建立群組失敗", @@ -110,6 +112,5 @@ "team.manage_collaborators": "管理協作者", "team.no_collaborators": "目前沒有協作者", "team.org.org": "組織", - "team.write_role_member": "可寫入權限", - "team.collaborator.added": "已新增" + "team.write_role_member": "可寫入權限" } diff --git a/projects/app/public/imgs/app/agentPreview.svg b/projects/app/public/imgs/app/agentPreview.svg new file mode 100644 index 000000000..36492a657 --- /dev/null +++ b/projects/app/public/imgs/app/agentPreview.svg @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/projects/app/public/imgs/app/createButton.png b/projects/app/public/imgs/app/createButton.png new file mode 100644 index 0000000000000000000000000000000000000000..189d1260a9edc4a2b8d8cca0ce069fb08ff0f658 GIT binary patch literal 203772 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

siCwnHNs1 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 $Zdb@LiD+UZW+B+f%qkI=h-xi01R9EQ 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 SVUZ;O0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/app/public/imgs/app/mcpToolsPreview.svg b/projects/app/public/imgs/app/mcpToolsPreview.svg new file mode 100644 index 000000000..484178ad0 --- /dev/null +++ b/projects/app/public/imgs/app/mcpToolsPreview.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/app/public/imgs/app/moreTemplateBg.svg b/projects/app/public/imgs/app/moreTemplateBg.svg new file mode 100644 index 000000000..cc68c339c --- /dev/null +++ b/projects/app/public/imgs/app/moreTemplateBg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/app/pluginPreview.svg b/projects/app/public/imgs/app/pluginPreview.svg new file mode 100644 index 000000000..bfd903bb8 --- /dev/null +++ b/projects/app/public/imgs/app/pluginPreview.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/app/simpleAgentPreview.svg b/projects/app/public/imgs/app/simpleAgentPreview.svg new file mode 100644 index 000000000..384c584e4 --- /dev/null +++ b/projects/app/public/imgs/app/simpleAgentPreview.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/app/public/imgs/app/simpleDeprecated.svg b/projects/app/public/imgs/app/simpleDeprecated.svg new file mode 100644 index 000000000..91ae16e7b --- /dev/null +++ b/projects/app/public/imgs/app/simpleDeprecated.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/app/public/imgs/app/workflowPreview.svg b/projects/app/public/imgs/app/workflowPreview.svg new file mode 100644 index 000000000..ec251639f --- /dev/null +++ b/projects/app/public/imgs/app/workflowPreview.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/projects/app/public/imgs/bot.svg b/projects/app/public/imgs/bot.svg new file mode 100644 index 000000000..405753439 --- /dev/null +++ b/projects/app/public/imgs/bot.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/app/public/imgs/botClosed.svg b/projects/app/public/imgs/botClosed.svg new file mode 100644 index 000000000..4e71c9813 --- /dev/null +++ b/projects/app/public/imgs/botClosed.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/app/public/imgs/botTextCN.svg b/projects/app/public/imgs/botTextCN.svg new file mode 100644 index 000000000..dce434c9c --- /dev/null +++ b/projects/app/public/imgs/botTextCN.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/projects/app/public/imgs/botTextEn.svg b/projects/app/public/imgs/botTextEn.svg new file mode 100644 index 000000000..8c106e010 --- /dev/null +++ b/projects/app/public/imgs/botTextEn.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/projects/app/src/components/Layout/HelperBot.tsx b/projects/app/src/components/Layout/HelperBot.tsx new file mode 100644 index 000000000..0ca32ab4c --- /dev/null +++ b/projects/app/src/components/Layout/HelperBot.tsx @@ -0,0 +1,205 @@ +import { Box, Flex, Spinner, Center } from '@chakra-ui/react'; +import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import { useToggle } from 'ahooks'; +import { useRouter } from 'next/router'; +import { useState, useEffect, useMemo } from 'react'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { StandardSubLevelEnum } from '@fastgpt/global/support/wallet/sub/constants'; +import { useTranslation } from 'next-i18next'; +import { getWorkorderURL } from '@/web/common/workorder/api'; + +const BotShowRouter: { [key: string]: boolean } = { + '/dashboard/agent': true, + '/dashboard/tool': true, + '/dashboard/systemTool': true, + '/dashboard/templateMarket': true, + '/dashboard/mcpServer': true, + '/dashboard/evaluation': true, + '/dataset/list': true, + '/account/info': true +}; + +const HelperBot = () => { + const router = useRouter(); + const { i18n } = useTranslation(); + const [open, setOpen] = useToggle(true); + const [showChat, setShowChat] = useToggle(false); + const [isLoading, setIsLoading] = useState(true); + + const { feConfigs, subPlans } = useSystemStore(); + const { teamPlanStatus } = useUserStore(); + const isPlanUser = useMemo(() => { + if (!teamPlanStatus) return false; + if (teamPlanStatus.standard?.currentSubLevel !== StandardSubLevelEnum.free) return true; + if (teamPlanStatus.datasetMaxSize !== subPlans?.standard?.free?.maxDatasetSize) return true; + if (teamPlanStatus.totalPoints !== subPlans?.standard?.free?.totalPoints) return true; + return false; + }, [ + subPlans?.standard?.free?.maxDatasetSize, + subPlans?.standard?.free?.totalPoints, + teamPlanStatus + ]); + + const showWorkorder = feConfigs?.show_workorder && isPlanUser; + const botIframeUrl = feConfigs?.botIframeUrl; + + useEffect(() => { + if (showChat) { + setIsLoading(true); + } + }, [showChat]); + + useEffect(() => { + const handleMessage = async (event: MessageEvent) => { + if (event.data?.type === 'workorderRequest') { + try { + const data = await getWorkorderURL(); + if (data?.redirectUrl) { + window.open(data.redirectUrl); + } + } catch (error) { + console.error('Failed to create workorder:', error); + } + } + }; + + window.addEventListener('message', handleMessage); + return () => window.removeEventListener('message', handleMessage); + }, []); + + if (!botIframeUrl || !BotShowRouter[router.pathname]) { + return null; + } + + return ( + <> + {showChat && open && ( + + {isLoading && ( +

+ + + +
+ )} + setShowChat.set(false)} + > + + + {/* iframe */} + setIsLoading(false)} + /> + + )} + + + {open && ( + <> + { + e.stopPropagation(); + setOpen.set(false); + setShowChat.set(false); + }} + > + + + {!showChat && ( + + {i18n.language === 'zh-CN' ? ( + + ) : ( + + )} + + )} + + )} + + { + if (open) { + setShowChat.toggle(); + } else { + setOpen.set(true); + } + }} + /> + + + ); +}; + +export default HelperBot; diff --git a/projects/app/src/components/Layout/WorkorderButton.tsx b/projects/app/src/components/Layout/WorkorderButton.tsx deleted file mode 100644 index 7177b46c5..000000000 --- a/projects/app/src/components/Layout/WorkorderButton.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { getWorkorderURL } from '@/web/common/workorder/api'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import { Box, Flex } from '@chakra-ui/react'; -import { StandardSubLevelEnum } from '@fastgpt/global/support/wallet/sub/constants'; -import Icon from '@fastgpt/web/components/common/Icon'; -import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { useSystem } from '@fastgpt/web/hooks/useSystem'; -import { useToggle } from 'ahooks'; -import { useTranslation } from 'next-i18next'; -import { useRouter } from 'next/router'; -import { useMemo } from 'react'; - -const WorkOrderShowRouter: { [key: string]: boolean } = { - '/dashboard/apps': true, - '/dataset/list': true, - '/plugin': true -}; - -function WorkorderButton() { - const router = useRouter(); - const [open, setOpen] = useToggle(true); - const { t } = useTranslation(); - - const { feConfigs, subPlans } = useSystemStore(); - const { teamPlanStatus } = useUserStore(); - - const { isPc } = useSystem(); - - const { runAsync: onFeedback } = useRequest2(getWorkorderURL, { - manual: true, - onSuccess(data) { - if (data) { - window.open(data.redirectUrl); - } - } - }); - - const showWorkorder = WorkOrderShowRouter[router.pathname]; - - const isPlanUser = useMemo(() => { - if (!teamPlanStatus) return false; - if (teamPlanStatus.standard?.currentSubLevel !== StandardSubLevelEnum.free) return true; - if (teamPlanStatus.datasetMaxSize !== subPlans?.standard?.free?.maxDatasetSize) return true; - if (teamPlanStatus.totalPoints !== subPlans?.standard?.free?.totalPoints) return true; - return false; - }, [ - subPlans?.standard?.free?.maxDatasetSize, - subPlans?.standard?.free?.totalPoints, - teamPlanStatus - ]); - - return showWorkorder && feConfigs?.show_workorder && isPlanUser && isPc ? ( - <> - {open ? ( - - setOpen.set(false)} - > - - - - - - {t('common:question_feedback')} - - - - ) : ( - setOpen.set(true)} - > - - - )} - - ) : null; -} - -export default WorkorderButton; diff --git a/projects/app/src/components/Layout/index.tsx b/projects/app/src/components/Layout/index.tsx index 9d49ff1b7..7ddc37453 100644 --- a/projects/app/src/components/Layout/index.tsx +++ b/projects/app/src/components/Layout/index.tsx @@ -14,8 +14,8 @@ import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useDebounceEffect, useMount } from 'ahooks'; import { useTranslation } from 'next-i18next'; import { useToast } from '@fastgpt/web/hooks/useToast'; -import WorkorderButton from './WorkorderButton'; import { useCheckCoupon } from './hooks/checkCoupon'; +import HelperBot from './HelperBot'; const Navbar = dynamic(() => import('./navbar')); const NavbarPhone = dynamic(() => import('./navbarPhone')); @@ -176,7 +176,7 @@ const Layout = ({ children }: { children: JSX.Element }) => { )} - + )} diff --git a/projects/app/src/components/Layout/navbar.tsx b/projects/app/src/components/Layout/navbar.tsx index fecc08db3..e07ade1be 100644 --- a/projects/app/src/components/Layout/navbar.tsx +++ b/projects/app/src/components/Layout/navbar.tsx @@ -11,6 +11,8 @@ import { useTranslation } from 'next-i18next'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; +import MyImage from '@fastgpt/web/components/common/Image/MyImage'; +import { LOGO_ICON } from '@fastgpt/global/common/system/constants'; export enum NavbarTypeEnum { normal = 'normal', @@ -55,12 +57,14 @@ const Navbar = ({ unread }: { unread: number }) => { label: t('common:navbar.Studio'), icon: 'navbar/dashboardLight', activeIcon: 'navbar/dashboardFill', - link: `/dashboard/apps`, + link: `/dashboard/agent`, activeLink: [ - '/dashboard/apps', + '/dashboard/agent', + '/dashboard/create', '/app/detail', + '/dashboard/tool', + '/dashboard/systemTool', '/dashboard/templateMarket', - '/dashboard/[pluginGroupId]', '/dashboard/mcpServer', '/dashboard/evaluation', '/dashboard/evaluation/create' @@ -73,13 +77,6 @@ const Navbar = ({ unread }: { unread: number }) => { link: `/dataset/list`, activeLink: ['/dataset/list', '/dataset/detail'] }, - { - label: t('common:navbar.plugin'), - icon: 'core/app/pluginLight', - activeIcon: 'core/app/pluginFill', - link: '/plugin/tool', - activeLink: ['/plugin/tool'] - }, { label: t('common:navbar.Account'), icon: 'navbar/userLight', @@ -129,16 +126,8 @@ const Navbar = ({ unread }: { unread: number }) => { bg={isSecondNavbarPage ? 'myGray.50' : 'transparent'} > {/* logo */} - router.push('/account/info')} - > - + + {/* 导航列表 */} @@ -254,6 +243,10 @@ const Navbar = ({ unread }: { unread: number }) => { )} + + router.push('/account/info')}> + + ); }; diff --git a/projects/app/src/components/Layout/navbarPhone.tsx b/projects/app/src/components/Layout/navbarPhone.tsx index 338d98a3a..9a1dd249e 100644 --- a/projects/app/src/components/Layout/navbarPhone.tsx +++ b/projects/app/src/components/Layout/navbarPhone.tsx @@ -27,12 +27,13 @@ const NavbarPhone = ({ unread }: { unread: number }) => { label: t('common:navbar.Studio'), icon: 'core/app/aiLight', activeIcon: 'core/app/aiFill', - link: `/dashboard/apps`, + link: `/dashboard/agent`, activeLink: [ - '/dashboard/apps', + '/dashboard/agent', '/app/detail', + '/dashboard/tool', + '/dashboard/systemTool', '/dashboard/templateMarket', - '/dashboard/[pluginGroupId]', '/dashboard/mcpServer', '/dashboard/evaluation', '/dashboard/evaluation/create' @@ -47,13 +48,6 @@ const NavbarPhone = ({ unread }: { unread: number }) => { activeLink: ['/dataset/list', '/dataset/detail'], unread: 0 }, - { - label: t('common:navbar.plugin'), - icon: 'core/app/pluginLight', - activeIcon: 'core/app/pluginFill', - link: '/plugin/tool', - activeLink: ['/plugin/tool'] - }, { label: t('common:navbar.Account'), icon: 'support/user/userLight', diff --git a/projects/app/src/components/common/Textarea/MyTextarea/index.tsx b/projects/app/src/components/common/Textarea/MyTextarea/index.tsx index 4108261f3..c4c95aa24 100644 --- a/projects/app/src/components/common/Textarea/MyTextarea/index.tsx +++ b/projects/app/src/components/common/Textarea/MyTextarea/index.tsx @@ -123,42 +123,45 @@ const Editor = React.memo(function Editor({ }, []); return ( - -