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

86 lines
2.1 KiB
JavaScript
Raw Normal View History

// Pure vanilla JS no Svelte imports
const BASE = import.meta.env.VITE_API_URL ?? 'http://localhost:8000';
export const chatStore = (() => {
let chatId = $state(null); // null until first message
let messages = $state([]);
let input = $state('');
let loading = $state(false);
/* ── helpers ── */
async function createChat() {
const res = await fetch(`${BASE}/chats`, { method: 'POST' });
const { id } = await res.json();
return id;
}
async function sendUserMessage(text) {
await fetch(`${BASE}/chats/${chatId}/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: text })
});
}
function streamAssistantReply() {
const source = new EventSource(`${BASE}/chats/${chatId}/stream`);
let botMsg = { id: Date.now(), text: '', me: false, sender: 'bot' };
messages = [...messages, botMsg];
source.onmessage = (ev) => {
console.log(ev.data);
if (ev.data === '[DONE]') {
source.close();
loading = false;
return;
}
messages = messages.map((m, i) =>
i === messages.length - 1 ? { ...m, text: m.text + ev.data } : m
);
};
source.onerror = () => {
source.close();
loading = false;
};
}
async function send() {
const text = input.trim();
if (!text || loading) return;
// add user bubble immediately
messages = [...messages, { id: Date.now(), text, me: true, sender: 'user' }];
input = '';
loading = true;
try {
if (!chatId) chatId = await createChat();
await sendUserMessage(text);
streamAssistantReply();
} catch {
messages = [
...messages,
{ id: Date.now(), text: 'Sorry, something went wrong.', me: false, sender: 'bot' }
];
loading = false;
}
}
function handleKey(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
send();
}
}
return {
get messages() { return messages; },
get input() { return input; },
set input(v) { input = v; },
get loading() { return loading; },
send,
handleKey
};
})();