Initial commit
This commit is contained in:
98
frontend/js/api.js
Normal file
98
frontend/js/api.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Chat Switchboard - API Module
|
||||
* Handles communication with LLM backends
|
||||
*/
|
||||
|
||||
const API = {
|
||||
/**
|
||||
* Fetch available models from endpoint
|
||||
*/
|
||||
async fetchModels(endpoint, apiKey) {
|
||||
const response = await fetch(endpoint.replace(/\/$/, '') + '/models', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API Error: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const models = data.data || data.models || data || [];
|
||||
|
||||
return Array.isArray(models)
|
||||
? models.map(m => ({
|
||||
id: m.id || m.name || m,
|
||||
owned_by: m.owned_by || null
|
||||
})).sort((a, b) => a.id.localeCompare(b.id))
|
||||
: [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Send chat completion request
|
||||
*/
|
||||
async sendMessage(settings, messages, onChunk = null) {
|
||||
const endpoint = settings.apiEndpoint.replace(/\/$/, '') + '/chat/completions';
|
||||
|
||||
const controller = new AbortController();
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${settings.apiKey}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: settings.model,
|
||||
messages: messages.map(m => ({ role: m.role, content: m.content })),
|
||||
max_tokens: settings.maxTokens,
|
||||
temperature: settings.temperature,
|
||||
top_p: settings.topP,
|
||||
presence_penalty: settings.presencePenalty,
|
||||
stream: settings.stream
|
||||
}),
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(`API Error: ${response.status} - ${error}`);
|
||||
}
|
||||
|
||||
let content = '';
|
||||
|
||||
if (settings.stream && onChunk) {
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
const lines = chunk.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.slice(6);
|
||||
if (data === '[DONE]') continue;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
const delta = parsed.choices?.[0]?.delta?.content || '';
|
||||
content += delta;
|
||||
onChunk(content, delta);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const data = await response.json();
|
||||
content = data.choices?.[0]?.message?.content || 'No response';
|
||||
}
|
||||
|
||||
return { content, controller };
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user