katialira commited on
Commit
0f867da
·
verified ·
1 Parent(s): 1511aff

Setup DIGITAL twin

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +281 -0
  3. klira.png +3 -0
  4. requirements.txt +4 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ klira.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import json
4
+ import random
5
+ import chromadb
6
+ import requests
7
+ import gradio as gr
8
+ from openai import OpenAI
9
+ from pprint import pprint
10
+
11
+ #------------------------------
12
+ #- SETUP
13
+ #------------------------------
14
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
15
+ if OPENAI_API_KEY is None:
16
+ raise ValueError("OpenAI key not found")
17
+
18
+ PUSHOVER_USER_KEY = os.getenv("PUSHOVER_USER_KEY")
19
+ PUSHOVER_API_TOKEN = os.getenv("PUSHOVER_API_TOKEN")
20
+ PUSHOVER_URL = "https://api.pushover.net/1/messages.json"
21
+
22
+ client = OpenAI()
23
+
24
+ #------------------------------
25
+ #- Load documents
26
+ #------------------------------
27
+ doc_personal_info ="""
28
+ Here's facts about Katia:
29
+ - Has one sister.
30
+ - She is a software engineer and AI enthusiast.
31
+ - Her favorite animal are dogs, especially her dogs named "Robin" and "Mila", both are living with her parents in Mexico City.
32
+ - She is a fan of the TV show "Brooklyn Nine-Nine" and has watched it multiple times.
33
+ - Katia still doesn't know how to spell engineer.
34
+
35
+ Communication style:
36
+ - Katia is a very friendly and approachable person. She is always willing to help others and is known for her positive attitude and sense of humor.
37
+ - She is a good listener.
38
+ - If she can, she will try to convince you to go to the gym with her.
39
+ """
40
+
41
+ doc_education_and_experience ="""
42
+ Career history:
43
+ - Katia has been working as a software engineer for a long time.
44
+ - Started her career as a web designer, moved to design and develop wordpress websites.
45
+ - From there learnt PHP and started working as a backend developer.
46
+ - Moved again from PHP to Python and started working as a full stack developer in Django.
47
+ - Moved once again from Python to JavaScript and started working as a vanilla JS frontend developer.
48
+ - 4 years ago she started working with React and has been working with it ever since.
49
+ - 2004-2007: Studied Computer Science at Instituto Politécnico Nacional in Mexico City.
50
+ - 2008-2011: Studied Digital design.
51
+ - 2014-2017: Katia was working at McCann Worldgroup as a Developer using Python and Django.
52
+ - 2017-2019: Katia worked as a web engineer at a NYC design agency from their Mexico City office.
53
+ - 2020-2026: Works at LL, based in Montreal, Canada. She is a senior frontend engineer and has been working with React for the past 4 years.
54
+ """
55
+
56
+ doc_food_choices ="""
57
+ Katia grew up in Mexico City eating tacos of any kind a few times every week.\
58
+ Her favorite tacos are al pastor tacos and her mom's golden chicken tacos with guacamole.\
59
+ One of the downsides of living in Canada is that she can't find good and cheap tacos\
60
+ whenever the craving hits her.
61
+ Her least favorite food is poached eggs, eew.
62
+ She drinks coffee every morning and looks forward to hot sunny days to get an iced coffee in the afternoon.
63
+ """
64
+
65
+ doc_hobbies ="""
66
+ - She loves to read books. Her goal this year is to read 36 books, so far she's behind schedule, but she is determined to catch up.
67
+ - She just ran a 4k in 40 minutes, her personal record.
68
+ - Katia does crossfit 3 times a week. And every Sunday goes to a weightlifting class.
69
+ """
70
+
71
+ #------------------------------
72
+ #- Chunking documents
73
+ #------------------------------
74
+ def chunk_text(text, chunk_size=450, overlap=50):
75
+ chunks = []
76
+ for i in range(0, len(text), chunk_size - overlap):
77
+ chunk = text[i:i + chunk_size]
78
+ chunks.append(chunk)
79
+ return chunks
80
+
81
+ #------------------------------
82
+ #- RAG everything
83
+ #------------------------------
84
+ documents = [
85
+ {"text": doc_personal_info,"source": "KL Personal Info",},
86
+ {"text": doc_education_and_experience,"source": "KL Education and Experience",},
87
+ {"text": doc_food_choices,"source": "KL Food Choices",},
88
+ {"text": doc_hobbies,"source": "KL Hobbies",}
89
+ ]
90
+
91
+ chunks = []
92
+ ids = []
93
+ metadatas = []
94
+
95
+ for doc in documents:
96
+ chunks_ = chunk_text(doc["text"])
97
+ ids_ = [str(uuid.uuid4()) for _ in range(len(chunks_))]
98
+ metadata_ = [{"source": doc["source"], "chunk_index": i} for i in range(len(chunks_))]
99
+ chunks.extend(chunks_)
100
+ ids.extend(ids_)
101
+ metadatas.extend(metadata_)
102
+
103
+ # Print for logs
104
+ print(len(chunks))
105
+ for i, chunk in enumerate(chunks):
106
+ print(f"-- chunk {i+1} (ID: {ids[i]}) -- s: {metadatas[i]['source']} i: {metadatas[i]['chunk_index']} --")
107
+ print(f"{chunk}... \n")
108
+
109
+ # Generate embeddings for the chunks
110
+ response = client.embeddings.create(
111
+ model="text-embedding-3-small",
112
+ input=chunks
113
+ )
114
+
115
+ embeddings = [item.embedding for item in response.data]
116
+ # Verify embeddings for logs
117
+ print(f"Generated {len(embeddings)}")
118
+ print(f"Each embedding has {len(embeddings[0])} vectors")
119
+
120
+ # Initialize in file (Persistent storage)
121
+ chroma_client = chromadb.PersistentClient("./katiatwin_db")
122
+
123
+ # Alternative: Initialize in memory storage
124
+ # chroma_client = chromadb.Client()
125
+
126
+ # Get or Create + empty collection
127
+ collection = chroma_client.get_or_create_collection(name="digital_twin_kl")
128
+ if collection.get()["ids"]:
129
+ collection.delete(collection.get()["ids"])
130
+
131
+ # Prepare data for storage
132
+ collection.add(
133
+ ids=ids,
134
+ metadatas=metadatas,
135
+ documents=chunks,
136
+ embeddings=embeddings
137
+ )
138
+ # Collection logs
139
+ pprint(collection.get())
140
+
141
+ #------------------------------
142
+ #- Tools
143
+ #------------------------------
144
+ def send_notifications(message: str):
145
+ if PUSHOVER_USER_KEY is None or PUSHOVER_API_TOKEN is None:
146
+ return("Notification not sent: Pushover not configured correctly.")
147
+ payload = {"user": PUSHOVER_USER_KEY, "token": PUSHOVER_API_TOKEN, "message": message}
148
+ requests.post(PUSHOVER_URL, data = payload)
149
+ return(f"Notification sent: {message}")
150
+
151
+ # Describe pushover tool for LLM
152
+ send_notifications_function = {
153
+ "name": "send_notifications",
154
+ "description": "Send notification to the real Katia via Pushover when:\
155
+ 1. Someone wants to get in touch with her or collaborate on a project. Ask their contact name and email and only send the notification if they provide it.\
156
+ 2. You don't know the answer to a question and want to ask her for help. Send AUTOMATICALLY for Katia to add the answer",
157
+ "parameters": {
158
+ "type": "object",
159
+ "properties": {
160
+ "message": {"type": "string", "description": "The notification message to send to the user."}
161
+ },
162
+ "required": ["message"]
163
+ }
164
+ }
165
+
166
+ def dice_roll():
167
+ return random.randint(1,6)
168
+
169
+ # Define roll dice function
170
+ roll_dice_function = {
171
+ "name": "dice_roll",
172
+ "description": "Simulate a dice roll",
173
+ "parameters": {"type": "object","properties": {}, "required": []}
174
+ }
175
+
176
+ tools = [
177
+ {"type": "function", "function": send_notifications_function},
178
+ {"type": "function", "function": roll_dice_function}
179
+ ]
180
+
181
+ #------------------------------
182
+ #- Tool Handler
183
+ #------------------------------
184
+ def handle_tool_call(tool_calls):
185
+ tool_results = []
186
+ for tool_call in tool_calls:
187
+ tool_name = tool_call.function.name
188
+ args = json.loads(tool_call.function.arguments)
189
+ # print(f"Tool name {tool_name}")
190
+ # Route to appropriate function based on the tool name
191
+ if tool_name == "send_notifications":
192
+ arg_message = args["message"]
193
+ tool_content = send_notifications(arg_message)
194
+ # print(f"(2) Sent notification: {arg_message}")
195
+ elif tool_name == "dice_roll":
196
+ tool_content = f"Dice roll result: {dice_roll()}"
197
+ # print(f"(3) {tool_content}")
198
+ else:
199
+ tool_content = f"Unknown tool: {tool_name}"
200
+ tool_call_response = {
201
+ "role": "tool",
202
+ "content":tool_content,
203
+ "tool_call_id": tool_call.id,
204
+ }
205
+ tool_results.append(tool_call_response)
206
+ return tool_results
207
+
208
+ #------------------------------
209
+ #- System message
210
+ #------------------------------
211
+ system_message="""You are a digital twin of Katia Lira.
212
+ When people talk to you, respond as Katia - in first person, using her personality, and knowledge.
213
+
214
+ Important: do not make things up. If you don't know the answer, say "I don't know".
215
+ The only factual information available to you is what's in the system message.
216
+ You cannot get any information from the internet, any other source or make them up.
217
+
218
+ IMPORTANT: Whenever you don't know the answer to a question about Katia,
219
+ always send a notification to her via Pushover with the question and ask her for help without letting the user know.
220
+ """
221
+
222
+ #------------------------------
223
+ #- Main response function
224
+ #------------------------------
225
+ def respond_ai(message, history):
226
+ # RAG: Embed the query
227
+ response = client.embeddings.create(
228
+ model="text-embedding-3-small",
229
+ input=[message]
230
+ )
231
+ query_embedding = response.data[0].embedding
232
+ # RAG: Search query in chromaDB
233
+ results = collection.query(
234
+ n_results=3,
235
+ query_embeddings=[query_embedding]
236
+ )
237
+ # RAG: Stitch retrieved chunks together to create the context for the response
238
+ context = "\n--\n".join(results["documents"][0])
239
+ # RAG: Print logs for debugging
240
+ print(f"**User message: {message}** \n<<Retrieved chunks>>")
241
+ for a,b in zip(results["documents"][0], results["metadatas"][0]):
242
+ print(f"<Doc: {b['source']} -- Chunk: {b['chunk_index']}>\n{a}\n")
243
+
244
+ # Update system message with context and prepare messages for LLM
245
+ system_message_enhanced = system_message + context
246
+ messages = [{"role": "system", "content": system_message_enhanced}] + history + [{"role": "user", "content": message}]
247
+ # Call LLM to get a response
248
+ response = client.chat.completions.create(
249
+ model="gpt-4.1-mini",
250
+ messages=messages,
251
+ tools=tools,
252
+ )
253
+ message = response.choices[0].message
254
+
255
+ # Check if the LLM wants to call a tool
256
+ while message.tool_calls:
257
+ from pprint import pprint
258
+ pprint(message.tool_calls)
259
+
260
+ tool_results = handle_tool_call(message.tool_calls)
261
+ messages.append(message)
262
+ messages.extend(tool_results)
263
+ response = client.chat.completions.create(
264
+ model="gpt-4.1-mini",
265
+ messages=messages,
266
+ )
267
+ message = response.choices[0].message
268
+
269
+ return(message.content)
270
+
271
+ #------------------------------
272
+ #- Launch gradio
273
+ #------------------------------
274
+
275
+ gr.ChatInterface(
276
+ fn=respond_ai,
277
+ title="Katia's Digital Twin",
278
+ chatbot=gr.Chatbot(avatar_images=(None, "klira.png")),
279
+ description="This is a digital twin of Katia Lira. You can ask her questions about her life, hobbies, and experiences. If she doesn't know the answer, she will send a notification to the real Katia for help.",
280
+ examples=["What are your favorite hobbies?", "What's your favorite food?"],
281
+ ).launch()
klira.png ADDED

Git LFS Details

  • SHA256: 4c948b8264165b06d65951bf91dcc66ecbd24fdb044804b12f2148e2721cbbf6
  • Pointer size: 131 Bytes
  • Size of remote file: 354 kB
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ openai
3
+ chromadb
4
+ requests