|
|
import os |
|
|
import gradio as gr |
|
|
from pdfminer.high_level import extract_text |
|
|
from docx import Document |
|
|
from groq import Groq |
|
|
|
|
|
key = "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" |
|
|
client = Groq(api_key=key) |
|
|
|
|
|
|
|
|
def read_pdf(file_path): |
|
|
return extract_text(file_path) |
|
|
|
|
|
|
|
|
def read_docx(file_path): |
|
|
doc = Document(file_path) |
|
|
return "\n".join([paragraph.text for paragraph in doc.paragraphs]) |
|
|
|
|
|
|
|
|
def optimize_cv_with_llama(cv_text, job_description): |
|
|
try: |
|
|
|
|
|
system_message = ( |
|
|
"You are an experienced Human Resource expert with 30 years of experience working for major international companies. " |
|
|
"Your task is to optimize the following CV or Resume based on the provided job description. Tailor the CV content to align " |
|
|
"with the job requirements and highlight relevant skills, experiences, and achievements. Present the optimized CV in Markdown format. " |
|
|
"Ensure the formatting is professional and avoid adding extra comments." |
|
|
) |
|
|
|
|
|
|
|
|
completion = client.chat.completions.create( |
|
|
model="meta-llama/llama-4-maverick-17b-128e-instruct", |
|
|
messages=[ |
|
|
{"role": "system", "content": system_message}, |
|
|
{"role": "user", "content": f"Job Description:\n{job_description}\n\nCV:\n{cv_text}"} |
|
|
], |
|
|
temperature=0.7, |
|
|
max_tokens=2048, |
|
|
top_p=0.9, |
|
|
stream=False, |
|
|
) |
|
|
|
|
|
|
|
|
return ''.join([chunk.message.content for chunk in completion.choices]) |
|
|
except Exception as e: |
|
|
return f"Error optimizing CV: {e}" |
|
|
|
|
|
|
|
|
from markdown2 import markdown |
|
|
from bs4 import BeautifulSoup |
|
|
|
|
|
def save_markdown_to_docx(markdown_text, output_docx_path="optimized_cv.docx"): |
|
|
|
|
|
html_content = markdown(markdown_text) |
|
|
|
|
|
|
|
|
soup = BeautifulSoup(html_content, "html.parser") |
|
|
|
|
|
|
|
|
doc = Document() |
|
|
|
|
|
|
|
|
for element in soup.descendants: |
|
|
if element.name == "h1": |
|
|
para = doc.add_paragraph(element.text, style="Heading 1") |
|
|
elif element.name == "h2": |
|
|
para = doc.add_paragraph(element.text, style="Heading 2") |
|
|
elif element.name == "h3": |
|
|
para = doc.add_paragraph(element.text, style="Heading 3") |
|
|
elif element.name == "ul": |
|
|
for li in element.find_all("li"): |
|
|
doc.add_paragraph(li.text, style="List Bullet") |
|
|
elif element.name == "ol": |
|
|
for li in element.find_all("li"): |
|
|
doc.add_paragraph(li.text, style="List Number") |
|
|
elif element.name == "p": |
|
|
doc.add_paragraph(element.text) |
|
|
elif element.name == "strong": |
|
|
para = doc.add_paragraph() |
|
|
run = para.add_run(element.text) |
|
|
run.bold = True |
|
|
elif element.name == "em": |
|
|
para = doc.add_paragraph() |
|
|
run = para.add_run(element.text) |
|
|
run.italic = True |
|
|
elif element.name == "a": |
|
|
para = doc.add_paragraph(element.text) |
|
|
|
|
|
para.add_run(f" ({element.get('href')})").italic = True |
|
|
|
|
|
|
|
|
doc.save(output_docx_path) |
|
|
return output_docx_path |
|
|
|
|
|
|
|
|
def process_inputs(cv_file, job_description): |
|
|
try: |
|
|
|
|
|
file_path = cv_file.name |
|
|
file_extension = os.path.splitext(file_path)[1].lower() |
|
|
if file_extension == ".pdf": |
|
|
cv_text = read_pdf(file_path) |
|
|
elif file_extension == ".docx": |
|
|
cv_text = read_docx(file_path) |
|
|
else: |
|
|
return "Unsupported file format", None |
|
|
|
|
|
|
|
|
optimized_markdown = optimize_cv_with_llama(cv_text, job_description) |
|
|
|
|
|
|
|
|
output_docx_path = "optimized_cv.docx" |
|
|
save_markdown_to_docx(optimized_markdown, output_docx_path) |
|
|
|
|
|
return optimized_markdown, output_docx_path |
|
|
except Exception as e: |
|
|
return f"Error processing inputs: {e}", None |
|
|
|
|
|
|
|
|
def main(): |
|
|
with gr.Blocks() as app: |
|
|
|
|
|
gr.Markdown( |
|
|
""" |
|
|
# **AI-Powered CV or Resume Optimizer** |
|
|
Optimize your CV or Resume to match job descriptions using the power of AI. |
|
|
This application processes your uploaded CV along with a job description and generates a tailored, professional CV ready for submission. |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
file_input = gr.File(label="Upload Your Resume (PDF or DOCX)", file_count="single", |
|
|
file_types=[".pdf", ".docx"], |
|
|
type="filepath" |
|
|
) |
|
|
|
|
|
|
|
|
job_description = gr.Textbox( |
|
|
label="Job Description", |
|
|
placeholder="Paste the job description here...", |
|
|
lines=5 |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
markdown_output = gr.Markdown(label="Optimized CV Preview") |
|
|
download_button = gr.File(label="Download Optimized CV (DOCX)") |
|
|
|
|
|
|
|
|
run_button = gr.Button("Optimize CV") |
|
|
|
|
|
|
|
|
def handle_inputs(cv_file, job_description): |
|
|
markdown_text, docx_path = process_inputs(cv_file, job_description) |
|
|
return markdown_text, docx_path |
|
|
|
|
|
run_button.click( |
|
|
handle_inputs, |
|
|
inputs=[file_input, job_description], |
|
|
outputs=[markdown_output, download_button] |
|
|
) |
|
|
|
|
|
app.launch() |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |