SarahXia0405 commited on
Commit
2f04655
·
verified ·
1 Parent(s): eacc9fc

Update web/src/lib/api.ts

Browse files
Files changed (1) hide show
  1. web/src/lib/api.ts +151 -56
web/src/lib/api.ts CHANGED
@@ -1,46 +1,25 @@
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
  }
@@ -55,50 +34,166 @@ async function parseJsonSafe(res: Response) {
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
  }
 
1
  // web/src/lib/api.ts
2
+ // Aligns with api/server.py routes:
3
+ // POST /api/login, /api/chat, /api/upload, /api/export, /api/summary, /api/feedback
4
+ // GET /api/memoryline
5
 
6
+ export type LearningMode = "general" | "concept" | "socratic" | "exam" | "assignment" | "summary";
7
+ export type LanguagePref = "Auto" | "English" | "中文";
8
+ export type DocType = "Syllabus" | "Lecture Slides / PPT" | "Literature Review / Paper" | "Other Course Document";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  const DEFAULT_TIMEOUT_MS = 20000;
11
 
 
12
  function getBaseUrl() {
13
+ // Vite env: VITE_API_BASE can be "", "http://localhost:8000", etc.
14
  const v = (import.meta as any)?.env?.VITE_API_BASE as string | undefined;
15
+ return v && v.trim() ? v.trim() : "";
16
  }
17
 
18
  async function fetchWithTimeout(input: RequestInfo, init?: RequestInit, timeoutMs = DEFAULT_TIMEOUT_MS) {
19
  const controller = new AbortController();
20
  const id = setTimeout(() => controller.abort(), timeoutMs);
21
  try {
22
+ return await fetch(input, { ...init, signal: controller.signal });
 
23
  } finally {
24
  clearTimeout(id);
25
  }
 
34
  }
35
  }
36
 
37
+ function errMsg(data: any, fallback: string) {
38
+ return (data && (data.error || data.detail || data.message)) ? String(data.error || data.detail || data.message) : fallback;
39
+ }
40
+
41
+ // --------------------
42
+ // /api/login
43
+ // --------------------
44
+ export type ApiLoginReq = {
45
+ name: string;
46
+ user_id: string;
47
+ };
48
+
49
+ export type ApiLoginResp =
50
+ | { ok: true; user: { name: string; user_id: string } }
51
+ | { ok: false; error: string };
52
+
53
+ export async function apiLogin(payload: ApiLoginReq): Promise<ApiLoginResp> {
54
  const base = getBaseUrl();
55
+ const res = await fetchWithTimeout(`${base}/api/login`, {
56
  method: "POST",
57
  headers: { "Content-Type": "application/json" },
58
  body: JSON.stringify(payload),
59
  });
60
 
61
  const data = await parseJsonSafe(res);
62
+ if (!res.ok) throw new Error(errMsg(data, `apiLogin failed (${res.status})`));
63
+ return data as ApiLoginResp;
64
+ }
65
 
66
+ // --------------------
67
+ // /api/chat
68
+ // --------------------
69
+ export type ApiChatReq = {
70
+ user_id: string;
71
+ message: string;
72
+ learning_mode: string; // backend expects string (not strict union)
73
+ language_preference?: string; // "Auto" | "English" | "中文"
74
+ doc_type?: string; // "Syllabus" | "Lecture Slides / PPT" | ...
75
+ };
76
+
77
+ export type ApiChatRef = { source_file?: string; section?: string };
78
+
79
+ export type ApiChatResp = {
80
+ reply: string;
81
+ session_status_md: string;
82
+ refs: ApiChatRef[];
83
+ latency_ms: number;
84
+ };
85
+
86
+ export async function apiChat(payload: ApiChatReq): Promise<ApiChatResp> {
87
+ const base = getBaseUrl();
88
+ const res = await fetchWithTimeout(`${base}/api/chat`, {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/json" },
91
+ body: JSON.stringify({
92
+ language_preference: "Auto",
93
+ doc_type: "Syllabus",
94
+ ...payload,
95
+ }),
96
+ }, 60000); // chat can be slow
97
 
98
+ const data = await parseJsonSafe(res);
99
+ if (!res.ok) throw new Error(errMsg(data, `apiChat failed (${res.status})`));
 
 
100
 
101
+ // backend returns { reply, session_status_md, refs, latency_ms }
102
+ return data as ApiChatResp;
103
  }
