UNPKG

aigit-cli

Version:

AI-powered git commit message generator using OpenAI or DeepSeek

196 lines (161 loc) 5.92 kB
import OpenAI from 'openai'; import { config, getCurrentApiKey, getCurrentModel, validateModel } from './config.js'; import chalk from 'chalk'; // 延迟初始化AI客户端 let openaiClient = null; let deepseekClient = null; function getOpenAIClient(apiKey) { if (!openaiClient) { openaiClient = new OpenAI({ apiKey: apiKey || getCurrentApiKey(), }); } return openaiClient; } function getDeepSeekClient(apiKey) { if (!deepseekClient) { deepseekClient = new OpenAI({ apiKey: apiKey || getCurrentApiKey(), baseURL: 'https://api.deepseek.com/v1', }); } return deepseekClient; } // 获取DeepSeek的默认模型 function getDeepSeekDefaultModel() { return 'deepseek-chat'; } function getAIClient(provider, apiKey) { if (provider === 'openai') { return getOpenAIClient(apiKey); } else if (provider === 'deepseek') { return getDeepSeekClient(apiKey); } return getOpenAIClient(apiKey); // 默认使用OpenAI } // 根据风格生成不同的prompt function generatePrompt(diff, options) { const { language, style } = options; // 简化prompt模板,减少token消耗 const stylePrompts = { conventional: `用${language}生成Conventional Commits格式的commit message。 格式:<type>(<scope>): <description> 类型:feat, fix, docs, style, refactor, test, chore 要求:简洁,不超过50字符 代码变更: ${diff}`, simple: `用${language}生成简洁的commit message。 要求:简洁明了,不超过30字符 代码变更: ${diff}`, detailed: `用${language}生成详细的commit message。 第一行:简短摘要(不超过50字符) 空行后:详细描述 代码变更: ${diff}` }; return stylePrompts[style] || stylePrompts.conventional; } // 智能处理diff内容,减少token消耗 function optimizeDiff(diff) { if (!diff) return ''; // 移除不必要的上下文信息 let optimized = diff // 移除文件路径前缀(如果太长) .replace(/^diff --git a\/(.+?) b\/(.+?)$/gm, '') // 移除index行 .replace(/^index [a-f0-9]+\.\.[0-9]+ [0-9]+$/gm, '') // 移除---和+++行 .replace(/^--- a\/(.+)$/gm, '') .replace(/^\+\+\+ b\/(.+)$/gm, '') // 移除@@行(但保留行号信息) .replace(/^@@ -[0-9,]+ \+[0-9,]+ @@.*$/gm, '') // 移除空行 .replace(/^\s*$/gm, '') // 限制每行长度 .split('\n') .map(line => line.length > 100 ? line.substring(0, 100) + '...' : line) .join('\n'); // 进一步限制总长度 const maxLength = config.maxDiffLength || 2000; if (optimized.length > maxLength) { // 智能截断:保留开头和结尾,中间截断 const start = Math.floor(maxLength * 0.6); const end = Math.floor(maxLength * 0.4); optimized = optimized.substring(0, start) + '\n... (内容已截断) ...\n' + optimized.substring(optimized.length - end); } return optimized; } // 生成commit message export async function generateCommitMessage(diff, options = {}) { try { // 合并配置选项 const finalOptions = { provider: config.provider, model: validateModel(config.provider, options.model || getCurrentModel()), temperature: parseFloat(options.temperature) || config.temperature, language: options.language || config.language, style: options.style || config.style, maxTokens: config.maxTokens }; // 优化diff内容,减少token消耗 const optimizedDiff = optimizeDiff(diff); if (!optimizedDiff.trim()) { throw new Error('没有有效的代码变更内容'); } const prompt = generatePrompt(optimizedDiff, finalOptions); const client = getAIClient(finalOptions.provider); const completion = await client.chat.completions.create({ model: finalOptions.model, messages: [ { role: 'system', content: '你是专业的软件开发工程师,擅长编写git commit message。' }, { role: 'user', content: prompt } ], temperature: finalOptions.temperature, max_tokens: finalOptions.maxTokens, }); const commitMessage = completion.choices[0]?.message?.content?.trim(); if (!commitMessage) { throw new Error('AI未能生成有效的commit message'); } // 将commit message转换为小写 const lowercaseMessage = commitMessage.toLowerCase(); return lowercaseMessage; } catch (error) { const providerName = config.provider === 'openai' ? 'OpenAI' : 'DeepSeek'; if (error.code === 'insufficient_quota' || error.message.includes('Insufficient Balance')) { throw new Error(`${providerName} API余额不足,请充值账户`); } else if (error.code === 'invalid_api_key') { throw new Error(`${providerName} API密钥无效,请检查配置`); } else if (error.code === 'rate_limit_exceeded') { throw new Error(`${providerName} API请求频率超限,请稍后重试`); } else if (error.code === 'model_not_found' || error.message.includes('Model Not Exist')) { throw new Error(`${providerName} 模型不存在,请检查模型名称`); } else { throw new Error(`${providerName} API调用失败: ${error.message}`); } } } // 验证commit message格式 export function validateCommitMessage(message, style = 'conventional') { if (!message || message.trim().length === 0) { return { valid: false, error: 'Commit message不能为空' }; } if (style === 'conventional') { // 检查Conventional Commits格式 const conventionalRegex = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?(!)?: .+/; if (!conventionalRegex.test(message.split('\n')[0])) { return { valid: false, error: '不符合Conventional Commits格式,应为: <type>(<scope>): <description>' }; } } return { valid: true }; }