Spaces:
Sleeping
Sleeping
File size: 7,227 Bytes
c1d533e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | 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
}
|