webinterface/client/js/api_service.js

168 lines
6.6 KiB
JavaScript

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<any>} 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
}
}