thaidinhz1 commited on
Commit
0918009
·
1 Parent(s): 24cbd41

Remove upload feature: broken with scanned PDFs, replaced by fixed corpus

Browse files
Files changed (2) hide show
  1. api.py +1 -17
  2. static/index.html +2 -96
api.py CHANGED
@@ -1,12 +1,11 @@
1
  import json
2
  from contextlib import asynccontextmanager
3
- from fastapi import FastAPI, UploadFile, File, HTTPException
4
  from fastapi.staticfiles import StaticFiles
5
  from fastapi.responses import HTMLResponse, StreamingResponse
6
  from pydantic import BaseModel
7
  from src.rag import answer, retrieve
8
  from src.reranker import _get_model
9
- from src.uploader import ingest_pdf, get_uploaded_files
10
 
11
 
12
  @asynccontextmanager
@@ -57,21 +56,6 @@ def query(req: QueryRequest):
57
  )
58
 
59
 
60
- @app.post("/upload")
61
- async def upload(file: UploadFile = File(...)):
62
- if not file.filename.endswith(".pdf"):
63
- raise HTTPException(status_code=400, detail="Chỉ hỗ trợ file PDF")
64
- content = await file.read()
65
- if len(content) > 20 * 1024 * 1024:
66
- raise HTTPException(status_code=400, detail="File quá lớn (tối đa 20MB)")
67
- result = ingest_pdf(file.filename, content)
68
- return result
69
-
70
-
71
- @app.get("/documents")
72
- def documents():
73
- return get_uploaded_files()
74
-
75
 
76
  @app.post("/stream")
77
  async def stream(req: QueryRequest):
 
1
  import json
2
  from contextlib import asynccontextmanager
3
+ from fastapi import FastAPI, HTTPException
4
  from fastapi.staticfiles import StaticFiles
5
  from fastapi.responses import HTMLResponse, StreamingResponse
6
  from pydantic import BaseModel
7
  from src.rag import answer, retrieve
8
  from src.reranker import _get_model
 
9
 
10
 
11
  @asynccontextmanager
 
56
  )
57
 
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  @app.post("/stream")
61
  async def stream(req: QueryRequest):
static/index.html CHANGED
@@ -38,26 +38,6 @@
38
  .card { background: var(--surface); border-radius: 12px; padding: 16px; box-shadow: 0 2px 8px var(--shadow); border: 1px solid var(--border); }
39
  .card h3 { font-size: 0.83rem; font-weight: 700; color: var(--text2); margin-bottom: 10px; }
40
 
