gitlab-ai-review 1.0.3 → 1.1.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
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { getConfig, validateConfig } from './lib/config.js';
7
7
  import { GitLabClient } from './lib/gitlab-client.js';
8
+ import { AIClient } from './lib/ai-client.js';
8
9
 
9
10
  /**
10
11
  * GitLab AI Review SDK 主类
@@ -12,7 +13,7 @@ import { GitLabClient } from './lib/gitlab-client.js';
12
13
  export class GitLabAIReview {
13
14
  constructor(options = {}) {
14
15
  this.name = 'GitLab AI Review SDK';
15
- this.version = '1.0.0';
16
+ this.version = '1.0.3';
16
17
 
17
18
  // 如果传入了配置,使用手动配置;否则使用自动检测
18
19
  if (options.token || options.gitlab) {
@@ -26,6 +27,10 @@ export class GitLabAIReview {
26
27
  projectId: options.projectId || options.project?.projectId,
27
28
  mergeRequestIid: options.mergeRequestIid || options.project?.mergeRequestIid,
28
29
  },
30
+ ai: {
31
+ arkApiKey: options.arkApiKey || options.ai?.arkApiKey,
32
+ guardConfig: options.guardConfig || options.ai?.guardConfig,
33
+ },
29
34
  };
30
35
  } else {
31
36
  // 自动检测模式(CI/CD)
@@ -37,6 +42,14 @@ export class GitLabAIReview {
37
42
  token: this.config.gitlab.token,
38
43
  host: this.config.gitlab.url,
39
44
  });
45
+
46
+ // 创建 AI 客户端(如果配置了 API Key)
47
+ if (this.config.ai?.arkApiKey) {
48
+ this.aiClient = new AIClient({
49
+ apiKey: this.config.ai.arkApiKey,
50
+ ...options.aiConfig,
51
+ });
52
+ }
40
53
  }
41
54
 
42
55
  /**
@@ -112,6 +125,71 @@ export class GitLabAIReview {
112
125
  );
113
126
  }
114
127
 
128
+ /**
129
+ * 获取 AI 客户端
130
+ */
131
+ getAIClient() {
132
+ if (!this.aiClient) {
133
+ throw new Error('AI 客户端未初始化,请确保配置了 ARK_API_KEY');
134
+ }
135
+ return this.aiClient;
136
+ }
137
+
138
+ /**
139
+ * AI 审查单个文件的代码变更
140
+ */
141
+ async reviewCodeChange(change) {
142
+ const aiClient = this.getAIClient();
143
+ const guardConfig = this.config.ai?.guardConfig?.content || '';
144
+
145
+ return aiClient.reviewCode({
146
+ diff: change.diff,
147
+ fileName: change.new_path || change.old_path,
148
+ guardConfig,
149
+ });
150
+ }
151
+
152
+ /**
153
+ * AI 审查 MR 的所有代码变更
154
+ */
155
+ async reviewMergeRequest() {
156
+ const changes = await this.getMergeRequestChanges();
157
+ const aiClient = this.getAIClient();
158
+ const guardConfig = this.config.ai?.guardConfig?.content || '';
159
+
160
+ return aiClient.reviewMultipleFiles(changes, guardConfig);
161
+ }
162
+
163
+ /**
164
+ * AI 审查 MR 并自动添加评论
165
+ */
166
+ async reviewAndComment() {
167
+ const reviews = await this.reviewMergeRequest();
168
+
169
+ // 构建评论内容
170
+ let comment = '## 🤖 AI 代码审查报告\n\n';
171
+
172
+ for (const review of reviews) {
173
+ if (review.status === 'error') {
174
+ comment += `### ❌ ${review.fileName}\n`;
175
+ comment += `审查失败: ${review.error}\n\n`;
176
+ continue;
177
+ }
178
+
179
+ comment += `### 📄 ${review.fileName}\n\n`;
180
+
181
+ if (review.reasoning) {
182
+ comment += `**思考过程**:\n${review.reasoning}\n\n`;
183
+ }
184
+
185
+ comment += `**审查意见**:\n${review.content}\n\n`;
186
+ comment += `---\n\n`;
187
+ }
188
+
189
+ // 添加评论到 MR
190
+ return this.addComment(comment);
191
+ }
192
+
115
193
  /**
116
194
  * 测试方法
117
195
  */
