File size: 6,586 Bytes
f8387c1
 
95242f6
f8387c1
e98268a
f8387c1
 
28634dd
f8387c1
c9135a2
28634dd
 
c9135a2
f8387c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888d73b
ef396f3
 
 
95242f6
888d73b
 
 
95242f6
28634dd
888d73b
f8387c1
888d73b
 
28634dd
888d73b
 
ef396f3
888d73b
f8387c1
888d73b
c9135a2
888d73b
c9135a2
28634dd
f8387c1
888d73b
 
c9135a2
888d73b
f8387c1
888d73b
67ff76d
888d73b
93fba37
888d73b
 
93fba37
888d73b
 
 
 
 
 
 
 
93fba37
67ff76d
28634dd
93fba37
67ff76d
f8387c1
28634dd
f8387c1
28634dd
f8387c1
 
 
 
 
28634dd
 
 
 
f8387c1
c9135a2
28634dd
 
 
95242f6
c9135a2
28634dd
 
 
 
67ff76d
28634dd
 
ef396f3
67ff76d
 
 
ef396f3
28634dd
f8387c1
d422d60
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
import os
from cerebras.cloud.sdk import Cerebras
from markitdown import MarkItDown
from weasyprint import HTML
import markdown
import gradio as gr

# Ensure you get the API key from environment variables
api_key = os.environ.get("CEREBRAS_API_KEY")

# Initialize MarkItDown instance
md_converter = MarkItDown()

# Functions for resume optimization
def create_prompt(resume_string: str, jd_string: str) -> str:
    """
    Creates a detailed prompt for AI-powered resume optimization based on a job description.
    """
    return f"""
You are a professional resume optimization expert specializing in tailoring resumes to specific job descriptions. Your goal is to optimize my resume and provide actionable suggestions for improvement to align with the target role.

### Guidelines:
1. **Relevance**:  
   - Prioritize experiences, skills, and achievements **most relevant to the job description**.  
   - Remove or de-emphasize irrelevant details to ensure a **concise** and **targeted** resume.
   - Limit work experience section to 2-3 most relevant roles
   - Limit bullet points under each role to 2-3 most relevant impacts

2. **Action-Driven Results**:  
   - Use **strong action verbs** and **quantifiable results** (e.g., percentages, revenue, efficiency improvements) to highlight impact.  

3. **Keyword Optimization**:  
   - Integrate **keywords** and phrases from the job description naturally to optimize for ATS (Applicant Tracking Systems).  

4. **Additional Suggestions** *(If Gaps Exist)*:  
   - If the resume does not fully align with the job description, suggest:  
     1. **Additional technical or soft skills** that I could add to make my profile stronger.  
     2. **Certifications or courses** I could pursue to bridge the gap.  
     3. **Project ideas or experiences** that would better align with the role.  

5. **Formatting**:  
   - Output the tailored resume in **clean Markdown format**.  
   - Include an **"Additional Suggestions"** section at the end with actionable improvement recommendations.  

---

### Input:
- **My resume**:  
{resume_string}

- **The job description**:  
{jd_string}

---

### Output:  
1. **Tailored Resume**:  
   - A resume in **Markdown format** that emphasizes relevant experience, skills, and achievements.  
   - Incorporates job description **keywords** to optimize for ATS.  
   - Uses strong language and is no longer than **one page**.

2. **Additional Suggestions** *(if applicable)*:  
   - List **skills** that could strengthen alignment with the role.  
   - Recommend **certifications or courses** to pursue.  
   - Suggest **specific projects or experiences** to develop.
"""

def get_resume_response(prompt: str, api_key: str, model: str = "llama-3.3-70b", temperature: float = 0.7) -> str:
    client = Cerebras(api_key=api_key)
    stream = client.chat.completions.create(
        messages=[
            {"role": "system", "content": "Expert resume writer"},
            {"role": "user", "content": prompt}
        ],
        model=model,
        stream=True,
        temperature=temperature,
        max_completion_tokens=1024,
        top_p=1
    )

    response_string = ""
    for chunk in stream:
        response_string += chunk.choices[0].delta.content or ""
    return response_string

def process_resume(resume, jd_string):
    """
    Process the uploaded resume and job description, optimize it, and return the result.
    """
    try:
        # Read file content from the resume path
        with open(resume.name, "r", encoding="utf-8") as file:
            resume_string = file.read()

        # Create optimization prompt
        prompt = create_prompt(resume_string, jd_string)

        # Generate response using AI
        response_string = get_resume_response(prompt, api_key)
        response_list = response_string.split("## Additional Suggestions")

        # Extract new resume and suggestions for improvement
        new_resume = response_list[0].strip()
        suggestions = "## Additional Suggestions \n\n" + response_list[1].strip() if len(response_list) > 1 else ""

        # Save the optimized resume
        optimized_file_path = "resumes/optimized_resume.md"
        os.makedirs("resumes", exist_ok=True)  # Ensure the directory exists
        with open(optimized_file_path, "w", encoding="utf-8") as f:
            f.write(new_resume)

        # Return the results
        return resume_string, new_resume, optimized_file_path, suggestions
    except Exception as e:
        return f"Error processing file: {str(e)}", "", None, ""

def export_resume(new_resume):
    """
    Convert a markdown resume to PDF format and save it.

    Args:
        new_resume (str): The resume content in markdown format

    Returns:
        str: A message indicating success or failure of the PDF export
    """
    try:
        # Convert Markdown to HTML
        html_content = markdown.markdown(new_resume)
        
        # Convert HTML to PDF and save
        output_pdf_file = "resumes/optimized_resume.pdf"
        os.makedirs("resumes", exist_ok=True)  # Ensure the directory exists
        HTML(string=html_content).write_pdf(output_pdf_file, stylesheets=['resumes/style.css'])

        return output_pdf_file  # Return the file path for download
    except Exception as e:
        return f"Failed to export resume: {str(e)} πŸ’”"

# Gradio App
with gr.Blocks() as app:
    gr.Markdown("# Resume Optimizer πŸ“„")
    gr.Markdown("Upload your resume, paste the job description, and get actionable insights!")

    with gr.Row():
        resume_input = gr.File(label="Upload Your Resume")    
        jd_input = gr.Textbox(label="Paste the Job Description Here", lines=9, interactive=True, placeholder="Paste job description...")

    run_button = gr.Button("Optimize Resume πŸ€–")

    with gr.Row():
        before_md = gr.Markdown(label="Original Resume (Before)")
        after_md = gr.Markdown(label="Optimized Resume (After)")
        output_suggestions = gr.Markdown(label="Suggestions")

    with gr.Row():
        download_before = gr.File(label="Download Original Resume")
        download_after = gr.File(label="Download Optimized Resume")
        
    export_button = gr.Button("Export Optimized Resume as PDF πŸš€")
    export_result = gr.File(label="Download PDF")

    # Bindings
    run_button.click(
        process_resume,
        inputs=[resume_input, jd_input],
        outputs=[before_md, after_md, download_before, download_after, output_suggestions]
    )
    export_button.click(export_resume, inputs=[after_md], outputs=[export_result])

app.launch()