Spaces:
Running
Running
import gradio as gr | |
import json | |
from datetime import datetime | |
import os | |
import tempfile | |
# Import the resume agent (assuming the previous code is saved as resume_agent.py) | |
from resume_agent import ResumeAgent, get_sample_resume, get_sample_job_description | |
class GradioResumeApp: | |
"""Gradio interface for the Resume Optimization Agent""" | |
def __init__(self): | |
self.agent = ResumeAgent() | |
self.sample_resume = get_sample_resume() | |
self.sample_job_desc = get_sample_job_description() | |
def process_resume(self, resume_file, resume_text, job_file, job_text, api_key): | |
"""Process resume optimization request""" | |
# Validate API key | |
if not api_key or api_key.strip() == "": | |
return self._create_error_output("β Please provide a valid Gemini API key") | |
# Set API key | |
import google.generativeai as genai | |
try: | |
genai.configure(api_key=api_key.strip()) | |
except Exception as e: | |
return self._create_error_output(f"β Invalid API key: {str(e)}") | |
# Get resume content | |
resume_content = self._get_content(resume_file, resume_text, self.sample_resume, "resume") | |
if not resume_content: | |
return self._create_error_output("β No resume content provided") | |
# Get job description content | |
job_content = self._get_content(job_file, job_text, self.sample_job_desc, "job description") | |
try: | |
# Process optimization | |
results = self.agent.optimize_resume(resume_content, job_content) | |
# Format results for display | |
return self._format_results(results) | |
except Exception as e: | |
return self._create_error_output(f"β Error during optimization: {str(e)}") | |
def _get_content(self, file, text, sample, content_type): | |
"""Extract content from file or text input""" | |
if file is not None: | |
try: | |
content = file.decode('utf-8') if isinstance(file, bytes) else file.read() | |
return content | |
except Exception as e: | |
print(f"Error reading {content_type} file: {str(e)}") | |
if text and text.strip(): | |
return text.strip() | |
return sample | |
def _create_error_output(self, error_message): | |
"""Create error output tuple""" | |
return ( | |
error_message, # summary | |
"", # experience_analysis | |
"", # keyword_analysis | |
"", # design_suggestions | |
"", # editing_suggestions | |
"" # full_results | |
) | |
def _format_results(self, results): | |
"""Format optimization results for Gradio display""" | |
# New Summary | |
new_summary = results.get("new_summary", "No summary generated") | |
# Experience Analysis | |
experience_analysis = "" | |
if "experience_matching" in results: | |
exp_data = results["experience_matching"] | |
if isinstance(exp_data, dict) and "ranked_experiences" in exp_data: | |
experience_analysis = "π **Experience Relevance Analysis:**\n\n" | |
for i, exp in enumerate(exp_data["ranked_experiences"][:3], 1): | |
if isinstance(exp, dict): | |
score = exp.get("relevance_score", "N/A") | |
points = exp.get("matching_points", []) | |
experience_analysis += f"**Experience {i}:** Score {score}/10\n" | |
experience_analysis += f"Key matches: {', '.join(points[:3])}\n\n" | |
else: | |
experience_analysis = "Experience analysis completed" | |
# Keyword Analysis | |
keyword_analysis = "" | |
if "keyword_optimization" in results: | |
kw_data = results["keyword_optimization"] | |
if isinstance(kw_data, dict): | |
ats_score = kw_data.get("ats_score", "N/A") | |
missing_kw = kw_data.get("missing_keywords", []) | |
keyword_analysis = f"π― **ATS Optimization Score:** {ats_score}/100\n\n" | |
if missing_kw: | |
keyword_analysis += f"**Missing Keywords:** {', '.join(missing_kw[:10])}\n\n" | |
recommendations = kw_data.get("recommendations", []) | |
if recommendations: | |
keyword_analysis += "**Recommendations:**\n" | |
for rec in recommendations[:3]: | |
keyword_analysis += f"β’ {rec}\n" | |
else: | |
keyword_analysis = "Keyword optimization completed" | |
# Design Suggestions | |
design_suggestions = "" | |
if "design_suggestions" in results: | |
design_data = results["design_suggestions"] | |
if isinstance(design_data, dict): | |
template = design_data.get("recommended_template", "Standard") | |
layout_tips = design_data.get("layout_suggestions", []) | |
design_suggestions = f"π¨ **Recommended Template:** {template}\n\n" | |
if layout_tips: | |
design_suggestions += "**Layout Suggestions:**\n" | |
for tip in layout_tips[:5]: | |
design_suggestions += f"β’ {tip}\n" | |
else: | |
design_suggestions = "Design suggestions generated" | |
# Editing Suggestions | |
editing_suggestions = "" | |
if "editing_suggestions" in results: | |
edit_data = results["editing_suggestions"] | |
if isinstance(edit_data, dict): | |
score = edit_data.get("overall_score", "N/A") | |
feedback = edit_data.get("summary_feedback", "") | |
editing_suggestions = f"βοΈ **Overall Quality Score:** {score}/100\n\n" | |
if feedback: | |
editing_suggestions += f"**Feedback:** {feedback}\n\n" | |
grammar_errors = edit_data.get("grammar_errors", []) | |
if grammar_errors: | |
editing_suggestions += "**Grammar Improvements:**\n" | |
for error in grammar_errors[:3]: | |
if isinstance(error, dict): | |
original = error.get("original", "") | |
corrected = error.get("corrected", "") | |
editing_suggestions += f"β’ '{original}' β '{corrected}'\n" | |
else: | |
editing_suggestions = "Editing analysis completed" | |
# Full Results (JSON) | |
full_results = json.dumps(results, indent=2, default=str) | |
return ( | |
new_summary, | |
experience_analysis, | |
keyword_analysis, | |
design_suggestions, | |
editing_suggestions, | |
full_results | |
) | |
def create_interface(self): | |
"""Create and return Gradio interface""" | |
with gr.Blocks( | |
title="AI Resume Optimizer", | |
theme=gr.themes.Soft(), | |
css=""" | |
.gradio-container { | |
max-width: 1200px !important; | |
} | |
.main-header { | |
text-align: center; | |
margin-bottom: 30px; | |
} | |
""" | |
) as interface: | |
gr.HTML(""" | |
<div class="main-header"> | |
<h1>π AI Resume Optimization Agent</h1> | |
<p>Upload your resume and job description to get AI-powered optimization suggestions</p> | |
</div> | |
""") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.HTML("<h2>π Input</h2>") | |
# API Key input | |
api_key = gr.Textbox( | |
label="π Gemini API Key", | |
placeholder="Enter your Gemini API key here...", | |
type="password", | |
info="Get your free API key from Google AI Studio" | |
) | |
# Resume input | |
with gr.Tab("Resume Upload"): | |
resume_file = gr.File( | |
label="Upload Resume (PDF/TXT/DOCX)", | |
file_types=[".pdf", ".txt", ".docx"] | |
) | |
with gr.Tab("Resume Text"): | |
resume_text = gr.Textbox( | |
label="Paste Resume Text", | |
placeholder="Paste your resume content here...", | |
lines=8, | |
value=self.sample_resume | |
) | |
# Job description input | |
with gr.Tab("Job Description Upload"): | |
job_file = gr.File( | |
label="Upload Job Description", | |
file_types=[".pdf", ".txt", ".docx"] | |
) | |
with gr.Tab("Job Description Text"): | |
job_text = gr.Textbox( | |
label="Paste Job Description", | |
placeholder="Paste job description here...", | |
lines=6, | |
value=self.sample_job_desc | |
) | |
# Optimize button | |
optimize_btn = gr.Button( | |
"π Optimize Resume", | |
variant="primary", | |
size="lg" | |
) | |
with gr.Column(scale=1): | |
gr.HTML("<h2>π Results</h2>") | |
with gr.Tab("β¨ New Summary"): | |
summary_output = gr.Textbox( | |
label="Optimized Professional Summary", | |
lines=4, | |
interactive=False | |
) | |
with gr.Tab("π Experience Analysis"): | |
experience_output = gr.Markdown( | |
label="Experience Relevance Analysis" | |
) | |
with gr.Tab("π― Keywords & ATS"): | |
keyword_output = gr.Markdown( | |
label="Keyword Optimization & ATS Score" | |
) | |
with gr.Tab("π¨ Design Tips"): | |
design_output = gr.Markdown( | |
label="Design & Formatting Suggestions" | |
) | |
with gr.Tab("βοΈ Editing Tips"): | |
editing_output = gr.Markdown( | |
label="Grammar & Content Improvements" | |
) | |
with gr.Tab("π Full Report"): | |
full_output = gr.Code( | |
label="Complete Analysis (JSON)", | |
language="json" | |
) | |
# Event handlers | |
optimize_btn.click( | |
fn=self.process_resume, | |
inputs=[resume_file, resume_text, job_file, job_text, api_key], | |
outputs=[ | |
summary_output, | |
experience_output, | |
keyword_output, | |
design_output, | |
editing_output, | |
full_output | |
] | |
) | |
# Example section | |
with gr.Row(): | |
gr.HTML(""" | |
<div style="margin-top: 30px; padding: 20px; background-color: #f8f9fa; border-radius: 10px;"> | |
<h3>π‘ Quick Start Guide:</h3> | |
<ol> | |
<li>Get your free Gemini API key from <a href="https://makersuite.google.com/app/apikey" target="_blank">Google AI Studio</a></li> | |
<li>Upload your resume or use the sample provided</li> | |
<li>Add a job description you're targeting (optional)</li> | |
<li>Click "Optimize Resume" to get AI-powered suggestions</li> | |
</ol> | |
<p><strong>Features:</strong> Professional Summary Generation β’ Experience Matching β’ ATS Optimization β’ Design Suggestions β’ Grammar & Style Improvements</p> | |
</div> | |
""") | |
return interface | |
def main(): | |
"""Launch the Gradio app""" | |
app = GradioResumeApp() | |
interface = app.create_interface() | |
# Launch the app | |
interface.launch( | |
server_name="0.0.0.0", # Allow external access | |
server_port=7860, | |
share=True, # Create public link | |
debug=True | |
) | |
if __name__ == "__main__": | |
main() | |