rfprfprfp / backend /app /routers /rfp_processing.py
Varun10000's picture
Upload 51 files
c1d533e verified
Raw
History Blame Contribute Delete
7.23 kB
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from sqlalchemy.orm import Session
from typing import List
from app.database import get_db
from app.models import UploadedRFP, Question
from app.schemas import ProcessQuestionRequest, AIAnswerResponse, SimilarQuestion
from app.services.pdf_service import pdf_service
from app.services.similarity_service import similarity_service
from app.services.groq_service import groq_service
router = APIRouter(prefix="/api/rfp-processing", tags=["RFP Processing"])
@router.post("/upload")
async def upload_rfp(file: UploadFile = File(...), db: Session = Depends(get_db)):
"""Upload a new RFP PDF and extract text from first 3 pages"""
if not file.filename.endswith('.pdf'):
raise HTTPException(status_code=400, detail="Only PDF files are allowed")
uploaded_rfp = await pdf_service.save_and_extract_pdf(file, db, extract_pages=3)
return {
"id": uploaded_rfp.id,
"filename": uploaded_rfp.filename,
"uploaded_at": uploaded_rfp.uploaded_at,
"text_preview": uploaded_rfp.text_content[:500] + "..." if len(uploaded_rfp.text_content) > 500 else uploaded_rfp.text_content
}
@router.get("/uploaded-rfps")
def list_uploaded_rfps(skip: int = 0, limit: int = 50, db: Session = Depends(get_db)):
"""List all uploaded RFPs"""
rfps = db.query(UploadedRFP).offset(skip).limit(limit).all()
return [
{
"id": rfp.id,
"filename": rfp.filename,
"rfp_name": rfp.rfp_name if hasattr(rfp, 'rfp_name') and rfp.rfp_name else rfp.filename.rsplit('.', 1)[0],
"uploaded_at": rfp.uploaded_at
}
for rfp in rfps
]
@router.delete("/uploaded-rfps/{rfp_id}")
def delete_uploaded_rfp(rfp_id: int, db: Session = Depends(get_db)):
"""Delete an uploaded RFP"""
uploaded_rfp = db.query(UploadedRFP).filter(UploadedRFP.id == rfp_id).first()
if not uploaded_rfp:
raise HTTPException(status_code=404, detail="Uploaded RFP not found")
# Delete the file from disk if it exists
import os
if uploaded_rfp.file_path and os.path.exists(uploaded_rfp.file_path):
try:
os.remove(uploaded_rfp.file_path)
except Exception as e:
print(f"Error deleting file: {e}")
db.delete(uploaded_rfp)
db.commit()
return {"message": "Uploaded RFP deleted successfully"}
@router.post("/process-question", response_model=AIAnswerResponse)
async def process_question(request: ProcessQuestionRequest, db: Session = Depends(get_db)):
"""
Process a question against a new RFP:
1. Find similar questions from database
2. Get new RFP content (if provided)
3. Generate AI answer combining both
4. Provide gap analysis
"""
# Get uploaded RFP (optional)
uploaded_rfp = None
new_rfp_content = ""
if request.uploaded_rfp_id:
uploaded_rfp = db.query(UploadedRFP).filter(
UploadedRFP.id == request.uploaded_rfp_id
).first()
if not uploaded_rfp:
raise HTTPException(status_code=404, detail="Uploaded RFP not found")
new_rfp_content = uploaded_rfp.text_content
# Check for exact match first
exact_match = similarity_service.check_exact_match(db, request.question_text)
if exact_match:
# Return exact match answer
return AIAnswerResponse(
ai_answer=exact_match.answer_text,
gap_analysis="Exact match found in database. This is the stored answer from previous RFP." if not new_rfp_content else "Exact match found. No new information in current RFP.",
similar_questions=[
SimilarQuestion(
question_id=exact_match.id,
question_text=exact_match.question_text,
answer_text=exact_match.answer_text,
page_start=exact_match.page_start,
page_end=exact_match.page_end,
rfp_title=exact_match.rfp.title,
similarity_score=1.0
)
],
new_rfp_content=new_rfp_content[:1000] + "..." if new_rfp_content else "No new RFP uploaded"
)
# Find similar questions - get more matches for AI to consider
similar_questions = similarity_service.find_similar_questions(
db,
request.question_text,
top_k=10, # Get more answers for AI to reference
threshold=0.15 # Lower threshold to include more potential matches
)
# Format similar questions for response
similar_questions_list = [
SimilarQuestion(
question_id=q.id,
question_text=q.question_text,
answer_text=q.answer_text,
page_start=q.page_start,
page_end=q.page_end,
rfp_title=q.rfp.title,
similarity_score=score
)
for q, score in similar_questions
]
print(f"Found {len(similar_questions_list)} similar questions")
for sq in similar_questions_list:
print(f" - {sq.question_text} (score: {sq.similarity_score:.2f})")
# Prepare previous answers for AI
previous_answers = [
{
"question_text": q.question_text,
"answer_text": q.answer_text,
"page_start": q.page_start,
"page_end": q.page_end,
"rfp_title": q.rfp.title,
"similarity_score": score
}
for q, score in similar_questions
]
# Generate AI answer with HuggingFace
prompt = f"""Question: {request.question_text}
New RFP Content:
{new_rfp_content if new_rfp_content else "No new RFP content provided"}
Previous Similar Answers:
{chr(10).join([f"- {ans['answer_text']} (from {ans['rfp_title']}, score: {ans['similarity_score']:.2f})" for ans in previous_answers[:5]])}
{request.additional_context if request.additional_context else ""}
Provide a comprehensive answer and gap analysis."""
ai_answer = groq_service.generate_chat_response(prompt)
ai_response = {
'ai_answer': ai_answer,
'gap_analysis': "Generated using HuggingFace Mistral-7B model based on available context."
}
return AIAnswerResponse(
ai_answer=ai_response['ai_answer'],
gap_analysis=ai_response['gap_analysis'],
similar_questions=similar_questions_list,
new_rfp_content=new_rfp_content[:1000] + "..." if new_rfp_content else "No new RFP uploaded"
)
@router.get("/uploaded-rfps/{rfp_id}/content")
def get_rfp_content(rfp_id: int, db: Session = Depends(get_db)):
"""Get the full extracted content of an uploaded RFP"""
uploaded_rfp = db.query(UploadedRFP).filter(UploadedRFP.id == rfp_id).first()
if not uploaded_rfp:
raise HTTPException(status_code=404, detail="Uploaded RFP not found")
return {
"id": uploaded_rfp.id,
"filename": uploaded_rfp.filename,
"content": uploaded_rfp.text_content
}