Spaces:
Sleeping
Sleeping
File size: 9,173 Bytes
df0629c 7f2a827 4c9e5cc df0629c 4c9e5cc df0629c 4c9e5cc df0629c 4c9e5cc df0629c 4c9e5cc df0629c 7f2a827 4c9e5cc a4decd8 7f2a827 4c9e5cc a4decd8 4c9e5cc a4decd8 df0629c 4c9e5cc df0629c 7f2a827 a0768ff df0629c a0768ff 2b9969b a0768ff 7f2a827 a4decd8 7f2a827 df0629c 7f2a827 df0629c a0768ff 7f2a827 4c9e5cc 1f44056 7f2a827 4c9e5cc 7f2a827 4c9e5cc 7f2a827 4c9e5cc 7f2a827 df0629c 7f2a827 df0629c 7f2a827 |
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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
import os
import sys
import shutil
import datetime
import json
import gradio as gr
# Ensure `src` is in Python's module search path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "src")))
from markdown_pdf import MarkdownPdf, Section
from gradio_pdf import PDF
from resume_crew.crew import ResumeCrew
from crewai.knowledge.source.pdf_knowledge_source import PDFKnowledgeSource
# Set backend directories for Hugging Face Spaces
UPLOAD_DIR = "/tmp/uploads"
OUTPUT_DIR = "/tmp/output"
os.makedirs(UPLOAD_DIR, exist_ok=True)
os.makedirs(OUTPUT_DIR, exist_ok=True)
def convert_md_to_pdf(md_path: str) -> str:
"""
Convert a local .md file to .pdf using markdown-pdf.
Returns the resulting PDF file path, or an empty string if conversion fails.
"""
if not os.path.isfile(md_path):
return ""
with open(md_path, "r", encoding="utf-8") as f:
md_content = f.read()
pdf_obj = MarkdownPdf(toc_level=2)
pdf_obj.add_section(Section(md_content))
pdf_path = os.path.splitext(md_path)[0] + ".pdf"
pdf_obj.save(pdf_path)
return pdf_path if os.path.isfile(pdf_path) else ""
def process_resume(openai_api_key, serper_api_key, model_choice, new_resume, company_name, job_url):
"""
Processes the uploaded resume using ResumeCrew and converts the output Markdown files to PDFs.
Handles errors gracefully and stops execution upon failure.
"""
try:
current_date = datetime.datetime.now().strftime("%Y%m%d")
# --- Ensure a resume file is uploaded ---
if new_resume is None or not (hasattr(new_resume, "name") and new_resume.name.strip() != ""):
return ("Error: Please upload a resume.", None, None, None, None, None, None)
# --- Set API keys ---
os.environ["OPENAI_API_KEY"] = openai_api_key or ""
os.environ["SERPER_API_KEY"] = serper_api_key or ""
# --- Save uploaded file ---
try:
if hasattr(new_resume, "read"):
original_filename = os.path.basename(new_resume.name)
file_data = new_resume.read()
else:
original_filename = os.path.basename(new_resume)
file_data = None
base_filename, ext = os.path.splitext(original_filename)
new_resume_filename = f"{base_filename}_{current_date}{ext}"
physical_path = os.path.join("knowledge", new_resume_filename)
os.makedirs("knowledge", exist_ok=True)
if file_data is not None:
with open(physical_path, "wb") as f:
f.write(file_data)
else:
shutil.copy(new_resume, physical_path)
except Exception as e:
return (f"Error saving the uploaded resume: {str(e)}", None, None, None, None, None, None)
# --- Initialize ResumeCrew ---
try:
crew_instance = ResumeCrew(
model=model_choice,
openai_api_key=openai_api_key,
serper_api_key=serper_api_key,
resume_pdf_path=new_resume_filename
)
except Exception as e:
return (f"Error initializing ResumeCrew: {str(e)}", None, None, None, None, None, None)
# --- Run the resume processing ---
try:
crew_instance.crew().kickoff(inputs={'job_url': job_url, 'company_name': company_name})
except Exception as e:
return (f"Error during resume processing: {str(e)}", None, None, None, None, None, None)
# --- Retrieve output files ---
try:
job_analysis_path = os.path.join("output", "job_analysis.json")
with open(job_analysis_path, "r") as f:
job_data = json.load(f)
position_name = job_data.get("job_title", "position")
except Exception:
position_name = "position"
optimized_resume_path = os.path.join("output", "optimized_resume.md")
candidate_name = "candidate"
try:
with open(optimized_resume_path, "r") as f:
first_line = f.readline()
if first_line.startswith("#"):
candidate_name = first_line.lstrip("#").strip().replace(" ", "_")
except Exception:
candidate_name = "candidate"
# --- Create the output folder ---
try:
folder_name = f"{company_name}_{position_name}_{candidate_name}_{current_date}"
new_output_dir = os.path.join("output", folder_name)
os.makedirs(new_output_dir, exist_ok=True)
for filename in os.listdir("output"):
file_path = os.path.join("output", filename)
if file_path == new_output_dir:
continue
if filename.endswith(".json") or filename.endswith(".md"):
if os.path.isfile(file_path):
shutil.move(file_path, os.path.join(new_output_dir, filename))
except Exception as e:
return (f"Error organizing output files: {str(e)}", None, None, None, None, None, None)
# --- Convert Markdown to PDF ---
def md_to_pdf_in_dir(md_filename):
try:
md_path = os.path.join(new_output_dir, md_filename)
if os.path.isfile(md_path):
return convert_md_to_pdf(md_path)
return ""
except Exception as e:
return f"Error converting {md_filename} to PDF: {str(e)}"
pdf_opt = md_to_pdf_in_dir("optimized_resume.md")
pdf_final = md_to_pdf_in_dir("final_report.md")
pdf_int = md_to_pdf_in_dir("interview_questions.md")
message = f"Processing completed using model {model_choice}. Output saved in: {new_output_dir}"
return (message, pdf_opt, pdf_opt, pdf_final, pdf_final, pdf_int, pdf_int)
except Exception as e:
return (f"Unexpected error: {str(e)}", None, None, None, None, None, None)
# --- Define available models ---
model_choices = {
"GPT-4o-mini": "gpt-4o-mini-2024-07-18",
"GPT-4o": "gpt-4o-2024-08-06",
"o3-mini": "o3-mini-2025-01-31",
"o1-mini": "o1-mini-2024-09-12"
}
with gr.Blocks(css=".output-column { width: 700px; }") as demo:
with gr.Row():
# Left pane: Input fields
with gr.Column(scale=1):
gr.Markdown("## Resume Optimization System")
gr.Markdown(
"Create an optimized resume, job research report, and interview question sheet "
"by simply uploading your resume, entering the company name, and providing the job posting URL. "
"This tool leverages multi-agentic AI and web search to analyze job descriptions, research the company, and "
"tailor your resume for better ATS compatibility and job relevance."
)
openai_api_key_input = gr.Textbox(label="OpenAI API Key", type="password", placeholder="Enter OpenAI API Key")
serper_api_key_input = gr.Textbox(label="Serper API Key", type="password", placeholder="Enter Serper API Key")
model_dropdown = gr.Dropdown(
choices=list(model_choices.values()),
label="Select Model",
value="gpt-4o-2024-08-06",
interactive=True,
info="Select the model to use for processing."
)
new_resume_file = gr.File(label="Upload New Resume PDF", file_types=[".pdf"])
company_name_text = gr.Textbox(label="Company Name", placeholder="Enter company name")
job_url_text = gr.Textbox(label="Job URL", placeholder="Enter job posting URL")
run_button = gr.Button("Run")
# Right pane: Output display
with gr.Column(scale=2, elem_classes="output-column"): # Scale set to an integer to avoid warnings
gr.Markdown("## Processing Status")
status_output = gr.Textbox(label="Status")
with gr.Tabs():
with gr.Tab("Optimized Resume PDF"):
pdf_opt_download = gr.File(label="Download Optimized Resume")
pdf_opt_viewer = PDF(label="View Optimized Resume")
with gr.Tab("Final Report PDF"):
pdf_final_download = gr.File(label="Download Final Report")
pdf_final_viewer = PDF(label="View Final Report")
with gr.Tab("Interview Questions PDF"):
pdf_int_download = gr.File(label="Download Interview Questions")
pdf_int_viewer = PDF(label="View Interview Questions")
run_button.click(
process_resume,
inputs=[
openai_api_key_input,
serper_api_key_input,
model_dropdown,
new_resume_file,
company_name_text,
job_url_text
],
outputs=[
status_output,
pdf_opt_viewer, pdf_opt_download,
pdf_final_viewer, pdf_final_download,
pdf_int_viewer, pdf_int_download
]
)
if __name__ == "__main__":
demo.launch()
|