// API client for AnyCoder backend import axios, { AxiosInstance } from 'axios'; import type { Model, AuthStatus, CodeGenerationRequest, DeploymentRequest, DeploymentResponse, Language, } from '@/types'; // Use relative URLs in production (Next.js rewrites will proxy to backend) // In local dev without rewrites, use localhost:8000 const API_URL = process.env.NEXT_PUBLIC_API_URL || (typeof window !== 'undefined' && window.location.hostname !== 'localhost' ? '' // Use relative URLs in production (proxied by Next.js) : 'http://localhost:8000'); // Local development class ApiClient { private client: AxiosInstance; private token: string | null = null; constructor() { this.client = axios.create({ baseURL: API_URL, headers: { 'Content-Type': 'application/json', }, }); // Add auth token to requests if available this.client.interceptors.request.use((config) => { if (this.token) { config.headers.Authorization = `Bearer ${this.token}`; } return config; }); // Load token from localStorage on client side if (typeof window !== 'undefined') { this.token = localStorage.getItem('hf_oauth_token'); } } setToken(token: string | null) { this.token = token; // Note: OAuth token is stored by auth.ts, not here // We just keep it in memory for API calls } getToken(): string | null { return this.token; } async getModels(): Promise { const response = await this.client.get('/api/models'); return response.data; } async getLanguages(): Promise<{ languages: Language[] }> { const response = await this.client.get<{ languages: Language[] }>('/api/languages'); return response.data; } async getAuthStatus(): Promise { try { const response = await this.client.get('/api/auth/status'); return response.data; } catch (error) { return { authenticated: false, message: 'Not authenticated', }; } } // Stream-based code generation using EventSource (Server-Sent Events) generateCodeStream( request: CodeGenerationRequest, onChunk: (content: string) => void, onComplete: (code: string) => void, onError: (error: string) => void ): () => void { const url = new URL('/api/generate', API_URL); const eventSource = new EventSource( url.toString() + '?' + new URLSearchParams({ query: request.query, language: request.language, model_id: request.model_id, provider: request.provider, }) ); eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); if (data.type === 'chunk' && data.content) { onChunk(data.content); } else if (data.type === 'complete' && data.code) { onComplete(data.code); eventSource.close(); } else if (data.type === 'error') { onError(data.message || 'Unknown error occurred'); eventSource.close(); } } catch (error) { console.error('Error parsing SSE data:', error); } }; eventSource.onerror = (error) => { console.error('EventSource error:', error); onError('Connection error occurred'); eventSource.close(); }; // Return cleanup function return () => { eventSource.close(); }; } // Alternative: WebSocket-based generation generateCodeWebSocket( request: CodeGenerationRequest, onChunk: (content: string) => void, onComplete: (code: string) => void, onError: (error: string) => void ): WebSocket { const wsUrl = API_URL.replace('http', 'ws') + '/ws/generate'; const ws = new WebSocket(wsUrl); ws.onopen = () => { ws.send(JSON.stringify(request)); }; ws.onmessage = (event) => { try { const data = JSON.parse(event.data); if (data.type === 'chunk' && data.content) { onChunk(data.content); } else if (data.type === 'complete' && data.code) { onComplete(data.code); ws.close(); } else if (data.type === 'error') { onError(data.message || 'Unknown error occurred'); ws.close(); } } catch (error) { console.error('Error parsing WebSocket data:', error); } }; ws.onerror = (error) => { console.error('WebSocket error:', error); onError('Connection error occurred'); }; return ws; } async deploy(request: DeploymentRequest): Promise { const response = await this.client.post('/api/deploy', request); return response.data; } logout() { this.token = null; } } // Export singleton instance export const apiClient = new ApiClient();