Spaces:
Running
Running
Commit
·
f7933a5
1
Parent(s):
22351fe
Made it look better :)
Browse files- app.py +43 -52
- main.py +62 -15
- prompts.yaml +2 -0
- tools/resumescraper.py +1 -1
app.py
CHANGED
@@ -1,58 +1,49 @@
|
|
1 |
-
import
|
2 |
-
import
|
3 |
-
from
|
4 |
-
from
|
|
|
5 |
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
if input_method == "Text":
|
17 |
-
text = resume_text
|
18 |
-
else:
|
19 |
-
if pdf_file is None:
|
20 |
-
return "No PDF uploaded."
|
21 |
-
# Check if pdf_file is a string (i.e. a file path) or a file-like object
|
22 |
-
if isinstance(pdf_file, str):
|
23 |
-
with open(pdf_file, "rb") as f:
|
24 |
-
file_bytes = f.read()
|
25 |
-
else:
|
26 |
-
file_bytes = pdf_file.read()
|
27 |
-
file_obj = io.BytesIO(file_bytes)
|
28 |
-
text = extract_text_from_pdf(file_obj)
|
29 |
-
|
30 |
-
if not text.strip():
|
31 |
-
return "No resume text found."
|
32 |
-
|
33 |
-
agent = create_agent()
|
34 |
-
# Instruct the agent to roast the resume using the resume text.
|
35 |
-
response = agent.run(f"Roast this resume: {text}")
|
36 |
-
return response
|
37 |
|
|
|
|
|
38 |
|
39 |
-
def
|
40 |
-
|
41 |
-
|
42 |
-
else:
|
43 |
-
return gr.update(visible=False), gr.update(visible=True)
|
44 |
-
|
45 |
-
with gr.Blocks() as demo:
|
46 |
-
gr.Markdown("# Resume Roaster")
|
47 |
-
gr.Markdown("Enter your resume as text or upload a PDF to receive a humorous, professional roast!")
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
54 |
|
55 |
-
|
56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
|
58 |
-
|
|
|
1 |
+
import yaml
|
2 |
+
from smolagents import CodeAgent, HfApiModel
|
3 |
+
from smolagents.tools import Tool
|
4 |
+
from tools.resumescraper import ResumeScraperTool
|
5 |
+
from huggingface_hub import InferenceClient
|
6 |
|
7 |
+
class FinalAnswerTool(Tool):
|
8 |
+
name = "final_answer"
|
9 |
+
description = "Use this tool to provide your final roast"
|
10 |
+
inputs = {
|
11 |
+
"answer": {
|
12 |
+
"type": "string",
|
13 |
+
"description": "The final roast for the resume"
|
14 |
+
}
|
15 |
+
}
|
16 |
+
output_type = "string"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
+
def forward(self, answer: str) -> str:
|
19 |
+
return answer
|
20 |
|
21 |
+
def create_agent():
|
22 |
+
final_answer = FinalAnswerTool()
|
23 |
+
resume_scraper = ResumeScraperTool()
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
+
# Instantiate HfApiModel using Qwen/Qwen2.5-Coder-32B-Instruct for roasting.
|
26 |
+
model = HfApiModel(
|
27 |
+
max_tokens=2096,
|
28 |
+
temperature=0.5,
|
29 |
+
model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
|
30 |
+
custom_role_conversions=None,
|
31 |
+
)
|
32 |
|
33 |
+
# Create a dedicated InferenceClient using your public endpoint
|
34 |
+
client = InferenceClient("https://jc26mwg228mkj8dw.us-east-1.aws.endpoints.huggingface.cloud")
|
35 |
+
# Override the model's client with our dedicated client
|
36 |
+
model.client = client
|
37 |
+
|
38 |
+
with open("prompts.yaml", 'r') as stream:
|
39 |
+
prompt_templates = yaml.safe_load(stream)
|
40 |
+
|
41 |
+
agent = CodeAgent(
|
42 |
+
model=model,
|
43 |
+
tools=[resume_scraper, final_answer],
|
44 |
+
max_steps=6,
|
45 |
+
verbosity_level=1,
|
46 |
+
prompt_templates=prompt_templates
|
47 |
+
)
|
48 |
|
49 |
+
return agent
|
main.py
CHANGED
@@ -24,7 +24,6 @@ def process_resume(input_method, resume_text, pdf_file):
|
|
24 |
file_bytes = f.read()
|
25 |
else:
|
26 |
file_bytes = pdf_file.read()
|
27 |
-
import io
|
28 |
file_obj = io.BytesIO(file_bytes)
|
29 |
text = extract_text_from_pdf(file_obj)
|
30 |
|
@@ -32,28 +31,76 @@ def process_resume(input_method, resume_text, pdf_file):
|
|
32 |
return "No resume text found."
|
33 |
|
34 |
agent = create_agent()
|
35 |
-
#
|
36 |
-
# the agent should output plain text that we can simply print.
|
37 |
response = agent.run(f"Roast this resume: {text}")
|
38 |
return response
|
39 |
|
40 |
def toggle_inputs(method):
|
|
|
41 |
if method == "Text":
|
42 |
-
return gr.update(visible=True), gr.update(visible=False)
|
43 |
-
else:
|
44 |
return gr.update(visible=False), gr.update(visible=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
-
with gr.Blocks() as demo:
|
47 |
-
gr.
|
48 |
-
|
|
|
49 |
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
55 |
|
56 |
-
|
|
|
57 |
submit_btn.click(fn=process_resume, inputs=[input_method, resume_text, pdf_file], outputs=output)
|
58 |
|
59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
file_bytes = f.read()
|
25 |
else:
|
26 |
file_bytes = pdf_file.read()
|
|
|
27 |
file_obj = io.BytesIO(file_bytes)
|
28 |
text = extract_text_from_pdf(file_obj)
|
29 |
|
|
|
31 |
return "No resume text found."
|
32 |
|
33 |
agent = create_agent()
|
34 |
+
# Use the agent to roast the resume.
|
|
|
35 |
response = agent.run(f"Roast this resume: {text}")
|
36 |
return response
|
37 |
|
38 |
def toggle_inputs(method):
|
39 |
+
# Updated order: pdf_file first, then resume_text.
|
40 |
if method == "Text":
|
|
|
|
|
41 |
return gr.update(visible=False), gr.update(visible=True)
|
42 |
+
else:
|
43 |
+
return gr.update(visible=True), gr.update(visible=False)
|
44 |
+
|
45 |
+
css_custom = """
|
46 |
+
footer {visibility: hidden;}
|
47 |
+
.center {
|
48 |
+
margin: 0 auto;
|
49 |
+
text-align: center;
|
50 |
+
}
|
51 |
+
@keyframes beat {
|
52 |
+
0%, 20%, 40%, 60%, 80%, 100% { transform: scale(1); }
|
53 |
+
10%, 30%, 50%, 70%, 90% { transform: scale(1.2); }
|
54 |
+
}
|
55 |
+
.beating-heart {
|
56 |
+
display: inline-block;
|
57 |
+
animation: beat 2s infinite;
|
58 |
+
}
|
59 |
+
.fire-effect {
|
60 |
+
font-size: 2.5em;
|
61 |
+
font-weight: bold;
|
62 |
+
background: linear-gradient(45deg, red, orange, yellow);
|
63 |
+
background-size: 200%;
|
64 |
+
-webkit-background-clip: text;
|
65 |
+
-webkit-text-fill-color: transparent;
|
66 |
+
animation: fireAnimation 3s linear infinite;
|
67 |
+
}
|
68 |
+
@keyframes fireAnimation {
|
69 |
+
0% { background-position: 0%; }
|
70 |
+
50% { background-position: 100%; }
|
71 |
+
100% { background-position: 0%; }
|
72 |
+
}
|
73 |
+
/* Center the radio button options */
|
74 |
+
div[role="radiogroup"] {
|
75 |
+
display: flex;
|
76 |
+
justify-content: center;
|
77 |
+
}
|
78 |
+
"""
|
79 |
|
80 |
+
with gr.Blocks(css=css_custom) as demo:
|
81 |
+
with gr.Column(elem_classes="center"):
|
82 |
+
gr.Markdown('<div class="fire-effect">Resume Roaster</div>')
|
83 |
+
gr.Markdown("Upload your resume as a PDF (default) or paste the text to receive a humorous, professional roast!")
|
84 |
|
85 |
+
# Reordered radio choices so that PDF is first.
|
86 |
+
input_method = gr.Radio(choices=["PDF", "Text"], label="Input Method", value="PDF")
|
87 |
+
# PDF upload comes first
|
88 |
+
pdf_file = gr.File(label="Upload Resume PDF", file_types=[".pdf"], visible=True)
|
89 |
+
resume_text = gr.Textbox(label="Resume Text", lines=10, visible=False)
|
90 |
+
output = gr.Textbox(label="Roast Result", lines=10)
|
91 |
+
submit_btn = gr.Button("Roast It!")
|
92 |
|
93 |
+
# Adjusted toggle outputs to match new order.
|
94 |
+
input_method.change(fn=toggle_inputs, inputs=input_method, outputs=[pdf_file, resume_text])
|
95 |
submit_btn.click(fn=process_resume, inputs=[input_method, resume_text, pdf_file], outputs=output)
|
96 |
|
97 |
+
gr.Markdown(
|
98 |
+
"""
|
99 |
+
<div id="custom_footer" style="text-align: center; margin-top: 20px; font-size: 14px;">
|
100 |
+
Made with <span class="beating-heart">💓</span> by Kuber Mehta<br>
|
101 |
+
All jokes are crafted in good humor to provide professional levity.
|
102 |
+
</div>
|
103 |
+
"""
|
104 |
+
)
|
105 |
+
|
106 |
+
demo.launch(share=False)
|
prompts.yaml
CHANGED
@@ -3,12 +3,14 @@ system_prompt: |
|
|
3 |
Your job is to create a humorous, professional roast of a resume.
|
4 |
Focus on teasing overly verbose descriptions, excessive buzzwords, and generic statements,
|
5 |
while keeping the tone light and appropriate for a professional setting.
|
|
|
6 |
task_prompt: |
|
7 |
Using the provided resume details, craft a roast that:
|
8 |
1. References key sections such as Summary, Experience, Education, and Skills.
|
9 |
2. Is humorous but not mean-spirited.
|
10 |
3. Maintains a professional tone.
|
11 |
4. Adds creative flair that makes the roast both entertaining and insightful.
|
|
|
12 |
final_answer:
|
13 |
pre_messages: "Final Roast:"
|
14 |
post_messages: ""
|
|
|
3 |
Your job is to create a humorous, professional roast of a resume.
|
4 |
Focus on teasing overly verbose descriptions, excessive buzzwords, and generic statements,
|
5 |
while keeping the tone light and appropriate for a professional setting.
|
6 |
+
|
7 |
task_prompt: |
|
8 |
Using the provided resume details, craft a roast that:
|
9 |
1. References key sections such as Summary, Experience, Education, and Skills.
|
10 |
2. Is humorous but not mean-spirited.
|
11 |
3. Maintains a professional tone.
|
12 |
4. Adds creative flair that makes the roast both entertaining and insightful.
|
13 |
+
|
14 |
final_answer:
|
15 |
pre_messages: "Final Roast:"
|
16 |
post_messages: ""
|
tools/resumescraper.py
CHANGED
@@ -46,4 +46,4 @@ class ResumeScraperTool(Tool):
|
|
46 |
end = lower_text.find("\n\n", start)
|
47 |
sections["skills"] = resume_text[start + len("skills:"): end].strip() if end != -1 else resume_text[start + len("skills:"):].strip()
|
48 |
|
49 |
-
return sections
|
|
|
46 |
end = lower_text.find("\n\n", start)
|
47 |
sections["skills"] = resume_text[start + len("skills:"): end].strip() if end != -1 else resume_text[start + len("skills:"):].strip()
|
48 |
|
49 |
+
return sections
|