@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,217 @@
|
|
1
|
+
import * as fs from 'fs/promises';
|
2
|
+
import * as path from 'path';
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
4
|
+
import { BaseRole } from './base-role';
|
5
|
+
import { WriteDirectory, WriteContent } from '../actions/write-tutorial';
|
6
|
+
import type { Directory } from '../actions/write-tutorial';
|
7
|
+
import type { Message, MESSAGE_ROUTE } from '../types/message';
|
8
|
+
import type { LLMProvider } from '../types/llm';
|
9
|
+
|
10
|
+
/**
|
11
|
+
* 教程助手配置接口
|
12
|
+
*/
|
13
|
+
export interface TutorialAssistantConfig {
|
14
|
+
llm: LLMProvider;
|
15
|
+
language?: string;
|
16
|
+
outputDir?: string;
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* 教程助手,输入一个句子生成Markdown格式的教程文档
|
21
|
+
*/
|
22
|
+
export class TutorialAssistant extends BaseRole {
|
23
|
+
language: string;
|
24
|
+
outputDir: string;
|
25
|
+
llm: LLMProvider;
|
26
|
+
|
27
|
+
topic = '';
|
28
|
+
mainTitle = '';
|
29
|
+
totalContent = '';
|
30
|
+
|
31
|
+
constructor(config: TutorialAssistantConfig) {
|
32
|
+
super(
|
33
|
+
'Stitch',
|
34
|
+
'Tutorial Assistant',
|
35
|
+
'Generate tutorial documents',
|
36
|
+
'Strictly follow Markdown\'s syntax, with neat and standardized layout',
|
37
|
+
[]
|
38
|
+
);
|
39
|
+
|
40
|
+
console.log('[TutorialAssistant] Initializing with config:', {
|
41
|
+
language: config.language || 'Chinese',
|
42
|
+
outputDir: config.outputDir || path.join(process.cwd(), 'tutorials')
|
43
|
+
});
|
44
|
+
|
45
|
+
this.language = config.language || 'Chinese';
|
46
|
+
this.outputDir = config.outputDir || path.join(process.cwd(), 'tutorials');
|
47
|
+
this.llm = config.llm;
|
48
|
+
|
49
|
+
// 初始化动作
|
50
|
+
this.initializeActions();
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* 初始化动作列表
|
55
|
+
*/
|
56
|
+
private initializeActions(): void {
|
57
|
+
console.log('[TutorialAssistant] Initializing actions');
|
58
|
+
|
59
|
+
// 初始时只设置目录生成动作
|
60
|
+
const writeDirectory = new WriteDirectory({
|
61
|
+
llm: this.llm,
|
62
|
+
language: this.language,
|
63
|
+
});
|
64
|
+
|
65
|
+
console.log('[TutorialAssistant] Created WriteDirectory action');
|
66
|
+
this.actions = [writeDirectory];
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* 处理目录结构
|
71
|
+
* @param directory 目录结构
|
72
|
+
*/
|
73
|
+
private async handleDirectory(directory: Directory): Promise<void> {
|
74
|
+
console.log('[TutorialAssistant] Handling directory structure:', JSON.stringify(directory, null, 2));
|
75
|
+
|
76
|
+
this.mainTitle = directory.title;
|
77
|
+
this.totalContent += `# ${this.mainTitle}\n\n`;
|
78
|
+
|
79
|
+
// 将所有章节内容生成动作添加到动作列表
|
80
|
+
const actions = [...this.actions];
|
81
|
+
|
82
|
+
console.log(`[TutorialAssistant] Processing ${directory.directory.length} sections`);
|
83
|
+
|
84
|
+
for (const section of directory.directory) {
|
85
|
+
const sectionKey = Object.keys(section)[0];
|
86
|
+
console.log(`[TutorialAssistant] Creating WriteContent action for section: ${sectionKey}`);
|
87
|
+
|
88
|
+
const writeContent = new WriteContent({
|
89
|
+
llm: this.llm,
|
90
|
+
language: this.language,
|
91
|
+
directory: section,
|
92
|
+
});
|
93
|
+
|
94
|
+
actions.push(writeContent);
|
95
|
+
}
|
96
|
+
|
97
|
+
console.log(`[TutorialAssistant] Updated actions list, now contains ${actions.length} actions`);
|
98
|
+
this.actions = actions;
|
99
|
+
}
|
100
|
+
|
101
|
+
/**
|
102
|
+
* 处理消息
|
103
|
+
* @param message 输入消息
|
104
|
+
* @returns 处理结果
|
105
|
+
*/
|
106
|
+
async react(message: Message): Promise<Message> {
|
107
|
+
console.log('[TutorialAssistant] Starting react method with message:', message.content);
|
108
|
+
|
109
|
+
try {
|
110
|
+
// 保存主题
|
111
|
+
this.topic = message.content;
|
112
|
+
console.log(`[TutorialAssistant] Set topic: "${this.topic}"`);
|
113
|
+
|
114
|
+
// 执行所有动作
|
115
|
+
console.log(`[TutorialAssistant] Starting execution of ${this.actions.length} actions`);
|
116
|
+
|
117
|
+
for (let i = 0; i < this.actions.length; i++) {
|
118
|
+
const action = this.actions[i];
|
119
|
+
console.log(`[TutorialAssistant] Running action ${i+1}/${this.actions.length}: ${action.constructor.name}`);
|
120
|
+
|
121
|
+
// 为动作设置主题参数
|
122
|
+
if ('setArg' in action && typeof action.setArg === 'function') {
|
123
|
+
console.log(`[TutorialAssistant] Setting topic argument for action: "${this.topic}"`);
|
124
|
+
action.setArg('topic', this.topic);
|
125
|
+
}
|
126
|
+
|
127
|
+
// 执行动作
|
128
|
+
console.log(`[TutorialAssistant] Executing action ${action.constructor.name}`);
|
129
|
+
const result = await action.run();
|
130
|
+
console.log(`[TutorialAssistant] Action ${action.constructor.name} completed with status: ${result.status}`);
|
131
|
+
|
132
|
+
if (result.status === 'failed') {
|
133
|
+
console.error(`[TutorialAssistant] Action failed: ${result.content}`);
|
134
|
+
return this.createMessage(`Failed to generate tutorial: ${result.content}`);
|
135
|
+
}
|
136
|
+
|
137
|
+
// 处理目录生成结果
|
138
|
+
if (action instanceof WriteDirectory) {
|
139
|
+
console.log('[TutorialAssistant] Processing WriteDirectory result');
|
140
|
+
if (result.instructContent) {
|
141
|
+
console.log('[TutorialAssistant] Directory structure generated, handling it');
|
142
|
+
await this.handleDirectory(result.instructContent as Directory);
|
143
|
+
} else {
|
144
|
+
console.warn('[TutorialAssistant] WriteDirectory action did not produce instructContent');
|
145
|
+
}
|
146
|
+
}
|
147
|
+
// 处理内容生成结果
|
148
|
+
else if (action instanceof WriteContent) {
|
149
|
+
console.log('[TutorialAssistant] Processing WriteContent result');
|
150
|
+
if (this.totalContent.length > 0) {
|
151
|
+
this.totalContent += '\n\n\n';
|
152
|
+
}
|
153
|
+
console.log(`[TutorialAssistant] Adding content (${result.content.length} characters)`);
|
154
|
+
this.totalContent += result.content;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
// 保存生成的内容到文件
|
159
|
+
console.log('[TutorialAssistant] All actions completed, saving content to file');
|
160
|
+
const filePath = await this.saveToFile();
|
161
|
+
console.log(`[TutorialAssistant] Content saved to ${filePath}`);
|
162
|
+
|
163
|
+
return this.createMessage(`Tutorial generated successfully and saved to ${filePath}`);
|
164
|
+
} catch (error) {
|
165
|
+
console.error('[TutorialAssistant] Error in react method:', error);
|
166
|
+
return this.createMessage(`Error generating tutorial: ${error}`);
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* 创建消息对象
|
172
|
+
* @param content 消息内容
|
173
|
+
* @returns 消息对象
|
174
|
+
*/
|
175
|
+
private createMessage(content: string): Message {
|
176
|
+
console.log(`[TutorialAssistant] Creating message: ${content}`);
|
177
|
+
return {
|
178
|
+
id: uuidv4(),
|
179
|
+
content,
|
180
|
+
role: this.profile,
|
181
|
+
causedBy: 'TutorialAssistant',
|
182
|
+
sentFrom: this.name,
|
183
|
+
sendTo: new Set(['*']),
|
184
|
+
instructContent: null,
|
185
|
+
};
|
186
|
+
}
|
187
|
+
|
188
|
+
/**
|
189
|
+
* 保存内容到文件
|
190
|
+
* @returns 文件路径
|
191
|
+
*/
|
192
|
+
private async saveToFile(): Promise<string> {
|
193
|
+
try {
|
194
|
+
console.log(`[TutorialAssistant] Saving content (${this.totalContent.length} characters) to file`);
|
195
|
+
|
196
|
+
// 确保输出目录存在
|
197
|
+
console.log(`[TutorialAssistant] Creating output directory: ${this.outputDir}`);
|
198
|
+
await fs.mkdir(this.outputDir, { recursive: true });
|
199
|
+
|
200
|
+
// 生成带时间戳的文件名
|
201
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
202
|
+
const fileName = `${this.mainTitle || 'Tutorial'}_${timestamp}.md`;
|
203
|
+
const filePath = path.join(this.outputDir, fileName);
|
204
|
+
|
205
|
+
console.log(`[TutorialAssistant] Writing to file: ${filePath}`);
|
206
|
+
|
207
|
+
// 写入文件
|
208
|
+
await fs.writeFile(filePath, this.totalContent);
|
209
|
+
console.log(`[TutorialAssistant] File written successfully: ${filePath}`);
|
210
|
+
|
211
|
+
return filePath;
|
212
|
+
} catch (error) {
|
213
|
+
console.error('[TutorialAssistant] Error saving tutorial file:', error);
|
214
|
+
throw error;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
@@ -0,0 +1,144 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import type { Skill, SkillConfig, SkillContext, SkillResult } from '../types/skill';
|
3
|
+
import type { Action } from '../types/action';
|
4
|
+
import type { LLMProvider } from '../types/llm';
|
5
|
+
import { SkillContextSchema, SkillResultSchema } from '../types/skill';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* 技能基类
|
9
|
+
* 提供技能系统的基础功能实现
|
10
|
+
*/
|
11
|
+
export abstract class BaseSkill implements Skill {
|
12
|
+
name: string;
|
13
|
+
description: string;
|
14
|
+
context: SkillContext;
|
15
|
+
llm: LLMProvider;
|
16
|
+
actions: Action[];
|
17
|
+
|
18
|
+
constructor(config: SkillConfig) {
|
19
|
+
// 验证配置
|
20
|
+
const validConfig = z.object({
|
21
|
+
name: z.string(),
|
22
|
+
description: z.string(),
|
23
|
+
llm: z.any(),
|
24
|
+
actions: z.array(z.any()).optional(),
|
25
|
+
args: z.record(z.any()).optional(),
|
26
|
+
}).parse(config);
|
27
|
+
|
28
|
+
this.name = validConfig.name;
|
29
|
+
this.description = validConfig.description;
|
30
|
+
this.llm = validConfig.llm;
|
31
|
+
this.actions = validConfig.actions || [];
|
32
|
+
|
33
|
+
// 构建上下文
|
34
|
+
this.context = SkillContextSchema.parse({
|
35
|
+
name: validConfig.name,
|
36
|
+
description: validConfig.description,
|
37
|
+
actions: this.actions,
|
38
|
+
args: validConfig.args || {},
|
39
|
+
llm: validConfig.llm,
|
40
|
+
});
|
41
|
+
}
|
42
|
+
|
43
|
+
/**
|
44
|
+
* 执行技能
|
45
|
+
* 子类必须实现此方法
|
46
|
+
*/
|
47
|
+
abstract execute(args?: Record<string, any>): Promise<SkillResult>;
|
48
|
+
|
49
|
+
/**
|
50
|
+
* 验证技能是否可用
|
51
|
+
* 子类可以覆盖此方法以提供自定义验证
|
52
|
+
*/
|
53
|
+
async validate(): Promise<boolean> {
|
54
|
+
return this.actions.length > 0;
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* 处理技能执行异常
|
59
|
+
* @param error 错误对象
|
60
|
+
*/
|
61
|
+
async handleError(error: Error): Promise<void> {
|
62
|
+
console.error(`Skill ${this.name} failed:`, error);
|
63
|
+
// 子类可以覆盖此方法以提供自定义错误处理
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* 创建技能执行结果
|
68
|
+
* @param success 是否成功
|
69
|
+
* @param message 结果消息
|
70
|
+
* @param data 结果数据
|
71
|
+
* @param error 错误信息
|
72
|
+
*/
|
73
|
+
protected createResult(
|
74
|
+
success: boolean,
|
75
|
+
message: string,
|
76
|
+
data?: any,
|
77
|
+
error?: any
|
78
|
+
): SkillResult {
|
79
|
+
return SkillResultSchema.parse({
|
80
|
+
success,
|
81
|
+
message,
|
82
|
+
data,
|
83
|
+
error,
|
84
|
+
});
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* 获取技能参数
|
89
|
+
* @param key 参数键
|
90
|
+
* @returns 参数值
|
91
|
+
*/
|
92
|
+
protected getArg<T>(key: string): T | undefined {
|
93
|
+
return this.context.args?.[key] as T;
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* 设置技能参数
|
98
|
+
* @param key 参数键
|
99
|
+
* @param value 参数值
|
100
|
+
*/
|
101
|
+
protected setArg<T>(key: string, value: T): void {
|
102
|
+
if (!this.context.args) {
|
103
|
+
this.context.args = {};
|
104
|
+
}
|
105
|
+
this.context.args[key] = value;
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* 执行动作序列
|
110
|
+
* @param actions 要执行的动作
|
111
|
+
* @returns 执行结果
|
112
|
+
*/
|
113
|
+
protected async executeActions(actions: Action[]): Promise<SkillResult> {
|
114
|
+
try {
|
115
|
+
const results = [];
|
116
|
+
for (const action of actions) {
|
117
|
+
const result = await action.run();
|
118
|
+
results.push(result);
|
119
|
+
|
120
|
+
if (result.status === 'failed') {
|
121
|
+
return this.createResult(
|
122
|
+
false,
|
123
|
+
`Action ${action.name} failed: ${result.content}`,
|
124
|
+
results
|
125
|
+
);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
return this.createResult(
|
130
|
+
true,
|
131
|
+
'All actions completed successfully',
|
132
|
+
results
|
133
|
+
);
|
134
|
+
} catch (error) {
|
135
|
+
await this.handleError(error as Error);
|
136
|
+
return this.createResult(
|
137
|
+
false,
|
138
|
+
`Failed to execute actions: ${(error as Error).message}`,
|
139
|
+
undefined,
|
140
|
+
error
|
141
|
+
);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
@@ -0,0 +1,120 @@
|
|
1
|
+
import { BaseSkill } from './base-skill';
|
2
|
+
import { AnalyzeTask } from '../actions/analyze-task';
|
3
|
+
import type { SkillConfig, SkillResult } from '../types/skill';
|
4
|
+
import type { ActionOutput } from '../types/action';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* 代码审查技能
|
8
|
+
* 使用 LLM 进行代码审查并提供改进建议
|
9
|
+
*/
|
10
|
+
export class CodeReviewSkill extends BaseSkill {
|
11
|
+
constructor(config: SkillConfig) {
|
12
|
+
super({
|
13
|
+
...config,
|
14
|
+
name: 'code_review',
|
15
|
+
description: 'Review code and provide improvement suggestions',
|
16
|
+
});
|
17
|
+
|
18
|
+
// 添加代码分析动作
|
19
|
+
this.actions.push(
|
20
|
+
new AnalyzeTask({
|
21
|
+
name: 'analyze_code',
|
22
|
+
description: 'Analyze code structure and quality',
|
23
|
+
llm: this.llm,
|
24
|
+
})
|
25
|
+
);
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* 执行代码审查
|
30
|
+
* @param args 执行参数
|
31
|
+
* @returns 审查结果
|
32
|
+
*/
|
33
|
+
async execute(args?: Record<string, any>): Promise<SkillResult> {
|
34
|
+
try {
|
35
|
+
// 验证输入
|
36
|
+
if (!args?.code) {
|
37
|
+
return this.createResult(false, 'No code provided for review');
|
38
|
+
}
|
39
|
+
|
40
|
+
// 设置分析参数
|
41
|
+
this.actions[0].context.args = {
|
42
|
+
task: `Review the following code and provide improvement suggestions:
|
43
|
+
${args.code}
|
44
|
+
|
45
|
+
Please analyze:
|
46
|
+
1. Code structure and organization
|
47
|
+
2. Potential bugs or issues
|
48
|
+
3. Performance considerations
|
49
|
+
4. Best practices compliance
|
50
|
+
5. Suggested improvements
|
51
|
+
|
52
|
+
Please provide specific examples and explanations for each point.`,
|
53
|
+
};
|
54
|
+
|
55
|
+
// 执行代码分析
|
56
|
+
const result = await this.executeActions(this.actions);
|
57
|
+
if (!result.success) {
|
58
|
+
return result;
|
59
|
+
}
|
60
|
+
|
61
|
+
// 处理分析结果
|
62
|
+
const analysisResults = result.data as ActionOutput[];
|
63
|
+
const analysis = analysisResults[0].content;
|
64
|
+
|
65
|
+
return this.createResult(
|
66
|
+
true,
|
67
|
+
'Code review completed successfully',
|
68
|
+
{
|
69
|
+
analysis,
|
70
|
+
suggestions: this.extractSuggestions(analysis),
|
71
|
+
}
|
72
|
+
);
|
73
|
+
} catch (error) {
|
74
|
+
await this.handleError(error as Error);
|
75
|
+
return this.createResult(
|
76
|
+
false,
|
77
|
+
`Code review failed: ${(error as Error).message}`,
|
78
|
+
undefined,
|
79
|
+
error
|
80
|
+
);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* 从分析结果中提取建议
|
86
|
+
* @param analysis 分析结果文本
|
87
|
+
* @returns 建议列表
|
88
|
+
*/
|
89
|
+
private extractSuggestions(analysis: string): string[] {
|
90
|
+
const suggestions: string[] = [];
|
91
|
+
const lines = analysis.split('\n');
|
92
|
+
|
93
|
+
let inSuggestionSection = false;
|
94
|
+
for (const line of lines) {
|
95
|
+
if (line.toLowerCase().includes('suggested improvements')) {
|
96
|
+
inSuggestionSection = true;
|
97
|
+
continue;
|
98
|
+
}
|
99
|
+
|
100
|
+
if (inSuggestionSection && line.trim()) {
|
101
|
+
// 移除序号和点号
|
102
|
+
const suggestion = line.replace(/^\d+[.)][ \t]*/, '').trim();
|
103
|
+
if (suggestion) {
|
104
|
+
suggestions.push(suggestion);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
return suggestions;
|
110
|
+
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* 自定义错误处理
|
114
|
+
*/
|
115
|
+
async handleError(error: Error): Promise<void> {
|
116
|
+
await super.handleError(error);
|
117
|
+
this.setArg('lastError', error.message);
|
118
|
+
// 可以添加特定的错误处理逻辑,如通知代码作者等
|
119
|
+
}
|
120
|
+
}
|
@@ -0,0 +1,155 @@
|
|
1
|
+
import { z } from 'zod';
|
2
|
+
import type { Tool, ToolConfig, ToolContext, ToolResult } from '../types/tool';
|
3
|
+
import { ToolContextSchema, ToolResultSchema } from '../types/tool';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 工具基类
|
7
|
+
* 提供工具系统的基础功能实现
|
8
|
+
*/
|
9
|
+
export abstract class BaseTool implements Tool {
|
10
|
+
name: string;
|
11
|
+
description: string;
|
12
|
+
version: string;
|
13
|
+
category: string;
|
14
|
+
context: ToolContext;
|
15
|
+
|
16
|
+
constructor(config: ToolConfig) {
|
17
|
+
// 验证配置
|
18
|
+
const validConfig = z.object({
|
19
|
+
name: z.string(),
|
20
|
+
description: z.string(),
|
21
|
+
version: z.string(),
|
22
|
+
category: z.string(),
|
23
|
+
args: z.record(z.any()).optional(),
|
24
|
+
metadata: z.record(z.any()).optional(),
|
25
|
+
}).parse(config);
|
26
|
+
|
27
|
+
this.name = validConfig.name;
|
28
|
+
this.description = validConfig.description;
|
29
|
+
this.version = validConfig.version;
|
30
|
+
this.category = validConfig.category;
|
31
|
+
|
32
|
+
// 构建上下文
|
33
|
+
this.context = ToolContextSchema.parse({
|
34
|
+
name: validConfig.name,
|
35
|
+
description: validConfig.description,
|
36
|
+
args: validConfig.args || {},
|
37
|
+
metadata: validConfig.metadata || {},
|
38
|
+
state: {},
|
39
|
+
});
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* 执行工具
|
44
|
+
* 子类必须实现此方法
|
45
|
+
*/
|
46
|
+
abstract execute(args?: Record<string, any>): Promise<ToolResult>;
|
47
|
+
|
48
|
+
/**
|
49
|
+
* 验证工具是否可用
|
50
|
+
* 子类可以覆盖此方法以提供自定义验证
|
51
|
+
*/
|
52
|
+
async validate(): Promise<boolean> {
|
53
|
+
return true;
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* 处理工具执行异常
|
58
|
+
* @param error 错误对象
|
59
|
+
*/
|
60
|
+
async handleError(error: Error): Promise<void> {
|
61
|
+
console.error(`Tool ${this.name} failed:`, error);
|
62
|
+
// 子类可以覆盖此方法以提供自定义错误处理
|
63
|
+
}
|
64
|
+
|
65
|
+
/**
|
66
|
+
* 获取工具帮助信息
|
67
|
+
* 子类可以覆盖此方法以提供更详细的帮助信息
|
68
|
+
*/
|
69
|
+
getHelp(): string {
|
70
|
+
return `
|
71
|
+
Tool: ${this.name} (v${this.version})
|
72
|
+
Category: ${this.category}
|
73
|
+
Description: ${this.description}
|
74
|
+
|
75
|
+
Arguments:
|
76
|
+
${this.formatArgs()}
|
77
|
+
`.trim();
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* 创建工具执行结果
|
82
|
+
* @param success 是否成功
|
83
|
+
* @param message 结果消息
|
84
|
+
* @param data 结果数据
|
85
|
+
* @param error 错误信息
|
86
|
+
* @param metadata 结果元数据
|
87
|
+
*/
|
88
|
+
protected createResult(
|
89
|
+
success: boolean,
|
90
|
+
message: string,
|
91
|
+
data?: any,
|
92
|
+
error?: any,
|
93
|
+
metadata?: Record<string, any>
|
94
|
+
): ToolResult {
|
95
|
+
return ToolResultSchema.parse({
|
96
|
+
success,
|
97
|
+
message,
|
98
|
+
data,
|
99
|
+
error,
|
100
|
+
metadata,
|
101
|
+
});
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* 获取工具参数
|
106
|
+
* @param key 参数键
|
107
|
+
* @returns 参数值
|
108
|
+
*/
|
109
|
+
protected getArg<T>(key: string): T | undefined {
|
110
|
+
return this.context.args?.[key] as T;
|
111
|
+
}
|
112
|
+
|
113
|
+
/**
|
114
|
+
* 设置工具参数
|
115
|
+
* @param key 参数键
|
116
|
+
* @param value 参数值
|
117
|
+
*/
|
118
|
+
protected setArg<T>(key: string, value: T): void {
|
119
|
+
if (!this.context.args) {
|
120
|
+
this.context.args = {};
|
121
|
+
}
|
122
|
+
this.context.args[key] = value;
|
123
|
+
}
|
124
|
+
|
125
|
+
/**
|
126
|
+
* 获取工具状态
|
127
|
+
* @param key 状态键
|
128
|
+
* @returns 状态值
|
129
|
+
*/
|
130
|
+
protected getState<T>(key: string): T | undefined {
|
131
|
+
return this.context.state?.[key] as T;
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* 设置工具状态
|
136
|
+
* @param key 状态键
|
137
|
+
* @param value 状态值
|
138
|
+
*/
|
139
|
+
protected setState<T>(key: string, value: T): void {
|
140
|
+
if (!this.context.state) {
|
141
|
+
this.context.state = {};
|
142
|
+
}
|
143
|
+
this.context.state[key] = value;
|
144
|
+
}
|
145
|
+
|
146
|
+
/**
|
147
|
+
* 格式化参数说明
|
148
|
+
*/
|
149
|
+
private formatArgs(): string {
|
150
|
+
const args = this.context.args || {};
|
151
|
+
return Object.entries(args)
|
152
|
+
.map(([key, value]) => `- ${key}: ${typeof value}`)
|
153
|
+
.join('\n');
|
154
|
+
}
|
155
|
+
}
|