Sazzz02 commited on
Commit
35ea9d6
Β·
verified Β·
1 Parent(s): 02047ca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -80
app.py CHANGED
@@ -4,146 +4,137 @@ import os
4
  import PyPDF2
5
  from io import BytesIO
6
 
7
- # API config
8
  HF_API_TOKEN = os.getenv("HF_API_TOKEN")
9
  HF_API_URL = "https://api-inference.huggingface.co/models/HuggingFaceH4/zephyr-7b-beta"
10
  MODEL_CHECK_PROMPT = "Say hello!"
11
 
12
- # --- API/Model Health Check ---
13
  def check_api_ready():
14
  if not HF_API_TOKEN:
15
- return False, "❌ HF_API_TOKEN not set. Please add your Hugging Face token as a repo secret."
16
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
17
  try:
18
- resp = requests.post(HF_API_URL, headers=headers, json={"inputs": MODEL_CHECK_PROMPT}, timeout=15)
19
  if resp.status_code == 200:
20
  data = resp.json()
21
  if isinstance(data, dict) and "error" in data:
22
- return False, f"❌ Model error: {data['error']}"
23
  if isinstance(data, list) and data[0].get("generated_text"):
24
- return True, "βœ… Model is up and responding!"
25
  elif resp.status_code == 401:
26
- return False, "❌ Unauthorized. API token missing or invalid."
27
  else:
28
- return False, f"❌ Unexpected API error: {resp.text}"
29
  except Exception as e:
30
- return False, f"❌ API connection error: {str(e)}"
31
 
32
- # --- Lottie Animation Sidebar ---
33
  def lottie_html():
34
- # Robotics-Students.json must exist in app root folder
35
  return """
36
  <script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
37
  <lottie-player src='Robotics-Students.json' background='transparent' speed='1' style='width:340px; height:340px; margin-bottom:12px;' loop autoplay></lottie-player>
38
  """
39
 
40
- # --- PDF Text Extraction ---
41
  def extract_text_from_pdf(pdf_file):
42
- pdf_reader = PyPDF2.PdfReader(BytesIO(pdf_file.read()))
43
- text = ""
44
- for page in pdf_reader.pages:
45
- content = page.extract_text()
46
- if content:
47
- text += content + "\n"
48
- return text.strip()
49
-
50
- # --- AI-Powered Interview Questions ---
51
  def ai_generate_questions(resume_text, job_title):
52
  prompt = (
53
- f"You are an AI interview prep coach.\n"
54
  f"Candidate Resume:\n{resume_text}\n"
55
  f"Target Role: {job_title}\n"
56
- f"Generate 10 realistic interview questions based on the resume, "
57
- f"and add a 1–2 sentence coaching tip for each."
58
  )
59
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
60
- payload = {
61
- "inputs": prompt,
62
- "parameters": {"max_new_tokens": 512, "temperature": 0.7, "return_full_text": False},
63
- }
64
  try:
65
  response = requests.post(HF_API_URL, headers=headers, json=payload, timeout=60)
66
  data = response.json()
67
- if isinstance(data, dict) and "error" in data:
68
  return f"<div style='color:red;'>❌ API Error: {data['error']}</div>"
69
  return data[0]["generated_text"].strip()
70
  except Exception as e:
71
  return f"<div style='color:red;'>❌ Exception: {str(e)}</div>"
72
 
73
- # --- HTML Output Rendering ---
74
- def render_output_html(is_ready, msg, name, role, status, queue, inner_html):
75
- sidebar = lottie_html() if is_ready else ""
76
- status_panel = (
77
- f"<div style='background:#f4f6fa;padding:18px;border-radius:16px;max-width:340px;box-shadow:0 2px 14px #ccd4e6b3;font-family:sans-serif;'>"
78
- f"<div style='font-size:1.25rem;font-weight:700;'>ROBOT RESUME ANALYZER</div>"
79
- f"<div style='margin-top:10px;'><b>Status:</b> <span style='color:#058c42'>{status}</span></div>"
80
- f"<div style='margin-top:10px;'><b>Current:</b> {name}</div>"
81
- f"<div><b>Position:</b> {role}</div>"
82
- f"<div style='margin-top:10px;'><b>Analysis Progress:</b></div>"
83
- f"<div style='font-size:2rem;'>πŸ‘₯</div>"
84
- f"<div><b>QUEUE</b></div>"
85
- f"<div style='font-size:1.5rem;font-weight:700;'>{queue}</div>"
86
- f"<div><i>Analyzing Next...</i></div>"
87
- f"{('<div style=\"color:#46af41; font-weight:600; margin-top:10px;\">'+msg+'</div>') if msg else ''}"
88
- f"</div>"
89
- )
90
- return (
91
- sidebar +
92
- status_panel +
93
- "<div style='max-width:620px;margin-left:30px;display:inline-block;vertical-align:top;'>" +
94
- inner_html +
95
- "</div>"
96
- )
 
 
 
97
 
98
- # --- Main App Logic ---
99
  def interface(pdf_file, job_title):
