jzou19950715 commited on
Commit
c0f87d7
·
verified ·
1 Parent(s): d734edc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -48
app.py CHANGED
@@ -11,6 +11,8 @@ import io
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
 
 
14
  CONVERSATION_PROMPT = """You are LOSS DOG, a professional profile builder. Your goal is to have natural conversations
15
  with users to gather information about their professional background across 9 categories:
16
 
@@ -135,26 +137,47 @@ Return the data in this exact structure:
135
  }
136
 
137
  IMPORTANT: Return ONLY the JSON. Do not add any explanation text."""
138
- import json
139
- import logging
140
- from datetime import datetime
141
- from typing import Dict, List, Optional, Any
142
- import gradio as gr
143
- from openai import AsyncOpenAI
144
- import PyPDF2
145
- import io
146
-
147
- # ... (previous imports and prompts remain the same)
148
-
149
  class ProfileBuilder:
150
  def __init__(self):
151
  self.conversation_history = []
152
  self.client = None
 
153
 
154
  def _initialize_client(self, api_key: str) -> None:
155
- if not api_key.startswith("sk-"):
156
- raise ValueError("Invalid API key format")
157
- self.client = AsyncOpenAI(api_key=api_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
  async def extract_from_pdf(self, pdf_content: bytes) -> str:
160
  """Extract text from PDF file"""
@@ -164,22 +187,22 @@ class ProfileBuilder:
164
  text = ""
165
  for page in pdf_reader.pages:
166
  text += page.extract_text()
 
167
  return text
168
  except Exception as e:
169
- logger.error(f"Error extracting PDF: {str(e)}")
170
  raise
171
 
172
  async def process_pdf(self, pdf_path: str, api_key: str) -> Dict[str, Any]:
173
- """Process PDF resume and extract information"""
174
  try:
175
- if not self.client:
176
- self._initialize_client(api_key)
177
 
 
178
  with open(pdf_path, 'rb') as file:
179
- pdf_content = file.read()
180
- resume_text = await self.extract_from_pdf(pdf_content)
181
 
182
- # Use the extraction prompt directly on PDF content
183
  completion = await self.client.chat.completions.create(
184
  model="gpt-4o-mini",
185
  messages=[
@@ -189,10 +212,12 @@ class ProfileBuilder:
189
  temperature=0.3
190
  )
191
 
 
192
  response_text = completion.choices[0].message.content.strip()
193
  profile_data = json.loads(response_text)
194
 
195
- return {
 
196
  "profile_data": profile_data,
197
  "metadata": {
198
  "generated_at": datetime.now().isoformat(),
@@ -200,11 +225,65 @@ class ProfileBuilder:
200
  }
201
  }
202
 
 
 
203
  except Exception as e:
204
- logger.error(f"Error processing PDF: {str(e)}")
205
  return {"error": str(e)}
206
 
207
- # ... (rest of the ProfileBuilder class remains the same)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
  def create_gradio_interface():
210
  builder = ProfileBuilder()
@@ -212,15 +291,18 @@ def create_gradio_interface():
212
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
213
  gr.Markdown("# 🐕 LOSS DOG - Professional Profile Builder")
214
 
 
215
  api_key = gr.Textbox(
216
  label="OpenAI API Key",
217
  type="password",
218
  placeholder="Enter your OpenAI API key"
219
  )
220
 
 
221
  with gr.Tabs() as tabs:
 
222
  with gr.Tab("Upload Resume"):
223
- upload_text = gr.Markdown("""
224
  # Upload Your Resume
225
  Upload your existing resume in PDF format and let LOSS DOG extract your professional profile.
226
  """)
@@ -230,12 +312,16 @@ def create_gradio_interface():
230
  )
231
  process_pdf_btn = gr.Button("Process Resume")
232
 
 
233
  with gr.Tab("Chat with AI"):
234
- chat_text = gr.Markdown("""
235
  # Chat with LOSS DOG
236
  Start a conversation with LOSS DOG to build your professional profile from scratch.
237
  """)
238
- chatbot = gr.Chatbot(label="Conversation")
 
 
 
239
  with gr.Row():
240
  msg = gr.Textbox(
241
  label="Message",
@@ -243,14 +329,28 @@ def create_gradio_interface():
243
  )
244
  send = gr.Button("Send")
245
 
 
246
  with gr.Column():
247
- generate_btn = gr.Button("Generate Profile")
248
  profile_output = gr.JSON(label="Generated Profile")
249
  download_btn = gr.File(label="Download Profile")
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  async def on_pdf_upload(pdf, key):
252
  if not pdf:
253
- return {"error": "No PDF file uploaded"}
254
 
255
  try:
256
  result = await builder.process_pdf(pdf.name, key)
@@ -267,37 +367,24 @@ def create_gradio_interface():
267
  except Exception as e:
268
  return {"error": str(e)}, None
269
 
270
- # Event handlers remain the same for chat functionality
271
- async def on_message(message: str, history: List[List[str]], key: str):
272
- if not message.strip():
273
- return history, None
274
-
275
- result = await builder.process_message(message, key)
276
-
277
- if "error" in result:
278
- return history, {"error": result["error"]}
279
-
280
- history = history + [[message, result["response"]]]
281
- return history, None
282
-
283
  async def on_generate():
284
  profile, filename = await builder.generate_profile()
285
  if "error" in profile:
286
- return profile, None
287
  return profile["profile_data"], filename
288
 
289
  # Bind events
290
  msg.submit(
291
  on_message,
292
  inputs=[msg, chatbot, api_key],
293
- outputs=[chatbot, profile_output]
294
- ).then(lambda: "", None, msg)
295
 
296
  send.click(
297
  on_message,
298
  inputs=[msg, chatbot, api_key],
299
- outputs=[chatbot, profile_output]
300
- ).then(lambda: "", None, msg)
301
 
302
  process_pdf_btn.click(
303
  on_pdf_upload,
@@ -315,4 +402,8 @@ def create_gradio_interface():
315
  if __name__ == "__main__":
316
  demo = create_gradio_interface()
317
  demo.queue()
318
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
14
+ # Keep your existing CONVERSATION_PROMPT and EXTRACTION_PROMPT here
15
+
16
  CONVERSATION_PROMPT = """You are LOSS DOG, a professional profile builder. Your goal is to have natural conversations
17
  with users to gather information about their professional background across 9 categories:
18
 
 
137
  }
138
 
139
  IMPORTANT: Return ONLY the JSON. Do not add any explanation text."""
 
 
 
 
 
 
 
 
 
 
 
140
  class ProfileBuilder:
141
  def __init__(self):
142
  self.conversation_history = []
143
  self.client = None
144
+ self.pdf_text = None
145
 
146
  def _initialize_client(self, api_key: str) -> None:
147
+ """Initialize OpenAI client if not already initialized"""
148
+ if not self.client:
149
+ if not api_key.startswith("sk-"):
150
+ raise ValueError("Invalid API key format")
151
+ self.client = AsyncOpenAI(api_key=api_key)
152
+
153
+ async def process_message(self, message: str, api_key: str) -> Dict[str, Any]:
154
+ """Process a chat message"""
155
+ try:
156
+ # Initialize client if needed
157
+ self._initialize_client(api_key)
158
+
159
+ # Add user message to history
160
+ self.conversation_history.append({"role": "user", "content": message})
161
+
162
+ # Get AI response
163
+ completion = await self.client.chat.completions.create(
164
+ model="gpt-4o-mini",
165
+ messages=[
166
+ {"role": "system", "content": CONVERSATION_PROMPT},
167
+ *self.conversation_history
168
+ ],
169
+ temperature=0.7
170
+ )
171
+
172
+ # Extract and store response
173
+ ai_message = completion.choices[0].message.content
174
+ self.conversation_history.append({"role": "assistant", "content": ai_message})
175
+
176
+ return {"response": ai_message}
177
+
178
+ except Exception as e:
179
+ logger.error(f"Error in message processing: {str(e)}")
180
+ return {"error": str(e)}
181
 
182
  async def extract_from_pdf(self, pdf_content: bytes) -> str:
183
  """Extract text from PDF file"""
 
187
  text = ""
188
  for page in pdf_reader.pages:
189
  text += page.extract_text()
190
+ self.pdf_text = text # Store the PDF text
191
  return text
192
  except Exception as e:
193
+ logger.error(f"PDF extraction error: {str(e)}")
194
  raise
195
 
196
  async def process_pdf(self, pdf_path: str, api_key: str) -> Dict[str, Any]:
197
+ """Process PDF resume"""
198
  try:
199
+ self._initialize_client(api_key)
 
200
 
201
+ # Read and extract PDF content
202
  with open(pdf_path, 'rb') as file:
203
+ resume_text = await self.extract_from_pdf(file.read())
 
204
 
205
+ # Process with AI
206
  completion = await self.client.chat.completions.create(
207
  model="gpt-4o-mini",
208
  messages=[
 
212
  temperature=0.3
213
  )
214
 
215
+ # Parse response
216
  response_text = completion.choices[0].message.content.strip()
217
  profile_data = json.loads(response_text)
218
 
219
+ # Create profile object
220
+ profile = {
221
  "profile_data": profile_data,
222
  "metadata": {
223
  "generated_at": datetime.now().isoformat(),
 
225
  }
226
  }
227
 
228
+ return profile
229
+
230
  except Exception as e:
231
+ logger.error(f"PDF processing error: {str(e)}")
232
  return {"error": str(e)}
233
 
234
+ async def generate_profile(self) -> tuple[Dict[str, Any], Optional[str]]:
235
+ """Generate profile from conversation or PDF"""
236
+ try:
237
+ if not self.client:
238
+ raise ValueError("OpenAI client not initialized")
239
+
240
+ # Determine source and prepare content
241
+ if self.conversation_history:
242
+ content = "\n".join(
243
+ f"{msg['role']}: {msg['content']}"
244
+ for msg in self.conversation_history
245
+ )
246
+ source = "conversation"
247
+ elif self.pdf_text:
248
+ content = self.pdf_text
249
+ source = "pdf"
250
+ else:
251
+ raise ValueError("No content available for profile generation")
252
+
253
+ # Get AI extraction
254
+ completion = await self.client.chat.completions.create(
255
+ model="gpt-4o-mini",
256
+ messages=[
257
+ {"role": "system", "content": EXTRACTION_PROMPT},
258
+ {"role": "user", "content": f"Extract profile information from this {source}:\n\n{content}"}
259
+ ],
260
+ temperature=0.3
261
+ )
262
+
263
+ # Parse response
264
+ response_text = completion.choices[0].message.content.strip()
265
+ profile_data = json.loads(response_text)
266
+
267
+ # Create profile
268
+ profile = {
269
+ "profile_data": profile_data,
270
+ "metadata": {
271
+ "generated_at": datetime.now().isoformat(),
272
+ "source": source
273
+ }
274
+ }
275
+
276
+ # Save to file
277
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
278
+ filename = f"profile_{timestamp}.json"
279
+ with open(filename, 'w', encoding='utf-8') as f:
280
+ json.dump(profile, f, indent=2)
281
+
282
+ return profile, filename
283
+
284
+ except Exception as e:
285
+ logger.error(f"Profile generation error: {str(e)}")
286
+ return {"error": str(e)}, None
287
 
288
  def create_gradio_interface():
289
  builder = ProfileBuilder()
 
291
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
292
  gr.Markdown("# 🐕 LOSS DOG - Professional Profile Builder")
293
 
294
+ # Common API key input
295
  api_key = gr.Textbox(
296
  label="OpenAI API Key",
297
  type="password",
298
  placeholder="Enter your OpenAI API key"
299
  )
300
 
301
+ # Tab interface
302
  with gr.Tabs() as tabs:
303
+ # Resume Upload Tab
304
  with gr.Tab("Upload Resume"):
305
+ gr.Markdown("""
306
  # Upload Your Resume
307
  Upload your existing resume in PDF format and let LOSS DOG extract your professional profile.
308
  """)
 
312
  )
313
  process_pdf_btn = gr.Button("Process Resume")
314
 
315
+ # Chat Tab
316
  with gr.Tab("Chat with AI"):
317
+ gr.Markdown("""
318
  # Chat with LOSS DOG
319
  Start a conversation with LOSS DOG to build your professional profile from scratch.
320
  """)
321
+ chatbot = gr.Chatbot(
322
+ label="Conversation",
323
+ height=400
324
+ )
325
  with gr.Row():
326
  msg = gr.Textbox(
327
  label="Message",
 
329
  )
330
  send = gr.Button("Send")
331
 
332
+ # Common output section
333
  with gr.Column():
334
+ generate_btn = gr.Button("Generate Profile", variant="primary")
335
  profile_output = gr.JSON(label="Generated Profile")
336
  download_btn = gr.File(label="Download Profile")
337
 
338
+ # Event handlers
339
+ async def on_message(message: str, history: List[List[str]], key: str):
340
+ if not message.strip():
341
+ return history, None, None
342
+
343
+ result = await builder.process_message(message, key)
344
+
345
+ if "error" in result:
346
+ return history, {"error": result["error"]}, None
347
+
348
+ new_history = history + [[message, result["response"]]]
349
+ return new_history, None, None
350
+
351
  async def on_pdf_upload(pdf, key):
352
  if not pdf:
353
+ return {"error": "No PDF file uploaded"}, None
354
 
355
  try:
356
  result = await builder.process_pdf(pdf.name, key)
 
367
  except Exception as e:
368
  return {"error": str(e)}, None
369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  async def on_generate():
371
  profile, filename = await builder.generate_profile()
372
  if "error" in profile:
373
+ return {"error": profile["error"]}, None
374
  return profile["profile_data"], filename
375
 
376
  # Bind events
377
  msg.submit(
378
  on_message,
379
  inputs=[msg, chatbot, api_key],
380
+ outputs=[chatbot, profile_output, msg]
381
+ )
382
 
383
  send.click(
384
  on_message,
385
  inputs=[msg, chatbot, api_key],
386
+ outputs=[chatbot, profile_output, msg]
387
+ )
388
 
389
  process_pdf_btn.click(
390
  on_pdf_upload,
 
402
  if __name__ == "__main__":
403
  demo = create_gradio_interface()
404
  demo.queue()
405
+ demo.launch(
406
+ server_name="0.0.0.0",
407
+ server_port=7860,
408
+ share=True
409
+ )