Spaces:
Sleeping
Sleeping
| # File: src/evaluator.py | |
| # Purpose: Score candidate responses using Groq and produce a hiring report | |
| import json | |
| import re | |
| from typing import List, Dict, Any | |
| from groq import Groq | |
| from config import GROQ_API_KEY, GROQ_LLM_MODEL, SCORE_WEIGHTS | |
| _groq = Groq(api_key=GROQ_API_KEY) | |
| EVALUATION_PROMPT = """You are an expert technical recruiter evaluating a candidate's interview. | |
| Score the candidate on these three dimensions (0-10 scale): | |
| 1. communication_score: Clarity, structure, and articulation of responses. | |
| 2. technical_score: Depth, accuracy, and relevance of technical knowledge. | |
| 3. confidence_score: Certainty, specificity, and ownership shown in answers. | |
| Also extract: | |
| - skills: JSON array of technical skills demonstrated. | |
| - strengths: 2-3 bullet points. | |
| - weaknesses: 1-2 bullet points. | |
| - recommendation: One of ["Proceed to Technical Round", "Proceed with Caution", "Do Not Proceed"]. | |
| - summary: One paragraph summary for the recruiter. | |
| Respond ONLY with a valid JSON object. No markdown, no extra text. | |
| Schema: | |
| { | |
| "communication_score": <float>, | |
| "technical_score": <float>, | |
| "confidence_score": <float>, | |
| "skills": [<string>], | |
| "strengths": [<string>], | |
| "weaknesses": [<string>], | |
| "recommendation": <string>, | |
| "summary": <string> | |
| } | |
| """ | |
| def evaluate_candidate(candidate_name: str, transcript: List[Dict[str, str]]) -> Dict[str, Any]: | |
| transcript_text = "\n".join(f"{t['speaker']}: {t['text']}" for t in transcript) | |
| response = _groq.chat.completions.create( | |
| model=GROQ_LLM_MODEL, | |
| messages=[ | |
| {"role": "system", "content": EVALUATION_PROMPT}, | |
| {"role": "user", "content": f"Candidate: {candidate_name}\n\nTranscript:\n{transcript_text}"}, | |
| ], | |
| temperature=0, | |
| max_tokens=800, | |
| ) | |
| raw = response.choices[0].message.content.strip() | |
| raw = re.sub(r"```json|```", "", raw).strip() | |
| result = json.loads(raw) | |
| overall = ( | |
| result["communication_score"] * SCORE_WEIGHTS["communication"] | |
| + result["technical_score"] * SCORE_WEIGHTS["technical_skills"] | |
| + result["confidence_score"] * SCORE_WEIGHTS["confidence"] | |
| ) | |
| result["overall_score"] = round(overall, 2) | |
| return result | |
| def format_report(candidate_name: str, phone: str, evaluation: Dict[str, Any]) -> str: | |
| skills_str = "\n".join(f" - {s}" for s in evaluation.get("skills", [])) | |
| strengths_str = "\n".join(f" - {s}" for s in evaluation.get("strengths", [])) | |
| weaknesses_str = "\n".join(f" - {w}" for w in evaluation.get("weaknesses", [])) | |
| return f""" | |
| AI Interview Screening Report | |
| ============================== | |
| Candidate : {candidate_name} | |
| Phone : {phone} | |
| Scores | |
| ------ | |
| Communication : {evaluation['communication_score']:.1f} / 10 | |
| Technical : {evaluation['technical_score']:.1f} / 10 | |
| Confidence : {evaluation['confidence_score']:.1f} / 10 | |
| Overall : {evaluation['overall_score']:.1f} / 10 | |
| Skills Identified | |
| ----------------- | |
| {skills_str} | |
| Strengths | |
| --------- | |
| {strengths_str} | |
| Areas to Probe Further | |
| ----------------------- | |
| {weaknesses_str} | |
| Summary | |
| ------- | |
| {evaluation.get('summary', '')} | |
| Recommendation | |
| -------------- | |
| {evaluation.get('recommendation', 'N/A')} | |
| ============================== | |
| """.strip() |