incize
Version:
AI Commit Copilot for Power Developers
263 lines (262 loc) • 9.78 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RealAIService = void 0;
const PromptFormatter_1 = require("./PromptFormatter");
class RealAIService {
promptFormatter;
config;
constructor(config = {}) {
this.promptFormatter = new PromptFormatter_1.PromptFormatter();
this.config = {
defaultModel: 'claude-3-5-sonnet',
maxTokens: 2000,
temperature: 0.1,
...config
};
}
/**
* Analyze diff using real AI models
*/
async analyze(diff, commit, repository, options = {}) {
const model = options.model || this.config.defaultModel;
try {
if (model === 'mock') {
return this.analyzeWithMock(diff, commit, repository);
}
const prompt = options.focus
? this.promptFormatter.createFocusedPrompt(diff, commit, options.focus)
: this.promptFormatter.formatAnalysisPrompt(diff, commit, repository);
if (model === 'claude-3-5-sonnet') {
return this.analyzeWithClaude(prompt);
}
else if (model === 'gpt-4o') {
return this.analyzeWithGPT(prompt);
}
else {
throw new Error(`Unsupported model: ${model}`);
}
}
catch (error) {
console.error(`AI analysis failed with model ${model}:`, error);
// Fallback to mock analysis
return this.analyzeWithMock(diff, commit, repository);
}
}
/**
* Analyze using Anthropic Claude
*/
async analyzeWithClaude(prompt) {
if (!this.config.anthropicApiKey) {
throw new Error('Anthropic API key not configured');
}
try {
const { default: Anthropic } = await Promise.resolve().then(() => __importStar(require('@anthropic-ai/sdk')));
const anthropic = new Anthropic({
apiKey: this.config.anthropicApiKey,
});
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: this.config.maxTokens,
temperature: this.config.temperature,
messages: [
{
role: 'user',
content: `${prompt.systemPrompt}\n\n${prompt.userPrompt}`,
},
],
});
const content = response.content[0];
if (!content || content.type !== 'text') {
throw new Error('Unexpected response type from Claude');
}
return this.parseAIResponse(content.text);
}
catch (error) {
throw new Error(`Claude API error: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Analyze using OpenAI GPT
*/
async analyzeWithGPT(prompt) {
if (!this.config.openaiApiKey) {
throw new Error('OpenAI API key not configured');
}
try {
const OpenAI = await Promise.resolve().then(() => __importStar(require('openai')));
const openai = new OpenAI.default({
apiKey: this.config.openaiApiKey,
});
const response = await openai.chat.completions.create({
model: 'gpt-4o',
max_tokens: this.config.maxTokens,
temperature: this.config.temperature,
messages: [
{
role: 'system',
content: prompt.systemPrompt,
},
{
role: 'user',
content: prompt.userPrompt,
},
],
});
const content = response.choices[0]?.message?.content;
if (!content) {
throw new Error('No content in GPT response');
}
return this.parseAIResponse(content);
}
catch (error) {
throw new Error(`GPT API error: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Fallback to mock analysis
*/
async analyzeWithMock(diff, commit, _repository) {
const { MockAIAnalyzer } = await Promise.resolve().then(() => __importStar(require('./MockAIAnalyzer')));
const mockAnalyzer = new MockAIAnalyzer();
const prompt = this.promptFormatter.createMockPrompt(diff, commit);
return mockAnalyzer.analyze(prompt);
}
/**
* Parse AI response into structured format
*/
parseAIResponse(response) {
try {
// Try to extract JSON from the response
const jsonMatch = response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
const jsonStr = jsonMatch[0];
const parsed = JSON.parse(jsonStr);
return {
summary: parsed.summary || 'Analysis completed',
riskScore: parsed.riskScore || 50,
riskLevel: parsed.riskLevel || 'medium',
suggestions: parsed.suggestions || [],
changelog: parsed.changelog || '',
testSuggestions: parsed.testSuggestions || [],
confidence: parsed.confidence || 0.8,
};
}
}
catch (error) {
console.warn('Failed to parse JSON response, using text parsing');
}
// Fallback: parse text response
return this.parseTextResponse(response);
}
/**
* Parse text response when JSON parsing fails
*/
parseTextResponse(response) {
const lines = response.split('\n');
let summary = 'Analysis completed';
let riskScore = 50;
let riskLevel = 'medium';
let suggestions = [];
let changelog = '';
let testSuggestions = [];
let confidence = 0.8;
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.toLowerCase().includes('summary:')) {
summary = trimmed.replace(/summary:\s*/i, '');
}
else if (trimmed.toLowerCase().includes('risk score:') || trimmed.toLowerCase().includes('risk:')) {
const scoreMatch = trimmed.match(/(\d+)/);
if (scoreMatch) {
riskScore = parseInt(scoreMatch[1] || '50');
if (riskScore < 20)
riskLevel = 'low';
else if (riskScore < 50)
riskLevel = 'medium';
else if (riskScore < 80)
riskLevel = 'high';
else
riskLevel = 'critical';
}
}
else if (trimmed.toLowerCase().includes('suggestion:') || trimmed.toLowerCase().includes('suggestions:')) {
suggestions.push(trimmed.replace(/suggestion[s]?:\s*/i, ''));
}
else if (trimmed.toLowerCase().includes('changelog:')) {
changelog = trimmed.replace(/changelog:\s*/i, '');
}
else if (trimmed.toLowerCase().includes('test:') || trimmed.toLowerCase().includes('tests:')) {
testSuggestions.push(trimmed.replace(/test[s]?:\s*/i, ''));
}
}
return {
summary,
riskScore,
riskLevel,
suggestions,
changelog,
testSuggestions,
confidence,
};
}
/**
* Set API keys
*/
setApiKeys(anthropicKey, openaiKey) {
if (anthropicKey)
this.config.anthropicApiKey = anthropicKey;
if (openaiKey)
this.config.openaiApiKey = openaiKey;
}
/**
* Check if API keys are configured
*/
isConfigured() {
return !!(this.config.anthropicApiKey || this.config.openaiApiKey);
}
/**
* Get available models
*/
getAvailableModels() {
const models = ['mock'];
if (this.config.anthropicApiKey)
models.push('claude-3-5-sonnet');
if (this.config.openaiApiKey)
models.push('gpt-4o');
return models;
}
}
exports.RealAIService = RealAIService;