99 lines
3.2 KiB
JavaScript
99 lines
3.2 KiB
JavaScript
/**
|
|
* 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 };
|
|
}
|
|
};
|