github-actions[bot] commited on
Commit
01e41e5
ยท
1 Parent(s): e95b583

๐Ÿš€ Auto-deploy backend from GitHub (4a4f1ca)

Browse files
Files changed (2) hide show
  1. routes/practice.py +5 -4
  2. routes/rag_routes.py +83 -2
routes/practice.py CHANGED
@@ -192,7 +192,8 @@ def _parse_questions_response(raw: str, count: int) -> List[Dict[str, Any]]:
192
 
193
  def _build_question_prompt(subject: str, competency: str, difficulty: str, count: int) -> tuple[str, str]:
194
  system_prompt = (
195
- "You are an expert math educator for Filipino Senior High School students. "
 
196
  "Generate exactly " + str(count) + " multiple-choice math questions "
197
  "for the subject \"" + subject + "\" focused on competency: \"" + competency + "\". "
198
  "Difficulty level: " + difficulty + ". "
@@ -202,12 +203,12 @@ def _build_question_prompt(subject: str, competency: str, difficulty: str, count
202
  "\"correct_index\": 0-3, \"explanation\": \"...\", "
203
  "\"competency\": \"...\", \"difficulty\": \"...\", "
204
  "\"bloomsLevel\": \"Remember|Understand|Apply|Analyze|Evaluate|Create\" }] }. "
205
- "All questions and options MUST be written in English. Make questions clear and unambiguous."
206
  )
207
  user_message = (
208
- f"Generate {count} multiple-choice math questions for {subject}, "
209
  f"competency: {competency}, difficulty: {difficulty}. "
210
- f"Return only the JSON, no explanation."
211
  )
212
  return system_prompt, user_message
213
 
 
192
 
193
  def _build_question_prompt(subject: str, competency: str, difficulty: str, count: int) -> tuple[str, str]:
194
  system_prompt = (
195
+ "You are a math question generator. "
196
+ "IMPORTANT: Write EVERYTHING in English. Do NOT use Tagalog, Filipino, or any other language. "
197
  "Generate exactly " + str(count) + " multiple-choice math questions "
198
  "for the subject \"" + subject + "\" focused on competency: \"" + competency + "\". "
199
  "Difficulty level: " + difficulty + ". "
 
203
  "\"correct_index\": 0-3, \"explanation\": \"...\", "
204
  "\"competency\": \"...\", \"difficulty\": \"...\", "
205
  "\"bloomsLevel\": \"Remember|Understand|Apply|Analyze|Evaluate|Create\" }] }. "
206
+ "All text must be in English only."
207
  )
208
  user_message = (
209
+ f"Generate {count} multiple-choice math questions in English for {subject}, "
210
  f"competency: {competency}, difficulty: {difficulty}. "
211
+ f"Write all questions, options, and explanations in English. Return only the JSON."
212
  )
213
  return system_prompt, user_message
214
 
routes/rag_routes.py CHANGED
@@ -27,7 +27,7 @@ from rag.curriculum_rag import (
27
  retrieve_lesson_pdf_context,
28
  summarize_retrieval_confidence,
29
  )
30
- from rag.vectorstore_loader import get_vectorstore_health, reset_vectorstore_singleton
31
 
32
  try:
33
  from firebase_admin import firestore as firebase_firestore
@@ -511,4 +511,85 @@ async def rag_analysis_context(request: Request, payload: RagAnalysisContextRequ
511
  chunks=chunks,
512
  )
513
 
514
- return {"curriculumContext": "\n".join(lines)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  retrieve_lesson_pdf_context,
28
  summarize_retrieval_confidence,
29
  )
30
+ from rag.vectorstore_loader import get_vectorstore_health, reset_vectorstore_singleton, get_vectorstore_components
31
 
32
  try:
33
  from firebase_admin import firestore as firebase_firestore
 
511
  chunks=chunks,
512
  )
513
 
