|
|
|
import { updateLastMessage } from './utils.js'; |
|
import { sessions, currentSessionIndex } from './sessions.js'; |
|
|
|
|
|
|
|
|
|
export async function callLLMStream(conversation, signal) { |
|
const session = sessions[currentSessionIndex]; |
|
const { model, temperature, maxTokens } = session.settings; |
|
|
|
if (model.startsWith("gpt-4o")) { |
|
return callOpenAIStream(session, conversation, model, temperature, maxTokens, signal); |
|
} else if (model.startsWith("claude")) { |
|
return callAnthropicStream(session, conversation, model, temperature, maxTokens, signal); |
|
} else if (model.startsWith("gemini")) { |
|
return callGoogleStream(session, conversation, model, temperature, maxTokens, signal); |
|
} else if (model.startsWith("huggingface")) { |
|
return callHuggingFaceStream(session, conversation, model.replace("huggingface/", ""), temperature, maxTokens, signal); |
|
} else if (model.startsWith("mistral")) { |
|
return callMistralStream(session, conversation, model, temperature, maxTokens, signal); |
|
} else { |
|
throw new Error("Unsupported model: " + model); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
async function processStream(reader, decoder, session, errorPrefix = "Stream") { |
|
let done = false; |
|
let aiMessage = ""; |
|
let buffer = ""; |
|
|
|
updateLastMessage(session, aiMessage, true); |
|
|
|
while (!done) { |
|
const { value, done: doneReading } = await reader.read(); |
|
done = doneReading; |
|
|
|
|
|
buffer += decoder.decode(value, { stream: true }); |
|
|
|
|
|
const parts = buffer.split("\n\n"); |
|
|
|
buffer = parts.pop(); |
|
|
|
|
|
for (const part of parts) { |
|
const trimmed = part.trim(); |
|
if (!trimmed.startsWith("data:")) continue; |
|
|
|
const dataStr = trimmed.substring(5).trim(); |
|
if (dataStr === "[DONE]") { |
|
done = true; |
|
break; |
|
} |
|
|
|
try { |
|
const parsed = JSON.parse(dataStr); |
|
const delta = parsed.choices[0].delta.content; |
|
if (delta) { |
|
aiMessage += delta; |
|
updateLastMessage(session, aiMessage, true); |
|
} |
|
} catch (err) { |
|
console.error(`${errorPrefix} parsing error:`, err, "Chunk:", dataStr); |
|
} |
|
} |
|
} |
|
|
|
|
|
if (buffer.trim()) { |
|
try { |
|
const trimmed = buffer.trim(); |
|
if (trimmed.startsWith("data:")) { |
|
const dataStr = trimmed.substring(5).trim(); |
|
if (dataStr !== "[DONE]") { |
|
const parsed = JSON.parse(dataStr); |
|
const delta = parsed.choices[0].delta.content; |
|
if (delta) { |
|
aiMessage += delta; |
|
updateLastMessage(session, aiMessage, true); |
|
} |
|
} |
|
} |
|
} catch (err) { |
|
console.error(`Final buffer parsing error:`, err, "Buffer:", buffer); |
|
} |
|
} |
|
|
|
updateLastMessage(session, aiMessage, false); |
|
return aiMessage; |
|
} |
|
|
|
|
|
|
|
|
|
function createRequestOptions(session, payload, signal) { |
|
return { |
|
method: "POST", |
|
headers: { |
|
"Content-Type": "application/json", |
|
"X-Session-ID": session.id, |
|
}, |
|
body: JSON.stringify(payload), |
|
signal: signal, |
|
}; |
|
} |
|
|
|
export async function callOpenAIStream(session, conversation, model, temperature, maxTokens, signal) { |
|
const response = await fetch("http://127.0.0.1:8000/openai_stream", |
|
createRequestOptions(session, { |
|
conversation: conversation, |
|
temperature: temperature, |
|
max_tokens: maxTokens, |
|
model: model, |
|
}, signal) |
|
); |
|
|
|
const reader = response.body.getReader(); |
|
const decoder = new TextDecoder("utf-8"); |
|
return processStream(reader, decoder, session, "OpenAI stream"); |
|
} |
|
|
|
export async function callAnthropicStream(session, conversation, model, temperature, maxTokens, signal) { |
|
model = model.toLowerCase().replace(/\s+/g, '-').replace(/\./g, '-'); |
|
console.log(`Calling Anthropic API with model: ${model}`); |
|
|
|
const response = await fetch("http://127.0.0.1:8000/anthropic_stream", |
|
createRequestOptions(session, { |
|
messages: conversation, |
|
temperature: temperature, |
|
max_tokens: maxTokens, |
|
model: model + "-latest", |
|
}, signal) |
|
); |
|
|
|
const reader = response.body.getReader(); |
|
const decoder = new TextDecoder("utf-8"); |
|
return processStream(reader, decoder, session, "Anthropic stream"); |
|
} |
|
|
|
export async function callGoogleStream(session, conversation, model, temperature, maxTokens, signal) { |
|
model = model.toLowerCase().replace(/\s+/g, '-'); |
|
console.log(`Calling Google (Gemini) API with model: ${model}`); |
|
|
|
const response = await fetch("http://127.0.0.1:8000/gemini_stream", |
|
createRequestOptions(session, { |
|
messages: conversation, |
|
temperature: temperature, |
|
max_tokens: maxTokens, |
|
model: model, |
|
}, signal) |
|
); |
|
|
|
const reader = response.body.getReader(); |
|
const decoder = new TextDecoder("utf-8"); |
|
return processStream(reader, decoder, session, "Gemini stream"); |
|
} |
|
|
|
export async function callHuggingFaceStream(session, conversation, model, temperature, maxTokens, signal) { |
|
console.log(`Calling Hugging Face API with model: ${model}`); |
|
|
|
const response = await fetch("http://127.0.0.1:8000/huggingface_stream", |
|
createRequestOptions(session, { |
|
messages: conversation, |
|
temperature: temperature, |
|
max_tokens: maxTokens, |
|
model: model, |
|
}, signal) |
|
); |
|
|
|
const reader = response.body.getReader(); |
|
const decoder = new TextDecoder("utf-8"); |
|
return processStream(reader, decoder, session, "Hugging Face stream"); |
|
} |
|
|
|
export async function callMistralStream(session, conversation, model, temperature, maxTokens, signal) { |
|
console.log(`Calling Mistral API with model: ${model}`); |
|
|
|
const response = await fetch("http://127.0.0.1:8000/mistral_stream", |
|
createRequestOptions(session, { |
|
conversation: conversation, |
|
temperature: temperature, |
|
max_tokens: maxTokens, |
|
model: model, |
|
}, signal) |
|
); |
|
|
|
const reader = response.body.getReader(); |
|
const decoder = new TextDecoder("utf-8"); |
|
return processStream(reader, decoder, session, "Mistral stream"); |
|
} |
|
|
|
|
|
|
|
|
|
export async function callLLMSummaryBatch(sessionId, conversation, model, temperature, maxTokens) { |
|
const loadingOverlay = document.getElementById("loadingOverlay"); |
|
loadingOverlay.classList.add("active"); |
|
|
|
|
|
const modelToEndpoint = { |
|
"gpt-4o": "openai_summary", |
|
"claude": "anthropic_summary", |
|
"gemini": "gemini_summary", |
|
"huggingface": "huggingface_summary", |
|
"mistral": "mistral_summary" |
|
}; |
|
|
|
|
|
const modelPrefix = Object.keys(modelToEndpoint).find(prefix => model.startsWith(prefix)); |
|
if (!modelPrefix) { |
|
throw new Error("Unsupported model for summary: " + model); |
|
} |
|
|
|
const endpoint = `http://127.0.0.1:8000/${modelToEndpoint[modelPrefix]}`; |
|
|
|
try { |
|
const response = await fetch(endpoint, { |
|
method: "POST", |
|
headers: { |
|
"Content-Type": "application/json", |
|
"X-Session-ID": sessionId, |
|
}, |
|
body: JSON.stringify({ |
|
conversation: conversation, |
|
temperature: temperature, |
|
max_tokens: maxTokens, |
|
model: model, |
|
}), |
|
}); |
|
|
|
const responseData = await response.json(); |
|
const summaryText = responseData.summary; |
|
sessions[currentSessionIndex].summary = summaryText; |
|
|
|
const summaryOverlay = document.getElementById('summaryOverlay'); |
|
if (summaryOverlay.classList.contains('active')) { |
|
document.getElementById('summaryContent').innerHTML = marked.parse(summaryText); |
|
} |
|
|
|
return summaryText; |
|
} finally { |
|
loadingOverlay.classList.remove("active"); |
|
} |
|
} |
|
|