Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, Depends, HTTPException | |
| from sqlalchemy.orm import Session | |
| from typing import List | |
| from app.database import get_db | |
| from app.models import RFP, Question | |
| from app.schemas import ( | |
| RFPCreate, RFPUpdate, RFPResponse, | |
| QuestionCreate, QuestionUpdate, QuestionResponse, QuestionWithRFP | |
| ) | |
| from app.services.similarity_service import similarity_service | |
| router = APIRouter(prefix="/api/knowledge-base", tags=["Knowledge Base"]) | |
| # RFP Endpoints | |
| def create_rfp(rfp: RFPCreate, db: Session = Depends(get_db)): | |
| """Create a new RFP""" | |
| db_rfp = RFP(**rfp.model_dump()) | |
| db.add(db_rfp) | |
| db.commit() | |
| db.refresh(db_rfp) | |
| return db_rfp | |
| def list_rfps(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): | |
| """List all RFPs""" | |
| rfps = db.query(RFP).offset(skip).limit(limit).all() | |
| return rfps | |
| def get_rfp(rfp_id: int, db: Session = Depends(get_db)): | |
| """Get a specific RFP""" | |
| rfp = db.query(RFP).filter(RFP.id == rfp_id).first() | |
| if not rfp: | |
| raise HTTPException(status_code=404, detail="RFP not found") | |
| return rfp | |
| def update_rfp(rfp_id: int, rfp_update: RFPUpdate, db: Session = Depends(get_db)): | |
| """Update an RFP""" | |
| db_rfp = db.query(RFP).filter(RFP.id == rfp_id).first() | |
| if not db_rfp: | |
| raise HTTPException(status_code=404, detail="RFP not found") | |
| for key, value in rfp_update.model_dump(exclude_unset=True).items(): | |
| setattr(db_rfp, key, value) | |
| db.commit() | |
| db.refresh(db_rfp) | |
| return db_rfp | |
| def delete_rfp(rfp_id: int, db: Session = Depends(get_db)): | |
| """Delete an RFP and all its questions""" | |
| db_rfp = db.query(RFP).filter(RFP.id == rfp_id).first() | |
| if not db_rfp: | |
| raise HTTPException(status_code=404, detail="RFP not found") | |
| db.delete(db_rfp) | |
| db.commit() | |
| return {"message": "RFP deleted successfully"} | |
| # Question Endpoints | |
| def create_question(question: QuestionCreate, db: Session = Depends(get_db)): | |
| """Create a new question and generate its embedding""" | |
| # Verify RFP exists | |
| rfp = db.query(RFP).filter(RFP.id == question.rfp_id).first() | |
| if not rfp: | |
| raise HTTPException(status_code=404, detail="RFP not found") | |
| # Create question | |
| db_question = Question(**question.model_dump()) | |
| db.add(db_question) | |
| db.commit() | |
| db.refresh(db_question) | |
| # Generate and save embedding immediately | |
| try: | |
| print(f"Generating embedding for question {db_question.id}...") | |
| similarity_service.save_question_embedding(db, db_question.id, db_question.question_text) | |
| print(f"Embedding generated successfully for question {db_question.id}") | |
| except Exception as e: | |
| print(f"ERROR: Failed to generate embedding for question {db_question.id}: {e}") | |
| # Don't fail the request, but log the error | |
| return db_question | |
| def list_questions( | |
| skip: int = 0, | |
| limit: int = 100, | |
| rfp_id: int = None, | |
| db: Session = Depends(get_db) | |
| ): | |
| """List all questions, optionally filtered by RFP""" | |
| query = db.query(Question) | |
| if rfp_id: | |
| query = query.filter(Question.rfp_id == rfp_id) | |
| questions = query.offset(skip).limit(limit).all() | |
| return questions | |
| def get_question(question_id: int, db: Session = Depends(get_db)): | |
| """Get a specific question""" | |
| question = db.query(Question).filter(Question.id == question_id).first() | |
| if not question: | |
| raise HTTPException(status_code=404, detail="Question not found") | |
| return question | |
| def update_question( | |
| question_id: int, | |
| question_update: QuestionUpdate, | |
| db: Session = Depends(get_db) | |
| ): | |
| """Update a question and regenerate its embedding if text changed""" | |
| db_question = db.query(Question).filter(Question.id == question_id).first() | |
| if not db_question: | |
| raise HTTPException(status_code=404, detail="Question not found") | |
| update_data = question_update.model_dump(exclude_unset=True) | |
| question_text_changed = "question_text" in update_data | |
| for key, value in update_data.items(): | |
| setattr(db_question, key, value) | |
| db.commit() | |
| db.refresh(db_question) | |
| # Regenerate embedding if question text changed | |
| if question_text_changed: | |
| similarity_service.save_question_embedding(db, db_question.id, db_question.question_text) | |
| return db_question | |
| def delete_question(question_id: int, db: Session = Depends(get_db)): | |
| """Delete a question""" | |
| question = db.query(Question).filter(Question.id == question_id).first() | |
| if not question: | |
| raise HTTPException(status_code=404, detail="Question not found") | |
| db.delete(question) | |
| db.commit() | |
| return {"message": "Question deleted successfully"} | |
| def generate_all_embeddings(db: Session = Depends(get_db)): | |
| """Generate embeddings for all questions that don't have them""" | |
| questions = db.query(Question).all() | |
| generated = 0 | |
| failed = 0 | |
| for question in questions: | |
| try: | |
| similarity_service.save_question_embedding(db, question.id, question.question_text) | |
| generated += 1 | |
| print(f"Generated embedding for question {question.id}") | |
| except Exception as e: | |
| failed += 1 | |
| print(f"Failed to generate embedding for question {question.id}: {e}") | |
| return { | |
| "message": "Embedding generation complete", | |
| "total_questions": len(questions), | |
| "generated": generated, | |
| "failed": failed | |
| } | |