514
+ return {"curriculumContext": "\n".join(lines)}
515
+
516
+
517
+ # โ”€โ”€โ”€ RAG Management Endpoints (Admin) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
518
+
519
+ @router.get("/documents")
520
+ async def list_rag_documents():
521
+ """List all documents in the vectorstore grouped by source file."""
522
+ try:
523
+ _, collection, _ = get_vectorstore_components()
524
+ payload = collection.get(include=["metadatas"])
525
+ metadatas = payload.get("metadatas") or []
526
+ ids = payload.get("ids") or []
527
+
528
+ # Group by source_file
529
+ sources: Dict[str, Dict[str, Any]] = {}
530
+ for i, md in enumerate(metadatas):
531
+ if not isinstance(md, dict):
532
+ continue
533
+ source = md.get("source_file") or md.get("storage_path") or "unknown"
534
+ subject = md.get("subject") or "unknown"
535
+ if source not in sources:
536
+ sources[source] = {"source_file": source, "subject": subject, "chunk_count": 0, "chunk_ids": []}
537
+ sources[source]["chunk_count"] += 1
538
+ sources[source]["chunk_ids"].append(ids[i])
539
+
540
+ documents = sorted(sources.values(), key=lambda x: x["subject"])
541
+ # Don't send all chunk_ids to frontend (too large), just count
542
+ for doc in documents:
543
+ del doc["chunk_ids"]
544
+
545
+ return {"documents": documents, "total_chunks": len(ids)}
546
+ except Exception as exc:
547
+ raise HTTPException(status_code=500, detail=f"Failed to list RAG documents: {exc}")
548
+
549
+
550
+ @router.delete("/documents/by-subject/{subject}")
551
+ async def delete_rag_by_subject(subject: str):
552
+ """Delete all chunks for a given subject from the vectorstore."""
553
+ try:
554
+ _, collection, _ = get_vectorstore_components()
555
+ payload = collection.get(include=["metadatas"], where={"subject": subject})
556
+ ids = payload.get("ids") or []
557
+ if not ids:
558
+ return {"deleted": 0, "message": f"No chunks found for subject '{subject}'."}
559
+ collection.delete(ids=ids)
560
+ return {"deleted": len(ids), "message": f"Deleted {len(ids)} chunks for subject '{subject}'."}
561
+ except Exception as exc:
562
+ raise HTTPException(status_code=500, detail=f"Failed to delete: {exc}")
563
+
564
+
565
+ @router.delete("/documents/by-source")
566
+ async def delete_rag_by_source(source_file: str):
567
+ """Delete all chunks from a specific source file."""
568
+ try:
569
+ _, collection, _ = get_vectorstore_components()
570
+ payload = collection.get(include=["metadatas"], where={"source_file": source_file})
571
+ ids = payload.get("ids") or []
572
+ if not ids:
573
+ # Try storage_path as fallback
574
+ payload = collection.get(include=["metadatas"], where={"storage_path": source_file})
575
+ ids = payload.get("ids") or []
576
+ if not ids:
577
+ return {"deleted": 0, "message": f"No chunks found for source '{source_file}'."}
578
+ collection.delete(ids=ids)
579
+ return {"deleted": len(ids), "message": f"Deleted {len(ids)} chunks from '{source_file}'."}
580
+ except Exception as exc:
581
+ raise HTTPException(status_code=500, detail=f"Failed to delete: {exc}")
582
+
583
+
584
+ @router.delete("/documents/all")
585
+ async def purge_all_rag():
586
+ """Purge ALL chunks from the vectorstore. Requires re-ingestion after."""
587
+ try:
588
+ client, collection, _ = get_vectorstore_components()
589
+ # Delete the collection and recreate it empty
590
+ client.delete_collection("curriculum_chunks")
591
+ client.get_or_create_collection(name="curriculum_chunks", metadata={"hnsw:space": "cosine"})
592
+ reset_vectorstore_singleton()
593
+ return {"message": "All RAG content purged. Re-ingestion required."}
594
+ except Exception as exc:
595
+ raise HTTPException(status_code=500, detail=f"Failed to purge: {exc}")