from fastapi import APIRouter, Depends, HTTPException, status from chromadb import AsyncHttpClient from app.models import User from app.api.deps import get_db, get_current_user, get_chroma_client from app.schema import Quiz_input, QuizOutput, IngestRequest from .prompts import SYSTEM_PROMPT from fastapi import APIRouter, Depends, HTTPException from chromadb.api.models.Collection import Collection from app.api.deps import get_chroma_collection from app.llm import call_llm import uuid import logging router = APIRouter() logger = logging.getLogger("uvicorn.error") async def search_logic(query: str, collection: Collection, filter_dict: dict = None): logger.info(f"🔍 [Search Logic] Starting search for query: '{query}'") try: results = await collection.query( query_texts=[query], n_results=5, where=filter_dict ) logger.info(f"📄 [Search Logic] Raw results from DB: {results}") if results and results.get('documents') and len(results['documents']) > 0: raw_docs = results['documents'][0] valid_docs = [str(doc) for doc in raw_docs if doc is not None] logger.info(f"✅ [Search Logic] Processing: Found {len(raw_docs)} items. Valid text items: {len(valid_docs)}") if len(raw_docs) != len(valid_docs): logger.warning("⚠️ [Search Logic] Warning: Some documents contained NoneType and were skipped.") final_context = " ".join(valid_docs) return final_context else: logger.warning("⚠️ [Search Logic] No documents found for this query.") return "" except Exception as e: logger.error(f"❌ [Search Logic] CRITICAL ERROR: {str(e)}") return "" @router.get("/search_docs") async def search_documents( query: str, collection: Collection = Depends(get_chroma_collection) ): try: return await search_logic(query, collection) except Exception as e: raise HTTPException(500, f"ChromaDB Query Error: {e}") @router.post("/resume", response_model=QuizOutput, status_code=status.HTTP_201_CREATED) async def generate_quiz_resume( Input_model: Quiz_input, collection: Collection = Depends(get_chroma_collection), current_user: User = Depends(get_current_user) ): try: query = Input_model.parsed_doc + Input_model.user_prompt retrieved_context = await search_logic(query, collection) if not retrieved_context: raise ValueError("No context available to generate quiz.") prompt = await prompt_builder(Input_model.parsed_doc, Input_model.user_prompt, retrieved_context) quiz_data_obj = await call_llm(prompt) return quiz_data_obj except Exception as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f'Invalid Input: {str(e)}' ) async def ingest_logic(input_data:IngestRequest , collection: Collection): doc_id = input_data.id if input_data.id else str(uuid.uuid4()) await collection.add( ids = [doc_id], documents=[input_data.parsed_doc], metadatas=[{"user_prompt": input_data.user_prompt}] ) return { "status": "success", "id": doc_id, "stored_prompt": input_data.user_prompt } @router.post("/ingest", status_code=status.HTTP_201_CREATED) async def ingest_data( input_data: IngestRequest, collection: Collection = Depends(get_chroma_collection), current_user: User = Depends(get_current_user) ): try: return await ingest_logic(input_data, collection) except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail= f"Ingestion failed: {str(e)}" ) @router.post("/notes", response_model=QuizOutput, status_code=status.HTTP_201_CREATED) async def generate_quiz_notes( Input_model: IngestRequest, collection: Collection = Depends(get_chroma_collection), current_user: User = Depends(get_current_user) ): try: notes = Input_model await ingest_logic(notes, collection) query = Input_model.user_prompt retrieved_context = await search_logic(query, collection) if not retrieved_context: raise ValueError("No context available to generate quiz.") prompt = await prompt_builder(Input_model.parsed_doc, Input_model.user_prompt, retrieved_context) quiz_data_obj = await call_llm(prompt) return quiz_data_obj except Exception as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f'Invalid Input: {str(e)}' ) # #--------Helper Functions--------# async def prompt_builder(parsed_doc:str, user_prompt:str, docs:str=None): prompt = SYSTEM_PROMPT.format( user_prompt=user_prompt, parsed_info=parsed_doc, retrieved_docs=docs ) return prompt