gitlab-ai-review 1.1.0 → 1.2.0

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/index.js CHANGED
@@ -6,6 +6,7 @@
6
6
  import { getConfig, validateConfig } from './lib/config.js';
7
7
  import { GitLabClient } from './lib/gitlab-client.js';
8
8
  import { AIClient } from './lib/ai-client.js';
9
+ import * as PromptTools from './lib/prompt-tools.js';
9
10
 
10
11
  /**
11
12
  * GitLab AI Review SDK 主类
@@ -13,7 +14,7 @@ import { AIClient } from './lib/ai-client.js';
13
14
  export class GitLabAIReview {
14
15
  constructor(options = {}) {
15
16
  this.name = 'GitLab AI Review SDK';
16
- this.version = '1.0.3';
17
+ this.version = '1.2.0';
17
18
 
18
19
  // 如果传入了配置,使用手动配置;否则使用自动检测
19
20
  if (options.token || options.gitlab) {
@@ -137,27 +138,62 @@ export class GitLabAIReview {
137
138
 
138
139
  /**
139
140
  * AI 审查单个文件的代码变更
141
+ * @param {Object} change - 代码变更对象
142
+ * @returns {Promise<Object>} 审查结果
140
143
  */
141
144
  async reviewCodeChange(change) {
142
145
  const aiClient = this.getAIClient();
143
146
  const guardConfig = this.config.ai?.guardConfig?.content || '';
144
147
 
145
- return aiClient.reviewCode({
148
+ // 使用 PromptTools 构建消息
149
+ const messages = PromptTools.buildReviewMessages({
146
150
  diff: change.diff,
147
151
  fileName: change.new_path || change.old_path,
148
152
  guardConfig,
149
153
  });
154
+
155
+ // 调用 AI
156
+ return aiClient.sendMessage(messages);
150
157
  }
151
158
 
152
159
  /**
153
160
  * AI 审查 MR 的所有代码变更
161
+ * @returns {Promise<Array>} 审查结果数组
154
162
  */
155
163
  async reviewMergeRequest() {
156
164
  const changes = await this.getMergeRequestChanges();
157
165
  const aiClient = this.getAIClient();
158
166
  const guardConfig = this.config.ai?.guardConfig?.content || '';
159
167
 
160
- return aiClient.reviewMultipleFiles(changes, guardConfig);
168
+ const reviews = [];
169
+
170
+ for (const change of changes) {
171
+ try {
172
+ // 使用 PromptTools 构建消息
173
+ const messages = PromptTools.buildReviewMessages({
174
+ diff: change.diff,
175
+ fileName: change.new_path || change.old_path,
176
+ guardConfig,
177
+ });
178
+
179
+ // 调用 AI
180
+ const result = await aiClient.sendMessage(messages);
181
+
182
+ reviews.push({
183
+ fileName: change.new_path || change.old_path,
184
+ status: 'success',
185
+ ...result,
186
+ });
187
+ } catch (error) {
188
+ reviews.push({
189
+ fileName: change.new_path || change.old_path,
190
+ status: 'error',
191
+ error: error.message,
192
+ });
193
+ }
194
+ }
195
+
196
+ return reviews;
161
197
  }
162
198
 
163
199
  /**
@@ -198,10 +234,11 @@ export class GitLabAIReview {
198
234
  }
199
235
  }
200
236
 
201
- // 导出工具函数
237
+ // 导出工具函数和类
202
238
  export { getConfig, validateConfig } from './lib/config.js';
203
239
  export { GitLabClient } from './lib/gitlab-client.js';
204
240
  export { AIClient } from './lib/ai-client.js';
241
+ export { PromptTools };
205
242
 
206
243
  // 默认导出
207
244
  export default GitLabAIReview;
package/lib/ai-client.js CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * AI 客户端 - 基于 ARK API (豆包大模型)
3
+ * 只负责调用 AI API,不处理业务逻辑
3
4
  */
4
5
 
5
6
  import OpenAI from 'openai';
6
7
 
7
8
  /**
8
- * AI 客户端类
9
+ * AI 客户端类 - 纯粹的 API 调用封装
9
10
  */
10
11
  export class AIClient {
11
12
  constructor(config = {}) {
@@ -27,54 +28,51 @@ export class AIClient {
27
28
  }
28
29
 
29
30
  /**
30
- * 代码审查 - 非流式响应
31
- * @param {Object} params - 审查参数
32
- * @param {string} params.diff - 代码差异
33
- * @param {string} params.fileName - 文件名
34
- * @param {string} params.guardConfig - AI Review Guard 配置内容
35
- * @returns {Promise<string>} 审查结果
31
+ * 发送消息到 AI(非流式)
32
+ * @param {Array|string} prompt - 消息数组或单个提示词
33
+ * @param {Object} options - 可选参数
34
+ * @returns {Promise<Object>} AI 响应
36
35
  */
37
- async reviewCode({ diff, fileName, guardConfig = '' }) {
38
- const systemPrompt = this._buildSystemPrompt(guardConfig);
39
- const userPrompt = this._buildReviewPrompt(diff, fileName);
36
+ async sendMessage(prompt, options = {}) {
37
+ // 如果传入的是字符串,转换为消息数组
38
+ const messages = typeof prompt === 'string'
39
+ ? [{ role: 'user', content: prompt }]
40
+ : prompt;
40
41
 
41
42
  const completion = await this.openai.chat.completions.create({
42
- messages: [
43
- { role: 'system', content: systemPrompt },
44
- { role: 'user', content: userPrompt },
45
- ],
46
- model: this.model,
47
- reasoning_effort: this.config.reasoningEffort || 'medium',
43
+ messages,
44
+ model: options.model || this.model,
45
+ reasoning_effort: options.reasoningEffort || this.config.reasoningEffort || 'medium',
46
+ ...options,
48
47
  });
49
48
 
50
49
  return {
51
50
  reasoning: completion.choices[0]?.message?.reasoning_content || '',
52
51
  content: completion.choices[0]?.message?.content || '',
53
52
  usage: completion.usage,
53
+ raw: completion,
54
54
  };
55
55
  }
56
56
 
57
57
  /**
58
- * 代码审查 - 流式响应
59
- * @param {Object} params - 审查参数
60
- * @param {string} params.diff - 代码差异
61
- * @param {string} params.fileName - 文件名
62
- * @param {string} params.guardConfig - AI Review Guard 配置内容
58
+ * 发送消息到 AI(流式)
59
+ * @param {Array|string} prompt - 消息数组或单个提示词
63
60
  * @param {Function} onChunk - 流式回调函数
64
- * @returns {Promise<string>} 完整审查结果
61
+ * @param {Object} options - 可选参数
62
+ * @returns {Promise<Object>} 完整 AI 响应
65
63
  */
66
- async reviewCodeStream({ diff, fileName, guardConfig = '' }, onChunk) {
67
- const systemPrompt = this._buildSystemPrompt(guardConfig);
68
- const userPrompt = this._buildReviewPrompt(diff, fileName);
64
+ async sendMessageStream(prompt, onChunk, options = {}) {
65
+ // 如果传入的是字符串,转换为消息数组
66
+ const messages = typeof prompt === 'string'
67
+ ? [{ role: 'user', content: prompt }]
68
+ : prompt;
69
69
 
70
70
  const stream = await this.openai.chat.completions.create({
71
- messages: [
72
- { role: 'system', content: systemPrompt },
73
- { role: 'user', content: userPrompt },
74
- ],
75
- model: this.model,
76
- reasoning_effort: this.config.reasoningEffort || 'medium',
71
+ messages,
72
+ model: options.model || this.model,
73
+ reasoning_effort: options.reasoningEffort || this.config.reasoningEffort || 'medium',
77
74
  stream: true,
75
+ ...options,
78
76
  });
79
77
 
80
78
  let fullReasoning = '';
@@ -99,107 +97,22 @@ export class AIClient {
99
97
  }
100
98
 
101
99
  /**
102
- * 通用聊天接口
103
- * @param {Array} messages - 消息数组
104
- * @param {Object} options - 可选参数
105
- * @returns {Promise<Object>} AI 响应
100
+ * 获取原始 OpenAI 客户端(高级用法)
101
+ * @returns {OpenAI} OpenAI 客户端实例
106
102
  */
107
- async chat(messages, options = {}) {
108
- const completion = await this.openai.chat.completions.create({
109
- messages,
110
- model: options.model || this.model,
111
- reasoning_effort: options.reasoningEffort || 'medium',
112
- stream: options.stream || false,
113
- ...options,
114
- });
115
-
116
- if (options.stream) {
117
- return completion;
118
- }
119
-
120
- return {
121
- reasoning: completion.choices[0]?.message?.reasoning_content || '',
122
- content: completion.choices[0]?.message?.content || '',
123
- usage: completion.usage,
124
- };
125
- }
126
-
127
- /**
128
- * 构建系统提示词
129
- * @private
130
- */
131
- _buildSystemPrompt(guardConfig) {
132
- let prompt = `你是一个专业的代码审查助手,负责审查 GitLab Merge Request 的代码变更。
133
-
134
- 你的职责是:
135
- 1. 识别代码中的潜在问题(安全漏洞、性能问题、逻辑错误等)
136
- 2. 提供具体的改进建议
137
- 3. 指出不符合最佳实践的代码
138
- 4. 评估代码的可维护性和可读性
139
-
140
- 请以专业、建设性的语气提供审查意见。`;
141
-
142
- if (guardConfig) {
143
- prompt += `\n\n项目特定的审查规则:\n${guardConfig}`;
144
- }
145
-
146
- return prompt;
147
- }
148
-
149
- /**
150
- * 构建代码审查提示词
151
- * @private
152
- */
153
- _buildReviewPrompt(diff, fileName) {
154
- return `请审查以下代码变更:
155
-
156
- **文件名**: ${fileName}
157
-
158
- **代码差异**:
159
- \`\`\`diff
160
- ${diff}
161
- \`\`\`
162
-
163
- 请提供:
164
- 1. 主要问题(如果有)
165
- 2. 改进建议
166
- 3. 优点(如果有)
167
-
168
- 如果代码没有问题,请简要说明代码质量良好。`;
103
+ getClient() {
104
+ return this.openai;
169
105
  }
170
106
 
171
107
  /**
172
- * 批量审查多个文件的变更
173
- * @param {Array} changes - 变更数组
174
- * @param {string} guardConfig - AI Review Guard 配置
175
- * @returns {Promise<Array>} 审查结果数组
108
+ * 获取当前配置
109
+ * @returns {Object} 配置对象
176
110
  */
177
- async reviewMultipleFiles(changes, guardConfig = '') {
178
- const reviews = [];
179
-
180
- for (const change of changes) {
181
- try {
182
- const result = await this.reviewCode({
183
- diff: change.diff,
184
- fileName: change.new_path || change.old_path,
185
- guardConfig,
186
- });
187
-
188
- reviews.push({
189
- fileName: change.new_path || change.old_path,
190
- status: 'success',
191
- ...result,
192
- });
193
- } catch (error) {
194
- reviews.push({
195
- fileName: change.new_path || change.old_path,
196
- status: 'error',
197
- error: error.message,
198
- });
199
- }
200
- }
201
-
202
- return reviews;
111
+ getConfig() {
112
+ return {
113
+ model: this.model,
114
+ ...this.config,
115
+ };
203
116
  }
204
117
  }
205
118
 
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Prompt 工具 - 用于拼接代码审查的提示词
3
+ */
4
+
5
+ /**
6
+ * 构建系统提示词
7
+ * @param {string} guardConfig - AI Review Guard 配置内容
8
+ * @returns {string} 系统提示词
9
+ */
10
+ export function buildSystemPrompt(guardConfig = '') {
11
+ let prompt = `你是一个专业的代码审查助手,负责审查 GitLab Merge Request 的代码变更。
12
+
13
+ 你的职责是:
14
+ 1. 识别代码中的潜在问题(安全漏洞、性能问题、逻辑错误等)
15
+ 2. 提供具体的改进建议
16
+ 3. 指出不符合最佳实践的代码
17
+ 4. 评估代码的可维护性和可读性
18
+
19
+ 请以专业、建设性的语气提供审查意见。`;
20
+
21
+ if (guardConfig) {
22
+ prompt += `\n\n项目特定的审查规则:\n${guardConfig}`;
23
+ }
24
+
25
+ return prompt;
26
+ }
27
+
28
+ /**
29
+ * 构建代码审查的用户提示词
30
+ * @param {string} diff - 代码差异
31
+ * @param {string} fileName - 文件名
32
+ * @returns {string} 用户提示词
33
+ */
34
+ export function buildReviewPrompt(diff, fileName) {
35
+ return `请审查以下代码变更:
36
+
37
+ **文件名**: ${fileName}
38
+
39
+ **代码差异**:
40
+ \`\`\`diff
41
+ ${diff}
42
+ \`\`\`
43
+
44
+ 请提供:
45
+ 1. 主要问题(如果有)
46
+ 2. 改进建议
47
+ 3. 优点(如果有)
48
+
49
+ 如果代码没有问题,请简要说明代码质量良好。`;
50
+ }
51
+
52
+ /**
53
+ * 构建完整的审查消息数组
54
+ * @param {Object} params - 参数
55
+ * @param {string} params.diff - 代码差异
56
+ * @param {string} params.fileName - 文件名
57
+ * @param {string} params.guardConfig - AI Review Guard 配置
58
+ * @returns {Array} 消息数组
59
+ */
60
+ export function buildReviewMessages({ diff, fileName, guardConfig = '' }) {
61
+ return [
62
+ { role: 'system', content: buildSystemPrompt(guardConfig) },
63
+ { role: 'user', content: buildReviewPrompt(diff, fileName) },
64
+ ];
65
+ }
66
+
67
+ /**
68
+ * 构建批量审查的提示词
69
+ * @param {Array} changes - 代码变更数组
70
+ * @param {string} guardConfig - AI Review Guard 配置
71
+ * @returns {string} 批量审查提示词
72
+ */
73
+ export function buildBatchReviewPrompt(changes, guardConfig = '') {
74
+ let prompt = buildSystemPrompt(guardConfig);
75
+
76
+ prompt += '\n\n请审查以下 ' + changes.length + ' 个文件的代码变更:\n\n';
77
+
78
+ changes.forEach((change, index) => {
79
+ const fileName = change.new_path || change.old_path;
80
+ const status = change.new_file ? '新增' : change.deleted_file ? '删除' : '修改';
81
+
82
+ prompt += `## ${index + 1}. ${fileName} (${status})\n\n`;
83
+ prompt += '```diff\n';
84
+ prompt += change.diff || '(无差异内容)';
85
+ prompt += '\n```\n\n';
86
+ });
87
+
88
+ prompt += '\n请为每个文件提供审查意见,并在最后给出整体评价。';
89
+
90
+ return prompt;
91
+ }
92
+
93
+ /**
94
+ * 格式化 diff 内容(可选的预处理)
95
+ * @param {string} diff - 原始 diff
96
+ * @returns {string} 格式化后的 diff
97
+ */
98
+ export function formatDiff(diff) {
99
+ if (!diff) return '(无变更内容)';
100
+
101
+ // 移除过长的 diff(可选)
102
+ const maxLines = 500;
103
+ const lines = diff.split('\n');
104
+
105
+ if (lines.length > maxLines) {
106
+ return lines.slice(0, maxLines).join('\n') +
107
+ `\n... (省略 ${lines.length - maxLines} 行)`;
108
+ }
109
+
110
+ return diff;
111
+ }
112
+
113
+ /**
114
+ * 从 change 对象中提取关键信息
115
+ * @param {Object} change - GitLab change 对象
116
+ * @returns {Object} 提取的信息
117
+ */
118
+ export function extractChangeInfo(change) {
119
+ return {
120
+ fileName: change.new_path || change.old_path,
121
+ oldPath: change.old_path,
122
+ newPath: change.new_path,
123
+ status: change.new_file ? 'added' :
124
+ change.deleted_file ? 'deleted' :
125
+ change.renamed_file ? 'renamed' :
126
+ 'modified',
127
+ diff: change.diff || '',
128
+ isNewFile: change.new_file,
129
+ isDeleted: change.deleted_file,
130
+ isRenamed: change.renamed_file,
131
+ };
132
+ }
133
+
134
+ export default {
135
+ buildSystemPrompt,
136
+ buildReviewPrompt,
137
+ buildReviewMessages,
138
+ buildBatchReviewPrompt,
139
+ formatDiff,
140
+ extractChangeInfo,
141
+ };
142
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlab-ai-review",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "GitLab AI Review SDK - 支持 CI/CD 自动配置和 ARK API",
5
5
  "main": "index.js",
6
6
  "type": "module",