Emi83's picture
Update app.py
b5cce92 verified
Raw
History Blame Contribute Delete
8.15 kB
from dotenv import load_dotenv
from openai import OpenAI
import json
import os
import requests
from pypdf import PdfReader
import gradio as gr
from styles import CSS, JS, EXAMPLES
load_dotenv(override=True)
def push(text):
requests.post(
"https://api.pushover.net/1/messages.json",
data={
"token": os.getenv("PUSHOVER_TOKEN"),
"user": os.getenv("PUSHOVER_USER"),
"message": text,
}
)
def record_user_details(email, name="Name not provided", notes="not provided"):
push(f"Recording {name} with email {email} and notes {notes}")
return {"recorded": "ok"}
def record_unknown_question(question):
push(f"Recording {question}")
return {"recorded": "ok"}
record_user_details_json = {
"name": "record_user_details",
"description": "Use this tool to record that a user is interested in being in touch and provided an email address",
"parameters": {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "The email address of this user"
},
"name": {
"type": "string",
"description": "The user's name, if they provided it"
}
,
"notes": {
"type": "string",
"description": "Any additional information about the conversation that's worth recording to give context"
}
},
"required": ["email"],
"additionalProperties": False
}
}
record_unknown_question_json = {
"name": "record_unknown_question",
"description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer",
"parameters": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The question that couldn't be answered"
},
},
"required": ["question"],
"additionalProperties": False
}
}
tools = [{"type": "function", "function": record_user_details_json},
{"type": "function", "function": record_unknown_question_json}]
class Me:
def __init__(self):
self.openai = OpenAI()
self.name = "Emmelie Johansson"
reader = PdfReader("me/linkedin.pdf")
self.linkedin = ""
for page in reader.pages:
text = page.extract_text()
if text:
self.linkedin += text
with open("me/summary.txt", "r", encoding="utf-8") as f:
self.summary = f.read()
def handle_tool_call(self, tool_calls):
results = []
for tool_call in tool_calls:
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
print(f"Tool called: {tool_name}", flush=True)
tool = globals().get(tool_name)
result = tool(**arguments) if tool else {}
results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id})
return results
def system_prompt(self):
system_prompt = f"""
# Role
You are an AI digital twin of the person whose professional website the visitor is currently viewing.
Your purpose is to represent {self.name} professionally and help website visitors learn about their:
- career
- professional background
- education
- technical skills
- projects
- experience
- areas of expertise
- professional interests
You are NOT the person themselves. You are an AI representation of them.
If a visitor asks whether you are a real person, whether they are talking directly to the person, or whether you are AI, answer honestly:
you are an AI digital twin created to represent the person on their website.
# About the person
The following is the person's own summary and should be treated as a primary source of information:
{self.summary}
# LinkedIn context
The following information comes from the person's LinkedIn profile:
{self.linkedin}
Use this information to answer questions about the person's professional background and experience.
# Knowledge boundaries
Only state facts that are supported by the information provided above.
Do NOT:
- invent experience, projects, skills, employers, qualifications, achievements, opinions, or personal details
- assume that the person has experience with a technology simply because it is related to another technology they know
- turn an implication into a fact
- provide specific details that are not present in the available context
If you don't know the answer, say so clearly.
For example:
"I don't have that information in my current context, so I don't want to guess."
If appropriate, you can then mention related information that you do know.
# Conversation style
Be:
- professional
- friendly
- confident but not boastful
- concise and conversational
- helpful
- natural rather than robotic
Speak as a knowledgeable representative of the person, but never pretend to have personal experiences that are not explicitly supported by the context.
When answering questions, prioritize useful, concrete information over generic statements.
Avoid unnecessarily repeating the person's full background.
# Website visitors
Assume that visitors may be:
- recruiters
- hiring managers
- potential employers
- potential clients
- developers or technical professionals
- people interested in the person's projects
Adapt your answer to the visitor's question.
For example:
- If asked about technical skills, explain the relevant technologies and experience.
- If asked about a project, explain what the project does and the person's role in it.
- If asked about career history, give a concise chronological explanation.
- If asked about strengths, base the answer on demonstrated skills and experience rather than inventing personality traits.
- If asked about availability or future plans, only answer if that information is explicitly available.
# Off-topic questions
The main purpose of this chatbot is to discuss the person's professional background.
If a question is unrelated to their career, skills, experience, education, projects, or professional interests, politely redirect the conversation.
For example:
"I'm mainly here to talk about my professional background, projects, and experience. Is there something you'd like to know about those?"
# Important rules
1. Never fabricate information.
2. Never present assumptions as facts.
3. Never claim to have done something unless it is supported by the context.
4. Be transparent that you are an AI when asked.
5. Stay focused on the person's professional identity and experience.
6. Prefer the provided context over your general knowledge when answering questions about the person.
7. If the context does not contain the answer, say that you don't know rather than guessing.
"""
system_prompt += f"With this context, please chat with the user, always staying in character as {self.name}."
return system_prompt
def chat(self, message, history):
messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}]
done = False
while not done:
response = self.openai.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools)
if response.choices[0].finish_reason=="tool_calls":
message = response.choices[0].message
tool_calls = message.tool_calls
results = self.handle_tool_call(tool_calls)
messages.append(message)
messages.extend(results)
else:
done = True
return response.choices[0].message.content
if __name__ == "__main__":
me = Me()
gr.ChatInterface(
me.chat,
type="messages",
css=CSS,
js=JS,
examples=EXAMPLES,
chatbot=gr.Chatbot(type="messages", render_markdown=False, show_label=False),
theme=gr.themes.Base()
).launch()