abersbail's picture
Upload folder using huggingface_hub
6195bdc verified
Raw
History Blame Contribute Delete
9.33 kB
import os
import json
import pandas as pd
import gradio as gr
from langgraph_agent import LangGraphResumeAnalyzer
import utils
# Instantiate LangGraph Agent
analyzer = LangGraphResumeAnalyzer()
# FIXED CSS preventing vibrating / trembling layout bug by locking vertical scrollbar gutter permanently
CUSTOM_CSS = """
html {
overflow-y: scroll !important;
scroll-behavior: smooth !important;
}
body {
background-color: #090d16 !important;
font-family: 'Inter', system-ui, -apple-system, sans-serif !important;
color: #e2e8f0 !important;
margin: 0 !important;
padding: 0 !important;
min-height: 100vh !important;
}
.gradio-container {
background-color: #090d16 !important;
max-width: 1400px !important;
margin: 0 auto !important;
padding: 20px !important;
width: 100% !important;
box-sizing: border-box !important;
}
.card-panel {
background: linear-gradient(145deg, #131b2e, #0f172a);
border: 1px solid rgba(56, 189, 248, 0.2);
border-radius: 16px;
padding: 20px;
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
height: auto !important;
contain: content;
}
.btn-primary-audit {
background: linear-gradient(135deg, #0284c7, #4f46e5) !important;
color: white !important;
font-weight: 800 !important;
border-radius: 12px !important;
font-size: 1.1rem !important;
box-shadow: 0 4px 14px rgba(2, 132, 199, 0.4) !important;
}
"""
def handle_run_audit(file_obj, text_resume, job_desc):
"""
Executes LangGraph pipeline for PDF documents or images via NVIDIA Nemotron OCR & PyPDF.
"""
res = analyzer.run_langgraph_pipeline(
file_input=file_obj,
text_input=text_resume,
job_description=job_desc
)
analysis = res["analysis"]
ocr_info = res["ocr_result"]
overall_score = analysis.get("overall_ats_score_pct", 85)
ats_gauge_html = utils.generate_ats_score_html(overall_score)
subscores_html = utils.generate_subscores_html(
keyword_pct=analysis.get("keyword_match_pct", 82),
skills_pct=analysis.get("skills_match_pct", 88),
experience_pct=analysis.get("experience_fit_pct", 85),
format_pct=analysis.get("format_quality_pct", 90)
)
skill_badges_html = utils.format_skill_badges(
analysis.get("matched_skills", []),
analysis.get("missing_skills", [])
)
contact = analysis.get("contact_info", {})
summary_md = f"""
### πŸ‘€ Candidate Profile & Key Info
- **Full Name**: `{analysis.get('candidate_name', 'Alex Chen')}`
- **Estimated Experience**: `{analysis.get('estimated_years_experience', '6+ Years')}`
- **Email**: `{contact.get('email', 'N/A')}` | **Location**: `{contact.get('location', 'N/A')}`
#### πŸ“ Executive Recruiter Assessment
{analysis.get('executive_summary', 'No summary available.')}
#### 🎯 Key Candidate Strengths
"""
for strg in analysis.get("key_strengths", []):
summary_md += f"- **{strg}**\n"
summary_md += "\n#### πŸ’‘ Actionable Recommendations to Boost ATS Score to 98%+\n"
for tip in analysis.get("improvement_tips", []):
summary_md += f"- {tip}\n"
# AI Tailored Resume Rewriter Bullets
bullets_md = "### ✍️ AI Tailored Resume Bullet Point Rewriter\n*Copy & paste these optimized bullets into your resume to maximize ATS callback rates:*\n\n"
for idx, bullet in enumerate(analysis.get("optimized_resume_bullets", [])):
bullets_md += f"**{idx+1}.** `{bullet}`\n\n"
# OCR Info
ocr_meta_md = f"""
### πŸ‘οΈ NVIDIA Nemotron OCR & PDF Detection Engine
- **Engine Used**: `{ocr_info.get('model_used', 'NVIDIA Nemotron OCR v2')}`
- **Total Lines Extracted**: `{ocr_info.get('line_count', 0)}`
- **Extraction Status**: `{ocr_info.get('status', 'SUCCESS')}`
"""
detections_list = ocr_info.get("detections", [])
df_det = pd.DataFrame(detections_list) if detections_list else pd.DataFrame(columns=["text", "confidence"])
full_report_json = json.dumps({
"candidate": analysis.get('candidate_name'),
"overall_ats_score_pct": overall_score,
"subscores": {
"keywords": analysis.get("keyword_match_pct"),
"skills": analysis.get("skills_match_pct"),
"experience": analysis.get("experience_fit_pct"),
"formatting": analysis.get("format_quality_pct")
},
"ocr_engine": ocr_info.get('model_used'),
"analysis": analysis
}, indent=2)
return (
ats_gauge_html,
subscores_html,
skill_badges_html,
summary_md,
bullets_md,
res["timeline"],
ocr_meta_md,
df_det,
res["resume_text"],
full_report_json
)
def handle_rag_chat(user_question: str):
return analyzer.answer_rag_question(user_question)
with gr.Blocks(title="AI Resume Analyzer (RAG + LangGraph)") as demo:
gr.Markdown(
"""
# πŸ“„ Advanced AI Resume Analyzer (RAG + LangGraph)
### Multimodal PDF & Image OCR via NVIDIA Nemotron OCR v2/v1 & Groq LLM ATS Auditor
**Engineer / Creator:** `abersabil` (`@abersbail`) | **User ID:** `69b2ede7cec72416131a3260`
"""
)
with gr.Tabs():
# TAB 1: LangGraph ATS Audit & Matching
with gr.TabItem("πŸ“Š LangGraph ATS Audit & Matching"):
with gr.Row():
with gr.Column(scale=1, elem_classes=["card-panel"]):
gr.Markdown("### πŸ“₯ Upload PDF / Image Resume & Job Description")
resume_file_input = gr.File(
label="Upload Resume File (.pdf, .png, .jpg, .jpeg, .webp)",
file_types=[".pdf", ".png", ".jpg", ".jpeg", ".webp"]
)
resume_text_area = gr.Textbox(
value=utils.DEFAULT_SAMPLE_RESUME,
label="OR Paste Resume Text directly",
lines=7
)
job_desc_area = gr.Textbox(
value=utils.DEFAULT_JOB_DESCRIPTION,
label="Target Job Description (JD)",
lines=5
)
btn_audit = gr.Button("πŸš€ Run LangGraph RAG Audit", elem_classes=["btn-primary-audit"])
langgraph_timeline = gr.Textbox(label="LangGraph State Machine Stream", lines=6, interactive=False)
with gr.Column(scale=1):
ats_score_gauge = gr.HTML(utils.generate_ats_score_html(88))
ats_subscores_box = gr.HTML(utils.generate_subscores_html(85, 90, 85, 95))
skill_badges_box = gr.HTML("Click 'Run LangGraph RAG Audit' to analyze candidate skills.")
executive_summary_box = gr.Markdown("Candidate evaluation summary will appear here.")
tailored_bullets_box = gr.Markdown("Optimized resume bullets will appear here.")
# TAB 2: NVIDIA Nemotron OCR Scanner View
with gr.TabItem("πŸ‘οΈ NVIDIA Nemotron OCR Inspector"):
with gr.Row():
with gr.Column(scale=1):
ocr_metadata_box = gr.Markdown("Run audit to view NVIDIA Nemotron OCR v2/v1 detection metrics.")
ocr_detections_df = gr.Dataframe(label="Detected Lines & Confidence Scores")
with gr.Column(scale=1):
gr.Markdown("### πŸ“„ Extracted Resume Raw Text")
ocr_raw_text_box = gr.Textbox(lines=18, interactive=False)
# TAB 3: LangGraph RAG Candidate Chatbot
with gr.TabItem("πŸ’¬ Candidate RAG Chatbot"):
gr.Markdown("### πŸ€– Ask RAG Questions About Candidate Qualifications")
with gr.Row():
with gr.Column(scale=1):
rag_question_input = gr.Textbox(
label="Type Question about Candidate",
placeholder="e.g., What PyTorch & RAG experience does the candidate have?",
lines=2
)
btn_ask_rag = gr.Button("πŸ” Query RAG Vector Index", variant="primary")
with gr.Column(scale=1):
rag_answer_output = gr.Textbox(label="Grounded RAG Answer", lines=8, interactive=False)
# TAB 4: Full Audit Report Export
with gr.TabItem("πŸ“ Candidate Report Export"):
gr.Markdown("### πŸ“‘ Downloadable Candidate Evaluation JSON Report")
full_report_code = gr.Code(language="json", label="JSON Candidate Audit Report")
# Event Bindings
btn_audit.click(
fn=handle_run_audit,
inputs=[resume_file_input, resume_text_area, job_desc_area],
outputs=[
ats_score_gauge, ats_subscores_box, skill_badges_box,
executive_summary_box, tailored_bullets_box, langgraph_timeline,
ocr_metadata_box, ocr_detections_df, ocr_raw_text_box, full_report_code
]
)
btn_ask_rag.click(
fn=handle_rag_chat,
inputs=[rag_question_input],
outputs=[rag_answer_output]
)
if __name__ == "__main__":
demo.queue()
demo.launch(server_name="0.0.0.0", server_port=7860, css=CUSTOM_CSS)