@@ -123,6 +201,7 @@ export class GitLabAIReview {
123
201
  // 导出工具函数
124
202
  export { getConfig, validateConfig } from './lib/config.js';
125
203
  export { GitLabClient } from './lib/gitlab-client.js';
204
+ export { AIClient } from './lib/ai-client.js';
126
205
 
127
206
  // 默认导出
128
207
  export default GitLabAIReview;
@@ -0,0 +1,207 @@
1
+ /**
2
+ * AI 客户端 - 基于 ARK API (豆包大模型)
3
+ */
4
+
5
+ import OpenAI from 'openai';
6
+
7
+ /**
8
+ * AI 客户端类
9
+ */
10
+ export class AIClient {
11
+ constructor(config = {}) {
12
+ const apiKey = config.apiKey || process.env.ARK_API_KEY;
13
+ const baseURL = config.baseURL || 'https://ark.cn-beijing.volces.com/api/v3';
14
+ const model = config.model || 'doubao-seed-1-6-251015';
15
+
16
+ if (!apiKey) {
17
+ throw new Error('ARK_API_KEY 未配置');
18
+ }
19
+
20
+ this.openai = new OpenAI({
21
+ apiKey,
22
+ baseURL,
23
+ });
24
+
25
+ this.model = model;
26
+ this.config = config;
27
+ }
28
+
29
+ /**
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>} 审查结果
36
+ */
37
+ async reviewCode({ diff, fileName, guardConfig = '' }) {
38
+ const systemPrompt = this._buildSystemPrompt(guardConfig);
39
+ const userPrompt = this._buildReviewPrompt(diff, fileName);
40
+
41
+ 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',
48
+ });
49
+
50
+ return {
51
+ reasoning: completion.choices[0]?.message?.reasoning_content || '',
52
+ content: completion.choices[0]?.message?.content || '',
53
+ usage: completion.usage,
54
+ };
55
+ }
56
+
57
+ /**
58
+ * 代码审查 - 流式响应
59
+ * @param {Object} params - 审查参数
60
+ * @param {string} params.diff - 代码差异
61
+ * @param {string} params.fileName - 文件名
62
+ * @param {string} params.guardConfig - AI Review Guard 配置内容
63
+ * @param {Function} onChunk - 流式回调函数
64
+ * @returns {Promise<string>} 完整审查结果
65
+ */
66
+ async reviewCodeStream({ diff, fileName, guardConfig = '' }, onChunk) {
67
+ const systemPrompt = this._buildSystemPrompt(guardConfig);
68
+ const userPrompt = this._buildReviewPrompt(diff, fileName);
69
+
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',
77
+ stream: true,
78
+ });
79
+
80
+ let fullReasoning = '';
81
+ let fullContent = '';
82
+
83
+ for await (const part of stream) {
84
+ const reasoning = part.choices[0]?.delta?.reasoning_content || '';
85
+ const content = part.choices[0]?.delta?.content || '';
86
+
87
+ fullReasoning += reasoning;
88
+ fullContent += content;
89
+
90
+ if (onChunk) {
91
+ onChunk({ reasoning, content });
92
+ }
93
+ }
94
+
95
+ return {
96
+ reasoning: fullReasoning,
97
+ content: fullContent,
98
+ };
99
+ }
100
+
101
+ /**
102
+ * 通用聊天接口
103
+ * @param {Array} messages - 消息数组
104
+ * @param {Object} options - 可选参数
105
+ * @returns {Promise<Object>} AI 响应
106
+ */
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
+ 如果代码没有问题,请简要说明代码质量良好。`;
169
+ }
170
+
171
+ /**
172
+ * 批量审查多个文件的变更
173
+ * @param {Array} changes - 变更数组
174
+ * @param {string} guardConfig - AI Review Guard 配置
175
+ * @returns {Promise<Array>} 审查结果数组
176
+ */
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;
203
+ }
204
+ }
205
+
206
+ export default AIClient;
207
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlab-ai-review",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "GitLab AI Review SDK - 支持 CI/CD 自动配置和 ARK API",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -25,5 +25,8 @@
25
25
  "license": "MIT",
26
26
  "engines": {
27
27
  "node": ">=18.0.0"
28
+ },
29
+ "dependencies": {
30
+ "openai": "^4.73.0"
28
31
  }
29
32
  }