myservers/chatsbt/src/lib/chatStore.svelte.js

154 lines
3.5 KiB
JavaScript
Raw Normal View History

2025-07-31 15:59:19 -04:00
import { createChat, sendUserMessage, openStream, fetchModels } from "./chatApi.svelte.js";
2025-07-29 23:42:15 -04:00
const STORAGE_KEY = "chatHistory";
function loadHistory() {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
} catch {
return [];
}
}
function saveHistory(list) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
}
export const chatStore = (() => {
2025-07-29 23:42:15 -04:00
let chatId = $state(null);
let messages = $state([]);
let loading = $state(false);
2025-07-29 23:42:15 -04:00
let input = $state("");
2025-07-31 15:59:19 -04:00
let model = $state("qwen/qwen3-235b-a22b-2507"); // default
let models = $state([]);
let loadingModels = $state(true);
2025-07-29 23:42:15 -04:00
// public helpers
const history = $derived(loadHistory());
2025-07-29 23:42:15 -04:00
function pushHistory(id, title, msgs) {
console.log(`push history: ${id} - ${title}`);
const h = history.filter((c) => c.id !== id);
h.unshift({ id, title, messages: msgs });
saveHistory(h.slice(0, 50)); // keep last 50
}
2025-07-29 23:42:15 -04:00
async function selectChat(id) {
if (id === chatId) return;
chatId = id;
const stored = loadHistory().find((c) => c.id === id);
messages = stored?.messages || [];
loading = true;
loading = false;
window.history.replaceState({}, "", `/${id}`);
}
2025-07-29 23:42:15 -04:00
async function createAndSelect() {
const { id } = await createChat(model);
console.log(id);
selectChat(id);
return id;
}
async function send() {
2025-07-29 23:42:15 -04:00
if (!input.trim()) return;
if (!chatId) await createAndSelect();
const userMsg = { id: crypto.randomUUID(), role: "user", text: input };
messages = [...messages, userMsg];
pushHistory(chatId, userMsg.text.slice(0, 30), messages);
loading = true;
2025-07-29 23:42:15 -04:00
const { message_id } = await sendUserMessage(chatId, input, model);
input = "";
2025-07-29 23:42:15 -04:00
let assistantMsg = { id: message_id, role: "assistant", text: "" };
messages = [...messages, assistantMsg];
const es = openStream(chatId, message_id);
es.onmessage = (e) => {
assistantMsg = { ...assistantMsg, text: assistantMsg.text + e.data };
messages = [...messages.slice(0, -1), assistantMsg];
};
es.onerror = () => {
es.close();
loading = false;
2025-07-29 23:42:15 -04:00
};
es.addEventListener("done", (e) => {
console.log(e);
es.close();
loading = false;
pushHistory(chatId, userMsg.text.slice(0, 30), messages);
});
}
2025-07-31 15:59:19 -04:00
async function loadModels() {
loadingModels = true;
models = await fetchModels();
loadingModels = false;
// Set default model if available and not already set
if (models.length > 0 && !model) {
model = models[0].id || models[0];
}
}
function handleKey(e) {
2025-07-29 23:42:15 -04:00
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
send();
}
}
2025-07-29 23:42:15 -04:00
// initial route handling
const path = window.location.pathname.slice(1);
const storedHistory = loadHistory();
if (path && !storedHistory.find((c) => c.id === path)) {
createAndSelect();
} else if (path) {
selectChat(path);
}
2025-07-31 15:59:19 -04:00
// Load models on initialization
loadModels();
return {
2025-07-29 23:42:15 -04:00
get chatId() {
return chatId;
},
get messages() {
return messages;
},
get loading() {
return loading;
},
get input() {
return input;
},
set input(v) {
input = v;
},
get model() {
return model;
},
set model(v) {
model = v;
},
2025-07-31 15:59:19 -04:00
get models() {
return models;
},
get loadingModels() {
return loadingModels;
},
2025-07-29 23:42:15 -04:00
get history() {
return loadHistory();
},
selectChat,
send,
2025-07-29 23:42:15 -04:00
handleKey,
createAndSelect,
2025-07-31 15:59:19 -04:00
loadModels,
};
2025-07-29 23:42:15 -04:00
})();