Vehicoule's picture
# DeepSite - Complete Project Setup
83cb890 verified
```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<ApiStatus> {
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<GenerateResponse> {
try {
const response = await this.axiosInstance.post<GenerateResponse>(
'/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<ModelInfo[]> {
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<boolean> {
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<void> {
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<GenerateResponse> {
await this.enforceRateLimit();
return this.generate(request);
}
// Error recovery
private retryCount = new Map<string, number>();
private readonly MAX_RETRIES = 3;
async generateWithRetry(request: GenerateRequest): Promise<GenerateResponse> {
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;
```