SarahXia0405 commited on
Commit
dd83bc5
·
verified ·
1 Parent(s): 9bb5dde

Create lib/api.ts

Browse files
Files changed (1) hide show
  1. web/src/lib/api.ts +104 -0
web/src/lib/api.ts ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // web/src/lib/api.ts
2
+
3
+ export type ApiRole = "user" | "assistant";
4
+
5
+ export type ApiChatMessage = {
6
+ role: ApiRole;
7
+ content: string;
8
+ };
9
+
10
+ export type ApiChatRequest = {
11
+ message: string;
12
+
13
+ // optional context
14
+ history?: Array<[string, string]>; // (user, assistant)
15
+ learning_mode?: string; // e.g. "concept"
16
+ language_preference?: string; // "English" | "中文" | "auto"
17
+ doc_type?: string; // "Syllabus" | ...
18
+ course_outline?: string[]; // parsed syllabus topics
19
+ rag_context?: string | null; // if FE sends; usually BE builds
20
+ review_data?: string | null; // for review mode
21
+ };
22
+
23
+ export type ApiChatResponse = {
24
+ answer: string;
25
+ history?: Array<[string, string]>;
26
+ references?: string[];
27
+ };
28
+
29
+ const DEFAULT_TIMEOUT_MS = 20000;
30
+
31
+ // If you later deploy to a different domain, set VITE_API_BASE in env
32
+ function getBaseUrl() {
33
+ // Vite convention
34
+ const v = (import.meta as any)?.env?.VITE_API_BASE as string | undefined;
35
+ return (v && v.trim()) ? v.trim() : "";
36
+ }
37
+
38
+ async function fetchWithTimeout(input: RequestInfo, init?: RequestInit, timeoutMs = DEFAULT_TIMEOUT_MS) {
39
+ const controller = new AbortController();
40
+ const id = setTimeout(() => controller.abort(), timeoutMs);
41
+ try {
42
+ const res = await fetch(input, { ...init, signal: controller.signal });
43
+ return res;
44
+ } finally {
45
+ clearTimeout(id);
46
+ }
47
+ }
48
+
49
+ async function parseJsonSafe(res: Response) {
50
+ const text = await res.text();
51
+ try {
52
+ return text ? JSON.parse(text) : null;
53
+ } catch {
54
+ return { _raw: text };
55
+ }
56
+ }
57
+
58
+ export async function apiChat(payload: ApiChatRequest): Promise<ApiChatResponse> {
59
+ const base = getBaseUrl();
60
+ const res = await fetchWithTimeout(`${base}/api/chat`, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify(payload),
64
+ });
65
+
66
+ const data = await parseJsonSafe(res);
67
+
68
+ if (!res.ok) {
69
+ const msg =
70
+ (data && (data.error || data.message)) ||
71
+ `apiChat failed (${res.status})`;
72
+ throw new Error(msg);
73
+ }
74
+
75
+ // Be tolerant: map common server shapes
76
+ if (data?.answer) return data as ApiChatResponse;
77
+ if (data?.response) return { answer: data.response, history: data.history, references: data.references };
78
+ if (typeof data === "string") return { answer: data };
79
+
80
+ return { answer: "" };
81
+ }
82
+
83
+ export async function apiUpload(file: File, docType: string) {
84
+ const base = getBaseUrl();
85
+ const fd = new FormData();
86
+ fd.append("file", file);
87
+ fd.append("doc_type", docType);
88
+
89
+ const res = await fetchWithTimeout(`${base}/api/upload`, {
90
+ method: "POST",
91
+ body: fd,
92
+ }, 60000);
93
+
94
+ const data = await parseJsonSafe(res);
95
+
96
+ if (!res.ok) {
97
+ const msg =
98
+ (data && (data.error || data.message)) ||
99
+ `apiUpload failed (${res.status})`;
100
+ throw new Error(msg);
101
+ }
102
+
103
+ return data;
104
+ }