100
  is_ready, msg = check_api_ready()
101
  if not is_ready:
102
- out = f"<div style='color:red; font-size:1.1em;'><b>API/model check failed:</b> <br>{msg}</div>"
103
- return render_output_html(False, msg, "---", "---", "API/Model Error", 1, out)
104
 
105
  if not pdf_file:
106
- out = '<span style="color:red;">Please upload a PDF resume.</span>'
107
- return render_output_html(True, msg, "---", "---", "No resume uploaded", 1, out)
108
  if not job_title.strip():
109
- name = pdf_file.name.replace('.pdf', '')
110
- out = '<span style="color:red;">Please enter your target job title.</span>'
111
- return render_output_html(True, msg, name, "---", "Waiting for job input", 1, out)
112
 
113
- name = pdf_file.name.replace('.pdf', '')
114
  try:
115
  pdf_file.seek(0)
116
  resume_text = extract_text_from_pdf(pdf_file)
117
  if not resume_text:
118
- raise Exception("No text found in PDF.")
119
  except Exception as e:
120
- out = f"<div style='color:red;'>Failed to read PDF: {e}</div>"
121
- return render_output_html(True, msg, name, job_title, "Error", 0, out)
122
 
123
- yield render_output_html(True, msg, name, job_title, "Analyzing...", 1,
124
- "<i>⏳ Sending resume to AI coach, please wait...</i>"
125
- )
 
126
 
127
- ai_response = ai_generate_questions(resume_text, job_title)
128
- questions = [q.strip() for q in ai_response.split("\n") if q.strip()]
129
- output = "<div style='margin-top:20px;'><b>βœ… Interview Questions + Tips:</b></div>"
130
  for i, q in enumerate(questions[:10], 1):
131
- output += f"<div style='background:#fff;border-radius:10px;padding:14px;box-shadow:0 1px 5px #dde5f4ba;margin-top:10px;'><b>Q{i}:</b> {q}</div>"
 
132
 
133
- yield render_output_html(True, msg, name, job_title, "Done βœ…", 0, output)
134
 
 
135
  with gr.Interface(
136
  fn=interface,
137
  inputs=[
138
- gr.File(label="Upload Resume (PDF)", file_types=[".pdf"], type="binary"),
139
- gr.Textbox(label="Target Job Title", placeholder="e.g. Machine Learning Engineer"),
140
  ],
141
  outputs=gr.HTML(),
142
- title="Robot Resume Analyzer + AI Interview Coach (PDF Powered)",
143
- live=False,
144
  allow_flagging="never",
 
 
145
  ) as demo:
146
- pass
147
-
148
- if __name__ == "__main__":
149
  demo.launch()
 
4
  import PyPDF2
5
  from io import BytesIO
6
 
7
+ # Hugging Face API and model
8
  HF_API_TOKEN = os.getenv("HF_API_TOKEN")
9
  HF_API_URL = "https://api-inference.huggingface.co/models/HuggingFaceH4/zephyr-7b-beta"
10
  MODEL_CHECK_PROMPT = "Say hello!"
11
 
12
+ # βœ… Check whether the API key and model are working correctly
13
  def check_api_ready():
14
  if not HF_API_TOKEN:
15
+ return False, "❌ HF_API_TOKEN not set. Please add in Space secrets."
16
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
17
  try:
18
+ resp = requests.post(HF_API_URL, headers=headers, json={"inputs": MODEL_CHECK_PROMPT}, timeout=20)
19
  if resp.status_code == 200:
20
  data = resp.json()
21
  if isinstance(data, dict) and "error" in data:
22
+ return False, f"❌ Model Error: {data['error']}"
23
  if isinstance(data, list) and data[0].get("generated_text"):
24
+ return True, "βœ… Model is ready!"
25
  elif resp.status_code == 401:
26
+ return False, "❌ Unauthorized. Check your API token."
27
  else:
28
+ return False, f"❌ Unexpected API response: {resp.text}"
29
  except Exception as e:
30
+ return False, f"❌ API connection failed: {str(e)}"
31
 
32
+ # βœ… Tooltip animation HTML from Lottie JSON (place Robotics-Students.json in Space root)
33
  def lottie_html():
 
34
  return """
35
  <script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
36
  <lottie-player src='Robotics-Students.json' background='transparent' speed='1' style='width:340px; height:340px; margin-bottom:12px;' loop autoplay></lottie-player>
37
  """
38
 
39
+ # βœ… PDF text extractor
40
  def extract_text_from_pdf(pdf_file):
41
+ reader = PyPDF2.PdfReader(BytesIO(pdf_file.read()))
42
+ return "\n".join([page.extract_text() or "" for page in reader.pages]).strip()
43
+
44
+ # βœ… Generate questions and answers using language model
 
 
 
 
 
45
  def ai_generate_questions(resume_text, job_title):
46
  prompt = (
47
+ f"You are an AI interview coach.\n"
48
  f"Candidate Resume:\n{resume_text}\n"
49
  f"Target Role: {job_title}\n"
50
+ f"Generate 10 realistic interview questions based on this person’s resume, "
51
+ f"and for each question provide a coaching tip to help them answer effectively."
52
  )
