Nam Fam
add files
6e24a3d
import json
import uvicorn
from typing import Optional
from fastapi import FastAPI, UploadFile, File, Form, HTTPException
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
# Absolute imports from the project structure
from core.stt_service import STTService
from core.processing import parse_transcript_into_qa
from core.gemini_service import score_interview, postprocess_transcript
from core.resume_service import get_text_from_file, score_resume_with_gemini, extract_info_from_resume
from examples.mock_data import mock_qa_pairs, mock_job_description, mock_rubric_content
from dotenv import load_dotenv
load_dotenv()
app = FastAPI(
title="Intelcruit API",
description="API for AI-powered interview and resume analysis.",
version="0.2.0"
)
# --- CORS Middleware ---
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allow all origins for development
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
@app.get("/")
def read_root():
return {"message": "Welcome to the Intelcruit AI Scoring Engine"}
@app.post("/analyze_mock/")
async def analyze_mock_interview():
"""
Analyzes an interview using mock data. This is useful for testing the scoring
and processing logic without needing a live audio file.
"""
try:
print("--- USING MOCK DATA FOR ANALYSIS ---")
qa_pairs = mock_qa_pairs
jd_to_use = mock_job_description
rubric_to_use = mock_rubric_content
if not qa_pairs:
raise HTTPException(status_code=400, detail="Mock Q&A pairs are empty.")
scoring_results = score_interview(qa_pairs, jd_to_use, rubric_to_use)
return {
"message": "Mock analysis complete.",
"filename": "mock_audio.mp3",
"results": scoring_results
}
except HTTPException as e:
# Re-raise HTTPException to let FastAPI handle it
raise e
except Exception as e:
print(f"An unexpected error occurred during mock analysis: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/analyze/")
async def analyze_interview(
job_description: str = Form(...),
rubric_content: str = Form(...),
audio_file: Optional[UploadFile] = File(None),
transcript_content: Optional[str] = Form(None)
):
"""
Analyzes a live interview from an audio file or a provided transcript.
It transcribes the audio (if provided), parses it into Q&A pairs,
and then scores it against the provided job description and rubric.
"""
try:
jd_to_use = job_description
rubric_to_use = rubric_content
if audio_file:
print("--- PROCESSING LIVE AUDIO FILE ---")
audio_bytes = await audio_file.read()
mimetype = audio_file.content_type
stt_service = STTService()
transcript, deepgram_response = await stt_service.transcribe_audio(audio_bytes, mimetype)
cleaned_transcript = transcript # Using raw transcript for now
elif transcript_content:
print("--- PROCESSING PROVIDED TRANSCRIPT ---")
cleaned_transcript = transcript_content
else:
raise HTTPException(status_code=400, detail="Either audio_file or transcript_content must be provided.")
if not cleaned_transcript:
raise HTTPException(status_code=500, detail="Transcript is empty after processing.")
qa_pairs = parse_transcript_into_qa(cleaned_transcript)
if not qa_pairs:
raise HTTPException(status_code=400, detail="Could not parse any Q&A pairs from the transcript.")
scoring_results = score_interview(qa_pairs, jd_to_use, rubric_to_use)
return {
"message": "Analysis complete.",
"filename": audio_file.filename if audio_file else "transcript_input", # Adjust filename based on input
"results": scoring_results
}
except HTTPException as e:
raise e
except Exception as e:
print(f"Unhandled exception in /analyze/: {e}")
return JSONResponse(status_code=500, content={"error": f"An unexpected error occurred: {str(e)}"})
@app.post("/extract_from_resume/")
async def extract_resume_endpoint(resume_file: UploadFile = File(...)):
try:
print(f"Received resume for extraction: {resume_file.filename}")
file_bytes = await resume_file.read()
resume_text = get_text_from_file(file_bytes, resume_file.content_type)
if resume_text is None:
return JSONResponse(status_code=400, content={"error": "Could not extract text from the resume. The file might be corrupted or in an unsupported format."})
extracted_data = extract_info_from_resume(resume_text)
if "error" in extracted_data:
return JSONResponse(status_code=500, content=extracted_data)
return JSONResponse(content=extracted_data)
except Exception as e:
print(f"Error in /extract_from_resume/ endpoint: {e}")
return JSONResponse(status_code=500, content={"error": f"An unexpected error occurred: {str(e)}"})
@app.post("/analyze_resume/")
async def analyze_resume(
resume_file: UploadFile = File(...),
job_description: str = Form(...)
):
try:
if not resume_file or not job_description:
raise HTTPException(status_code=400, detail="Resume file and job description are required.")
resume_bytes = await resume_file.read()
mimetype = resume_file.content_type
resume_text = get_text_from_file(resume_bytes, mimetype)
if resume_text is None or not resume_text.strip():
raise HTTPException(status_code=400, detail=f"Could not extract text from the uploaded file: {resume_file.filename}. The file might be empty, corrupted, or an unsupported format.")
scoring_results = score_resume_with_gemini(resume_text, job_description)
if "error" in scoring_results:
raise HTTPException(status_code=500, detail=scoring_results["error"])
return {
"message": "Resume analysis complete.",
"filename": resume_file.filename,
"results": scoring_results
}
except HTTPException as e:
raise e
except Exception as e:
print(f"An unexpected error occurred during resume analysis: {e}")
raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)