mgbam commited on
Commit
1142c0d
Β·
verified Β·
1 Parent(s): b82d535

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -158
app.py CHANGED
@@ -1,204 +1,157 @@
1
- # app.py
2
-
3
  import os
4
- import io
5
  import json
6
- from datetime import datetime
7
-
8
- import streamlit as st
9
  import requests
 
 
10
  from docx import Document
11
  from PyPDF2 import PdfReader
12
 
13
  # ------------------------------------------------------------------------------
14
- # Groq API Setup
15
  # ------------------------------------------------------------------------------
16
-
17
- # Your Groq API key is expected to be saved as a secret in the environment
18
  GROQ_API_KEY = os.getenv("GROQ_API")
19
- # Replace the following endpoint with the actual Groq API endpoint if different.
20
- GROQ_ENDPOINT = "https://api.groq.ai/v1/generate"
21
 
22
- def generate_text(prompt: str, max_length: int = 1024) -> str:
23
- """
24
- Call the Groq API with a prompt and return the generated text.
25
- """
 
26
  headers = {
27
  "Authorization": f"Bearer {GROQ_API_KEY}",
28
  "Content-Type": "application/json"
29
  }
30
  payload = {
31
- "prompt": prompt,
32
- "max_length": max_length
 
 
33
  }
34
- response = requests.post(GROQ_ENDPOINT, headers=headers, json=payload)
35
- if response.ok:
36
- # Adjust according to the actual API response structure.
37
- return response.json().get("generated_text", "").strip()
38
- else:
39
- st.error(f"Error in Groq API call: {response.text}")
40
- return ""
41
 
42
  # ------------------------------------------------------------------------------
43
- # Helper Functions for File Reading
44
  # ------------------------------------------------------------------------------
45
-
46
- def read_docx(file_bytes: bytes) -> str:
47
- """Extract text from a DOCX file."""
48
- document = Document(io.BytesIO(file_bytes))
49
- return "\n".join([para.text for para in document.paragraphs])
50
-
51
- def read_pdf(file_bytes: bytes) -> str:
52
- """Extract text from a PDF file."""
53
- reader = PdfReader(io.BytesIO(file_bytes))
54
- text = []
55
- for page in reader.pages:
56
- extracted = page.extract_text()
57
- if extracted:
58
- text.append(extracted)
59
- return "\n".join(text)
60
-
61
- def read_plain(file_bytes: bytes) -> str:
62
- """Extract text from a plain text file."""
63
- return file_bytes.decode("utf-8", errors="ignore")
64
-
65
- def extract_resume_text(file_obj) -> str:
66
- """
67
- Determine file type and extract text accordingly.
68
- Supports PDF, DOCX, and plain text.
69
- """
70
  file_bytes = file_obj.read()
71
- file_obj.seek(0) # Reset pointer for further use if needed
72
- filename = file_obj.name if hasattr(file_obj, "name") else "resume.txt"
73
- ext = os.path.splitext(filename)[-1].lower()
74
 
75
  if ext == ".pdf":
76
- return read_pdf(file_bytes)
77
  elif ext in [".docx", ".doc"]:
78
- return read_docx(file_bytes)
79
  else:
80
- return read_plain(file_bytes)
81
 
82
  # ------------------------------------------------------------------------------
83
- # Core AI Functions Using Groq API
84
  # ------------------------------------------------------------------------------
 
 
 
 
 
 
 
85
 
86
- def parse_resume(resume_text: str) -> str:
87
- """
88
- Extract candidate details from the resume text.
89
- The prompt instructs the model to output a JSON with keys:
90
- first_name, last_name, location, work_experience, school_experience, and skills.
91
- """
92
- prompt = (
93
- "You are an expert resume parser. Extract the following keys from the resume text: "
94
- "first_name, last_name, location, work_experience, school_experience, and skills. "
95
- "Return the answer strictly in JSON format with no extra text.\n\n"
96
- f"Resume:\n{resume_text}"
97
- )
98
- return generate_text(prompt, max_length=512)
99
-
100
- def generate_cover_letter(candidate_json: str, job_description: str, date_str: str) -> str:
101
- """
102
- Generate a cover letter using the candidate's JSON profile, the job description,
103
- and the current date.
104
- """
105
- prompt = (
106
- "You are a seasoned career advisor and professional cover letter writer. "
107
- "Using the candidate information provided in JSON format, craft a persuasive and personalized cover letter "
108
- "tailored to the job description. Structure your answer with an introduction, body, and closing, "
109
- "and include the date {date}.\n\n"
110
- "Candidate JSON:\n{candidate_json}\n\n"
111
- "Job Description:\n{job_description}\n\n"
112
- "Cover Letter:".format(
113
- candidate_json=candidate_json, job_description=job_description, date=date_str
114
- )
115
- )
116
- return generate_text(prompt, max_length=1024)
117
-
118
- def generate_resume(first_name: str, last_name: str, location: str,
119
- work_experience: str, school_experience: str, skills: str) -> str:
120
- """
121
- Generate a professional resume using candidate information provided in a form.
122
- """
123
- candidate_info = {
124
  "first_name": first_name,
125
  "last_name": last_name,
126
  "location": location,
127
  "work_experience": work_experience,
128
  "school_experience": school_experience,
129
- "skills": skills,
130
- }
131
- candidate_str = json.dumps(candidate_info, indent=2)
132
-
133
- prompt = (
134
- "You are a professional resume writer. Using the candidate information provided in JSON, "
135
- "create a compelling, well-organized resume in plain text format. Include clear sections for "
136
- "personal details, work experience, education, and skills. Ensure the resume is concise and professional.\n\n"
137
- "Candidate Information:\n" + candidate_str + "\n\nResume:"
138
- )
139
- return generate_text(prompt, max_length=1024)
 
 
 
 
 
140
 
141
  # ------------------------------------------------------------------------------
142
- # Streamlit UI
143
  # ------------------------------------------------------------------------------
 
 
 
144
 
145
- st.set_page_config(page_title="AI-Powered Job Application Assistant", layout="wide")
146
- st.title("AI-Powered Job Application Assistant")
147
- st.markdown("Generate a **Cover Letter** from your resume or **Create a Resume** from scratch using AI via Groq API.")
148
 
149
- # Create tabs for the two functionalities
150
- tabs = st.tabs(["Cover Letter Generator", "Resume Creator"])
151
-
152
- # ----- Tab 1: Cover Letter Generator -----
153
  with tabs[0]:
154
- st.header("Cover Letter Generator")
155
- st.markdown("Upload your resume (PDF, DOCX, or TXT) and paste the job description below.")
156
-
157
- col1, col2 = st.columns(2)
158
-
159
- with col1:
160
- resume_file = st.file_uploader("Upload Your Resume", type=["pdf", "docx", "doc", "txt"])
161
-
162
- with col2:
163
- job_description = st.text_area("Job Description", height=200, placeholder="Paste the job description here...")
164
-
165
- if st.button("Generate Cover Letter"):
166
- if resume_file is None or not job_description.strip():
167
- st.error("Please upload a resume and enter a job description.")
168
- else:
169
- with st.spinner("Processing resume..."):
170
  resume_text = extract_resume_text(resume_file)
171
  candidate_json = parse_resume(resume_text)
172
- current_date = datetime.today().strftime("%d - %b - %Y")
173
- cover_letter = generate_cover_letter(candidate_json, job_description, current_date)
174
-
175
- st.subheader("Extracted Candidate Profile (JSON)")
176
- st.code(candidate_json, language="json")
177
-
178
- st.subheader("Generated Cover Letter")
179
- st.text_area("", cover_letter, height=300)
180
-
181
- # ----- Tab 2: Resume Creator -----
182
  with tabs[1]:
183
- st.header("Resume Creator")
184
- st.markdown("Fill in your details below to generate a professional resume from scratch.")
185
-
186
  with st.form("resume_form"):
187
  col1, col2 = st.columns(2)
