@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.
Files changed (113) hide show
  1. package/.eslintrc.json +23 -0
  2. package/.prettierrc +7 -0
  3. package/LICENSE +21 -0
  4. package/README-CN.md +754 -0
  5. package/README.md +238 -0
  6. package/bun.lock +1023 -0
  7. package/doc/TutorialAssistant.md +114 -0
  8. package/doc/VercelLLMProvider.md +164 -0
  9. package/eslint.config.js +55 -0
  10. package/examples/data-interpreter-example.ts +173 -0
  11. package/examples/qwen-direct-example.ts +60 -0
  12. package/examples/qwen-example.ts +62 -0
  13. package/examples/tutorial-assistant-example.ts +97 -0
  14. package/jest.config.ts +22 -0
  15. 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
  16. package/output/tutorials/Rust/346/225/231/347/250/213_2025-02-25T08-27-27-632Z.md +1967 -0
  17. 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
  18. 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
  19. 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
  20. package/package.json +58 -0
  21. package/plan-cn.md +321 -0
  22. package/plan.md +154 -0
  23. package/src/actions/analyze-task.ts +65 -0
  24. package/src/actions/base-action.ts +103 -0
  25. package/src/actions/di/execute-nb-code.ts +247 -0
  26. package/src/actions/di/write-analysis-code.ts +234 -0
  27. package/src/actions/write-tutorial.ts +232 -0
  28. package/src/config/browser.ts +33 -0
  29. package/src/config/config.ts +345 -0
  30. package/src/config/embedding.ts +26 -0
  31. package/src/config/llm.ts +36 -0
  32. package/src/config/mermaid.ts +37 -0
  33. package/src/config/omniparse.ts +25 -0
  34. package/src/config/redis.ts +34 -0
  35. package/src/config/s3.ts +33 -0
  36. package/src/config/search.ts +30 -0
  37. package/src/config/workspace.ts +20 -0
  38. package/src/index.ts +40 -0
  39. package/src/management/team.ts +168 -0
  40. package/src/memory/longterm.ts +218 -0
  41. package/src/memory/manager.ts +160 -0
  42. package/src/memory/types.ts +100 -0
  43. package/src/memory/working.ts +154 -0
  44. package/src/monitoring/system.ts +413 -0
  45. package/src/monitoring/types.ts +230 -0
  46. package/src/plugin/manager.ts +79 -0
  47. package/src/plugin/types.ts +114 -0
  48. package/src/provider/vercel-llm.ts +314 -0
  49. package/src/rag/base-rag.ts +194 -0
  50. package/src/rag/document-qa.ts +102 -0
  51. package/src/roles/base-role.ts +155 -0
  52. package/src/roles/data-interpreter.ts +360 -0
  53. package/src/roles/engineer.ts +1 -0
  54. package/src/roles/tutorial-assistant.ts +217 -0
  55. package/src/skills/base-skill.ts +144 -0
  56. package/src/skills/code-review.ts +120 -0
  57. package/src/tools/base-tool.ts +155 -0
  58. package/src/tools/file-system.ts +204 -0
  59. package/src/tools/tool-recommend.d.ts +14 -0
  60. package/src/tools/tool-recommend.ts +31 -0
  61. package/src/types/action.ts +38 -0
  62. package/src/types/config.ts +129 -0
  63. package/src/types/document.ts +354 -0
  64. package/src/types/llm.ts +64 -0
  65. package/src/types/memory.ts +36 -0
  66. package/src/types/message.ts +193 -0
  67. package/src/types/rag.ts +86 -0
  68. package/src/types/role.ts +67 -0
  69. package/src/types/skill.ts +71 -0
  70. package/src/types/task.ts +32 -0
  71. package/src/types/team.ts +55 -0
  72. package/src/types/tool.ts +77 -0
  73. package/src/types/workflow.ts +133 -0
  74. package/src/utils/common.ts +73 -0
  75. package/src/utils/yaml.ts +67 -0
  76. package/src/websocket/browser-client.ts +187 -0
  77. package/src/websocket/client.ts +186 -0
  78. package/src/websocket/server.ts +169 -0
  79. package/src/websocket/types.ts +125 -0
  80. package/src/workflow/executor.ts +193 -0
  81. package/src/workflow/executors/action-executor.ts +72 -0
  82. package/src/workflow/executors/condition-executor.ts +118 -0
  83. package/src/workflow/executors/parallel-executor.ts +201 -0
  84. package/src/workflow/executors/role-executor.ts +76 -0
  85. package/src/workflow/executors/sequence-executor.ts +196 -0
  86. package/tests/actions.test.ts +105 -0
  87. package/tests/benchmark/performance.test.ts +147 -0
  88. package/tests/config/config.test.ts +115 -0
  89. package/tests/config.test.ts +106 -0
  90. package/tests/e2e/setup.ts +74 -0
  91. package/tests/e2e/workflow.test.ts +88 -0
  92. package/tests/llm.test.ts +84 -0
  93. package/tests/memory/memory.test.ts +164 -0
  94. package/tests/memory.test.ts +63 -0
  95. package/tests/monitoring/monitoring.test.ts +225 -0
  96. package/tests/plugin/plugin.test.ts +183 -0
  97. package/tests/provider/bailian-llm.test.ts +98 -0
  98. package/tests/rag.test.ts +162 -0
  99. package/tests/roles.test.ts +88 -0
  100. package/tests/skills.test.ts +166 -0
  101. package/tests/team.test.ts +143 -0
  102. package/tests/tools.test.ts +170 -0
  103. package/tests/types/document.test.ts +181 -0
  104. package/tests/types/message.test.ts +122 -0
  105. package/tests/utils/yaml.test.ts +110 -0
  106. package/tests/utils.test.ts +74 -0
  107. package/tests/websocket/browser-client.test.ts +1 -0
  108. package/tests/websocket/websocket.test.ts +42 -0
  109. package/tests/workflow/parallel-executor.test.ts +224 -0
  110. package/tests/workflow/sequence-executor.test.ts +207 -0
  111. package/tests/workflow.test.ts +290 -0
  112. package/tsconfig.json +27 -0
  113. package/typedoc.json +25 -0