104
 
105
+ // --------------------
106
+ // /api/upload
107
+ // --------------------
108
+ export type ApiUploadResp = {
109
+ ok: boolean;
110
+ added_chunks?: number;
111
+ status_md?: string;
112
+ error?: string;
113
+ };
114
+
115
+ export async function apiUpload(args: { user_id: string; doc_type: string; file: File }): Promise<ApiUploadResp> {
116
  const base = getBaseUrl();
117
  const fd = new FormData();
118
+ fd.append("user_id", args.user_id);
119
+ fd.append("doc_type", args.doc_type);
120
+ fd.append("file", args.file);
121
 
122
+ const res = await fetchWithTimeout(`${base}/api/upload`, { method: "POST", body: fd }, 120000);
123
+ const data = await parseJsonSafe(res);
124
+ if (!res.ok) throw new Error(errMsg(data, `apiUpload failed (${res.status})`));
125
+ return data as ApiUploadResp;
126
+ }
127
+
128
+ // --------------------
129
+ // /api/export
130
+ // --------------------
131
+ export async function apiExport(payload: { user_id: string; learning_mode: string }): Promise<{ markdown: string }> {
132
+ const base = getBaseUrl();
133
+ const res = await fetchWithTimeout(`${base}/api/export`, {
134
  method: "POST",
135
+ headers: { "Content-Type": "application/json" },
136
+ body: JSON.stringify(payload),
137
+ });
138
 
139
  const data = await parseJsonSafe(res);
140
+ if (!res.ok) throw new Error(errMsg(data, `apiExport failed (${res.status})`));
141
+ return data as { markdown: string };
142
+ }
143
 
144
+ // --------------------
145
+ // /api/summary
146
+ // --------------------
147
+ export async function apiSummary(payload: { user_id: string; learning_mode: string; language_preference?: string }): Promise<{ markdown: string }> {
148
+ const base = getBaseUrl();
149
+ const res = await fetchWithTimeout(`${base}/api/summary`, {
150
+ method: "POST",
151
+ headers: { "Content-Type": "application/json" },
152
+ body: JSON.stringify({ language_preference: "Auto", ...payload }),
153
+ });
154
 
155
+ const data = await parseJsonSafe(res);
156
+ if (!res.ok) throw new Error(errMsg(data, `apiSummary failed (${res.status})`));
157
+ return data as { markdown: string };
158
+ }
159
+
160
+ // --------------------
161
+ // /api/feedback
162
+ // --------------------
163
+ export type ApiFeedbackReq = {
164
+ user_id: string;
165
+ rating: "helpful" | "not_helpful";
166
+ assistant_message_id?: string;
167
+ assistant_text: string;
168
+ user_text?: string;
169
+ comment?: string;
170
+ tags?: string[];
171
+ refs?: string[];
172
+ learning_mode?: string;
173
+ doc_type?: string;
174
+ timestamp_ms?: number;
175
+ };
176
+
177
+ export async function apiFeedback(payload: ApiFeedbackReq): Promise<{ ok: boolean }> {
178
+ const base = getBaseUrl();
179
+ const res = await fetchWithTimeout(`${base}/api/feedback`, {
180
+ method: "POST",
181
+ headers: { "Content-Type": "application/json" },
182
+ body: JSON.stringify(payload),
183
+ });
184
+
185
+ const data = await parseJsonSafe(res);
186
+ if (!res.ok) throw new Error(errMsg(data, `apiFeedback failed (${res.status})`));
187
+ return data as { ok: boolean };
188
+ }
189
+
190
+ // --------------------
191
+ // /api/memoryline
192
+ // --------------------
193
+ export async function apiMemoryline(user_id: string): Promise<{ next_review_label: string; progress_pct: number }> {
194
+ const base = getBaseUrl();
195
+ const res = await fetchWithTimeout(`${base}/api/memoryline?user_id=${encodeURIComponent(user_id)}`, { method: "GET" });
196
+ const data = await parseJsonSafe(res);
197
+ if (!res.ok) throw new Error(errMsg(data, `apiMemoryline failed (${res.status})`));
198
+ return data as { next_review_label: string; progress_pct: number };
199
  }