File size: 3,084 Bytes
abf3c4b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52739ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d9f07b6
44215ae
d9f07b6
 
 
 
 
 
 
 
 
44215ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Resolve where the API lives, in priority order:
//   1. An HF Space variable `DOCUASK_API_URL` (read at runtime β€” set it in the
//      Static Space settings so the URL can change without a rebuild).
//   2. A build-time `VITE_API_URL` (baked in via frontend/.env.production).
//   3. "/api" β€” local dev (Vite proxy) and the combined single-origin image.
function resolveApiBase() {
  if (typeof window !== "undefined") {
    const fromHf = window.huggingface?.variables?.DOCUASK_API_URL;
    if (fromHf) return fromHf.replace(/\/$/, "");
  }
  if (import.meta.env.VITE_API_URL) {
    return import.meta.env.VITE_API_URL.replace(/\/$/, "");
  }
  return "/api";
}

export const API_BASE = resolveApiBase();

/** Extract a human-readable message from a FastAPI error response. */
async function errorDetail(res) {
  try {
    const data = await res.json();
    if (typeof data.detail === "string") return data.detail;
    if (Array.isArray(data.detail) && data.detail[0]?.msg) return data.detail[0].msg;
  } catch {
    // fall through to a generic message
  }
  return `Upload failed (HTTP ${res.status}).`;
}

/** GET /health β†’ returns the status string ("ok"). */
export async function getHealth() {
  const res = await fetch(`${API_BASE}/health`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json();
  return data.status;
}

/** POST /documents with a PDF file. Resolves to the document payload. */
export async function uploadFile(file) {
  const form = new FormData();
  form.append("file", file);
  const res = await fetch(`${API_BASE}/documents`, { method: "POST", body: form });
  if (!res.ok) throw new Error(await errorDetail(res));
  return res.json();
}

/** POST /documents with raw text. Resolves to the document payload. */
export async function uploadText(text) {
  const form = new FormData();
  form.append("text", text);
  const res = await fetch(`${API_BASE}/documents`, { method: "POST", body: form });
  if (!res.ok) throw new Error(await errorDetail(res));
  return res.json();
}

/** POST /ask β€” resolves to { interaction_id, answer, source_passage, ... }. */
export async function askQuestion(documentId, question) {
  const res = await fetch(`${API_BASE}/ask`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ document_id: documentId, question }),
  });
  if (!res.ok) throw new Error(await errorDetail(res));
  return res.json();
}

/** POST /feedback β€” attach a πŸ‘/πŸ‘Ž ("up" | "down") to an interaction. */
export async function sendFeedback(interactionId, feedback) {
  const res = await fetch(`${API_BASE}/feedback`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ interaction_id: interactionId, feedback }),
  });
  if (!res.ok) throw new Error(await errorDetail(res));
  return res.json();
}

/** GET /stats β€” dashboard metrics. */
export async function getStats() {
  const res = await fetch(`${API_BASE}/stats`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}