Rochane commited on
Commit
e2741fa
·
1 Parent(s): 45620de

Add file upload RAG: main.py + index.html

Browse files
Files changed (2) hide show
  1. app/main.py +38 -2
  2. static/index.html +13 -0
app/main.py CHANGED
@@ -3,13 +3,14 @@
3
  import re
4
  from contextlib import asynccontextmanager
5
  from pathlib import Path
 
6
 
7
- from fastapi import FastAPI
8
  from fastapi.responses import FileResponse
9
  from fastapi.staticfiles import StaticFiles
10
  from pydantic import BaseModel
11
 
12
- from app.rag import load_corpus, retrieve
13
  from app.llm import build_system_prompt, chat, analyze_session
14
 
15
 
@@ -25,6 +26,8 @@ app = FastAPI(title="AIM Learning Companion", lifespan=lifespan)
25
  STATIC_DIR = Path(__file__).parent.parent / "static"
26
  app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
27
 
 
 
28
 
29
  class ChatRequest(BaseModel):
30
  message: str
@@ -84,6 +87,39 @@ async def api_chat(req: ChatRequest):
84
  return ChatResponse(reply=reply, phase=detected_phase)
85
 
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  @app.post("/api/analyze", response_model=AnalysisResponse)
88
  async def api_analyze(req: AnalysisRequest):
89
  analysis = await analyze_session(req.history)
 
3
  import re
4
  from contextlib import asynccontextmanager
5
  from pathlib import Path
6
+ from typing import List
7
 
8
+ from fastapi import FastAPI, UploadFile, File
9
  from fastapi.responses import FileResponse
10
  from fastapi.staticfiles import StaticFiles
11
  from pydantic import BaseModel
12
 
13
+ from app.rag import load_corpus, retrieve, add_documents, list_documents, delete_document
14
  from app.llm import build_system_prompt, chat, analyze_session
15
 
16
 
 
26
  STATIC_DIR = Path(__file__).parent.parent / "static"
27
  app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
28
 
29
+ ALLOWED_EXTENSIONS = {".txt", ".pdf", ".pptx", ".ppt", ".zip"}
30
+
31
 
32
  class ChatRequest(BaseModel):
33
  message: str
 
87
  return ChatResponse(reply=reply, phase=detected_phase)
88
 
89
 
90
+ @app.post("/api/upload")
91
+ async def api_upload(files: List[UploadFile] = File(...)):
92
+ """Upload one or more files (PDF, PPTX, TXT, ZIP) to the RAG corpus."""
93
+ file_data = []
94
+ skipped = []
95
+
96
+ for f in files:
97
+ ext = Path(f.filename).suffix.lower() if f.filename else ""
98
+ if ext not in ALLOWED_EXTENSIONS:
99
+ skipped.append({"filename": f.filename, "reason": f"Type non supporte: {ext}"})
100
+ continue
101
+ content = await f.read()
102
+ file_data.append((f.filename, content))
103
+
104
+ results = add_documents(file_data) if file_data else []
105
+ return {"results": results, "skipped": skipped}
106
+
107
+
108
+ @app.get("/api/documents")
109
+ async def api_documents():
110
+ """List all documents in the corpus."""
111
+ return {"documents": list_documents()}
112
+
113
+
114
+ @app.delete("/api/documents/{filename}")
115
+ async def api_delete_document(filename: str):
116
+ """Delete a document from the corpus."""
117
+ ok = delete_document(filename)
118
+ if ok:
119
+ return {"status": "ok"}
120
+ return {"status": "error", "message": "Fichier non trouve"}
121
+
122
+
123
  @app.post("/api/analyze", response_model=AnalysisResponse)
124
  async def api_analyze(req: AnalysisRequest):
125
  analysis = await analyze_session(req.history)
static/index.html CHANGED
@@ -18,6 +18,18 @@
18
  <input type="text" id="topic-input" placeholder="Ex : L'intelligence artificielle en formation professionnelle">
19
  </div>
20
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  <div class="form-group">
22
  <label>Mode</label>
23
  <div class="mode-selector">
@@ -41,6 +53,7 @@
41
  <div class="chat-header-left">
42
  <span id="mode-badge" class="badge badge-mode"></span>
43
  <span id="topic-badge" class="badge badge-topic"></span>
 
44
  </div>
45
  <div class="chat-header-right">
46
  <button id="btn-end-session" class="btn-end">Terminer la session</button>
 
18
  <input type="text" id="topic-input" placeholder="Ex : L'intelligence artificielle en formation professionnelle">
19
  </div>
20
 
21
+ <div class="form-group">
22
+ <label>Documents de reference (optionnel)</label>
23
+ <div class="upload-zone" id="upload-zone">
24
+ <div class="upload-icon">+</div>
25
+ <div class="upload-text">Glisse tes fichiers ici ou clique pour selectionner</div>
26
+ <div class="upload-hint">PDF, PPTX, TXT ou ZIP — plusieurs fichiers possibles</div>
27
+ <input type="file" id="file-input" multiple accept=".pdf,.pptx,.ppt,.txt,.zip" hidden>
28
+ </div>
29
+ <div class="upload-list" id="upload-list"></div>
30
+ <div class="upload-status" id="upload-status"></div>
31
+ </div>
32
+
33
  <div class="form-group">
34
  <label>Mode</label>
35
  <div class="mode-selector">
 
53
  <div class="chat-header-left">
54
  <span id="mode-badge" class="badge badge-mode"></span>
55
  <span id="topic-badge" class="badge badge-topic"></span>
56
+ <span id="docs-badge" class="badge badge-docs" style="display:none"></span>
57
  </div>
58
  <div class="chat-header-right">
59
  <button id="btn-end-session" class="btn-end">Terminer la session</button>