Skip to content

Commit f0905f5

Browse files
committed
fix: workflow (#5779)
* fix: toolresponse result * remove log * fix: value check * fix: workflow
1 parent 0c6d836 commit f0905f5

File tree

7 files changed

+1340
-181
lines changed

7 files changed

+1340
-181
lines changed

CLAUDE.md

Lines changed: 118 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,120 @@
11
# CLAUDE.md
22

3-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4-
5-
## Project Overview
6-
7-
FastGPT is an AI Agent construction platform providing out-of-the-box data processing, model invocation capabilities, and visual workflow orchestration through Flow. This is a full-stack TypeScript application built on NextJS with MongoDB/PostgreSQL backends.
8-
9-
**Tech Stack**: NextJS + TypeScript + ChakraUI + MongoDB + PostgreSQL (PG Vector)/Milvus
10-
11-
## Architecture
12-
13-
This is a monorepo using pnpm workspaces with the following key structure:
14-
15-
### Packages (Library Code)
16-
- `packages/global/` - Shared types, constants, utilities used across all projects
17-
- `packages/service/` - Backend services, database schemas, API controllers, workflow engine
18-
- `packages/web/` - Shared frontend components, hooks, styles, i18n
19-
- `packages/templates/` - Application templates for the template market
20-
21-
### Projects (Applications)
22-
- `projects/app/` - Main NextJS web application (frontend + API routes)
23-
- `projects/sandbox/` - NestJS code execution sandbox service
24-
- `projects/mcp_server/` - Model Context Protocol server implementation
25-
26-
### Key Directories
27-
- `document/` - Documentation site (NextJS app with content)
28-
- `plugins/` - External plugins (models, crawlers, etc.)
29-
- `deploy/` - Docker and Helm deployment configurations
30-
- `test/` - Centralized test files and utilities
31-
32-
## Development Commands
33-
34-
### Main Commands (run from project root)
35-
- `pnpm dev` - Start development for all projects (uses package.json workspace scripts)
36-
- `pnpm build` - Build all projects
37-
- `pnpm test` - Run tests using Vitest
38-
- `pnpm test:workflow` - Run workflow-specific tests
39-
- `pnpm lint` - Run ESLint across all TypeScript files with auto-fix
40-
- `pnpm format-code` - Format code using Prettier
41-
42-
### Project-Specific Commands
43-
**Main App (projects/app/)**:
44-
- `cd projects/app && pnpm dev` - Start NextJS dev server
45-
- `cd projects/app && pnpm build` - Build NextJS app
46-
- `cd projects/app && pnpm start` - Start production server
47-
48-
**Sandbox (projects/sandbox/)**:
49-
- `cd projects/sandbox && pnpm dev` - Start NestJS dev server with watch mode
50-
- `cd projects/sandbox && pnpm build` - Build NestJS app
51-
- `cd projects/sandbox && pnpm test` - Run Jest tests
52-
53-
**MCP Server (projects/mcp_server/)**:
54-
- `cd projects/mcp_server && bun dev` - Start with Bun in watch mode
55-
- `cd projects/mcp_server && bun build` - Build MCP server
56-
- `cd projects/mcp_server && bun start` - Start MCP server
57-
58-
### Utility Commands
59-
- `pnpm create:i18n` - Generate i18n translation files
60-
- `pnpm api:gen` - Generate OpenAPI documentation
61-
- `pnpm initIcon` - Initialize icon assets
62-
- `pnpm gen:theme-typings` - Generate Chakra UI theme typings
63-
64-
## Testing
65-
66-
The project uses Vitest for testing with coverage reporting. Key test commands:
67-
- `pnpm test` - Run all tests
68-
- `pnpm test:workflow` - Run workflow tests specifically
69-
- Test files are located in `test/` directory and `projects/app/test/`
70-
- Coverage reports are generated in `coverage/` directory
71-
72-
## Code Organization Patterns
73-
74-
### Monorepo Structure
75-
- Shared code lives in `packages/` and is imported using workspace references
76-
- Each project in `projects/` is a standalone application
77-
- Use `@fastgpt/global`, `@fastgpt/service`, `@fastgpt/web` imports for shared packages
78-
79-
### API Structure
80-
- NextJS API routes in `projects/app/src/pages/api/`
81-
- Core business logic in `packages/service/core/`
82-
- Database schemas in `packages/service/` with MongoDB/Mongoose
83-
84-
### Frontend Architecture
85-
- React components in `projects/app/src/components/` and `packages/web/components/`
86-
- Chakra UI for styling with custom theme in `packages/web/styles/theme.ts`
87-
- i18n support with files in `packages/web/i18n/`
88-
- State management using React Context and Zustand
89-
90-
### Workflow System
91-
- Visual workflow editor using ReactFlow
92-
- Workflow engine in `packages/service/core/workflow/`
93-
- Node definitions in `packages/global/core/workflow/template/`
94-
- Dispatch system for executing workflow nodes
95-
96-
## Development Notes
97-
98-
- **Package Manager**: Uses pnpm with workspace configuration
99-
- **Node Version**: Requires Node.js >=18.16.0, pnpm >=9.0.0
100-
- **Database**: Supports MongoDB, PostgreSQL with pgvector, or Milvus for vector storage
101-
- **AI Integration**: Supports multiple AI providers through unified interface
102-
- **Internationalization**: Full i18n support for Chinese, English, and Japanese
103-
104-
## Key File Patterns
105-
106-
- `.ts` and `.tsx` files use TypeScript throughout
107-
- Database schemas use Mongoose with TypeScript
108-
- API routes follow NextJS conventions
109-
- Component files use React functional components with hooks
110-
- Shared types defined in `packages/global/` with `.d.ts` files
111-
112-
## Environment Configuration
113-
114-
- Configuration files in `projects/app/data/config.json`
115-
- Environment-specific configs supported
116-
- Model configurations in `packages/service/core/ai/config/`
3+
本文件为 Claude Code (claude.ai/code) 在本仓库中工作时提供指导说明。
4+
5+
## 语言
6+
7+
中文
8+
9+
## 项目概述
10+
11+
FastGPT 是一个 AI Agent 构建平台,通过 Flow 提供开箱即用的数据处理、模型调用能力和可视化工作流编排。这是一个基于 NextJS 构建的全栈 TypeScript 应用,后端使用 MongoDB/PostgreSQL。
12+
13+
**技术栈**: NextJS + TypeScript + ChakraUI + MongoDB + PostgreSQL (PG Vector)/Milvus
14+
15+
## 架构
16+
17+
这是一个使用 pnpm workspaces 的 monorepo,主要结构如下:
18+
19+
### Packages (库代码)
20+
- `packages/global/` - 所有项目共享的类型、常量、工具函数
21+
- `packages/service/` - 后端服务、数据库模型、API 控制器、工作流引擎
22+
- `packages/web/` - 共享的前端组件、hooks、样式、国际化
23+
- `packages/templates/` - 模板市场的应用模板
24+
25+
### Projects (应用程序)
26+
- `projects/app/` - 主 NextJS Web 应用(前端 + API 路由)
27+
- `projects/sandbox/` - NestJS 代码执行沙箱服务
28+
- `projects/mcp_server/` - Model Context Protocol 服务器实现
29+
30+
### 关键目录
31+
- `document/` - 文档站点(NextJS 应用及内容)
32+
- `plugins/` - 外部插件(模型、爬虫等)
33+
- `deploy/` - Docker 和 Helm 部署配置
34+
- `test/` - 集中的测试文件和工具
35+
36+
## 开发命令
37+
38+
### 主要命令(从项目根目录运行)
39+
- `pnpm dev` - 启动所有项目的开发环境(使用 package.json 的 workspace 脚本)
40+
- `pnpm build` - 构建所有项目
41+
- `pnpm test` - 使用 Vitest 运行测试
42+
- `pnpm test:workflow` - 运行工作流相关测试
43+
- `pnpm lint` - 对所有 TypeScript 文件运行 ESLint 并自动修复
44+
- `pnpm format-code` - 使用 Prettier 格式化代码
45+
46+
### 项目专用命令
47+
**主应用 (projects/app/)**:
48+
- `cd projects/app && pnpm dev` - 启动 NextJS 开发服务器
49+
- `cd projects/app && pnpm build` - 构建 NextJS 应用
50+
- `cd projects/app && pnpm start` - 启动生产服务器
51+
52+
**沙箱 (projects/sandbox/)**:
53+
- `cd projects/sandbox && pnpm dev` - 以监视模式启动 NestJS 开发服务器
54+
- `cd projects/sandbox && pnpm build` - 构建 NestJS 应用
55+
- `cd projects/sandbox && pnpm test` - 运行 Jest 测试
56+
57+
**MCP 服务器 (projects/mcp_server/)**:
58+
- `cd projects/mcp_server && bun dev` - 使用 Bun 以监视模式启动
59+
- `cd projects/mcp_server && bun build` - 构建 MCP 服务器
60+
- `cd projects/mcp_server && bun start` - 启动 MCP 服务器
61+
62+
### 工具命令
63+
- `pnpm create:i18n` - 生成国际化翻译文件
64+
- `pnpm api:gen` - 生成 OpenAPI 文档
65+
- `pnpm initIcon` - 初始化图标资源
66+
- `pnpm gen:theme-typings` - 生成 Chakra UI 主题类型定义
67+
68+
## 测试
69+
70+
项目使用 Vitest 进行测试并生成覆盖率报告。主要测试命令:
71+
- `pnpm test` - 运行所有测试
72+
- `pnpm test:workflow` - 专门运行工作流测试
73+
- 测试文件位于 `test/` 目录和 `projects/app/test/`
74+
- 覆盖率报告生成在 `coverage/` 目录
75+
76+
## 代码组织模式
77+
78+
### Monorepo 结构
79+
- 共享代码存放在 `packages/` 中,通过 workspace 引用导入
80+
- `projects/` 中的每个项目都是独立的应用程序
81+
- 使用 `@fastgpt/global``@fastgpt/service``@fastgpt/web` 导入共享包
82+
83+
### API 结构
84+
- NextJS API 路由在 `projects/app/src/pages/api/`
85+
- 核心业务逻辑在 `packages/service/core/`
86+
- 数据库模型在 `packages/service/` 中,使用 MongoDB/Mongoose
87+
88+
### 前端架构
89+
- React 组件在 `projects/app/src/components/``packages/web/components/`
90+
- 使用 Chakra UI 进行样式设计,自定义主题在 `packages/web/styles/theme.ts`
91+
- 国际化支持文件在 `packages/web/i18n/`
92+
- 使用 React Context 和 Zustand 进行状态管理
93+
94+
### 工作流系统
95+
- 使用 ReactFlow 的可视化工作流编辑器
96+
- 工作流引擎在 `packages/service/core/workflow/`
97+
- 节点定义在 `packages/global/core/workflow/template/`
98+
- 用于执行工作流节点的调度系统
99+
100+
## 开发注意事项
101+
102+
- **包管理器**: 使用 pnpm 及 workspace 配置
103+
- **Node 版本**: 需要 Node.js >=18.16.0, pnpm >=9.0.0
104+
- **数据库**: 支持 MongoDB、带 pgvector 的 PostgreSQL 或 Milvus 向量存储
105+
- **AI 集成**: 通过统一接口支持多个 AI 提供商
106+
- **国际化**: 完整支持中文、英文和日文
107+
108+
## 关键文件模式
109+
110+
- `.ts``.tsx` 文件全部使用 TypeScript
111+
- 数据库模型使用 Mongoose 配合 TypeScript
112+
- API 路由遵循 NextJS 约定
113+
- 组件文件使用 React 函数式组件和 hooks
114+
- 共享类型定义在 `packages/global/``.d.ts` 文件中
115+
116+
## 环境配置
117+
118+
- 配置文件在 `projects/app/data/config.json`
119+
- 支持特定环境配置
120+
- 模型配置在 `packages/service/core/ai/config/`

document/content/docs/upgrading/4-13/4132.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ description: 'FastGPT V4.13.2 更新说明'
1818
1. LLM 模型默认支持图片,导致请求错误。
1919
2. Mongo 多副本切换时候,watch 未重新触发。
2020
3. 文本分块,所有策略用完后,未处理 LastText 数据。
21+
4. 变量输入框,number=0 时,无法通过校验。
22+
5. 工作流复杂循环并行判断异常。
2123

2224
## 🔨 插件更新
2325

document/data/doc-last-modified.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"document/content/docs/upgrading/4-12/4124.mdx": "2025-09-17T22:29:56+08:00",
114114
"document/content/docs/upgrading/4-13/4130.mdx": "2025-09-30T16:00:10+08:00",
115115
"document/content/docs/upgrading/4-13/4131.mdx": "2025-09-30T15:47:06+08:00",
116-
"document/content/docs/upgrading/4-13/4132.mdx": "2025-10-17T13:58:27+08:00",
116+
"document/content/docs/upgrading/4-13/4132.mdx": "2025-10-17T14:42:31+08:00",
117117
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
118118
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
119119
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",

packages/global/core/workflow/runtime/utils.ts

Lines changed: 28 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,6 @@ import { isValidReferenceValueFormat } from '../utils';
2121
import type { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
2222
import { isSecretValue } from '../../../common/secret/utils';
2323

24-
export const checkIsBranchNode = (node: RuntimeNodeItemType) => {
25-
if (node.catchError) return true;
26-
27-
const map: Record<any, boolean> = {
28-
[FlowNodeTypeEnum.classifyQuestion]: true,
29-
[FlowNodeTypeEnum.userSelect]: true,
30-
[FlowNodeTypeEnum.ifElseNode]: true
31-
};
32-
return !!map[node.flowNodeType];
33-
};
34-
3524
export const extractDeepestInteractive = (
3625
interactive: WorkflowInteractiveResponseType
3726
): WorkflowInteractiveResponseType => {
@@ -306,45 +295,50 @@ export const checkNodeRunStatus = ({
306295
}) => {
307296
const filterRuntimeEdges = filterWorkflowEdges(runtimeEdges);
308297

298+
const isStartNode = (nodeType: string) => {
299+
const map: Record<any, boolean> = {
300+
[FlowNodeTypeEnum.workflowStart]: true,
301+
[FlowNodeTypeEnum.pluginInput]: true
302+
};
303+
return !!map[nodeType];
304+
};
309305
const splitNodeEdges = (targetNode: RuntimeNodeItemType) => {
310306
const commonEdges: RuntimeEdgeItemType[] = [];
311307
const recursiveEdgeGroupsMap = new Map<string, RuntimeEdgeItemType[]>();
312308

313-
const getEdgeLastBranchHandle = ({
314-
startEdge,
315-
targetNodeId
316-
}: {
317-
startEdge: RuntimeEdgeItemType;
318-
targetNodeId: string;
319-
}): string | '' | undefined => {
309+
const sourceEdges = filterRuntimeEdges.filter((item) => item.target === targetNode.nodeId);
310+
311+
sourceEdges.forEach((sourceEdge) => {
320312
const stack: Array<{
321313
edge: RuntimeEdgeItemType;
322314
visited: Set<string>;
323-
lasestBranchHandle?: string;
324315
}> = [
325316
{
326-
edge: startEdge,
327-
visited: new Set([targetNodeId])
317+
edge: sourceEdge,
318+
visited: new Set([targetNode.nodeId])
328319
}
329320
];
330-
331321
const MAX_DEPTH = 3000;
332322
let iterations = 0;
333323

334324
while (stack.length > 0 && iterations < MAX_DEPTH) {
335325
iterations++;
336-
const { edge, visited, lasestBranchHandle } = stack.pop()!;
337-
338-
// Circle
326+
const { edge, visited } = stack.pop()!;
327+
328+
// Start node
329+
const sourceNode = nodesMap.get(edge.source);
330+
if (!sourceNode) continue;
331+
if (isStartNode(sourceNode.flowNodeType)) {
332+
commonEdges.push(sourceEdge);
333+
continue;
334+
}
335+
// Circle detected
339336
if (edge.source === targetNode.nodeId) {
340-
// 检查自身是否为分支节点
341-
const node = nodesMap.get(edge.source);
342-
if (!node) return '';
343-
const isBranch = checkIsBranchNode(node);
344-
if (isBranch) return edge.sourceHandle;
345-
346-
// 检测到环,并且环中包含当前节点. 空字符代表是一个无分支循环,属于死循环,则忽略这个边。
347-
return lasestBranchHandle ?? '';
337+
recursiveEdgeGroupsMap.set(edge.target, [
338+
...(recursiveEdgeGroupsMap.get(edge.target) || []),
339+
sourceEdge
340+
]);
341+
continue;
348342
}
349343

350344
if (visited.has(edge.source)) {
@@ -357,42 +351,12 @@ export const checkNodeRunStatus = ({
357351
// 查找目标节点的 source edges 并加入栈中
358352
const nextEdges = filterRuntimeEdges.filter((item) => item.target === edge.source);
359353
for (const nextEdge of nextEdges) {
360-
const node = nodesMap.get(nextEdge.target);
361-
if (!node) continue;
362-
const isBranch = checkIsBranchNode(node);
363-
364354
stack.push({
365355
edge: nextEdge,
366-
visited: newVisited,
367-
lasestBranchHandle: isBranch ? edge.sourceHandle : lasestBranchHandle
356+
visited: newVisited
368357
});
369358
}
370359
}
371-
372-
return;
373-
};
374-
375-
const sourceEdges = filterRuntimeEdges.filter((item) => item.target === targetNode.nodeId);
376-
sourceEdges.forEach((edge) => {
377-
const lastBranchHandle = getEdgeLastBranchHandle({
378-
startEdge: edge,
379-
targetNodeId: targetNode.nodeId
380-
});
381-
382-
// 无效的循环,这条边则忽略
383-
if (lastBranchHandle === '') return;
384-
385-
// 有效循环,则加入递归组
386-
if (lastBranchHandle) {
387-
recursiveEdgeGroupsMap.set(lastBranchHandle, [
388-
...(recursiveEdgeGroupsMap.get(lastBranchHandle) || []),
389-
edge
390-
]);
391-
}
392-
// 无循环的连线,则加入普通组
393-
else {
394-
commonEdges.push(edge);
395-
}
396360
});
397361

398362
return { commonEdges, recursiveEdgeGroups: Array.from(recursiveEdgeGroupsMap.values()) };

projects/app/src/components/core/app/formRender/LabelAndForm.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ const LabelAndFormRender = ({
6262
name={props.fieldName}
6363
rules={{
6464
validate: (value) => {
65-
if (!required || inputType === InputTypeEnum.switch) return true;
65+
if (!required) return true;
66+
if (typeof value === 'number' || typeof value === 'boolean') return true;
6667
return !!value;
6768
},
6869
...(!!props?.minLength

0 commit comments

Comments
 (0)