@@ -0,0 +1,247 @@
1
+ import { exec } from 'child_process';
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ import { v4 as uuidv4 } from 'uuid';
5
+ import type { LLMProvider } from '../../types/llm';
6
+
7
+ /**
8
+ * 执行Notebook代码动作
9
+ */
10
+ export class ExecuteNbCode {
11
+ private llm: LLMProvider;
12
+ private tempDir: string = 'temp';
13
+ private process: any = null;
14
+ private commonDependencies = ['pandas', 'numpy', 'matplotlib', 'seaborn', 'scikit-learn'];
15
+
16
+ constructor(llm: LLMProvider) {
17
+ this.llm = llm;
18
+ console.log('[ExecuteNbCode] Initializing ExecuteNbCode');
19
+
20
+ // Ensure temp directory exists
21
+ this.ensureTempDir();
22
+ }
23
+
24
+ /**
25
+ * 确保临时目录存在
26
+ */
27
+ private async ensureTempDir(): Promise<void> {
28
+ try {
29
+ await fs.mkdir(this.tempDir, { recursive: true });
30
+ console.log(`[ExecuteNbCode] Temporary directory created: ${this.tempDir}`);
31
+ } catch (error) {
32
+ console.error('[ExecuteNbCode] Error creating temporary directory:', error);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 执行代码
38
+ */
39
+ async run(code: string): Promise<[string, boolean]> {
40
+ console.log('[ExecuteNbCode] Starting code execution');
41
+ console.log('[ExecuteNbCode] Code length:', code.length, 'bytes');
42
+
43
+ try {
44
+ // Check for required dependencies
45
+ console.log('[ExecuteNbCode] Checking for required dependencies');
46
+ const missingDeps = await this.checkDependencies(code);
47
+
48
+ if (missingDeps.length > 0) {
49
+ console.log(`[ExecuteNbCode] Missing dependencies detected: ${missingDeps.join(', ')}`);
50
+ const installInstructions = this.getInstallInstructions(missingDeps);
51
+ return [installInstructions, false];
52
+ }
53
+
54
+ // Create a temporary Python file
55
+ const filename = `${uuidv4()}.py`;
56
+ const filepath = path.join(this.tempDir, filename);
57
+
58
+ console.log(`[ExecuteNbCode] Writing code to ${filepath}`);
59
+ await fs.writeFile(filepath, code);
60
+ console.log(`[ExecuteNbCode] Code written to temporary file, size: ${(await fs.stat(filepath)).size} bytes`);
61
+
62
+ // Execute the code
63
+ console.log('[ExecuteNbCode] Executing Python code');
64
+ const [result, success] = await this.executePython(filepath);
65
+
66
+ // Clean up
67
+ try {
68
+ await fs.unlink(filepath);
69
+ console.log(`[ExecuteNbCode] Removed temporary file: ${filepath}`);
70
+ } catch (cleanupError) {
71
+ console.warn(`[ExecuteNbCode] Failed to remove temporary file: ${filepath}`, cleanupError);
72
+ }
73
+
74
+ if (success) {
75
+ console.log(`[ExecuteNbCode] Execution successful, output length: ${result.length} bytes`);
76
+ } else {
77
+ console.error(`[ExecuteNbCode] Execution failed: ${result.substring(0, 200)}...`);
78
+ }
79
+
80
+ return [result, success];
81
+ } catch (error) {
82
+ console.error('[ExecuteNbCode] Error executing code:', error);
83
+ return [`Error executing code: ${error}`, false];
84
+ }
85
+ }
86
+
87
+ /**
88
+ * 检查代码中使用的依赖是否已安装
89
+ */
90
+ private async checkDependencies(code: string): Promise<string[]> {
91
+ console.log('[ExecuteNbCode] Analyzing code for import statements');
92
+ const importRegex = /import\s+([a-zA-Z0-9_,\s]+)(?:\s+as\s+[a-zA-Z0-9_]+)?|from\s+([a-zA-Z0-9_.]+)\s+import/g;
93
+ const matches = [...code.matchAll(importRegex)];
94
+
95
+ // Extract module names
96
+ const moduleSet = new Set<string>();
97
+ for (const match of matches) {
98
+ if (match[1]) {
99
+ // import X, Y, Z
100
+ const modules = match[1].split(',').map(m => m.trim().split('.')[0]);
101
+ modules.forEach(m => moduleSet.add(m));
102
+ } else if (match[2]) {
103
+ // from X import Y
104
+ moduleSet.add(match[2].split('.')[0]);
105
+ }
106
+ }
107
+
108
+ console.log(`[ExecuteNbCode] Detected modules: ${Array.from(moduleSet).join(', ')}`);
109
+
110
+ // Check if modules are installed
111
+ const missingDeps: string[] = [];
112
+ for (const module of moduleSet) {
113
+ // Skip standard library modules
114
+ if (['os', 'sys', 'time', 'datetime', 'json', 'math', 'random', 're'].includes(module)) {
115
+ continue;
116
+ }
117
+
118
+ // Check if module is installed
119
+ const isInstalled = await this.isModuleInstalled(module);
120
+ if (!isInstalled) {
121
+ missingDeps.push(module);
122
+ }
123
+ }
124
+
125
+ return missingDeps;
126
+ }
127
+
128
+ /**
129
+ * 检查模块是否已安装
130
+ */
131
+ private async isModuleInstalled(moduleName: string): Promise<boolean> {
132
+ return new Promise((resolve) => {
133
+ console.log(`[ExecuteNbCode] Checking if module is installed: ${moduleName}`);
134
+ const command = `python -c "import ${moduleName}" 2>/dev/null`;
135
+
136
+ exec(command, (error) => {
137
+ if (error) {
138
+ console.log(`[ExecuteNbCode] Module ${moduleName} is not installed`);
139
+ resolve(false);
140
+ return;
141
+ }
142
+
143
+ console.log(`[ExecuteNbCode] Module ${moduleName} is installed`);
144
+ resolve(true);
145
+ });
146
+ });
147
+ }
148
+
149
+ /**
150
+ * 获取安装说明
151
+ */
152
+ private getInstallInstructions(missingDeps: string[]): string {
153
+ const pipCommand = `pip install ${missingDeps.join(' ')}`;
154
+
155
+ return `
156
+ ## Missing Dependencies Detected
157
+
158
+ The following Python packages are required but not installed:
159
+ \`\`\`
160
+ ${missingDeps.join('\n')}
161
+ \`\`\`
162
+
163
+ Please install these dependencies using pip:
164
+ \`\`\`
165
+ ${pipCommand}
166
+ \`\`\`
167
+
168
+ After installing the dependencies, try running the analysis again.
169
+
170
+ Original Python Code:
171
+ \`\`\`python
172
+ ${this.getFirstLinesOfCode()}
173
+ \`\`\`
174
+ ...
175
+ `;
176
+ }
177
+
178
+ /**
179
+ * 获取要显示的代码的前几行
180
+ */
181
+ private getFirstLinesOfCode(): string {
182
+ // This would typically show the first few lines of the code
183
+ // Since we don't have the code at this point, we'll return a placeholder
184
+ return "# The generated code requires additional Python packages\n# Please install them using the pip command above";
185
+ }
186
+
187
+ /**
188
+ * 执行Python代码
189
+ */
190
+ private executePython(filepath: string): Promise<[string, boolean]> {
191
+ return new Promise((resolve) => {
192
+ const command = `python ${filepath}`;
193
+
194
+ console.log(`[ExecuteNbCode] Running command: ${command}`);
195
+
196
+ this.process = exec(command, (error, stdout, stderr) => {
197
+ if (error) {
198
+ console.error(`[ExecuteNbCode] Execution error: ${error.message}`);
199
+
200
+ // Check if the error is due to missing dependencies
201
+ if (stderr.includes('ModuleNotFoundError: No module named')) {
202
+ const match = stderr.match(/ModuleNotFoundError: No module named '([^']+)'/);
203
+ if (match && match[1]) {
204
+ const missingModule = match[1];
205
+ const installCmd = `pip install ${missingModule}`;
206
+ const errorMsg = `
207
+ ## Missing Dependency: ${missingModule}
208
+
209
+ Python cannot find the required module: \`${missingModule}\`
210
+
211
+ Please install it using pip:
212
+ \`\`\`
213
+ ${installCmd}
214
+ \`\`\`
215
+
216
+ After installing the dependency, try running the analysis again.
217
+ `;
218
+ resolve([errorMsg, false]);
219
+ return;
220
+ }
221
+ }
222
+
223
+ resolve([`Execution error: ${error.message}\n${stderr}`, false]);
224
+ return;
225
+ }
226
+
227
+ if (stderr) {
228
+ console.warn(`[ExecuteNbCode] Stderr: ${stderr}`);
229
+ }
230
+
231
+ console.log(`[ExecuteNbCode] Execution successful, stdout length: ${stdout.length}`);
232
+ resolve([stdout, true]);
233
+ });
234
+ });
235
+ }
236
+
237
+ /**
238
+ * 终止执行
239
+ */
240
+ async terminate(): Promise<void> {
241
+ if (this.process) {
242
+ console.log('[ExecuteNbCode] Terminating process');
243
+ this.process.kill();
244
+ this.process = null;
245
+ }
246
+ }
247
+ }
@@ -0,0 +1,234 @@
1
+ import { BaseAction } from '../base-action';
2
+ import type { ActionOutput } from '../../types/action';
3
+ import type { LLMProvider } from '../../types/llm';
4
+ import type { Message } from '../../types/message';
5
+
6
+ // Define data info prompt template
7
+ export const DATA_INFO = `
8
+ # Data Information
9
+ {info}
10
+ `;
11
+
12
+ /**
13
+ * ActionConfig配置接口
14
+ */
15
+ export interface ActionConfig {
16
+ name?: string;
17
+ description?: string;
18
+ args?: Record<string, any>;
19
+ llm: LLMProvider;
20
+ memory?: any;
21
+ workingMemory?: any;
22
+ }
23
+
24
+ /**
25
+ * WriteAnalysisCode配置接口
26
+ */
27
+ export interface WriteAnalysisCodeConfig extends ActionConfig {
28
+ llm: LLMProvider;
29
+ }
30
+
31
+ /**
32
+ * 编写数据分析代码动作
33
+ */
34
+ export class WriteAnalysisCode extends BaseAction {
35
+ constructor(config: WriteAnalysisCodeConfig) {
36
+ super({
37
+ ...config,
38
+ name: 'WriteAnalysisCode',
39
+ });
40
+ console.log('[WriteAnalysisCode] Initialized');
41
+ }
42
+
43
+ /**
44
+ * 运行动作
45
+ */
46
+ async run(): Promise<ActionOutput> {
47
+ try {
48
+ console.log('[WriteAnalysisCode] Starting run method');
49
+
50
+ // Get arguments
51
+ const userRequirement = this.getArg<string>('user_requirement') || '';
52
+ const planStatus = this.getArg<string>('plan_status') || '';
53
+ const toolInfo = this.getArg<string>('tool_info') || '';
54
+ const workingMemory = this.getArg<Message[]>('working_memory') || [];
55
+ const useReflection = this.getArg<boolean>('use_reflection') || false;
56
+
57
+ console.log(`[WriteAnalysisCode] User requirement: ${userRequirement.substring(0, 50)}...`);
58
+ console.log(`[WriteAnalysisCode] Use reflection: ${useReflection}`);
59
+
60
+ // Format working memory
61
+ const workingMemoryText = workingMemory
62
+ .map(msg => `${msg.role}: ${msg.content}`)
63
+ .join('\n\n');
64
+
65
+ // Generate prompt
66
+ const prompt = this.generateCodePrompt(
67
+ userRequirement,
68
+ workingMemoryText,
69
+ planStatus,
70
+ toolInfo,
71
+ useReflection
72
+ );
73
+
74
+ console.log(`[WriteAnalysisCode] Generated prompt (${prompt.length} characters)`);
75
+
76
+ // Generate code with LLM
77
+ console.log('[WriteAnalysisCode] Calling LLM to generate code');
78
+ const result = await this.llm.generate(prompt);
79
+
80
+ // Extract Python code
81
+ const code = this.extractPythonCode(result);
82
+ console.log(`[WriteAnalysisCode] Generated code (${code.length} characters)`);
83
+
84
+ return this.createOutput(code, 'completed');
85
+ } catch (error) {
86
+ console.error('[WriteAnalysisCode] Error generating code:', error);
87
+ return this.createOutput(`Error generating code: ${error}`, 'failed');
88
+ }
89
+ }
90
+
91
+ /**
92
+ * 生成代码提示词
93
+ */
94
+ private generateCodePrompt(
95
+ userRequirement: string,
96
+ workingMemory: string,
97
+ planStatus: string = '',
98
+ toolInfo: string = '',
99
+ useReflection: boolean = false
100
+ ): string {
101
+ // Construct the prompt
102
+ let prompt = `# Data Analysis Task
103
+ ## User Requirement
104
+ ${userRequirement}
105
+
106
+ `;
107
+
108
+ // Add working memory if available
109
+ if (workingMemory) {
110
+ prompt += `## Context and History
111
+ ${workingMemory}
112
+
113
+ `;
114
+ }
115
+
116
+ // Add plan status if available
117
+ if (planStatus) {
118
+ prompt += `## Current Plan Status
119
+ ${planStatus}
120
+
121
+ `;
122
+ }
123
+
124
+ // Add tool info if available
125
+ if (toolInfo) {
126
+ prompt += `## Available Tools
127
+ ${toolInfo}
128
+
129
+ `;
130
+ }
131
+
132
+ // Reflection instructions if enabled
133
+ if (useReflection) {
134
+ prompt += `## Reflection
135
+ Please review the previous code and execution results. Identify errors and issues, and make improvements.
136
+
137
+ `;
138
+ }
139
+
140
+ // Final instructions
141
+ prompt += `## Instructions
142
+ Please write clean, efficient Python code to analyze the data according to the user requirement.
143
+ Use pandas, numpy, matplotlib, and other data science libraries as appropriate.
144
+ Include comments to explain your code.
145
+ Make sure your code is complete and executable.
146
+ Do not include markdown annotations, just write the Python code directly.
147
+
148
+ `;
149
+
150
+ return prompt;
151
+ }
152
+
153
+ /**
154
+ * 从LLM响应中提取Python代码
155
+ */
156
+ private extractPythonCode(text: string): string {
157
+ // Try to extract code from markdown code blocks
158
+ const pythonBlockRegex = /```(?:python)?\s*([\s\S]*?)```/g;
159
+ const matches = text.match(pythonBlockRegex);
160
+
161
+ if (matches && matches.length > 0) {
162
+ // Extract code from the first code block
163
+ const codeBlock = matches[0];
164
+ return codeBlock.replace(/```(?:python)?\s*/, '').replace(/```$/, '').trim();
165
+ }
166
+
167
+ // If no code blocks found, assume the entire text is code
168
+ return text.trim();
169
+ }
170
+ }
171
+
172
+ /**
173
+ * CheckData配置接口
174
+ */
175
+ export interface CheckDataConfig extends ActionConfig {
176
+ llm: LLMProvider;
177
+ }
178
+
179
+ /**
180
+ * 检查数据动作
181
+ */
182
+ export class CheckData extends BaseAction {
183
+ constructor(config: CheckDataConfig) {
184
+ super({
185
+ ...config,
186
+ name: 'CheckData',
187
+ });
188
+ console.log('[CheckData] Initialized');
189
+ }
190
+
191
+ /**
192
+ * 运行动作
193
+ */
194
+ async run(): Promise<ActionOutput> {
195
+ try {
196
+ console.log('[CheckData] Starting run method');
197
+
198
+ // In a full implementation, this would need to accept a plan and
199
+ // create code to check the data based on the plan
200
+
201
+ // For simplicity, we'll just return a basic data check script
202
+ const code = `
203
+ import pandas as pd
204
+ import numpy as np
205
+
206
+ # Check if dataset exists
207
+ try:
208
+ # Try to read a potential dataset (this is just a placeholder)
209
+ df = pd.read_csv('data.csv')
210
+
211
+ # Display basic information about the dataset
212
+ print("Dataset Information:")
213
+ print(f"Shape: {df.shape}")
214
+ print("\\nFirst 5 rows:")
215
+ print(df.head())
216
+ print("\\nData types:")
217
+ print(df.dtypes)
218
+ print("\\nSummary statistics:")
219
+ print(df.describe())
220
+ print("\\nMissing values:")
221
+ print(df.isnull().sum())
222
+ except Exception as e:
223
+ print(f"Error loading dataset: {e}")
224
+ print("No dataset available for analysis.")
225
+ `;
226
+
227
+ console.log('[CheckData] Generated basic data check code');
228
+ return this.createOutput(code, 'completed');
229
+ } catch (error) {
230
+ console.error('[CheckData] Error generating data check code:', error);
231
+ return this.createOutput(`Error generating data check code: ${error}`, 'failed');
232
+ }
233
+ }
234
+ }
@@ -0,0 +1,232 @@
1
+ import { z } from 'zod';
2
+ import type { ActionOutput } from '../types/action';
3
+ import type { LLMProvider } from '../types/llm';
4
+ import { BaseAction } from './base-action';
5
+
6
+ /**
7
+ * 动作配置接口
8
+ */
9
+ export interface ActionConfig {
10
+ name?: string;
11
+ description?: string;
12
+ args?: Record<string, any>;
13
+ llm: LLMProvider;
14
+ memory?: any;
15
+ workingMemory?: any;
16
+ }
17
+
18
+ /**
19
+ * 目录结构类型定义
20
+ */
21
+ export type Directory = {
22
+ title: string;
23
+ directory: Array<Record<string, string[]>>;
24
+ };
25
+
26
+ /**
27
+ * 目录结构模式验证
28
+ */
29
+ export const DirectorySchema = z.object({
30
+ title: z.string(),
31
+ directory: z.array(z.record(z.string(), z.array(z.string()))),
32
+ });
33
+
34
+ /**
35
+ * 教程目录生成动作配置
36
+ */
37
+ export interface WriteDirectoryConfig extends ActionConfig {
38
+ language?: string;
39
+ }
40
+
41
+ /**
42
+ * 教程目录生成动作
43
+ */
44
+ export class WriteDirectory extends BaseAction {
45
+ language: string;
46
+
47
+ constructor(config: WriteDirectoryConfig) {
48
+ super({
49
+ ...config,
50
+ name: 'WriteDirectory',
51
+ });
52
+ this.language = config.language || 'Chinese';
53
+ console.log(`[WriteDirectory] Initialized with language: ${this.language}`);
54
+ }
55
+
56
+ /**
57
+ * 执行动作,生成教程目录结构
58
+ */
59
+ async run(): Promise<ActionOutput> {
60
+ try {
61
+ console.log('[WriteDirectory] Starting run() method');
62
+ const topic = this.getArg<string>('topic') || '';
63
+ console.log(`[WriteDirectory] Topic: "${topic}"`);
64
+
65
+ if (!topic) {
66
+ console.warn('[WriteDirectory] Topic is required but not provided');
67
+ return this.createOutput('Topic is required', 'failed');
68
+ }
69
+
70
+ const prompt = this.generateDirectoryPrompt(topic);
71
+ console.log(`[WriteDirectory] Generated prompt (${prompt.length} characters)`);
72
+ console.log('[WriteDirectory] Calling LLM to generate directory structure');
73
+
74
+ const result = await this.llm.generate(prompt);
75
+ console.log(`[WriteDirectory] Received response from LLM (${result.length} characters)`);
76
+
77
+ // 解析JSON结果
78
+ try {
79
+ console.log('[WriteDirectory] Parsing LLM response as JSON');
80
+ const jsonStr = result.replace(/```json|```/g, '').trim();
81
+ console.log(`[WriteDirectory] Cleaned JSON string: ${jsonStr.substring(0, 100)}...`);
82
+
83
+ const parsed = JSON.parse(jsonStr);
84
+ console.log('[WriteDirectory] JSON parsed successfully');
85
+
86
+ const directory = DirectorySchema.parse(parsed);
87
+ console.log(`[WriteDirectory] Directory schema validated: ${directory.title} with ${directory.directory.length} sections`);
88
+
89
+ return this.createOutput(
90
+ JSON.stringify(directory),
91
+ 'completed',
92
+ directory
93
+ );
94
+ } catch (e) {
95
+ console.error('[WriteDirectory] Failed to parse directory structure JSON:', e);
96
+ console.log('[WriteDirectory] Original LLM response:', result);
97
+
98
+ // 返回默认结构
99
+ const defaultDirectory: Directory = {
100
+ title: `Tutorial for ${topic}`,
101
+ directory: [
102
+ { "Introduction": ["Overview", "Prerequisites"] },
103
+ { "Main Content": ["Basic Concepts", "Advanced Usage"] },
104
+ { "Conclusion": ["Summary", "Next Steps"] }
105
+ ]
106
+ };
107
+
108
+ console.log('[WriteDirectory] Using default directory structure instead');
109
+ return this.createOutput(
110
+ JSON.stringify(defaultDirectory),
111
+ 'completed',
112
+ defaultDirectory
113
+ );
114
+ }
115
+ } catch (error) {
116
+ console.error('[WriteDirectory] Error generating directory:', error);
117
+ if (error instanceof Error) {
118
+ await this.handleException(error);
119
+ }
120
+ return this.createOutput(`Failed to generate directory: ${error}`, 'failed');
121
+ }
122
+ }
123
+
124
+ /**
125
+ * 生成目录结构的提示词
126
+ * @param topic 教程主题
127
+ * @returns 提示词
128
+ */
129
+ private generateDirectoryPrompt(topic: string): string {
130
+ const language = this.language === 'Chinese' ? '中文' : 'English';
131
+ return `请为主题"${topic}"创建一个完整的教程目录结构。目录应该结构清晰、内容全面、逻辑连贯。
132
+
133
+ 请使用以下JSON格式输出目录结构:
134
+ {
135
+ "title": "教程标题",
136
+ "directory": [
137
+ {"第一章标题": ["1.1 小节标题", "1.2 小节标题", ...]},
138
+ {"第二章标题": ["2.1 小节标题", "2.2 小节标题", ...]},
139
+ ...
140
+ ]
141
+ }
142
+
143
+ 请确保输出是有效的JSON格式,目录结构要反映${language}教程的完整性、系统性和专业性。`;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * 教程内容生成动作配置
149
+ */
150
+ export interface WriteContentConfig extends ActionConfig {
151
+ language?: string;
152
+ directory: Record<string, string[]>;
153
+ }
154
+
155
+ /**
156
+ * 教程内容生成动作
157
+ */
158
+ export class WriteContent extends BaseAction {
159
+ language: string;
160
+ directory: Record<string, string[]>;
161
+
162
+ constructor(config: WriteContentConfig) {
163
+ super({
164
+ ...config,
165
+ name: 'WriteContent',
166
+ });
167
+ this.language = config.language || 'Chinese';
168
+ this.directory = config.directory;
169
+
170
+ const sectionTitle = Object.keys(this.directory)[0];
171
+ const subsections = this.directory[sectionTitle];
172
+ console.log(`[WriteContent] Initialized for section "${sectionTitle}" with ${subsections.length} subsections`);
173
+ }
174
+
175
+ /**
176
+ * 执行动作,生成教程内容
177
+ */
178
+ async run(): Promise<ActionOutput> {
179
+ try {
180
+ console.log('[WriteContent] Starting run() method');
181
+
182
+ const topic = this.getArg<string>('topic') || '';
183
+ console.log(`[WriteContent] Topic: "${topic}"`);
184
+
185
+ if (!topic) {
186
+ console.warn('[WriteContent] Topic is required but not provided');
187
+ return this.createOutput('Topic is required', 'failed');
188
+ }
189
+
190
+ const sectionTitle = Object.keys(this.directory)[0];
191
+ console.log(`[WriteContent] Generating content for section: "${sectionTitle}"`);
192
+
193
+ const prompt = this.generateContentPrompt(topic);
194
+ console.log(`[WriteContent] Generated prompt (${prompt.length} characters)`);
195
+ console.log('[WriteContent] Calling LLM to generate content');
196
+
197
+ const result = await this.llm.generate(prompt);
198
+ console.log(`[WriteContent] Received response from LLM (${result.length} characters)`);
199
+
200
+ return this.createOutput(result.trim(), 'completed');
201
+ } catch (error) {
202
+ console.error('[WriteContent] Error generating content:', error);
203
+ if (error instanceof Error) {
204
+ await this.handleException(error);
205
+ }
206
+ return this.createOutput(`Failed to generate content: ${error}`, 'failed');
207
+ }
208
+ }
209
+
210
+ /**
211
+ * 生成内容的提示词
212
+ * @param topic 教程主题
213
+ * @returns 提示词
214
+ */
215
+ private generateContentPrompt(topic: string): string {
216
+ const language = this.language === 'Chinese' ? '中文' : 'English';
217
+ const sectionTitle = Object.keys(this.directory)[0];
218
+ const subsections = this.directory[sectionTitle];
219
+
220
+ return `请为主题"${topic}"下的"${sectionTitle}"章节编写详细内容。请包含以下小节:${subsections.join('、')}。
221
+
222
+ 要求:
223
+ 1. 使用${language}编写
224
+ 2. 严格遵循Markdown语法,布局整洁规范
225
+ 3. 内容应该专业、详细、通俗易懂
226
+ 4. 提供实用的信息和示例
227
+ 5. 每个小节都应该有适当的标题(使用## 和 ###)
228
+ 6. 总字数应该在1000-2000字之间
229
+
230
+ 请直接输出Markdown内容,不需要有额外的注释。`;
231
+ }
232
+ }