53
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
54
+ payload = {"inputs": prompt, "parameters": {"max_new_tokens": 512, "temperature": 0.7}}
55
+
 
 
56
  try:
57
  response = requests.post(HF_API_URL, headers=headers, json=payload, timeout=60)
58
  data = response.json()
59
+ if "error" in data:
60
  return f"<div style='color:red;'>❌ API Error: {data['error']}</div>"
61
  return data[0]["generated_text"].strip()
62
  except Exception as e:
63
  return f"<div style='color:red;'>❌ Exception: {str(e)}</div>"
64
 
65
+ # βœ… Format final output HTML
66
+ def render_output(name, job, status, queue, messages, model_message, show_lottie=True):
67
+ lottie_side = lottie_html() if show_lottie else ""
68
+ model_msg = f"<div style='margin-top:12px;font-size:0.9rem;color:#46a546;'>{model_message}</div>" if model_message else ""
69
+ left_panel = f"""
70
+ {lottie_side}
71
+ <div style='background:#f4f6fa;padding:18px;border-radius:16px;
72
+ max-width:340px;box-shadow:0 2px 14px #ccd4e6b3;font-family:sans-serif;'>
73
+ <div style='font-size:1.25rem;font-weight:700;'>πŸ€– ROBOT RESUME ANALYZER</div>
74
+ <div style='margin-top:10px;'><b>Status:</b> <span style='color:#058c42'>{status}</span></div>
75
+ <div style='margin-top:10px;'><b>Current:</b> {name}</div>
76
+ <div><b>Position:</b> {job}</div>
77
+ <div style='margin-top:10px;'><b>Analysis Progress:</b></div>
78
+ <div style='font-size:2rem;'>πŸ‘₯</div>
79
+ <div><b>QUEUE</b></div>
80
+ <div style='font-size:1.5rem;font-weight:700;'>{queue}</div>
81
+ <div><i>Analyzing Next...</i></div>
82
+ {model_msg}
83
+ </div>
84
+ """
85
+
86
+ return f"""
87
+ <div style='display:flex;gap:30px;'>
88
+ {left_panel}
89
+ <div style='max-width:620px;margin-top:15px;'>{messages}</div>
90
+ </div>
91
+ """
92
 
93
+ # βœ… Main app logic with staged UI updates
94
  def interface(pdf_file, job_title):
95
  is_ready, msg = check_api_ready()
96
  if not is_ready:
97
+ err_html = f"<div style='color:red;'><b>ERROR:</b> {msg}</div>"
98
+ return render_output("---", "---", "Unavailable", 0, err_html, msg, show_lottie=False)
99
 
100
  if not pdf_file:
101
+ return render_output("---", "---", "Waiting for PDF", 1, "<b>Please upload a PDF resume.</b>", msg)
102
+
103
  if not job_title.strip():
104
+ name = pdf_file.name.replace('.pdf','')
105
+ return render_output(name, "---", "Waiting for Job Title", 1, "<b>Please enter a target job title.</b>", msg)
 
106
 
107
+ name = pdf_file.name.replace(".pdf", "")
108
  try:
109
  pdf_file.seek(0)
110
  resume_text = extract_text_from_pdf(pdf_file)
111
  if not resume_text:
112
+ raise Exception("PDF contains no readable text")
113
  except Exception as e:
114
+ return render_output(name, job_title, "PDF Error", 1, f"<div style='color:red;'>Failed to extract PDF: {e}</div>", msg)
 
115
 
116
+ yield render_output(name, job_title, "Analyzing...", 1, "<i>Generating questions with AI...</i>", msg)
117
+
118
+ ai_output = ai_generate_questions(resume_text, job_title)
119
+ questions = [q.strip() for q in ai_output.split("\n") if q.strip()]
120
 
121
+ html = "<h3>βœ… Interview Questions & Coaching Tips</h3>"
 
 
122
  for i, q in enumerate(questions[:10], 1):
123
+ html += f"<div style='background:#fff;padding:16px;border-radius:10px;margin-bottom:12px;box-shadow:0 2px 6px #ddd;'>"
124
+ html += f"<b>Q{i}:</b> {q}</div>"
125
 
126
+ yield render_output(name, job_title, "Done βœ…", 0, html, msg)
127
 
128
+ # βœ… Launch Gradio app
129
  with gr.Interface(
130
  fn=interface,
131
  inputs=[
132
+ gr.File(label="Upload PDF Resume", type="binary", file_types=[".pdf"]),
133
+ gr.Textbox(label="Target Job Title", placeholder="e.g. Data Analyst"),
134
  ],
135
  outputs=gr.HTML(),
 
 
136
  allow_flagging="never",
137
+ title="πŸ€– AI Resume Analyzer + Interview Coach (PDF + Lottie)",
138
+ live=False,
139
  ) as demo:
 
 
 
140
  demo.launch()