41
- .drop-zone { border: 2px dashed var(--border); border-radius: 8px; padding: 18px 12px; text-align: center; cursor: pointer; transition: all 0.2s; }
42
- .drop-zone:hover, .drop-zone.drag-over { border-color: var(--primary); background: var(--primary-light); }
43
- .drop-zone .drop-icon { font-size: 1.6rem; margin-bottom: 6px; }
44
- .drop-zone p { font-size: 0.76rem; color: var(--text2); }
45
- .drop-zone span { font-size: 0.7rem; color: var(--text3); }
46
- #file-input { display: none; }
47
-
48
- .upload-btn { width: 100%; margin-top: 8px; padding: 8px; background: var(--primary); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 0.82rem; font-weight: 600; transition: background 0.2s; }
49
- .upload-btn:hover { background: var(--primary-dark); }
50
- .upload-btn:disabled { background: var(--border); color: var(--text3); cursor: not-allowed; }
51
- .upload-status { margin-top: 8px; font-size: 0.76rem; min-height: 16px; }
52
- .upload-status.ok { color: var(--green); }
53
- .upload-status.err { color: var(--red); }
54
- .upload-status.loading { color: var(--text3); }
55
-
56
- .doc-list-empty { font-size: 0.76rem; color: var(--text3); text-align: center; padding: 10px 0; }
57
- .doc-item { display: flex; align-items: flex-start; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--border); }
58
- .doc-item:last-child { border-bottom: none; }
59
- .doc-name { font-size: 0.76rem; color: var(--text); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
60
- .doc-meta { font-size: 0.68rem; color: var(--text3); margin-top: 2px; }
61
 
62
  /* Sample questions */
63
  .sample-q { display: block; width: 100%; text-align: left; background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; margin-bottom: 6px; cursor: pointer; font-size: 0.76rem; color: var(--text2); transition: all 0.15s; }
@@ -119,7 +99,6 @@
119
  .error-text { color: var(--red); font-size: 0.88rem; }
120
  .cursor { animation: blink 0.8s step-end infinite; color: var(--primary); }
121
  @keyframes blink { 50% { opacity: 0; } }
122
- .uploaded-badge { display: inline-block; background: var(--green-bg); color: var(--green-text); border-radius: 4px; padding: 1px 5px; font-size: 0.68rem; font-weight: 600; margin-left: 4px; }
123
  .ctx-badge { display: inline-block; background: #fef3c7; color: #92400e; border-radius: 4px; padding: 1px 5px; font-size: 0.65rem; font-weight: 600; margin-left: 4px; }
124
  [data-theme="dark"] .ctx-badge { background: #3a2a0a; color: #fbbf24; }
125
  .rewrite-badge { font-size: 0.74rem; color: var(--text3); font-style: italic; padding: 3px 0 0 4px; }
@@ -178,25 +157,6 @@
178
 
179
  <div class="layout">
180
  <div class="sidebar">
181
- <div class="card">
182
- <h3>📤 Upload tài liệu</h3>
183
- <div class="drop-zone" id="drop-zone" onclick="document.getElementById('file-input').click()">
184
- <div class="drop-icon">📄</div>
185
- <p>Kéo thả PDF vào đây</p>
186
- <span>hoặc bấm để chọn file</span>
187
- </div>
188
- <input type="file" id="file-input" accept=".pdf" onchange="handleFileSelect(this)">
189
- <button class="upload-btn" id="upload-btn" onclick="uploadFile()" disabled>Upload</button>
190
- <div class="upload-status" id="upload-status"></div>
191
- </div>
192
-
193
- <div class="card">
194
- <h3>📚 Tài liệu đã upload</h3>
195
- <div id="doc-list-content">
196
- <div class="doc-list-empty">Chưa có tài liệu nào</div>
197
- </div>
198
- </div>
199
-
200
  <div class="card">
201
  <h3>💡 Câu hỏi mẫu</h3>
202
  <button class="sample-q" onclick="askSample(this)">FPT hoạt động trong những lĩnh vực kinh doanh nào?</button>
@@ -217,7 +177,7 @@
217
  <div class="empty-state">
218
  <div class="icon">💬</div>
219
  <h2>Hãy đặt câu hỏi về tài liệu</h2>
220
- <p>Báo cáo tài chính FPT, TIG, UPCOM · hoặc upload PDF của bạn</p>
221
  </div>
222
  </div>
223
  </div>
@@ -323,58 +283,6 @@
323
  ask();
324
  }
325
 
326
- // Upload
327
- let selectedFile = null;
328
- const dropZone = document.getElementById('drop-zone');
329
- dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.classList.add('drag-over'); });
330
- dropZone.addEventListener('dragleave', () => dropZone.classList.remove('drag-over'));
331
- dropZone.addEventListener('drop', e => {
332
- e.preventDefault(); dropZone.classList.remove('drag-over');
333
- const file = e.dataTransfer.files[0];
334
- if (file && file.name.endsWith('.pdf')) setFile(file);
335
- });
336
-
337
- function handleFileSelect(input) { if (input.files[0]) setFile(input.files[0]); }
338
- function setFile(file) {
339
- selectedFile = file;
340
- document.getElementById('upload-btn').disabled = false;
341
- setStatus(`Đã chọn: ${file.name}`, '');
342
- }
343
- function setStatus(msg, type) {
344
- const el = document.getElementById('upload-status');
345
- el.textContent = msg; el.className = 'upload-status ' + type;
346
- }
347
-
348
- async function uploadFile() {
349
- if (!selectedFile) return;
350
- document.getElementById('upload-btn').disabled = true;
351
- setStatus('Đang xử lý...', 'loading');
352
- const formData = new FormData();
353
- formData.append('file', selectedFile);
354
- try {
355
- const res = await fetch('/upload', { method: 'POST', body: formData });
356
- if (!res.ok) { const err = await res.json(); setStatus('Lỗi: ' + err.detail, 'err'); document.getElementById('upload-btn').disabled = false; return; }
357
- const data = await res.json();
358
- setStatus(`✓ ${data.name} (${data.pages} trang, ${data.chunks} chunks)`, 'ok');
359
- selectedFile = null; document.getElementById('file-input').value = '';
360
- refreshDocList();
361
- } catch (e) { setStatus('Lỗi kết nối', 'err'); document.getElementById('upload-btn').disabled = false; }
362
- }
363
-
364
- async function refreshDocList() {
365
- const res = await fetch('/documents');
366
- const docs = await res.json();
367
- const el = document.getElementById('doc-list-content');
368
- if (!docs.length) { el.innerHTML = '<div class="doc-list-empty">Chưa có tài liệu nào</div>'; return; }
369
- el.innerHTML = docs.map(d => `
370
- <div class="doc-item">
371
- <div>
372
- <div class="doc-name" title="${d.name}">📄 ${d.name}</div>
373
- <div class="doc-meta">${d.pages} trang · ${d.chunks} chunks</div>
374
- </div>
375
- </div>`).join('');
376
- }
377
-
378
  function highlightCitations(text) {
379
  return text.replace(/\[(\d+)\]/g, '<span class="cite">[$1]</span>');
380
  }
