import { BASE_API_URL, API_KEY } from './config.js'; import { showAlert } from './shared_functions.js'; /** * Generic fetch wrapper for API calls * @param {string} endpoint API endpoint (e.g., '/api/auth/me') * @param {object} options Fetch options (method, headers, body) * @param {boolean} includeAuthHeader Add Authorization header? * @returns {Promise} Parsed JSON response or throws error */ async function fetchApi(endpoint, options = {}, includeAuthHeader = true) { const url = `${BASE_API_URL}${endpoint}`; const headers = { 'Content-Type': 'application/json', ...options.headers, }; if (includeAuthHeader) { const token = localStorage.getItem('token'); if (token) { headers['Authorization'] = `Bearer ${token}`; } else { // Handle cases where token is expected but missing (e.g., redirect to login) console.warn('Auth token missing for API call:', endpoint); // Optionally throw an error or redirect here // window.location.href = 'index.html'; // throw new Error('Authentication token not found.'); } } try { const response = await fetch(url, { ...options, headers }); if (!response.ok) { let errorData; try { errorData = await response.json(); } catch (e) { // If response is not JSON errorData = { message: response.statusText }; } console.error(`API Error ${response.status}:`, errorData); const error = new Error(errorData.message || `HTTP error! status: ${response.status}`); error.status = response.status; error.data = errorData; throw error; } // Handle responses with no content (e.g., DELETE) if (response.status === 204) { return null; } return await response.json(); } catch (error) { console.error(`Fetch error for ${url}:`, error); // Re-throw generic network errors or specific API errors if (error instanceof TypeError && error.message.includes('Failed to fetch')) { throw new Error(`Network Error: Could not connect to API (${url}).`); } throw error; // Re-throw API errors or other exceptions } } // --- Specific API Functions --- export async function getUserData() { return await fetchApi('/api/auth/me', { method: 'GET' }); } export async function getAdminData() { return await fetchApi('/api/auth/admin', { method: 'GET' }); } export async function getAllUsers() { return await fetchApi('/api/auth/users', { method: 'GET' }); } export async function deleteUserById(userId) { return await fetchApi(`/api/auth/user/${userId}`, { method: 'DELETE' }); } export async function getAvailableModels() { // Note: OpenAI endpoints might not require the Bearer token if using a proxy/backend key const requiresAuth = !API_KEY.startsWith('YOUR_'); // Simple check if a real key seems present const headers = requiresAuth ? { 'Authorization': `Bearer ${API_KEY}` } : {}; try { // Using BASE_API_URL assumes your proxy handles the /v1 path const data = await fetchApi('/v1/models', { method: 'GET', headers }, false); // Auth header likely not needed here if backend handles it // Return the first model ID, or handle multiple models if needed return data?.data?.[0]?.id || 'default-model-id'; // Provide a fallback } catch (error) { console.error("Error fetching models:", error); showAlert("Fehler beim Abrufen der Modelle vom Server.", "error"); throw error; // Allow caller to handle } } export async function streamChatCompletion(messages, systemPrompt, model) { const body = { model: model, messages: [{ role: "system", content: systemPrompt }, ...messages], stream: true, temperature: 0.5, // Adjust temperature as needed }; const requiresAuth = !API_KEY.startsWith('YOUR_'); const headers = requiresAuth ? { 'Authorization': `Bearer ${API_KEY}` } : {}; // This needs to return the raw Response object to handle the stream try { const response = await fetch(`${BASE_API_URL}/v1/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body) }); if (!response.ok || !response.body) { let errorData; try { errorData = await response.json(); } catch (e) { errorData = { message: response.statusText }; } console.error(`API Error ${response.status}:`, errorData); const error = new Error(errorData.message || `HTTP error! status: ${response.status}`); error.status = response.status; throw error; } return response; // Return the raw response for stream handling } catch (error) { console.error(`Fetch stream error:`, error); if (error instanceof TypeError && error.message.includes('Failed to fetch')) { throw new Error(`Network Error: Could not connect to API (${BASE_API_URL}).`); } throw error; } } export async function tokenizePrompt(promptMessages, systemPrompt, model) { // Format messages array into the specific string format expected by your /tokenize endpoint const formatArrayToCustomString = (array) => { return '[' + array.map(dict => '{' + Object.entries(dict).map(([key, value]) => `'${key}':'${value.replace(/'/g, "\\'")}'`).join(', ') + '}') // Escape single quotes in values .join(', ') + ']'; }; const messages = [{ role: "system", content: systemPrompt }, ...promptMessages]; const formattedString = `"${formatArrayToCustomString(messages)}"`; // Wrap in double quotes as per your example const body = { model: model, prompt: formattedString, add_special_tokens: true // Assuming this is correct for your backend }; const requiresAuth = !API_KEY.startsWith('YOUR_'); const headers = requiresAuth ? { 'Authorization': `Bearer ${API_KEY}` } : {}; try { // Assuming /tokenize is relative to BASE_API_URL return await fetchApi('/tokenize', { method: 'POST', body: JSON.stringify(body), headers }, false); } catch (error) { console.error("Error tokenizing prompt:", error); showAlert("Fehler bei der Token-Berechnung.", "error"); // Return a default structure or re-throw return { count: 0, max_model_len: 4096 }; // Example fallback } }