File size: 4,198 Bytes
97cdcdc 51c72d3 97cdcdc 51c72d3 97cdcdc 505be46 97cdcdc 51c72d3 9281337 97cdcdc 29fc8f7 97cdcdc 1f85315 29fc8f7 1f85315 29fc8f7 1f85315 505be46 1f85315 29fc8f7 d91db4a 29fc8f7 d91db4a 29fc8f7 1f85315 |
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 |
import os
import json
import re
import logging
import fitz # PyMuPDF
import google.generativeai as genai
from dotenv import load_dotenv
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
load_dotenv()
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
raise ValueError("GEMINI_API_KEY is missing.")
genai.configure(api_key=api_key)
def extract_text_from_stream(file_bytes: bytes) -> str:
text = ""
try:
with fitz.open(stream=file_bytes, filetype="pdf") as doc:
for page in doc:
text += page.get_text()
except Exception as e:
logger.error(f"PDF Extraction Error: {e}")
raise ValueError("Failed to extract text from PDF.")
return text
def get_available_model_name():
"""
Dynamically finds a working model from the user's account.
"""
try:
available_models = []
for m in genai.list_models():
if 'generateContent' in m.supported_generation_methods:
available_models.append(m.name)
if not available_models:
logger.error("No models found.")
return None
# Priority list: Try to find these specific powerful models first
preferred_order = [
"models/gemini-1.5-flash",
"models/gemini-1.5-pro",
"models/gemini-pro",
"models/gemini-1.0-pro"
]
# 1. Check if any preferred model is in the available list
for preferred in preferred_order:
if preferred in available_models:
logger.info(f"Selected Preferred Model: {preferred}")
return preferred
# 2. If none of the preferred ones exist, take the first available one
fallback = available_models[0]
logger.warning(f"Preferred models missing. Falling back to: {fallback}")
return fallback
except Exception as e:
logger.error(f"Error listing models: {e}")
return None
def analyze_resume(resume_text: str, job_description: str = None) -> dict:
# 1. FIND A WORKING MODEL (The Critical Fix)
model_name = get_available_model_name()
if not model_name:
return {"error": "CRITICAL: No available AI models found for this API Key."}
# 2. CONSTRUCT PROMPT
if job_description:
prompt = f"""
Act as a strict AI Recruiter. Compare the Resume against the Job Description.
RETURN JSON ONLY with this exact structure:
{{
"candidate": {{
"name": "string",
"email": "string",
"phone": "string",
"skills": ["list", "of", "candidate", "skills"],
"experience_years": "string or null"
}},
"match_analysis": {{
"score": integer_0_to_100,
"reasoning": "brief summary of why this score was given",
"matching_skills": ["skills in both resume and JD"],
"missing_skills": ["skills in JD but NOT in resume"],
"verdict": "Interview" | "Shortlist" | "Reject"
}}
}}
JOB DESCRIPTION:
{job_description[:5000]}
RESUME TEXT:
{resume_text[:10000]}
"""
else:
prompt = f"""
Extract structured data from the resume. Return JSON:
{{
"candidate": {{
"name": "string",
"email": "string",
"phone": "string",
"skills": ["list", "of", "skills"],
"summary": "string"
}}
}}
RESUME TEXT:
{resume_text[:10000]}
"""
# 3. GENERATE CONTENT
try:
model = genai.GenerativeModel(model_name)
response = model.generate_content(prompt)
raw = response.text.strip()
clean_json = re.sub(r'```json\s*|```', '', raw, flags=re.MULTILINE).strip()
return json.loads(clean_json)
except Exception as e:
logger.error(f"Analysis failed with model {model_name}: {e}")
return {"error": f"Analysis failed using {model_name}. Detail: {str(e)}"} |