```typescript import axios, { AxiosResponse, AxiosError } from 'axios'; import { GenerateRequest, GenerateResponse, ApiError, ApiStatus, ModelInfo, ApiProvider } from '../types'; const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000'; const API_TIMEOUT = 30000; // 30 seconds class ApiServiceClass { private axiosInstance = axios.create({ baseURL: API_BASE_URL, timeout: API_TIMEOUT, headers: { 'Content-Type': 'application/json', }, }); constructor() { // Request interceptor this.axiosInstance.interceptors.request.use( (config) => { console.log(`API Request: ${config.method?.toUpperCase()} ${config.url}`); return config; }, (error) => { console.error('Request Error:', error); return Promise.reject(error); } ); // Response interceptor this.axiosInstance.interceptors.response.use( (response) => { console.log(`API Response: ${response.status} ${response.config.url}`); return response; }, (error: AxiosError) => { console.error('Response Error:', error.response?.status, error.response?.data); // Transform axios errors to our custom error type const apiError = new ApiError( this.getErrorMessage(error), error.code, error.response?.status, error.response?.data ); return Promise.reject(apiError); } ); } private getErrorMessage(error: AxiosError): string { if (error.response?.data) { const data = error.response.data as any; if (data.error) { return typeof data.error === 'string' ? data.error : data.error.message; } if (data.message) { return data.message; } } if (error.request) { return 'Network error: Unable to connect to server'; } return error.message || 'An unknown error occurred'; } // Health check async healthCheck(): Promise { try { const response = await this.axiosInstance.get('/api/health'); const data = response.data; return { status: 'online', version: data.version, latency: response.headers['x-response-time'] ? parseInt(response.headers['x-response-time'] as string) : undefined, uptime: data.uptime }; } catch (error) { throw new ApiError('Health check failed', 'HEALTH_CHECK_FAILED'); } } // Generate website code async generate(request: GenerateRequest): Promise { try { const response = await this.axiosInstance.post( '/api/generate', { prompt: request.prompt, provider: request.provider, model: request.model, temperature: request.temperature || 0.7, max_tokens: request.maxTokens || 16000 }, { headers: request.apiKey ? { 'Authorization': `Bearer ${request.apiKey}` } : undefined } ); return response.data; } catch (error) { if (error instanceof ApiError) { throw error; } throw new ApiError('Generation failed', 'GENERATION_FAILED'); } } // Validate API key async validateKey(provider: ApiProvider, apiKey: string): Promise<{ valid: boolean; error?: string }> { try { const response = await this.axiosInstance.post('/api/validate-key', { provider, apiKey }); return { valid: response.data.valid, error: response.data.error }; } catch (error) { return { valid: false, error: error instanceof ApiError ? error.message : 'Validation failed' }; } } // Get available models for a provider async getModels(provider: ApiProvider): Promise { try { const response = await this.axiosInstance.get(`/api/models/${provider}`); const models = response.data.models || []; return models.map((model: string | any) => { if (typeof model === 'string') { return { id: model, name: model, contextLength: 4096 }; } return { id: model.id || model.name || model, name: model.name || model.id || model, description: model.description, contextLength: model.context_length || model.contextLength, pricing: model.pricing }; }); } catch (error) { console.error('Failed to fetch models:', error); return []; } } // Test API connectivity async testConnection(provider: ApiProvider, config: { apiKey?: string; baseUrl?: string }): Promise { try { // For now, just try to validate the key if (config.apiKey) { const validation = await this.validateKey(provider, config.apiKey); return validation.valid; } return false; } catch (error) { console.error('Connection test failed:', error); return false; } } // Rate limiting helpers private lastRequestTime = 0; private readonly MIN_REQUEST_INTERVAL = 1000; // 1 second private async enforceRateLimit(): Promise { const now = Date.now(); const timeSinceLastRequest = now - this.lastRequestTime; if (timeSinceLastRequest < this.MIN_REQUEST_INTERVAL) { const waitTime = this.MIN_REQUEST_INTERVAL - timeSinceLastRequest; await new Promise(resolve => setTimeout(resolve, waitTime)); } this.lastRequestTime = Date.now(); } // Wrapped generation with rate limiting async generateWithRateLimit(request: GenerateRequest): Promise { await this.enforceRateLimit(); return this.generate(request); } // Error recovery private retryCount = new Map(); private readonly MAX_RETRIES = 3; async generateWithRetry(request: GenerateRequest): Promise { const requestKey = `${request.provider}-${request.model}-${Date.now()}`; const retries = this.retryCount.get(requestKey) || 0; try { this.retryCount.set(requestKey, retries + 1); return await this.generate(request); } catch (error) { // Retry on network errors or 5xx status codes if (retries < this.MAX_RETRIES && (error instanceof ApiError && (error.status === undefined || error.status >= 500 || error.status === 0))) { console.log(`Retrying request (attempt ${retries + 1}/${this.MAX_RETRIES})`); await new Promise(resolve => setTimeout(resolve, Math.pow(2, retries) * 1000)); // Exponential backoff return this.generateWithRetry(request); } throw error; } finally { this.retryCount.delete(requestKey); } } } export const ApiService = new ApiServiceClass(); export default ApiService; ```