openai.ts•19.6 kB
import OpenAI from 'openai';
import { GitHubRepoInfo, ChatContext, RefactoringPlan, ArchitectureExplanation } from '../types/index.js';
export class OpenAIService {
private openai: OpenAI;
private currentConfig: {
apiKey: string;
model: string;
systemPrompt: string;
};
// Model characteristics for intelligent selection and warnings
private modelCharacteristics = {
'anthropic/claude-3.5-sonnet': { speed: 'fast', cost: 'medium', quality: 'high', recommended: true },
'anthropic/claude-3-opus': { speed: 'slow', cost: 'high', quality: 'highest', recommended: false },
'anthropic/claude-3-haiku': { speed: 'fastest', cost: 'low', quality: 'good', recommended: true },
'openai/gpt-4o': { speed: 'fast', cost: 'medium', quality: 'high', recommended: true },
'openai/gpt-4o-mini': { speed: 'fastest', cost: 'low', quality: 'good', recommended: true },
'openai/gpt-4-turbo': { speed: 'medium', cost: 'medium', quality: 'high', recommended: true },
'openai/o1-mini': { speed: 'slow', cost: 'high', quality: 'highest', recommended: false },
'openai/o1-preview': { speed: 'slowest', cost: 'highest', quality: 'highest', recommended: false },
'meta-llama/llama-3.1-405b-instruct': { speed: 'medium', cost: 'high', quality: 'high', recommended: false },
};
constructor() {
this.currentConfig = {
apiKey: process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY || '',
model: process.env.OPENAI_MODEL || 'anthropic/claude-3.5-sonnet',
systemPrompt: `You are CodeCompass AI, an expert assistant specialized in code analysis, refactoring, and architectural guidance. Your primary role is to help developers understand, analyze, and refactor code from GitHub repositories for integration into their own projects.
Core Capabilities:
1. Code Analysis: Understand code structure, patterns, and dependencies
2. Refactoring Guidance: Suggest improvements and modernization strategies
3. Architecture Explanation: Explain design patterns and architectural decisions
4. Integration Support: Help adapt code for different projects and frameworks
5. Best Practices: Recommend coding standards and optimization techniques
Response Guidelines:
- Provide clear, actionable advice with code examples
- Focus on practical solutions that can be implemented
- Consider maintainability, performance, and scalability
- Explain the reasoning behind recommendations
- Suggest multiple approaches when appropriate
- Include potential risks and trade-offs
Communication Style:
- Be concise but comprehensive
- Use code examples to illustrate points
- Structure responses with clear headings
- Prioritize actionable insights over theoretical discussions`,
};
this.openai = new OpenAI({
apiKey: this.currentConfig.apiKey,
baseURL: 'https://openrouter.ai/api/v1',
defaultHeaders: {
'HTTP-Referer': 'https://github.com/codecompass/codecompass-mcp',
'X-Title': 'CodeCompass MCP Server',
},
});
}
updateConfig(config: { apiKey?: string; model?: string; systemPrompt?: string }) {
if (config.apiKey) {
this.currentConfig.apiKey = config.apiKey;
this.openai = new OpenAI({
apiKey: config.apiKey,
baseURL: 'https://openrouter.ai/api/v1',
defaultHeaders: {
'HTTP-Referer': 'https://github.com/codecompass/codecompass-mcp',
'X-Title': 'CodeCompass MCP Server',
},
});
}
if (config.model) this.currentConfig.model = config.model;
if (config.systemPrompt) this.currentConfig.systemPrompt = config.systemPrompt;
}
// Intelligent model selection based on task type
private selectModel(requestedModel: string | undefined, taskType: 'review' | 'explain' | 'refactor', isBatchJob: boolean = false): string {
if (requestedModel && requestedModel !== 'auto') {
return requestedModel;
}
// Auto model selection logic
if (isBatchJob) {
// For batch jobs, prefer faster, cost-effective models
return 'openai/gpt-4o-mini';
}
// Task-specific model selection
switch (taskType) {
case 'review':
return 'anthropic/claude-3.5-sonnet'; // Good balance of quality and speed
case 'explain':
return 'openai/gpt-4o'; // Good for explanations
case 'refactor':
return 'anthropic/claude-3.5-sonnet'; // Good for strategic thinking
default:
return this.currentConfig.model;
}
}
// Generate model warning message
private generateModelWarning(model: string, isBatchJob: boolean = false): string | undefined {
const characteristics = this.modelCharacteristics[model as keyof typeof this.modelCharacteristics];
if (!characteristics) return undefined;
const warnings = [];
if (isBatchJob) {
if (characteristics.speed === 'slow' || characteristics.speed === 'slowest') {
warnings.push(`⚠️ Model ${model} is ${characteristics.speed} - batch job may take significant time`);
}
if (characteristics.cost === 'high' || characteristics.cost === 'highest') {
warnings.push(`💰 Model ${model} has ${characteristics.cost} cost - batch job may be expensive`);
}
} else {
if (characteristics.speed === 'slowest') {
warnings.push(`⚠️ Model ${model} is very slow - expect longer response times`);
}
if (characteristics.cost === 'highest') {
warnings.push(`💰 Model ${model} has highest cost - consider alternatives for frequent use`);
}
}
return warnings.length > 0 ? warnings.join('\n') : undefined;
}
// Log model selection for audit/debug
private logModelSelection(model: string, requestedModel: string | undefined, taskType: string, isBatchJob: boolean) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
taskType,
requestedModel: requestedModel || 'auto',
selectedModel: model,
isBatchJob,
characteristics: this.modelCharacteristics[model as keyof typeof this.modelCharacteristics]
};
console.log(`[MODEL_SELECTION] ${JSON.stringify(logEntry)}`);
}
async chatWithRepository(url: string, message: string, context?: ChatContext, model?: string, isBatchJob: boolean = false): Promise<{ content: string; modelUsed: string; warning?: string }> {
if (!this.currentConfig.apiKey) {
throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
}
// Select model intelligently
const modelToUse = this.selectModel(model, 'explain', isBatchJob);
// Log model selection
this.logModelSelection(modelToUse, model, 'explain_code', isBatchJob);
// Generate warning if needed
const warning = this.generateModelWarning(modelToUse, isBatchJob);
try {
const messages: any[] = [
{ role: 'system', content: this.currentConfig.systemPrompt },
];
// Add context if provided
if (context) {
const contextMessage = this.buildContextMessage(context);
messages.push({ role: 'system', content: contextMessage });
// Add conversation history
if (context.conversationHistory) {
messages.push(...context.conversationHistory.map(msg => ({
role: msg.role,
content: msg.content,
})));
}
}
messages.push({ role: 'user', content: message });
const response = await this.openai.chat.completions.create({
model: modelToUse,
messages,
max_tokens: 2000,
temperature: 0.7,
});
const content = response.choices[0].message.content || 'I apologize, but I couldn\'t generate a response.';
return {
content,
modelUsed: modelToUse,
warning
};
} catch (error: any) {
console.error('OpenAI API error:', error);
throw new Error(`Failed to generate response: ${error.message}`);
}
}
async suggestRefactoringPlan(url: string, targetProject: any, goals?: string[], model?: string, isBatchJob: boolean = false): Promise<{ content: string; modelUsed: string; warning?: string }> {
if (!this.currentConfig.apiKey) {
throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
}
// Select model intelligently
const modelToUse = this.selectModel(model, 'refactor', isBatchJob);
// Log model selection
this.logModelSelection(modelToUse, model, 'refactor_suggestions', isBatchJob);
// Generate warning if needed
const warning = this.generateModelWarning(modelToUse, isBatchJob);
const prompt = `
Analyze the repository at ${url} and create a comprehensive refactoring plan.
Target Project Context:
- Framework: ${targetProject.framework}
- Language: ${targetProject.language || 'Not specified'}
- Constraints: ${targetProject.constraints?.join(', ') || 'None specified'}
- Timeline: ${targetProject.timeline || 'Not specified'}
Refactoring Goals:
${goals?.map(goal => `- ${goal}`).join('\n') || '- General modernization and improvement'}
Please provide a detailed refactoring plan that includes:
1. Executive Summary
2. Phase-by-phase breakdown
3. Time estimates for each phase
4. Risk assessment
5. Success metrics
6. Recommended tools and resources
Format the response as a structured plan with clear sections and actionable items.
`;
try {
const response = await this.openai.chat.completions.create({
model: modelToUse,
messages: [
{ role: 'system', content: this.currentConfig.systemPrompt },
{ role: 'user', content: prompt },
],
max_tokens: 1800,
temperature: 0.7,
});
const content = response.choices[0].message.content || 'Failed to generate refactoring plan.';
return {
content,
modelUsed: modelToUse,
warning
};
} catch (error: any) {
throw new Error(`Failed to generate refactoring plan: ${error.message}`);
}
}
async explainArchitecture(url: string, repositoryInfo?: GitHubRepoInfo): Promise<string> {
if (!this.currentConfig.apiKey) {
throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
}
let contextInfo = '';
if (repositoryInfo) {
contextInfo = `
Repository Information:
- Name: ${repositoryInfo.name}
- Description: ${repositoryInfo.description || 'No description'}
- Primary Language: ${repositoryInfo.language || 'Unknown'}
- File Count: ${repositoryInfo.fileCount}
- Key Files: ${Object.keys(repositoryInfo.keyFiles).join(', ')}
File Structure:
${JSON.stringify(repositoryInfo.fileTree, null, 2)}
Key File Contents:
${Object.entries(repositoryInfo.keyFiles).map(([path, content]) =>
`--- ${path} ---\n${content.substring(0, 1000)}${content.length > 1000 ? '...' : ''}`
).join('\n\n')}
`;
}
const prompt = `
Explain the architecture of the repository at ${url} in detail.
${contextInfo}
Please provide a comprehensive architectural explanation that covers:
1. Overall Architecture Overview
2. Design Patterns Used
3. Project Structure and Organization
4. Data Flow and Components
5. Dependencies and External Integrations
6. Strengths and Potential Improvements
7. Scalability Considerations
8. Recommendations for Different Use Cases
Make the explanation accessible to developers who want to understand and potentially adapt this architecture for their own projects.
`;
try {
const response = await this.openai.chat.completions.create({
model: this.currentConfig.model,
messages: [
{ role: 'system', content: this.currentConfig.systemPrompt },
{ role: 'user', content: prompt },
],
max_tokens: 1800,
temperature: 0.7,
});
return response.choices[0].message.content || 'Failed to generate architecture explanation.';
} catch (error: any) {
throw new Error(`Failed to explain architecture: ${error.message}`);
}
}
async generateCodeReview(code: string, language: string, focusAreas?: string[], model?: string, isBatchJob: boolean = false): Promise<{ content: string; modelUsed: string; warning?: string }> {
if (!this.currentConfig.apiKey) {
throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
}
// Select model intelligently
const modelToUse = this.selectModel(model, 'review', isBatchJob);
// Log model selection
this.logModelSelection(modelToUse, model, 'code_review', isBatchJob);
// Generate warning if needed
const warning = this.generateModelWarning(modelToUse, isBatchJob);
const prompt = `
Please provide a comprehensive code review for the following ${language} code.
${focusAreas ? `Focus Areas: ${focusAreas.join(', ')}` : ''}
Code to review:
\`\`\`${language}
${code}
\`\`\`
Please provide feedback on:
1. Code Quality and Best Practices
2. Potential Bugs and Issues
3. Performance Optimizations
4. Security Considerations
5. Maintainability and Readability
6. Refactoring Suggestions
7. Testing Recommendations
Format your response with clear sections and provide specific code examples for improvements.
`;
try {
const response = await this.openai.chat.completions.create({
model: modelToUse,
messages: [
{ role: 'system', content: this.currentConfig.systemPrompt },
{ role: 'user', content: prompt },
],
max_tokens: 1800,
temperature: 0.7,
});
const content = response.choices[0].message.content || 'Failed to generate code review.';
return {
content,
modelUsed: modelToUse,
warning
};
} catch (error: any) {
throw new Error(`Failed to generate code review: ${error.message}`);
}
}
async generateRefactoringPlan(repositoryInfo: GitHubRepoInfo, goals: string[]): Promise<RefactoringPlan> {
if (!this.currentConfig.apiKey) {
throw new Error('OpenRouter API key not configured. Please set OPENROUTER_API_KEY environment variable.');
}
const prompt = `
Create a detailed refactoring plan for the following repository:
Repository: ${repositoryInfo.name}
Description: ${repositoryInfo.description}
Language: ${repositoryInfo.language}
File Count: ${repositoryInfo.fileCount}
Goals: ${goals.join(', ')}
Please provide a JSON response with the following structure:
{
"overview": "Brief overview of the refactoring plan",
"phases": [
{
"name": "Phase name",
"description": "Phase description",
"tasks": [
{
"name": "Task name",
"description": "Task description",
"type": "extract|transform|modernize|optimize|test",
"files": ["file1.js", "file2.js"],
"estimatedTimeHours": 4,
"priority": "low|medium|high"
}
],
"estimatedTimeHours": 16,
"dependencies": ["Phase 1", "Phase 2"]
}
],
"estimatedTimeHours": 40,
"risks": ["Risk 1", "Risk 2"],
"recommendations": ["Recommendation 1", "Recommendation 2"]
}
`;
try {
const response = await this.openai.chat.completions.create({
model: this.currentConfig.model,
messages: [
{ role: 'system', content: this.currentConfig.systemPrompt },
{ role: 'user', content: prompt },
],
max_tokens: 2000,
temperature: 0.7,
});
const content = response.choices[0].message.content;
if (!content) {
throw new Error('No response generated');
}
try {
return JSON.parse(content);
} catch (parseError) {
// If JSON parsing fails, return a basic structure
return {
overview: content.substring(0, 200) + '...',
phases: [],
estimatedTimeHours: 0,
risks: ['Failed to parse detailed plan'],
recommendations: ['Review the generated plan manually'],
};
}
} catch (error: any) {
throw new Error(`Failed to generate refactoring plan: ${error.message}`);
}
}
async generateWelcomeMessage(repositoryInfo: GitHubRepoInfo): Promise<string> {
if (!this.currentConfig.apiKey) {
// Return a default welcome message if no API key is configured
return `Hello! I've analyzed the ${repositoryInfo.name} repository and I'm ready to help you understand and refactor the codebase.
⚠️ **Setup Required**: To enable AI-powered features, please configure your OpenAI API key.
Repository Overview:
- **${repositoryInfo.name}** by ${repositoryInfo.owner}
- Language: ${repositoryInfo.language || 'Multiple'}
- Files: ${repositoryInfo.fileCount}
- Stars: ${repositoryInfo.stars}
I can help you with:
- Code analysis and understanding
- Refactoring suggestions
- Architecture explanations
- Component extraction
- Integration guidance
What would you like to know about this repository?`;
}
const prompt = `
Generate a welcoming introduction message for a repository analysis chat. The repository is:
- Name: ${repositoryInfo.name}
- Description: ${repositoryInfo.description || 'No description'}
- Primary Language: ${repositoryInfo.language || 'Unknown'}
- ${repositoryInfo.fileCount} files
- ${repositoryInfo.stars} stars
Create a friendly, professional greeting that:
1. Welcomes the user
2. Briefly summarizes what I found in the repository
3. Mentions what I can help with regarding refactoring and code analysis
4. Asks what they'd like to know
Keep it concise (3-4 sentences) and engaging.
`;
try {
const response = await this.openai.chat.completions.create({
model: this.currentConfig.model,
messages: [
{ role: 'system', content: this.currentConfig.systemPrompt },
{ role: 'user', content: prompt },
],
max_tokens: 300,
temperature: 0.8,
});
return response.choices[0].message.content ||
`Hello! I've analyzed the ${repositoryInfo.name} repository. I can help you understand the codebase, suggest refactoring opportunities, and guide you through integrating components into your own projects. What would you like to explore?`;
} catch (error) {
return `Hello! I've analyzed the ${repositoryInfo.name} repository. I can help you understand the codebase, suggest refactoring opportunities, and guide you through integrating components into your own projects. What would you like to explore?`;
}
}
private buildContextMessage(context: ChatContext): string {
let contextMessage = `Repository Context: ${context.repositoryUrl}\n`;
if (context.currentFile) {
contextMessage += `Current File: ${context.currentFile}\n`;
}
if (context.selectedCode) {
contextMessage += `Selected Code:\n\`\`\`\n${context.selectedCode}\n\`\`\`\n`;
}
if (context.refactoringGoals && context.refactoringGoals.length > 0) {
contextMessage += `Refactoring Goals: ${context.refactoringGoals.join(', ')}\n`;
}
return contextMessage;
}
}