UNPKG

incize

Version:

AI Commit Copilot for Power Developers

263 lines (262 loc) 9.78 kB
"use strict"; 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;