188
- with col1:
189
- first_name = st.text_input("First Name", placeholder="John")
190
- with col2:
191
- last_name = st.text_input("Last Name", placeholder="Doe")
192
-
193
- location = st.text_input("Location", placeholder="City, State, Country")
194
- work_experience = st.text_area("Work Experience", height=150, placeholder="List your past roles and achievements...")
195
- school_experience = st.text_area("Education", height=150, placeholder="List your academic qualifications...")
196
- skills = st.text_area("Skills", height=100, placeholder="List your key skills...")
197
-
198
- submit = st.form_submit_button("Generate Resume")
199
-
200
  if submit:
201
- with st.spinner("Generating resume..."):
202
  resume_text = generate_resume(first_name, last_name, location, work_experience, school_experience, skills)
203
- st.subheader("Generated Resume")
204
- st.text_area("", resume_text, height=400)
 
 
 
1
  import os
 
2
  import json
 
 
 
3
  import requests
4
+ import streamlit as st
5
+ from datetime import datetime
6
  from docx import Document
7
  from PyPDF2 import PdfReader
8
 
9
  # ------------------------------------------------------------------------------
10
+ # πŸš€ CONFIGURATION - API & SETTINGS
11
  # ------------------------------------------------------------------------------
 
 
12
  GROQ_API_KEY = os.getenv("GROQ_API")
13
+ GROQ_ENDPOINT = "https://api.groq.com/openai/v1/chat/completions"
14
+ GROQ_MODEL = "llama-3.3-70b-versatile"
15
 
16
+ # ------------------------------------------------------------------------------
17
+ # πŸ”₯ ADVANCED API CALL FUNCTION
18
+ # ------------------------------------------------------------------------------
19
+ def call_groq_api(messages, temperature=0.7):
20
+ """Handles API calls to Groq for chat-based completions with robust error handling."""
21
  headers = {
22
  "Authorization": f"Bearer {GROQ_API_KEY}",
23
  "Content-Type": "application/json"
24
  }
25
  payload = {
26
+ "model": GROQ_MODEL,
27
+ "messages": messages,
28
+ "temperature": temperature,
29
+ "max_tokens": 1024
30
  }
31
+ try:
32
+ response = requests.post(GROQ_ENDPOINT, headers=headers, json=payload, timeout=15)
33
+ response.raise_for_status()
34
+ return response.json()["choices"][0]["message"]["content"].strip()
35
+ except requests.exceptions.RequestException as e:
36
+ st.error(f"❌ API Request Failed: {e}")
37
+ return None
38
 
39
  # ------------------------------------------------------------------------------
40
+ # πŸ“„ FILE PROCESSING (PDF / DOCX)
41
  # ------------------------------------------------------------------------------
42
+ def extract_resume_text(file_obj):
43
+ """Extracts text from PDF or DOCX resumes."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  file_bytes = file_obj.read()
45
+ file_obj.seek(0)
46
+ ext = os.path.splitext(file_obj.name)[-1].lower()
 
47
 
48
  if ext == ".pdf":
49
+ return "\n".join(page.extract_text() for page in PdfReader(file_bytes).pages if page.extract_text())
50
  elif ext in [".docx", ".doc"]:
51
+ return "\n".join([para.text for para in Document(file_bytes).paragraphs])
52
  else:
53
+ return file_bytes.decode("utf-8", errors="ignore")
54
 
55
  # ------------------------------------------------------------------------------
56
+ # 🎯 AI-Powered Resume Parsing
57
  # ------------------------------------------------------------------------------
58
+ def parse_resume(resume_text):
59
+ """Extracts structured JSON resume details using Groq's Llama 3.3 model."""
60
+ messages = [
61
+ {"role": "system", "content": "You are a resume parsing expert. Extract structured data."},
62
+ {"role": "user", "content": f"Extract structured data from this resume:\n{resume_text}\n\nOutput as JSON."}
63
+ ]
64
+ return call_groq_api(messages, temperature=0.4)
65
 