@@ -385,7 +293,6 @@
385
  const items = sources.map((s, i) => {
386
  const rrfPct = Math.min(s.rrf_score * 3000, 100).toFixed(0);
387
  const rerankPct = Math.min(s.rerank_score * 150, 100).toFixed(0);
388
- const badge = s.uploaded ? '<span class="uploaded-badge">uploaded</span>' : '';
389
  const ctxBadge = s.has_context ? '<span class="ctx-badge">✦ context</span>' : '';
390
  const sid = `src-${++_srcCounter}`;
391
  const escapedText = s.text ? s.text.replace(/</g, '&lt;').replace(/>/g, '&gt;') : '';
@@ -396,7 +303,7 @@
396
  return `<div class="source-item" style="flex-direction:column;align-items:stretch;gap:6px">
397
  <div style="display:grid;grid-template-columns:1fr 130px;align-items:center;gap:10px">
398
  <div>
399
- <div class="source-name">📄 ${s.source}${badge}${ctxBadge}</div>
400
  <div class="source-page">Trang ${s.page}</div>
401
  </div>
402
  <div class="score-wrap">
@@ -509,7 +416,6 @@
509
  item.scrollIntoView({ behavior: 'smooth', block: 'end' });
510
  }
511
 
512
- refreshDocList();
513
  </script>
514
  </body>
515
  </html>
 
38
  .card { background: var(--surface); border-radius: 12px; padding: 16px; box-shadow: 0 2px 8px var(--shadow); border: 1px solid var(--border); }
39
  .card h3 { font-size: 0.83rem; font-weight: 700; color: var(--text2); margin-bottom: 10px; }
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  /* Sample questions */
43
  .sample-q { display: block; width: 100%; text-align: left; background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; margin-bottom: 6px; cursor: pointer; font-size: 0.76rem; color: var(--text2); transition: all 0.15s; }
 
99
  .error-text { color: var(--red); font-size: 0.88rem; }
100
  .cursor { animation: blink 0.8s step-end infinite; color: var(--primary); }
101
  @keyframes blink { 50% { opacity: 0; } }
 
102
  .ctx-badge { display: inline-block; background: #fef3c7; color: #92400e; border-radius: 4px; padding: 1px 5px; font-size: 0.65rem; font-weight: 600; margin-left: 4px; }
103
  [data-theme="dark"] .ctx-badge { background: #3a2a0a; color: #fbbf24; }
104
  .rewrite-badge { font-size: 0.74rem; color: var(--text3); font-style: italic; padding: 3px 0 0 4px; }
 
157
 
158
  <div class="layout">
159
  <div class="sidebar">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  <div class="card">
161
  <h3>💡 Câu hỏi mẫu</h3>
162
  <button class="sample-q" onclick="askSample(this)">FPT hoạt động trong những lĩnh vực kinh doanh nào?</button>
 
177
  <div class="empty-state">
178
  <div class="icon">💬</div>
179
  <h2>Hãy đặt câu hỏi về tài liệu</h2>
180
+ <p>Báo cáo tài chính VNM · FPT · HPG · MSN (2022–2023)</p>
181
  </div>
182
  </div>
183
  </div>
 
283
  ask();
284
  }
285
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  function highlightCitations(text) {
287
  return text.replace(/\[(\d+)\]/g, '<span class="cite">[$1]</span>');
288
  }
 
293
  const items = sources.map((s, i) => {
294
  const rrfPct = Math.min(s.rrf_score * 3000, 100).toFixed(0);
295
  const rerankPct = Math.min(s.rerank_score * 150, 100).toFixed(0);
 
296
  const ctxBadge = s.has_context ? '<span class="ctx-badge">✦ context</span>' : '';
297
  const sid = `src-${++_srcCounter}`;
298
  const escapedText = s.text ? s.text.replace(/</g, '&lt;').replace(/>/g, '&gt;') : '';
 
303
  return `<div class="source-item" style="flex-direction:column;align-items:stretch;gap:6px">
304
  <div style="display:grid;grid-template-columns:1fr 130px;align-items:center;gap:10px">
305
  <div>
306
+ <div class="source-name">📄 ${s.source}${ctxBadge}</div>
307
  <div class="source-page">Trang ${s.page}</div>
308
  </div>
309
  <div class="score-wrap">
 
416
  item.scrollIntoView({ behavior: 'smooth', block: 'end' });
417
  }
418
 
 
419
  </script>
420
  </body>
421
  </html>