Spaces:
Sleeping
Sleeping
snleela
commited on
Commit
·
e9907ee
1
Parent(s):
3147fcb
init commit
Browse files- .gitignore +1 -0
- Profile.pdf +0 -0
- README.md +3 -9
- app.py +144 -0
- requirements.txt +6 -0
- summary.txt +21 -0
- unknown_questions.csv +1 -0
- user_details.csv +1 -0
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
mycaag/*
|
Profile.pdf
ADDED
|
Binary file (61.5 kB). View file
|
|
|
README.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji: 🏆
|
| 4 |
-
colorFrom: purple
|
| 5 |
-
colorTo: blue
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 5.44.1
|
| 8 |
app_file: app.py
|
| 9 |
-
|
|
|
|
| 10 |
---
|
| 11 |
-
|
| 12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
---
|
| 2 |
+
title: career-chatbot
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
app_file: app.py
|
| 4 |
+
sdk: gradio
|
| 5 |
+
sdk_version: 4.44.1
|
| 6 |
---
|
|
|
|
|
|
app.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dotenv import load_dotenv
|
| 2 |
+
from openai import OpenAI
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from pypdf import PdfReader
|
| 6 |
+
import gradio as gr
|
| 7 |
+
import csv
|
| 8 |
+
|
| 9 |
+
# Load environment variables
|
| 10 |
+
load_dotenv(override=True)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
|
| 14 |
+
google_api_key = os.getenv("GOOGLE_API_KEY")
|
| 15 |
+
gemini = OpenAI(base_url=GEMINI_BASE_URL, api_key=google_api_key)
|
| 16 |
+
|
| 17 |
+
# CSV files for logging
|
| 18 |
+
USER_CSV = "user_details.csv"
|
| 19 |
+
UNKNOWN_CSV = "unknown_questions.csv"
|
| 20 |
+
|
| 21 |
+
# Ensure CSV files exist with headers
|
| 22 |
+
for file, headers in [(USER_CSV, ["email", "name", "notes"]),
|
| 23 |
+
(UNKNOWN_CSV, ["question"])]:
|
| 24 |
+
if not os.path.exists(file):
|
| 25 |
+
with open(file, "w", newline="", encoding="utf-8") as f:
|
| 26 |
+
writer = csv.writer(f)
|
| 27 |
+
writer.writerow(headers)
|
| 28 |
+
|
| 29 |
+
# Functions to log user details and unknown questions
|
| 30 |
+
def record_user_details(email, name="Name not provided", notes="not provided"):
|
| 31 |
+
"""Record user info to CSV"""
|
| 32 |
+
with open(USER_CSV, "a", newline="", encoding="utf-8") as f:
|
| 33 |
+
writer = csv.writer(f)
|
| 34 |
+
writer.writerow([email, name, notes])
|
| 35 |
+
return {"recorded": "ok"}
|
| 36 |
+
|
| 37 |
+
def record_unknown_question(question):
|
| 38 |
+
"""Record unanswered question to CSV"""
|
| 39 |
+
with open(UNKNOWN_CSV, "a", newline="", encoding="utf-8") as f:
|
| 40 |
+
writer = csv.writer(f)
|
| 41 |
+
writer.writerow([question])
|
| 42 |
+
return {"recorded": "ok"}
|
| 43 |
+
|
| 44 |
+
# JSON definitions for tools
|
| 45 |
+
record_user_details_json = {
|
| 46 |
+
"name": "record_user_details",
|
| 47 |
+
"description": "Use this tool to record that a user is interested in being in touch and provided an email address",
|
| 48 |
+
"parameters": {
|
| 49 |
+
"type": "object",
|
| 50 |
+
"properties": {
|
| 51 |
+
"email": {"type": "string", "description": "The email address of this user"},
|
| 52 |
+
"name": {"type": "string", "description": "The user's name, if provided"},
|
| 53 |
+
"notes": {"type": "string", "description": "Any additional information to provide context"}
|
| 54 |
+
},
|
| 55 |
+
"required": ["email"],
|
| 56 |
+
"additionalProperties": False
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
record_unknown_question_json = {
|
| 61 |
+
"name": "record_unknown_question",
|
| 62 |
+
"description": "Always use this tool to record any question that couldn't be answered",
|
| 63 |
+
"parameters": {
|
| 64 |
+
"type": "object",
|
| 65 |
+
"properties": {
|
| 66 |
+
"question": {"type": "string", "description": "The question that couldn't be answered"},
|
| 67 |
+
},
|
| 68 |
+
"required": ["question"],
|
| 69 |
+
"additionalProperties": False
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
tools = [
|
| 74 |
+
{"type": "function", "function": record_user_details_json},
|
| 75 |
+
{"type": "function", "function": record_unknown_question_json}
|
| 76 |
+
]
|
| 77 |
+
|
| 78 |
+
# Main class
|
| 79 |
+
class Me:
|
| 80 |
+
def __init__(self):
|
| 81 |
+
self.openai = gemini
|
| 82 |
+
self.name = "SnehaLeela"
|
| 83 |
+
|
| 84 |
+
# Load LinkedIn PDF text
|
| 85 |
+
reader = PdfReader("profile.pdf")
|
| 86 |
+
self.linkedin = ""
|
| 87 |
+
for page in reader.pages:
|
| 88 |
+
text = page.extract_text()
|
| 89 |
+
if text:
|
| 90 |
+
self.linkedin += text
|
| 91 |
+
|
| 92 |
+
# Load summary text
|
| 93 |
+
with open("./summary.txt", "r", encoding="utf-8") as f:
|
| 94 |
+
self.summary = f.read()
|
| 95 |
+
|
| 96 |
+
# Handle tool calls from LLM
|
| 97 |
+
def handle_tool_call(self, tool_calls):
|
| 98 |
+
results = []
|
| 99 |
+
for tool_call in tool_calls:
|
| 100 |
+
tool_name = tool_call.function.name
|
| 101 |
+
arguments = json.loads(tool_call.function.arguments)
|
| 102 |
+
tool = globals().get(tool_name)
|
| 103 |
+
result = tool(**arguments) if tool else {}
|
| 104 |
+
results.append({"role": "tool", "content": json.dumps(result), "tool_call_id": tool_call.id})
|
| 105 |
+
return results
|
| 106 |
+
|
| 107 |
+
# System prompt for LLM
|
| 108 |
+
def system_prompt(self):
|
| 109 |
+
system_prompt = (
|
| 110 |
+
f"You are acting as {self.name}. You are answering questions on {self.name}'s website, "
|
| 111 |
+
f"particularly questions related to {self.name}'s career, background, skills and experience. "
|
| 112 |
+
f"Your responsibility is to represent {self.name} for interactions on the website as faithfully as possible. "
|
| 113 |
+
f"If you don't know the answer to any question, use your record_unknown_question tool to record it. "
|
| 114 |
+
f"If the user is engaging in discussion, try to steer them towards providing their email using record_user_details tool."
|
| 115 |
+
f"\n\n## Summary:\n{self.summary}\n\n## LinkedIn Profile:\n{self.linkedin}\n\n"
|
| 116 |
+
f"With this context, please chat with the user, always staying in character as {self.name}."
|
| 117 |
+
)
|
| 118 |
+
return system_prompt
|
| 119 |
+
|
| 120 |
+
# Main chat function
|
| 121 |
+
def chat(self, message, history):
|
| 122 |
+
messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}]
|
| 123 |
+
done = False
|
| 124 |
+
while not done:
|
| 125 |
+
#response = self.openai.chat.completions.create(
|
| 126 |
+
response = self.openai.chat.completions.create(
|
| 127 |
+
model="gemini-2.5-flash-preview-05-20",
|
| 128 |
+
messages=messages,
|
| 129 |
+
tools=tools
|
| 130 |
+
)
|
| 131 |
+
if response.choices[0].finish_reason == "tool_calls":
|
| 132 |
+
message = response.choices[0].message
|
| 133 |
+
tool_calls = message.tool_calls
|
| 134 |
+
results = self.handle_tool_call(tool_calls)
|
| 135 |
+
messages.append(message)
|
| 136 |
+
messages.extend(results)
|
| 137 |
+
else:
|
| 138 |
+
done = True
|
| 139 |
+
return response.choices[0].message.content
|
| 140 |
+
|
| 141 |
+
# Launch Gradio chat interface
|
| 142 |
+
if __name__ == "__main__":
|
| 143 |
+
me = Me()
|
| 144 |
+
gr.ChatInterface(me.chat, type="messages").launch()
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
requests
|
| 2 |
+
python-dotenv
|
| 3 |
+
gradio
|
| 4 |
+
pypdf
|
| 5 |
+
openai
|
| 6 |
+
openai-agents
|
summary.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
My name is Sneha Leela and all my close friends call me Lee.I'm a Senior Technical Product Specialist with over 10 years of experience in AI, Data Science, and MLOps, and the last 3 years deeply focused on genomics and healthcare technology at M42. I’ve had the unique opportunity to work across engineering, bioinformatics, and product teams, giving me a 360° view of how to build impactful, scalable, and compliant tech solutions in healthcare.
|
| 2 |
+
|
| 3 |
+
My day-to-day involves bridging the gap between business and tech — defining technical flows, leading vendor evaluations, supporting RFPs, and working closely with AI, infra, and clinical teams to drive our life sciences platform forward. I’m most energized when solving complex problems, connecting the dots across teams, or simplifying a messy product spec into an actionable solution.
|
| 4 |
+
|
| 5 |
+
Outside of work, I’m all about traveling, staying active with sports (🏸 badminton is my favorite!), and competing just a little — I’ve won an average of 4 medals at G42 Sports Day in each of the past two years. Whether it’s hiking, planning quick weekend getaways, or learning something new, I love keeping life curious and balanced.
|
| 6 |
+
|
| 7 |
+
Last five years travel history (country names) UAE
|
| 8 |
+
Oman
|
| 9 |
+
Jordan
|
| 10 |
+
Serbia
|
| 11 |
+
Armenia
|
| 12 |
+
Italy
|
| 13 |
+
Bulgaria
|
| 14 |
+
Sweden
|
| 15 |
+
Finland
|
| 16 |
+
Georgia
|
| 17 |
+
France
|
| 18 |
+
Switzerland
|
| 19 |
+
Netherlands
|
| 20 |
+
kyrgyzstan
|
| 21 |
+
Greece
|
unknown_questions.csv
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
questionWho am I?
|
user_details.csv
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
email,name,notes
|