esteinholtz commited on
Commit
9dc6e68
·
1 Parent(s): e1d0ffd
Files changed (3) hide show
  1. app.py +160 -0
  2. pyproject.toml +46 -0
  3. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ from openai import OpenAI
3
+ import json
4
+ import os
5
+ import requests
6
+ from pypdf import PdfReader
7
+ import gradio as gr
8
+
9
+
10
+ load_dotenv(override=True)
11
+
12
+ def push(text):
13
+ requests.post(
14
+ "https://api.pushover.net/1/messages.json",
15
+ data={
16
+ "token": os.getenv("PUSHOVER_TOKEN"),
17
+ "user": os.getenv("PUSHOVER_USER"),
18
+ "message": text,
19
+ }
20
+ )
21
+
22
+
23
+ def record_user_details(email, name="Name not provided", notes="not provided"):
24
+ push(f"Recording {name} with email {email} and notes {notes}")
25
+ return {"recorded": "ok"}
26
+
27
+ def record_unknown_question(question):
28
+ push(f"Recording {question}")
29
+ return {"recorded": "ok"}
30
+
31
+ record_user_details_json = {
32
+ "name": "record_user_details",
33
+ "description": "Use this tool to record that a user is interested in being in touch and provided an email address",
34
+ "parameters": {
35
+ "type": "object",
36
+ "properties": {
37
+ "email": {
38
+ "type": "string",
39
+ "description": "The email address of this user"
40
+ },
41
+ "name": {
42
+ "type": "string",
43
+ "description": "The user's name, if they provided it"
44
+ }
45
+ ,
46
+ "notes": {
47
+ "type": "string",
48
+ "description": "Any additional information about the conversation that's worth recording to give context"
49
+ }
50
+ },
51
+ "required": ["email"],
52
+ "additionalProperties": False
53
+ }
54
+ }
55
+
56
+ record_unknown_question_json = {
57
+ "name": "record_unknown_question",
58
+ "description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer",
59
+ "parameters": {
60
+ "type": "object",
61
+ "properties": {
62
+ "question": {
63
+ "type": "string",
64
+ "description": "The question that couldn't be answered"
65
+ },
66
+ },
67
+ "required": ["question"],
68
+ "additionalProperties": False
69
+ }
70
+ }
71
+
72
+ tools = [{"type": "function", "function": record_user_details_json},
73
+ {"type": "function", "function": record_unknown_question_json}]
74
+
75
+
76
+ class Me:
77
+
78
+ def __init__(self):
79
+ self.openai = OpenAI()
80
+ self.name = "Erik Steinholtz"
81
+ reader = PdfReader("me/linkedin.pdf")
82
+ self.linkedin = ""
83
+ for page in reader.pages:
84
+ text = page.extract_text()
85
+ if text:
86
+ self.linkedin += text
87
+ with open("me/summary.txt", "r", encoding="utf-8") as f:
88
+ self.summary = f.read()
89
+
90
+
91
+ def handle_tool_call(self, tool_calls):
92
+ results = []
93
+ for tool_call in tool_calls:
94
+ tool_name = tool_call.function.name
95
+ arguments = json.loads(tool_call.function.arguments)
96
+ print(f"Tool called: {tool_name}", flush=True)
97
+ tool = globals().get(tool_name)
98
+ result = tool(**arguments) if tool else {}
99
+ results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id})
100
+ return results
101
+
102
+ def system_prompt(self):
103
+ system_prompt = f"You are acting as {self.name}. You are answering questions on {self.name}'s website, \
104
+ particularly questions related to {self.name}'s career, background, skills and experience. \
105
+ Your responsibility is to represent {self.name} for interactions on the website as faithfully as possible. \
106
+ You are given a summary of {self.name}'s background and LinkedIn profile which you can use to answer questions. \
107
+ Be professional and engaging, as if talking to a potential client or future employer who came across the website. \
108
+ If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \
109
+ If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. "
110
+
111
+ system_prompt += f"\n\n## Summary:\n{self.summary}\n\n## LinkedIn Profile:\n{self.linkedin}\n\n"
112
+ system_prompt += f"With this context, please chat with the user, always staying in character as {self.name}."
113
+ return system_prompt
114
+
115
+ def chat(self, message, history):
116
+ messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}]
117
+ done = False
118
+ while not done:
119
+ response = self.openai.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools)
120
+ if response.choices[0].finish_reason=="tool_calls":
121
+ message = response.choices[0].message
122
+ tool_calls = message.tool_calls
123
+ results = self.handle_tool_call(tool_calls)
124
+ messages.append(message)
125
+ messages.extend(results)
126
+ else:
127
+ done = True
128
+ return response.choices[0].message.content
129
+
130
+
131
+ if __name__ == "__main__":
132
+ import gradio as gr
133
+
134
+ # Header message
135
+ me = Me()
136
+ header = gr.Markdown("# CV chat with Erik Steinholtz")
137
+
138
+ # Your picture (local file or URL)
139
+ # If using a local file, make sure 'me/ESfoto.jpg' is in the same directory as app.py
140
+ picture = gr.Image("me/ESfoto.jpg", label="About Me", show_label=False, show_download_button=False, elem_id="profile-pic")
141
+
142
+ def greet(message, history):
143
+ # Placeholder function; replace with actual chat logic
144
+ return "Hello! what do you want to know about me?"
145
+
146
+ with gr.Blocks() as demo:
147
+ with gr.Row():
148
+ with gr.Column(scale=1):
149
+ header.render()
150
+ picture.render()
151
+ with gr.Column(scale=2):
152
+ gr.ChatInterface(
153
+ me.chat,
154
+ title="Informational dialogue",
155
+ type="messages"
156
+ #greet_message="Good day! what do you want to know about me?"
157
+ )
158
+
159
+
160
+ demo.launch()
pyproject.toml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "agents"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "anthropic>=0.49.0",
9
+ "autogen-agentchat>=0.4.9.2",
10
+ "autogen-ext[grpc,mcp,ollama,openai]>=0.4.9.2",
11
+ "bs4>=0.0.2",
12
+ "gradio>=5.22.0",
13
+ "httpx>=0.28.1",
14
+ "ipywidgets>=8.1.5",
15
+ "langchain-anthropic>=0.3.10",
16
+ "langchain-community>=0.3.20",
17
+ "langchain-experimental>=0.3.4",
18
+ "langchain-openai>=0.3.9",
19
+ "langgraph>=0.3.18",
20
+ "langgraph-checkpoint-sqlite>=2.0.6",
21
+ "langsmith>=0.3.18",
22
+ "lxml>=5.3.1",
23
+ "mcp-server-fetch>=2025.1.17",
24
+ "mcp[cli]>=1.5.0",
25
+ "openai>=1.68.2",
26
+ "openai-agents>=0.0.15",
27
+ "playwright>=1.51.0",
28
+ "plotly>=6.0.1",
29
+ "polygon-api-client>=1.14.5",
30
+ "psutil>=7.0.0",
31
+ "pypdf>=5.4.0",
32
+ "pypdf2>=3.0.1",
33
+ "python-dotenv>=1.0.1",
34
+ "requests>=2.32.3",
35
+ "semantic-kernel>=1.25.0",
36
+ "sendgrid>=6.11.0",
37
+ "setuptools>=78.1.0",
38
+ "smithery>=0.1.0",
39
+ "speedtest-cli>=2.1.3",
40
+ "wikipedia>=1.4.0",
41
+ ]
42
+
43
+ [dependency-groups]
44
+ dev = [
45
+ "ipykernel>=6.29.5",
46
+ ]
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ requests
2
+ python-dotenv
3
+ gradio
4
+ pypdf
5
+ openai
6
+ openai-agents