@louloulinx/metagpt 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +23 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README-CN.md +754 -0
- package/README.md +238 -0
- package/bun.lock +1023 -0
- package/doc/TutorialAssistant.md +114 -0
- package/doc/VercelLLMProvider.md +164 -0
- package/eslint.config.js +55 -0
- package/examples/data-interpreter-example.ts +173 -0
- package/examples/qwen-direct-example.ts +60 -0
- package/examples/qwen-example.ts +62 -0
- package/examples/tutorial-assistant-example.ts +97 -0
- package/jest.config.ts +22 -0
- package/output/tutorials/Go/350/257/255/350/250/200/347/274/226/347/250/213/346/225/231/347/250/213_2025-02-25T09-35-15-436Z.md +2208 -0
- package/output/tutorials/Rust/346/225/231/347/250/213_2025-02-25T08-27-27-632Z.md +1967 -0
- package/output/tutorials//345/246/202/344/275/225/344/275/277/347/224/250TypeScript/345/274/200/345/217/221Node.js/345/272/224/347/224/250_2025-02-25T08-14-39-605Z.md +1721 -0
- package/output/tutorials//346/225/260/345/255/227/347/273/217/346/265/216/345/255/246/346/225/231/347/250/213_2025-02-25T10-45-03-605Z.md +902 -0
- package/output/tutorials//346/232/250/345/215/227/345/244/247/345/255/246/346/225/260/345/255/227/347/273/217/346/265/216/345/255/246/345/244/215/350/257/225/350/265/204/346/226/231_2025-02-25T11-16-59-133Z.md +719 -0
- package/package.json +58 -0
- package/plan-cn.md +321 -0
- package/plan.md +154 -0
- package/src/actions/analyze-task.ts +65 -0
- package/src/actions/base-action.ts +103 -0
- package/src/actions/di/execute-nb-code.ts +247 -0
- package/src/actions/di/write-analysis-code.ts +234 -0
- package/src/actions/write-tutorial.ts +232 -0
- package/src/config/browser.ts +33 -0
- package/src/config/config.ts +345 -0
- package/src/config/embedding.ts +26 -0
- package/src/config/llm.ts +36 -0
- package/src/config/mermaid.ts +37 -0
- package/src/config/omniparse.ts +25 -0
- package/src/config/redis.ts +34 -0
- package/src/config/s3.ts +33 -0
- package/src/config/search.ts +30 -0
- package/src/config/workspace.ts +20 -0
- package/src/index.ts +40 -0
- package/src/management/team.ts +168 -0
- package/src/memory/longterm.ts +218 -0
- package/src/memory/manager.ts +160 -0
- package/src/memory/types.ts +100 -0
- package/src/memory/working.ts +154 -0
- package/src/monitoring/system.ts +413 -0
- package/src/monitoring/types.ts +230 -0
- package/src/plugin/manager.ts +79 -0
- package/src/plugin/types.ts +114 -0
- package/src/provider/vercel-llm.ts +314 -0
- package/src/rag/base-rag.ts +194 -0
- package/src/rag/document-qa.ts +102 -0
- package/src/roles/base-role.ts +155 -0
- package/src/roles/data-interpreter.ts +360 -0
- package/src/roles/engineer.ts +1 -0
- package/src/roles/tutorial-assistant.ts +217 -0
- package/src/skills/base-skill.ts +144 -0
- package/src/skills/code-review.ts +120 -0
- package/src/tools/base-tool.ts +155 -0
- package/src/tools/file-system.ts +204 -0
- package/src/tools/tool-recommend.d.ts +14 -0
- package/src/tools/tool-recommend.ts +31 -0
- package/src/types/action.ts +38 -0
- package/src/types/config.ts +129 -0
- package/src/types/document.ts +354 -0
- package/src/types/llm.ts +64 -0
- package/src/types/memory.ts +36 -0
- package/src/types/message.ts +193 -0
- package/src/types/rag.ts +86 -0
- package/src/types/role.ts +67 -0
- package/src/types/skill.ts +71 -0
- package/src/types/task.ts +32 -0
- package/src/types/team.ts +55 -0
- package/src/types/tool.ts +77 -0
- package/src/types/workflow.ts +133 -0
- package/src/utils/common.ts +73 -0
- package/src/utils/yaml.ts +67 -0
- package/src/websocket/browser-client.ts +187 -0
- package/src/websocket/client.ts +186 -0
- package/src/websocket/server.ts +169 -0
- package/src/websocket/types.ts +125 -0
- package/src/workflow/executor.ts +193 -0
- package/src/workflow/executors/action-executor.ts +72 -0
- package/src/workflow/executors/condition-executor.ts +118 -0
- package/src/workflow/executors/parallel-executor.ts +201 -0
- package/src/workflow/executors/role-executor.ts +76 -0
- package/src/workflow/executors/sequence-executor.ts +196 -0
- package/tests/actions.test.ts +105 -0
- package/tests/benchmark/performance.test.ts +147 -0
- package/tests/config/config.test.ts +115 -0
- package/tests/config.test.ts +106 -0
- package/tests/e2e/setup.ts +74 -0
- package/tests/e2e/workflow.test.ts +88 -0
- package/tests/llm.test.ts +84 -0
- package/tests/memory/memory.test.ts +164 -0
- package/tests/memory.test.ts +63 -0
- package/tests/monitoring/monitoring.test.ts +225 -0
- package/tests/plugin/plugin.test.ts +183 -0
- package/tests/provider/bailian-llm.test.ts +98 -0
- package/tests/rag.test.ts +162 -0
- package/tests/roles.test.ts +88 -0
- package/tests/skills.test.ts +166 -0
- package/tests/team.test.ts +143 -0
- package/tests/tools.test.ts +170 -0
- package/tests/types/document.test.ts +181 -0
- package/tests/types/message.test.ts +122 -0
- package/tests/utils/yaml.test.ts +110 -0
- package/tests/utils.test.ts +74 -0
- package/tests/websocket/browser-client.test.ts +1 -0
- package/tests/websocket/websocket.test.ts +42 -0
- package/tests/workflow/parallel-executor.test.ts +224 -0
- package/tests/workflow/sequence-executor.test.ts +207 -0
- package/tests/workflow.test.ts +290 -0
- package/tsconfig.json +27 -0
- package/typedoc.json +25 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type {
|
3
|
+
WorkflowExecutor,
|
4
|
+
WorkflowConfig,
|
5
|
+
WorkflowState,
|
6
|
+
WorkflowNode,
|
7
|
+
NodeExecutor,
|
8
|
+
WorkflowEvent
|
9
|
+
} from '../types/workflow';
|
10
|
+
import {
|
11
|
+
WorkflowStateSchema,
|
12
|
+
WorkflowNodeSchema,
|
13
|
+
WorkflowEventSchema
|
14
|
+
} from '../types/workflow';
|
15
|
+
import { generateId } from '../utils/common';
|
16
|
+
|
17
|
+
/**
|
18
|
+
* 基础工作流执行器实现
|
19
|
+
*/
|
20
|
+
export class BaseWorkflowExecutor extends EventEmitter implements WorkflowExecutor {
|
21
|
+
private config!: WorkflowConfig;
|
22
|
+
private state!: WorkflowState;
|
23
|
+
private nodeExecutors: Map<string, NodeExecutor>;
|
24
|
+
private isPaused: boolean = false;
|
25
|
+
private isStopped: boolean = false;
|
26
|
+
|
27
|
+
constructor() {
|
28
|
+
super();
|
29
|
+
this.nodeExecutors = new Map();
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* 注册节点执行器
|
34
|
+
* @param type 节点类型
|
35
|
+
* @param executor 执行器实例
|
36
|
+
*/
|
37
|
+
public registerNodeExecutor(type: string, executor: NodeExecutor): void {
|
38
|
+
this.nodeExecutors.set(type, executor);
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* 执行工作流
|
43
|
+
* @param config 工作流配置
|
44
|
+
*/
|
45
|
+
public async execute(config: WorkflowConfig): Promise<void> {
|
46
|
+
this.config = config;
|
47
|
+
this.state = WorkflowStateSchema.parse({
|
48
|
+
id: config.id,
|
49
|
+
status: 'running',
|
50
|
+
});
|
51
|
+
|
52
|
+
this.emit('workflow:start', this.createEvent('workflow:start'));
|
53
|
+
|
54
|
+
try {
|
55
|
+
await this.executeNode(this.findStartNode());
|
56
|
+
|
57
|
+
if (!this.isStopped) {
|
58
|
+
this.state.status = 'completed';
|
59
|
+
this.emit('workflow:complete', this.createEvent('workflow:complete'));
|
60
|
+
}
|
61
|
+
} catch (error) {
|
62
|
+
this.state.status = 'failed';
|
63
|
+
this.emit('workflow:fail', this.createEvent('workflow:fail', { error }));
|
64
|
+
throw error;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* 暂停工作流
|
70
|
+
*/
|
71
|
+
public async pause(): Promise<void> {
|
72
|
+
this.isPaused = true;
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* 恢复工作流
|
77
|
+
*/
|
78
|
+
public async resume(): Promise<void> {
|
79
|
+
this.isPaused = false;
|
80
|
+
if (this.state.currentNodeId) {
|
81
|
+
await this.executeNode(this.findNodeById(this.state.currentNodeId));
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* 停止工作流
|
87
|
+
*/
|
88
|
+
public async stop(): Promise<void> {
|
89
|
+
this.isStopped = true;
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* 获取工作流状态
|
94
|
+
*/
|
95
|
+
public getState(): WorkflowState {
|
96
|
+
return this.state;
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* 执行节点
|
101
|
+
* @param node 工作流节点
|
102
|
+
*/
|
103
|
+
private async executeNode(node: WorkflowNode): Promise<void> {
|
104
|
+
if (this.isStopped) return;
|
105
|
+
|
106
|
+
while (this.isPaused) {
|
107
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
108
|
+
}
|
109
|
+
|
110
|
+
this.state.currentNodeId = node.id;
|
111
|
+
this.emit('node:start', this.createEvent('node:start', { nodeId: node.id }));
|
112
|
+
|
113
|
+
try {
|
114
|
+
const executor = this.nodeExecutors.get(node.type);
|
115
|
+
if (!executor) {
|
116
|
+
throw new Error(`No executor found for node type: ${node.type}`);
|
117
|
+
}
|
118
|
+
|
119
|
+
if (!executor.validate(node)) {
|
120
|
+
throw new Error(`Invalid node configuration: ${node.id}`);
|
121
|
+
}
|
122
|
+
|
123
|
+
const result = await executor.execute(node, {
|
124
|
+
workflow: this.config,
|
125
|
+
state: this.state,
|
126
|
+
});
|
127
|
+
|
128
|
+
node.status = 'completed';
|
129
|
+
node.result = result;
|
130
|
+
this.state.completedNodeIds.push(node.id);
|
131
|
+
|
132
|
+
this.emit('node:complete', this.createEvent('node:complete', {
|
133
|
+
nodeId: node.id,
|
134
|
+
data: result,
|
135
|
+
}));
|
136
|
+
|
137
|
+
// 执行子节点
|
138
|
+
for (const childId of node.childIds) {
|
139
|
+
await this.executeNode(this.findNodeById(childId));
|
140
|
+
}
|
141
|
+
} catch (error) {
|
142
|
+
node.status = 'failed';
|
143
|
+
this.state.failedNodeIds.push(node.id);
|
144
|
+
|
145
|
+
this.emit('node:fail', this.createEvent('node:fail', {
|
146
|
+
nodeId: node.id,
|
147
|
+
error,
|
148
|
+
}));
|
149
|
+
|
150
|
+
throw error;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
* 查找起始节点
|
156
|
+
*/
|
157
|
+
private findStartNode(): WorkflowNode {
|
158
|
+
const startNode = this.config.nodes.find(node => !node.parentId);
|
159
|
+
if (!startNode) {
|
160
|
+
throw new Error('No start node found in workflow');
|
161
|
+
}
|
162
|
+
return startNode;
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* 根据ID查找节点
|
167
|
+
* @param id 节点ID
|
168
|
+
*/
|
169
|
+
private findNodeById(id: string): WorkflowNode {
|
170
|
+
const node = this.config.nodes.find(node => node.id === id);
|
171
|
+
if (!node) {
|
172
|
+
throw new Error(`Node not found: ${id}`);
|
173
|
+
}
|
174
|
+
return node;
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* 创建工作流事件
|
179
|
+
* @param type 事件类型
|
180
|
+
* @param data 事件数据
|
181
|
+
*/
|
182
|
+
private createEvent(
|
183
|
+
type: WorkflowEvent['type'],
|
184
|
+
data: Partial<WorkflowEvent> = {}
|
185
|
+
): WorkflowEvent {
|
186
|
+
return WorkflowEventSchema.parse({
|
187
|
+
type,
|
188
|
+
timestamp: Date.now(),
|
189
|
+
workflowId: this.config.id,
|
190
|
+
...data,
|
191
|
+
});
|
192
|
+
}
|
193
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import type { NodeExecutor, WorkflowNode } from '../../types/workflow';
|
2
|
+
import type { Action, ActionOutput } from '../../types/action';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* 动作节点执行器
|
6
|
+
* 用于执行动作类型的工作流节点
|
7
|
+
*/
|
8
|
+
export class ActionNodeExecutor implements NodeExecutor {
|
9
|
+
private status: string = 'pending';
|
10
|
+
private result: any = null;
|
11
|
+
|
12
|
+
/**
|
13
|
+
* 执行动作节点
|
14
|
+
* @param node 工作流节点
|
15
|
+
* @param context 执行上下文
|
16
|
+
*/
|
17
|
+
async execute(node: WorkflowNode, context: any): Promise<ActionOutput> {
|
18
|
+
try {
|
19
|
+
this.status = 'running';
|
20
|
+
|
21
|
+
// 获取动作实例
|
22
|
+
const action = this.getAction(node);
|
23
|
+
if (!action) {
|
24
|
+
throw new Error(`Action not found in node: ${node.id}`);
|
25
|
+
}
|
26
|
+
|
27
|
+
// 执行动作
|
28
|
+
const result = await action.run();
|
29
|
+
this.result = result;
|
30
|
+
this.status = 'completed';
|
31
|
+
|
32
|
+
return result;
|
33
|
+
} catch (error) {
|
34
|
+
this.status = 'failed';
|
35
|
+
this.result = error;
|
36
|
+
throw error;
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* 验证节点配置
|
42
|
+
* @param node 工作流节点
|
43
|
+
*/
|
44
|
+
validate(node: WorkflowNode): boolean {
|
45
|
+
return (
|
46
|
+
node.type === 'action' &&
|
47
|
+
!!node.config?.action
|
48
|
+
);
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* 获取节点状态
|
53
|
+
*/
|
54
|
+
getStatus(): string {
|
55
|
+
return this.status;
|
56
|
+
}
|
57
|
+
|
58
|
+
/**
|
59
|
+
* 获取执行结果
|
60
|
+
*/
|
61
|
+
getResult(): any {
|
62
|
+
return this.result;
|
63
|
+
}
|
64
|
+
|
65
|
+
/**
|
66
|
+
* 从节点配置中获取动作实例
|
67
|
+
* @param node 工作流节点
|
68
|
+
*/
|
69
|
+
private getAction(node: WorkflowNode): Action | undefined {
|
70
|
+
return node.config?.action as Action;
|
71
|
+
}
|
72
|
+
}
|
@@ -0,0 +1,118 @@
|
|
1
|
+
import type { NodeExecutor, WorkflowNode } from '../../types/workflow';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 条件节点配置
|
5
|
+
*/
|
6
|
+
interface ConditionConfig {
|
7
|
+
/** 条件表达式 */
|
8
|
+
expression: string;
|
9
|
+
/** 条件参数 */
|
10
|
+
params?: Record<string, any>;
|
11
|
+
/** 条件处理函数 */
|
12
|
+
handler?: (params: Record<string, any>) => boolean | Promise<boolean>;
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* 条件节点执行器
|
17
|
+
* 用于执行条件类型的工作流节点
|
18
|
+
*/
|
19
|
+
export class ConditionNodeExecutor implements NodeExecutor {
|
20
|
+
private status: string = 'pending';
|
21
|
+
private result: any = null;
|
22
|
+
|
23
|
+
/**
|
24
|
+
* 执行条件节点
|
25
|
+
* @param node 工作流节点
|
26
|
+
* @param context 执行上下文
|
27
|
+
*/
|
28
|
+
async execute(node: WorkflowNode, context: any): Promise<boolean> {
|
29
|
+
try {
|
30
|
+
this.status = 'running';
|
31
|
+
|
32
|
+
// 获取条件配置
|
33
|
+
const config = this.getConditionConfig(node);
|
34
|
+
if (!config) {
|
35
|
+
throw new Error(`Condition config not found in node: ${node.id}`);
|
36
|
+
}
|
37
|
+
|
38
|
+
// 执行条件判断
|
39
|
+
const result = await this.evaluateCondition(config, context);
|
40
|
+
this.result = result;
|
41
|
+
this.status = 'completed';
|
42
|
+
|
43
|
+
return result;
|
44
|
+
} catch (error) {
|
45
|
+
this.status = 'failed';
|
46
|
+
this.result = error;
|
47
|
+
throw error;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* 验证节点配置
|
53
|
+
* @param node 工作流节点
|
54
|
+
*/
|
55
|
+
validate(node: WorkflowNode): boolean {
|
56
|
+
const config = this.getConditionConfig(node);
|
57
|
+
return (
|
58
|
+
node.type === 'condition' &&
|
59
|
+
!!config &&
|
60
|
+
(!!config.expression || !!config.handler)
|
61
|
+
);
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* 获取节点状态
|
66
|
+
*/
|
67
|
+
getStatus(): string {
|
68
|
+
return this.status;
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* 获取执行结果
|
73
|
+
*/
|
74
|
+
getResult(): any {
|
75
|
+
return this.result;
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* 从节点配置中获取条件配置
|
80
|
+
* @param node 工作流节点
|
81
|
+
*/
|
82
|
+
private getConditionConfig(node: WorkflowNode): ConditionConfig | undefined {
|
83
|
+
return node.config?.condition as ConditionConfig;
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* 执行条件判断
|
88
|
+
* @param config 条件配置
|
89
|
+
* @param context 执行上下文
|
90
|
+
*/
|
91
|
+
private async evaluateCondition(
|
92
|
+
config: ConditionConfig,
|
93
|
+
context: any
|
94
|
+
): Promise<boolean> {
|
95
|
+
try {
|
96
|
+
// 如果提供了处理函数,优先使用处理函数
|
97
|
+
if (config.handler) {
|
98
|
+
return await config.handler(config.params || {});
|
99
|
+
}
|
100
|
+
|
101
|
+
// 否则使用表达式求值
|
102
|
+
const params = {
|
103
|
+
...config.params,
|
104
|
+
context,
|
105
|
+
};
|
106
|
+
|
107
|
+
// 使用 Function 构造函数创建表达式求值函数
|
108
|
+
const evaluator = new Function(
|
109
|
+
...Object.keys(params),
|
110
|
+
`return ${config.expression};`
|
111
|
+
);
|
112
|
+
|
113
|
+
return evaluator(...Object.values(params));
|
114
|
+
} catch (error) {
|
115
|
+
throw new Error(`Failed to evaluate condition: ${error}`);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
@@ -0,0 +1,201 @@
|
|
1
|
+
import type { NodeExecutor, WorkflowNode } from '../../types/workflow';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 并行节点配置
|
5
|
+
*/
|
6
|
+
interface ParallelConfig {
|
7
|
+
/** 最大并发数 */
|
8
|
+
maxConcurrency?: number;
|
9
|
+
/** 错误处理策略 */
|
10
|
+
errorStrategy?: 'fail-fast' | 'continue' | 'ignore';
|
11
|
+
/** 超时时间(ms) */
|
12
|
+
timeout?: number;
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* 并行节点执行器
|
17
|
+
* 用于并行执行多个子节点
|
18
|
+
*/
|
19
|
+
export class ParallelNodeExecutor implements NodeExecutor {
|
20
|
+
private status: string = 'pending';
|
21
|
+
private result: any = null;
|
22
|
+
|
23
|
+
/**
|
24
|
+
* 执行并行节点
|
25
|
+
* @param node 工作流节点
|
26
|
+
* @param context 执行上下文
|
27
|
+
*/
|
28
|
+
async execute(node: WorkflowNode, context: any): Promise<any[]> {
|
29
|
+
try {
|
30
|
+
this.status = 'running';
|
31
|
+
|
32
|
+
// 获取并行配置
|
33
|
+
const config = this.getParallelConfig(node);
|
34
|
+
|
35
|
+
// 获取子节点执行器
|
36
|
+
const childExecutors = this.getChildExecutors(node, context);
|
37
|
+
if (childExecutors.length === 0) {
|
38
|
+
throw new Error(`No child nodes found in parallel node: ${node.id}`);
|
39
|
+
}
|
40
|
+
|
41
|
+
// 执行子节点
|
42
|
+
const results = await this.executeParallel(childExecutors, config);
|
43
|
+
this.result = results;
|
44
|
+
this.status = 'completed';
|
45
|
+
|
46
|
+
return results;
|
47
|
+
} catch (error) {
|
48
|
+
this.status = 'failed';
|
49
|
+
this.result = error;
|
50
|
+
throw error;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* 验证节点配置
|
56
|
+
* @param node 工作流节点
|
57
|
+
*/
|
58
|
+
validate(node: WorkflowNode): boolean {
|
59
|
+
return (
|
60
|
+
node.type === 'parallel' &&
|
61
|
+
Array.isArray(node.childIds) &&
|
62
|
+
node.childIds.length > 0
|
63
|
+
);
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* 获取节点状态
|
68
|
+
*/
|
69
|
+
getStatus(): string {
|
70
|
+
return this.status;
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* 获取执行结果
|
75
|
+
*/
|
76
|
+
getResult(): any {
|
77
|
+
return this.result;
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* 从节点配置中获取并行配置
|
82
|
+
* @param node 工作流节点
|
83
|
+
*/
|
84
|
+
private getParallelConfig(node: WorkflowNode): ParallelConfig {
|
85
|
+
return {
|
86
|
+
maxConcurrency: Infinity,
|
87
|
+
errorStrategy: 'fail-fast',
|
88
|
+
timeout: 0,
|
89
|
+
...(node.config?.parallel || {}),
|
90
|
+
};
|
91
|
+
}
|
92
|
+
|
93
|
+
/**
|
94
|
+
* 获取子节点执行器列表
|
95
|
+
* @param node 工作流节点
|
96
|
+
* @param context 执行上下文
|
97
|
+
*/
|
98
|
+
private getChildExecutors(
|
99
|
+
node: WorkflowNode,
|
100
|
+
context: any
|
101
|
+
): Array<() => Promise<any>> {
|
102
|
+
return node.childIds.map(childId => {
|
103
|
+
const childNode = context.workflow.nodes.find(
|
104
|
+
(n: WorkflowNode) => n.id === childId
|
105
|
+
);
|
106
|
+
if (!childNode) {
|
107
|
+
throw new Error(`Child node not found: ${childId}`);
|
108
|
+
}
|
109
|
+
|
110
|
+
const executor = context.workflow.nodeExecutors.get(childNode.type);
|
111
|
+
if (!executor) {
|
112
|
+
throw new Error(`No executor found for node type: ${childNode.type}`);
|
113
|
+
}
|
114
|
+
|
115
|
+
return () => executor.execute(childNode, context);
|
116
|
+
});
|
117
|
+
}
|
118
|
+
|
119
|
+
/**
|
120
|
+
* 并行执行子节点
|
121
|
+
* @param executors 执行器列表
|
122
|
+
* @param config 并行配置
|
123
|
+
*/
|
124
|
+
private async executeParallel(
|
125
|
+
executors: Array<() => Promise<any>>,
|
126
|
+
config: ParallelConfig
|
127
|
+
): Promise<any[]> {
|
128
|
+
const results: any[] = [];
|
129
|
+
const errors: Error[] = [];
|
130
|
+
|
131
|
+
// 创建执行队列
|
132
|
+
const queue = [...executors];
|
133
|
+
const running = new Set<Promise<void>>();
|
134
|
+
|
135
|
+
while (queue.length > 0 || running.size > 0) {
|
136
|
+
// 检查是否可以启动新的执行器
|
137
|
+
while (
|
138
|
+
queue.length > 0 &&
|
139
|
+
running.size < (config.maxConcurrency || Infinity)
|
140
|
+
) {
|
141
|
+
const executor = queue.shift()!;
|
142
|
+
const promise = (async () => {
|
143
|
+
try {
|
144
|
+
const result = await this.executeWithTimeout(
|
145
|
+
executor(),
|
146
|
+
config.timeout
|
147
|
+
);
|
148
|
+
results.push(result);
|
149
|
+
} catch (error) {
|
150
|
+
errors.push(error as Error);
|
151
|
+
if (config.errorStrategy === 'fail-fast') {
|
152
|
+
queue.length = 0; // 清空队列
|
153
|
+
throw error;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
})();
|
157
|
+
|
158
|
+
running.add(promise);
|
159
|
+
// 执行完成后从运行集合中移除
|
160
|
+
promise.finally(() => running.delete(promise));
|
161
|
+
}
|
162
|
+
|
163
|
+
// 等待任意一个执行器完成
|
164
|
+
if (running.size > 0) {
|
165
|
+
await Promise.race(running);
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
// 处理错误
|
170
|
+
if (errors.length > 0 && config.errorStrategy !== 'ignore') {
|
171
|
+
throw new AggregateError(
|
172
|
+
errors,
|
173
|
+
`${errors.length} parallel executions failed`
|
174
|
+
);
|
175
|
+
}
|
176
|
+
|
177
|
+
return results;
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* 带超时的执行
|
182
|
+
* @param promise Promise
|
183
|
+
* @param timeout 超时时间
|
184
|
+
*/
|
185
|
+
private async executeWithTimeout<T>(
|
186
|
+
promise: Promise<T>,
|
187
|
+
timeout?: number
|
188
|
+
): Promise<T> {
|
189
|
+
if (!timeout || timeout <= 0) {
|
190
|
+
return promise;
|
191
|
+
}
|
192
|
+
|
193
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
194
|
+
setTimeout(() => {
|
195
|
+
reject(new Error(`Execution timed out after ${timeout}ms`));
|
196
|
+
}, timeout);
|
197
|
+
});
|
198
|
+
|
199
|
+
return Promise.race([promise, timeoutPromise]);
|
200
|
+
}
|
201
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import type { NodeExecutor, WorkflowNode } from '../../types/workflow';
|
2
|
+
import type { Role } from '../../types/role';
|
3
|
+
import type { Message } from '../../types/message';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 角色节点执行器
|
7
|
+
* 用于执行角色类型的工作流节点
|
8
|
+
*/
|
9
|
+
export class RoleNodeExecutor implements NodeExecutor {
|
10
|
+
private status: string = 'pending';
|
11
|
+
private result: any = null;
|
12
|
+
|
13
|
+
/**
|
14
|
+
* 执行角色节点
|
15
|
+
* @param node 工作流节点
|
16
|
+
* @param context 执行上下文
|
17
|
+
*/
|
18
|
+
async execute(node: WorkflowNode, context: any): Promise<Message> {
|
19
|
+
try {
|
20
|
+
this.status = 'running';
|
21
|
+
|
22
|
+
// 获取角色实例
|
23
|
+
const role = this.getRole(node);
|
24
|
+
if (!role) {
|
25
|
+
throw new Error(`Role not found in node: ${node.id}`);
|
26
|
+
}
|
27
|
+
|
28
|
+
// 执行角色行为
|
29
|
+
await role.observe();
|
30
|
+
await role.think();
|
31
|
+
const result = await role.act();
|
32
|
+
|
33
|
+
this.result = result;
|
34
|
+
this.status = 'completed';
|
35
|
+
|
36
|
+
return result;
|
37
|
+
} catch (error) {
|
38
|
+
this.status = 'failed';
|
39
|
+
this.result = error;
|
40
|
+
throw error;
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* 验证节点配置
|
46
|
+
* @param node 工作流节点
|
47
|
+
*/
|
48
|
+
validate(node: WorkflowNode): boolean {
|
49
|
+
return (
|
50
|
+
node.type === 'role' &&
|
51
|
+
!!node.config?.role
|
52
|
+
);
|
53
|
+
}
|
54
|
+
|
55
|
+
/**
|
56
|
+
* 获取节点状态
|
57
|
+
*/
|
58
|
+
getStatus(): string {
|
59
|
+
return this.status;
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* 获取执行结果
|
64
|
+
*/
|
65
|
+
getResult(): any {
|
66
|
+
return this.result;
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* 从节点配置中获取角色实例
|
71
|
+
* @param node 工作流节点
|
72
|
+
*/
|
73
|
+
private getRole(node: WorkflowNode): Role | undefined {
|
74
|
+
return node.config?.role as Role;
|
75
|
+
}
|
76
|
+
}
|