Spaces:
Running
Running
github-actions[bot] commited on
Commit ยท
01e41e5
1
Parent(s): e95b583
๐ Auto-deploy backend from GitHub (4a4f1ca)
Browse files- routes/practice.py +5 -4
- 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
|
|
|
|
| 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
|
| 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
|
| 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}")
|