diff --git a/public/index.html b/public/index.html
index 659a1bd..9483b3e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -21,8 +21,7 @@
diff --git a/public/script.js b/public/script.js
index 5e69869..8de68af 100644
--- a/public/script.js
+++ b/public/script.js
@@ -12,6 +12,28 @@ const sendBtn = document.getElementById("send");
let isProcessing = false;
let chatHistory = JSON.parse(localStorage.getItem('chatHistory')) || [];
+const displayModels = async () => {
+ const select = document.getElementById("model");
+
+ fetch("/models")
+ .then(response => response.json())
+ .then(data => {
+ // Clear loading option
+ select.innerHTML = "";
+
+ data.models.forEach(model => {
+ const option = document.createElement("option");
+ option.value = model.model;
+ option.textContent = model.name;
+ select.appendChild(option);
+ });
+ })
+ .catch(err => {
+ console.error("Failed to load models:", err);
+ select.innerHTML = ``;
+ });
+}
+
async function post(url, data) {
try {
const res = await fetch(url, {
@@ -172,7 +194,7 @@ async function sendMessage() {
const loadingMsg = addMessage('', false, true, false);
try {
const model = document.getElementById("model").value;
- const res = await post("/chat", { message: msg, model });
+ const res = await post("/chat", {message: msg, model});
if (messagesDiv.contains(loadingMsg)) {
messagesDiv.removeChild(loadingMsg);
}
@@ -230,4 +252,5 @@ msgInput.addEventListener('input', function () {
window.addEventListener('load', () => {
checkSession();
+ displayModels()
});
\ No newline at end of file
diff --git a/server.js b/server.js
index 8f3491d..dac7386 100644
--- a/server.js
+++ b/server.js
@@ -64,38 +64,83 @@ const requireLogin = (req, res, next) => {
next();
};
+app.get("/models", async (req, res) => {
+ const response = await fetch("https://ai.krishd.ch/api/tags");
+ res.json(await response.json());
+})
+
app.post("/chat", requireLogin, async (req, res) => {
const user = req.session.user;
const { message, model } = req.body;
const selectedModel = model || "mistral:latest";
+
try {
if (!chats[user]) chats[user] = [];
chats[user].push({ role: "user", content: message });
if (chats[user].length > 20) chats[user] = chats[user].slice(-20);
+
const messagesPayload = chats[user].map(m => ({
role: m.role === "bot" ? "assistant" : "user",
content: m.content
}));
+
const response = await fetch("https://ai.krishd.ch/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: selectedModel,
messages: messagesPayload,
- stream: false
+ stream: true
})
});
- const data = await response.json();
- const reply = data?.message?.content || "[No response]";
- chats[user].push({ role: "bot", content: reply });
+
+ if (!response.ok) {
+ throw new Error(`AI HTTP ${response.status}`);
+ }
+
+ const reader = response.body.getReader();
+ const decoder = new TextDecoder();
+
+ let buffer = "";
+ let fullReply = "";
+
+ while (true) {
+ const { value, done } = await reader.read();
+ if (done) break;
+
+ buffer += decoder.decode(value, { stream: true });
+
+ let lines = buffer.split("\n");
+ buffer = lines.pop(); // keep incomplete JSON
+
+ for (const line of lines) {
+ if (!line.trim()) continue;
+
+ const json = JSON.parse(line);
+
+ if (json.done) {
+ break;
+ }
+
+ if (json.message?.content) {
+ fullReply += json.message.content;
+ }
+ }
+ }
+
+ chats[user].push({ role: "bot", content: fullReply });
saveChats();
- res.json({ reply });
+ console.log("Reply length:", fullReply.length);
+
+ res.json({ reply: fullReply });
+
} catch (err) {
console.error("AI chat error:", err);
res.status(500).json({ error: "AI request failed" });
}
});
+
async function checkSession() {
const res = await get('/session');
if (res.user) {