recoder-shared
Version:
Shared types, utilities, and configurations for Recoder
540 lines (458 loc) • 16.9 kB
text/typescript
/**
* 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;