66
+ # ------------------------------------------------------------------------------
67
+ # ✍️ AI-Powered Cover Letter Generator
68
+ # ------------------------------------------------------------------------------
69
+ def generate_cover_letter(candidate_json, job_description):
70
+ """Generates a professional cover letter using structured candidate data."""
71
+ date_str = datetime.today().strftime("%d - %b - %Y")
72
+ messages = [
73
+ {"role": "system", "content": "You are a top-tier career advisor. Write persuasive cover letters."},
74
+ {"role": "user", "content": f"""
75
+ Generate a professional cover letter using:
76
+ - Candidate Profile: {candidate_json}
77
+ - Job Description: {job_description}
78
+ - Date: {date_str}
79
+
80
+ The cover letter should be engaging, personalized, and formatted professionally.
81
+ """}
82
+ ]
83
+ return call_groq_api(messages, temperature=0.5)
84
+
85
+ # ------------------------------------------------------------------------------
86
+ # πŸ“œ AI-Powered Resume Creator
87
+ # ------------------------------------------------------------------------------
88
+ def generate_resume(first_name, last_name, location, work_experience, school_experience, skills):
89
+ """Generates a well-formatted professional resume."""
90
+ candidate_data = json.dumps({
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  "first_name": first_name,
92
  "last_name": last_name,
93
  "location": location,
94
  "work_experience": work_experience,
95
  "school_experience": school_experience,
96
+ "skills": skills
97
+ }, indent=2)
98
+
99
+ messages = [
100
+ {"role": "system", "content": "You are a professional resume writer."},
101
+ {"role": "user", "content": f"""
102
+ Create a structured, ATS-friendly resume using:
103
+ {candidate_data}
104
+
105
+ Ensure:
106
+ - Clear sections (Personal Info, Experience, Education, Skills)
107
+ - Professional formatting
108
+ - A compelling summary if possible.
109
+ """}
110
+ ]
111
+ return call_groq_api(messages, temperature=0.5)
112
 
113
  # ------------------------------------------------------------------------------
114
+ # 🎨 STREAMLIT UI DESIGN
115
  # ------------------------------------------------------------------------------
116
+ st.set_page_config(page_title="AI-Powered Job Assistant", layout="wide")
117
+ st.title("πŸš€ AI-Powered Resume & Cover Letter Generator")
118
+ st.markdown("#### Generate a **Resume** or a **Cover Letter** using **Llama 3.3-70B** powered by **Groq AI**.")
119
 
120
+ tabs = st.tabs(["πŸ“„ Cover Letter Generator", "πŸ“‘ Resume Creator"])
 
 
121
 
122
+ # ----- COVER LETTER GENERATOR -----
 
 
 
123
  with tabs[0]:
124
+ st.header("πŸ“„ Cover Letter Generator")
125
+ resume_file = st.file_uploader("Upload Your Resume", type=["pdf", "docx", "txt"])
126
+ job_description = st.text_area("Paste Job Description", height=200)
127
+
128
+ if st.button("πŸ”Ή Generate Cover Letter"):
129
+ if resume_file and job_description.strip():
130
+ with st.spinner("✨ Processing resume..."):
 
 
 
 
 
 
 
 
 
131
  resume_text = extract_resume_text(resume_file)
132
  candidate_json = parse_resume(resume_text)
133
+ cover_letter = generate_cover_letter(candidate_json, job_description)
134
+ st.success("βœ… Cover Letter Generated!")
135
+ st.text_area("πŸ“œ Your Cover Letter:", cover_letter, height=300)
136
+ else:
137
+ st.warning("⚠️ Please upload a resume and paste the job description.")
138
+
139
+ # ----- RESUME CREATOR -----
 
 
 
140
  with tabs[1]:
141
+ st.header("πŸ“‘ Resume Creator")
142
+
 
143
  with st.form("resume_form"):
144
  col1, col2 = st.columns(2)
145
+ first_name = col1.text_input("First Name")
146
+ last_name = col2.text_input("Last Name")
147
+ location = st.text_input("Location")
148
+ work_experience = st.text_area("Work Experience", height=150)
149
+ school_experience = st.text_area("Education", height=150)
150
+ skills = st.text_area("Skills", height=100)
151
+ submit = st.form_submit_button("πŸ“‘ Generate Resume")
152
+
 
 
 
 
153
  if submit:
154
+ with st.spinner("πŸ“ Creating Resume..."):
155
  resume_text = generate_resume(first_name, last_name, location, work_experience, school_experience, skills)
156
+ st.success("βœ… Resume Generated!")
157
+ st.text_area("πŸ“œ Your Resume:", resume_text, height=400)