UNPKG

recoder-shared

Version:

Shared types, utilities, and configurations for Recoder

540 lines (458 loc) 16.9 kB
/** * Shared AI Provider Integration for Recoder.xyz Ecosystem * * Provides consistent AI provider access across CLI, Web Platform, and VS Code Extension */ import { UnifiedConfigManager } from '../config/unified-config'; import { SessionManager } from '../session/session-manager'; export interface AIGenerationRequest { prompt: string; context?: { files?: Array<{ name: string; content: string }>; projectType?: string; language?: string; framework?: string; }; options?: { temperature?: number; maxTokens?: number; provider?: string; model?: string; includeTests?: boolean; includeDocs?: boolean; realCodeOnly?: boolean; }; metadata?: { sessionId?: string; platform: 'cli' | 'web' | 'extension'; userId?: string; }; } export interface AIGenerationResult { code: string; explanation?: string; confidence: number; provider: string; model: string; tokens: { input: number; output: number; total: number; }; metadata: { language: string; framework?: string; dependencies: string[]; realFunctionality: boolean; qualityScore: number; securityScore: number; }; timing: { startTime: number; endTime: number; duration: number; }; } export interface AIProvider { name: string; displayName: string; models: string[]; useCases: string[]; isAvailable(): Promise<boolean>; generateCode(request: AIGenerationRequest): Promise<AIGenerationResult>; testConnection(): Promise<boolean>; } /** * Claude AI Provider Implementation */ export class ClaudeProvider implements AIProvider { name = 'claude'; displayName = 'Claude (Anthropic)'; models = ['claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022']; useCases = ['complex-logic', 'architecture', 'security', 'code-review']; async isAvailable(): Promise<boolean> { const config = UnifiedConfigManager.getInstance('cli'); return config.getAIProviders()['claude'].enabled; } async testConnection(): Promise<boolean> { try { const config = UnifiedConfigManager.getInstance('cli'); const claudeConfig = config.getAIProviders()['claude']; if (!claudeConfig.apiKey) { return false; } const response = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': claudeConfig.apiKey, 'anthropic-version': '2023-06-01' }, body: JSON.stringify({ model: 'claude-3-5-haiku-20241022', max_tokens: 10, messages: [{ role: 'user', content: 'Hello' }] }) }); return response.ok; } catch (error) { return false; } } async generateCode(request: AIGenerationRequest): Promise<AIGenerationResult> { const startTime = Date.now(); const config = UnifiedConfigManager.getInstance('cli'); const claudeConfig = config.getAIProviders()['claude']; if (!claudeConfig.apiKey) { throw new Error('Claude API key not configured'); } const enhancedPrompt = this.buildProductionPrompt(request); const model = request.options?.model || this.models[0]; try { const response = await fetch('https://api.anthropic.com/v1/messages', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': claudeConfig.apiKey, 'anthropic-version': '2023-06-01' }, body: JSON.stringify({ model, max_tokens: request.options?.maxTokens || 4000, temperature: request.options?.temperature || 0.1, messages: [{ role: 'user', content: enhancedPrompt }] }) }); if (!response.ok) { throw new Error(`Claude API error: ${response.status}`); } const data = await response.json(); const endTime = Date.now(); return this.parseResponse(data, request, startTime, endTime); } catch (error) { throw new Error(`Claude generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } private buildProductionPrompt(request: AIGenerationRequest): string { const systemPrompt = `You are Recoder, an AI that generates REAL, PRODUCTION-READY code. Never create mock implementations. CRITICAL REQUIREMENTS: - Generate ACTUAL working code with real functionality - Include proper error handling and validation - Use real API integrations and database operations - Include comprehensive TypeScript types - Generate working implementations, not placeholders - Add proper security measures - Use environment variables for configuration ${request.options?.includeTests ? '- Include comprehensive test suites' : ''} ${request.options?.includeDocs ? '- Include detailed documentation' : ''}`; let contextInfo = ''; if (request.context) { contextInfo = `\n\nProject Context: - Language: ${request.context.language || 'typescript'} - Framework: ${request.context.framework || 'Not specified'} - Project Type: ${request.context.projectType || 'General'} - Files: ${request.context.files?.length || 0} files in context`; } return `${systemPrompt}${contextInfo}\n\nUser Request: ${request.prompt}\n\nGenerate complete, production-ready code:`; } private parseResponse(data: any, request: AIGenerationRequest, startTime: number, endTime: number): AIGenerationResult { const content = data.content[0]?.text || ''; const code = this.extractCodeFromResponse(content); return { code, explanation: this.extractExplanation(content), confidence: 0.9, provider: 'claude', model: request.options?.model || this.models[0], tokens: { input: data.usage?.input_tokens || 0, output: data.usage?.output_tokens || 0, total: (data.usage?.input_tokens || 0) + (data.usage?.output_tokens || 0) }, metadata: { language: request.context?.language || 'typescript', framework: request.context?.framework, dependencies: this.extractDependencies(code), realFunctionality: true, qualityScore: 0.9, securityScore: 0.85 }, timing: { startTime, endTime, duration: endTime - startTime } }; } private extractCodeFromResponse(response: string): string { const codeBlockRegex = /```(?:\w+)?\n([\s\S]*?)\n```/g; const matches = response.match(codeBlockRegex); if (matches && matches.length > 0) { return matches .map(match => match.replace(/```(?:\w+)?\n/, '').replace(/\n```$/, '')) .sort((a, b) => b.length - a.length)[0]; } return response; } private extractExplanation(response: string): string { const lines = response.split('\n'); const explanationStart = lines.findIndex(line => line.includes('## Explanation')); const codeStart = lines.findIndex(line => line.includes('```')); if (explanationStart >= 0 && codeStart > explanationStart) { return lines.slice(explanationStart + 1, codeStart).join('\n').trim(); } return 'Code generated successfully with Claude AI'; } private extractDependencies(code: string): string[] { const dependencies: string[] = []; const importRegex = /import.*from ['"]([^'"]+)['"]/g; let match; while ((match = importRegex.exec(code)) !== null) { const packageName = match[1]; if (!packageName.startsWith('.') && !packageName.startsWith('/')) { dependencies.push(packageName.split('/')[0]); } } return [...new Set(dependencies)]; } } /** * Groq AI Provider Implementation */ export class GroqProvider implements AIProvider { name = 'groq'; displayName = 'Groq'; models = ['llama-3.1-70b-versatile', 'llama-3.1-8b-instant']; useCases = ['fast-generation', 'api-endpoints', 'prototyping', 'react']; async isAvailable(): Promise<boolean> { const config = UnifiedConfigManager.getInstance('cli'); return config.getAIProviders()['groq'].enabled; } async testConnection(): Promise<boolean> { try { const config = UnifiedConfigManager.getInstance('cli'); const groqConfig = config.getAIProviders()['groq']; if (!groqConfig.apiKey) { return false; } const response = await fetch('https://api.groq.com/openai/v1/models', { headers: { 'Authorization': `Bearer ${groqConfig.apiKey}` } }); return response.ok; } catch (error) { return false; } } async generateCode(request: AIGenerationRequest): Promise<AIGenerationResult> { const startTime = Date.now(); const config = UnifiedConfigManager.getInstance('cli'); const groqConfig = config.getAIProviders()['groq']; if (!groqConfig.apiKey) { throw new Error('Groq API key not configured'); } const model = request.options?.model || this.models[0]; try { const response = await fetch('https://api.groq.com/openai/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${groqConfig.apiKey}` }, body: JSON.stringify({ model, messages: [ { role: 'system', content: 'You are Recoder, an AI that generates real, production-ready code. Never create mock implementations.' }, { role: 'user', content: request.prompt } ], temperature: request.options?.temperature || 0.2, max_tokens: request.options?.maxTokens || 4000 }) }); if (!response.ok) { throw new Error(`Groq API error: ${response.status}`); } const data = await response.json(); const endTime = Date.now(); return this.parseResponse(data, request, startTime, endTime); } catch (error) { throw new Error(`Groq generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } private parseResponse(data: any, request: AIGenerationRequest, startTime: number, endTime: number): AIGenerationResult { const content = data.choices[0]?.message?.content || ''; const code = this.extractCodeFromResponse(content); return { code, explanation: 'Fast code generation with Groq LLaMA', confidence: 0.8, provider: 'groq', model: request.options?.model || this.models[0], tokens: { input: data.usage?.prompt_tokens || 0, output: data.usage?.completion_tokens || 0, total: data.usage?.total_tokens || 0 }, metadata: { language: request.context?.language || 'typescript', framework: request.context?.framework, dependencies: this.extractDependencies(code), realFunctionality: true, qualityScore: 0.8, securityScore: 0.75 }, timing: { startTime, endTime, duration: endTime - startTime } }; } private extractCodeFromResponse(response: string): string { const codeBlockRegex = /```(?:\w+)?\n([\s\S]*?)\n```/g; const matches = response.match(codeBlockRegex); if (matches && matches.length > 0) { return matches[0].replace(/```(?:\w+)?\n/, '').replace(/\n```$/, ''); } return response; } private extractDependencies(code: string): string[] { // Similar implementation to Claude return []; } } /** * Shared AI Provider Manager */ export class SharedAIProviderManager { private static instance: SharedAIProviderManager; private providers: Map<string, AIProvider> = new Map(); private config: UnifiedConfigManager; private sessionManager: SessionManager; private constructor(platform: 'cli' | 'web' | 'extension') { this.config = UnifiedConfigManager.getInstance(platform); this.sessionManager = SessionManager.getInstance(platform); this.initializeProviders(); } static getInstance(platform: 'cli' | 'web' | 'extension'): SharedAIProviderManager { if (!SharedAIProviderManager.instance) { SharedAIProviderManager.instance = new SharedAIProviderManager(platform); } return SharedAIProviderManager.instance; } private initializeProviders(): void { this.providers.set('claude', new ClaudeProvider()); this.providers.set('groq', new GroqProvider()); // Additional providers would be added here } async generateCode(request: AIGenerationRequest): Promise<AIGenerationResult> { // Intelligent provider selection if not specified const provider = request.options?.provider || await this.selectOptimalProvider(request); const aiProvider = this.providers.get(provider); if (!aiProvider) { throw new Error(`Provider ${provider} not found`); } if (!(await aiProvider.isAvailable())) { throw new Error(`Provider ${provider} is not available`); } try { const result = await aiProvider.generateCode(request); // Log to session if available if (request.metadata?.sessionId) { this.sessionManager.setCurrentSession(request.metadata.sessionId); this.sessionManager.addMessage(request.prompt, 'user'); this.sessionManager.addMessage(result.code, 'assistant', { provider: result.provider, model: result.model, tokens: result.tokens.total, confidence: result.confidence }); } // Validate code quality if enabled if (this.config.getConfig().qualityValidation) { await this.validateCodeQuality(result); } return result; } catch (error) { // Try fallback provider const fallbackProvider = await this.getFallbackProvider(provider); if (fallbackProvider) { console.warn(`Primary provider failed, falling back to ${fallbackProvider.name}`); return await fallbackProvider.generateCode(request); } throw error; } } private async selectOptimalProvider(request: AIGenerationRequest): Promise<string> { const enabledProviders = this.config.getEnabledProviders(); if (enabledProviders.length === 0) { throw new Error('No AI providers enabled'); } // Analyze prompt for best provider selection const promptLower = request.prompt.toLowerCase(); // Complex logic, security -> Claude if (promptLower.includes('security') || promptLower.includes('auth') || promptLower.includes('complex') || promptLower.includes('architecture')) { const claude = enabledProviders.find(p => p.name === 'claude'); if (claude) return 'claude'; } // Fast prototyping, APIs -> Groq if (promptLower.includes('api') || promptLower.includes('endpoint') || promptLower.includes('fast') || promptLower.includes('react')) { const groq = enabledProviders.find(p => p.name === 'groq'); if (groq) return 'groq'; } // Default to first enabled provider return enabledProviders[0].name; } private async getFallbackProvider(failedProvider: string): Promise<AIProvider | null> { const enabledProviders = this.config.getEnabledProviders(); const fallback = enabledProviders.find(p => p.name !== failedProvider); if (fallback) { return this.providers.get(fallback.name) || null; } return null; } private async validateCodeQuality(result: AIGenerationResult): Promise<void> { const mockIndicators = [ 'TODO:', 'placeholder', 'mock implementation', 'not implemented', 'throw new Error("Not implemented")' ]; const lowerCode = result.code.toLowerCase(); const hasMockImplementation = mockIndicators.some(indicator => lowerCode.includes(indicator.toLowerCase()) ); if (hasMockImplementation) { throw new Error('Generated code contains mock implementations. Recoder only generates real, production-ready code.'); } } getAvailableProviders(): AIProvider[] { return Array.from(this.providers.values()); } async testAllProviders(): Promise<Record<string, boolean>> { const results: Record<string, boolean> = {}; for (const [name, provider] of this.providers) { try { results[name] = await provider.testConnection(); } catch (error) { results[name] = false; } } return results; } } // Export factory functions for each platform export const createCLIAIProvider = () => SharedAIProviderManager.getInstance('cli'); export const createWebAIProvider = () => SharedAIProviderManager.getInstance('web'); export const createExtensionAIProvider = () => SharedAIProviderManager.getInstance('extension'); export default SharedAIProviderManager;