| async function requestJson(url, options = {}) { |
| const res = await fetch(url, { credentials: "include", ...options }); |
| const text = await res.text(); |
| let body; |
| try { |
| body = text ? JSON.parse(text) : null; |
| } catch { |
| body = { data: null, message: text, success: res.ok }; |
| } |
| if (!res.ok && !(body && body.success === false)) { |
| throw new Error((body && body.message) || `HTTP ${res.status}`); |
| } |
| return body; |
| } |
|
|
| async function getHealth() { |
| return requestJson(window.API_ENDPOINTS.HEALTH); |
| } |
|
|
| async function getAvailableTimeOptions() { |
| const body = await requestJson(window.API_ENDPOINTS.AVAILABLE_TIME); |
| return (body && body.data) || []; |
| } |
|
|
| async function loginUser(minutes) { |
| const url = `${window.API_ENDPOINTS.USER_LOGIN}/${minutes}`; |
| const res = await requestJson(url); |
| if (res && res.success) { |
| const durationMs = minutes * 60 * 1000; |
| const expiryTime = Date.now() + durationMs; |
| localStorage.setItem("session_expiry", expiryTime); |
| localStorage.setItem("session_duration_minutes", minutes); |
| window.location.href = window.PAGES.CHAT; |
| } else { |
| throw new Error((res && res.message) || "Failed to start session"); |
| } |
| } |
|
|
| async function trainModel() { |
| return requestJson(window.API_ENDPOINTS.TRAIN, { method: "GET" }); |
| } |
|
|
| function streamChat({ body, file, onChunk, onDone, onError }) { |
| const fd = new FormData(); |
| fd.append("body", JSON.stringify(body)); |
| if (file) fd.append("file", file); |
|
|
| fetch(window.API_ENDPOINTS.CHAT, { |
| method: "POST", |
| credentials: "include", |
| body: fd, |
| }) |
| .then(async (res) => { |
| if (!res.ok || !res.body) { |
| const txt = await res.text(); |
| throw new Error(txt || `HTTP ${res.status}`); |
| } |
| const reader = res.body.getReader(); |
| const decoder = new TextDecoder(); |
| let buffer = ""; |
| while (true) { |
| const { value, done } = await reader.read(); |
| if (done) break; |
| buffer += decoder.decode(value, { stream: true }); |
| let idx; |
| while ((idx = buffer.indexOf("\n")) >= 0) { |
| const line = buffer.slice(0, idx).trim(); |
| buffer = buffer.slice(idx + 1); |
| if (line) onChunk(line); |
| } |
| } |
| onDone && onDone(); |
| }) |
| .catch((err) => onError && onError(err)); |
| } |
|
|
| function hasSessionCookie() { |
| return document.cookie.split(";").some((c) => c.trim().startsWith("thread_id=")); |
| } |
|
|
| window.api = { |
| getHealth, |
| getAvailableTimeOptions, |
| loginUser, |
| trainModel, |
| streamChat, |
| hasSessionCookie, |
| }; |
|
|