Spaces:
No application file
No application file
Liss, Alex (NYC-HUG)
commited on
Commit
·
9657598
1
Parent(s):
ad7a3f9
executing task 1.3 very slow and steady, part 1
Browse files- docs/PRD.md +323 -0
- docs/Phase 1/Task 1.3 Memory & Persona Implementation.md +145 -0
- docs/Phase 1/z_Task 1.3 Memory & Persona Implementation_Attempt 1 and 2.md +352 -0
- docs/Phase 1/z_Task 1.3 Memory & Persona Implementation_Debug Plan_Attempt 1.md +401 -0
- docs/Phase 1/z_Task 1.3 Memory & Persona Implementation_Debug Plan_Attempt 2.md +98 -0
- z_utils/neo4j_cleanup.py +69 -0
- z_utils/persona_session_ids.json +4 -0
- z_utils/persona_uuids.json +4 -0
- z_utils/restart_space.py +50 -0
- z_utils/set_secrets.py +77 -0
- z_utils/zep_setup.py +445 -0
- z_utils/zep_test.py +63 -0
docs/PRD.md
ADDED
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Intelligent Fan Experiences AI POC:
|
2 |
+
Product Requirements Document
|
3 |
+
Date Started: Apr 2, 2025
|
4 |
+
Last Update: Apr 10, 2025
|
5 |
+
Owners: Alex Liss Chistopher Bunker
|
6 |
+
Technical Advisors: Bobby Knapp, Jon Hackett
|
7 |
+
Situation
|
8 |
+
From a strategic perspective, Intelligent Fan Experiences (IX) can be a game changer for Huge. It’s more than a new offering—it’s an on-ramp into an industry primed for reinvention. Sports organizations across leagues are grappling with aging fan bases, fragmented attention, and underleveraged data. IX gives us a way to frame AI not as a back-office efficiency play, but as a top-line growth lever tied to fan acquisition, retention, and monetization by appealing to new audiences (younger Millennial and Gen Z) with AI-native experiences. It positions Huge as a strategic innovation partner, not just a vendor.
|
9 |
+
It’s also a high-emotion, high-visibility entry point into a massive global industry. By showing up with IP that directly addresses sports clients’ most urgent fan challenges, IX can unlock net-new conversations across leagues, teams, venues, and sponsors. It’s a wedge we can use to open doors, prove our value fast, and expand from there—into media, entertainment, and beyond.
|
10 |
+
From a technical perspective, in order to scale up IX, we need to scale up our GTM operations especially around demonstrating our capabilities to win new business. Most of our amazing AI work (Google, BBVA, Schneider, Hublot, etc) is under NDA and can’t be shared. So, let’s proactively make a publicly demo-able AI application, which encapsulates our IX principles, to help accelerate our sales pipeline.
|
11 |
+
|
12 |
+
Our hypothesis is that:
|
13 |
+
By developing thought leadership content alongside AI demos we can SHOW (not tell) how we build IX and get clients excited about buying it
|
14 |
+
If we build proactively then helps us scale up delivery – leveraging an MCP-centric architecture and giving us an additional instance of our MCP / IX Accelerator
|
15 |
+
|
16 |
+
Objective
|
17 |
+
Deploy a one-two punch of an Intelligent Fan Experiences blog piece / presentation keynote along with an AI demo, with the AI demo to achieve the following:
|
18 |
+
|
19 |
+
[Experience Objective] Demonstrate a novel and impactful approach to solve existing challenges across the fan journey including:
|
20 |
+
Awareness: reach new fans, make them aware of the team / sport / league
|
21 |
+
Attraction – bring a fan to take action (e.g. watch a game, attend a game, buy merch, attend a watch party)
|
22 |
+
Attachment – bring a fan to take repeated action
|
23 |
+
Allegiance – fan becomes an evangelist and recruits others to become fans
|
24 |
+
[Sales Pipeline Objective] get clients interested in this, both new logos and organic, once the thought leadership + AI demo is complete, we can generate buzz, leads, and set-up inbound briefs through content distribution
|
25 |
+
E.g. a IFX whitepaper deployed online (measure reach, site visits)
|
26 |
+
Gated form to Unlock IFX for sports (capture emails, quantify warm leads)
|
27 |
+
Within the IFX white paper there’s a link fo the AI demo site (measure user engagement with that)
|
28 |
+
|
29 |
+
Experience Principles
|
30 |
+
Form Factor & Distribution:
|
31 |
+
For the purposes of doing a public demo, this AI experience will live as a website, (Thought starter – let’s consider how this could look like a mobile phone).
|
32 |
+
|
33 |
+
As part of the storytelling in the thought leadership piece, we should imagine this AI experience living with the existing platforms of the Team / League/ Fan Platform. (See FIFA 24 pitch example of a sparkly AI button a user can click while engaging with existing app / web ecosystem.) It should be mobile-native to reach younger Millenials and Gen Z, we know from our [FILL IN].
|
34 |
+
|
35 |
+
When starting the experience we would also ask the user to select one of a series of pre-created synthetic fan profiles. This allows us to demonstrate the unique capabilities of the experience when personalizing to user preferences, while also NOT requiring the human user to enter their personal information into the experience.
|
36 |
+
|
37 |
+
|
38 |
+
Non-negotiables:
|
39 |
+
Snackable
|
40 |
+
Seamless (natural language)
|
41 |
+
Smart (dynamic for what you need at that moment)
|
42 |
+
[Shoppable – not for this demo]
|
43 |
+
|
44 |
+
|
45 |
+
Non-negotiables:
|
46 |
+
The core principles of IX are Conversational, Anticipatory, Personal, and Future Ready. As we look to deploy this in Q2 2025, we need to ensure it’s sufficiently on the leading edge of AI innovation, including:
|
47 |
+
Multimodal outputs (images, assets)
|
48 |
+
It’s not enough to just connect some data to an LLM and let people chat with it. We need it to ✨, something like the following.
|
49 |
+
Old 👎
|
50 |
+
User: How did the 49ers do in their last game?
|
51 |
+
AI: The 49ers lost to the Minnesota Vikings by a score of 23-17 on September 10, 2024.
|
52 |
+
New 😇
|
53 |
+
User: How did the 49ers do in their last game?
|
54 |
+
AI:
|
55 |
+
|
56 |
+
The 49ers lost on the road to the Minnesota Vikings by a score of 23-17 on September 10, 2024. Click the link above to see the highlights.
|
57 |
+
The principle that this illustrates is that the AI output is not just multimodal and also dynamic to the user query. For example, a search for ‘tell about the star players on the 49ers’ would return an image of the player’s bio pic and stat snippets, link to social media handles, etc.
|
58 |
+
Personalized
|
59 |
+
Personalize the response to the users query based on profile and contextual data
|
60 |
+
For example, let’s imagine a (female?) user engaging with the app and looking to learn more about the players. Based on the user’s preferences, we’d know they follow Olivia Copelo (fashion influencer / married to running back Christin McCaffrey), and that recommendation is seamlessly surfaced to the user
|
61 |
+
For exploration: when a user starts the experience they select their level of experience (e.g. novice / intermediate / expert) – customize the responses, the content, the functionality based on that
|
62 |
+
We can show people what it looks like to ‘graduate’ from one level to the next, how they accrue points to ‘level up’
|
63 |
+
Conversational
|
64 |
+
The AI experience will be multi-modal and chat-oriented. When the user selects a pre-build persona they will have that fan’s preferences and history loaded
|
65 |
+
(on the back-end we’ll try implementing this with Zep for agent conversational memory).
|
66 |
+
We will also increment that memory graph with the actions taken by the user in the course of the session(s).
|
67 |
+
Back-end: MCP-centric structure aligned with our IX Tech Accelerator
|
68 |
+
The features of the app are built as MCP servers which are called by this app but can be reused across other apps, services, integrations, etc.
|
69 |
+
|
70 |
+
Scope
|
71 |
+
|
72 |
+
Primary Use Cases & Key Features:
|
73 |
+
|
74 |
+
Note – once we agree on the use cases below we should start breaking them down into actual features.
|
75 |
+
|
76 |
+
Stage
|
77 |
+
Job to be Done
|
78 |
+
Feature
|
79 |
+
Awareness
|
80 |
+
Come across something exciting
|
81 |
+
1. Team Search (E)
|
82 |
+
|
83 |
+
|
84 |
+
Get the vibe of the team
|
85 |
+
1. Team Search (A/ B)
|
86 |
+
|
87 |
+
|
88 |
+
See what everyone’s talking about
|
89 |
+
1. Team Search (C/D)
|
90 |
+
Attraction
|
91 |
+
Understand the rules
|
92 |
+
4. Rule Search
|
93 |
+
|
94 |
+
|
95 |
+
Learn about key players
|
96 |
+
2. Player Search
|
97 |
+
|
98 |
+
|
99 |
+
Start following the sport
|
100 |
+
TBD – Need to refine this – get notifications?
|
101 |
+
Attachment
|
102 |
+
Feel emotionally invested
|
103 |
+
3. Game Search
|
104 |
+
|
105 |
+
|
106 |
+
Personalized content
|
107 |
+
0. Persona Selection
|
108 |
+
|
109 |
+
|
110 |
+
Interact with other fans
|
111 |
+
5. Fan Community Search
|
112 |
+
Allegiant
|
113 |
+
TBD
|
114 |
+
|
115 |
+
|
116 |
+
|
117 |
+
|
118 |
+
Feature List
|
119 |
+
0. Persona Selection
|
120 |
+
When the user starts the app they will be presented with 3 fan personas which will represent novice/ casual, intermediate/ engaged and advanced / super fan profiles. The app will respond differently based on the selected profile.
|
121 |
+
(WIP throught: implement this through Zep so entering a persona_id selects a prebuilt memory and set of preferences for that persona, which comes through conversationally as well as in which content assets are retrieved for each user
|
122 |
+
Personalization routes
|
123 |
+
Based on the user’s platforms e.g. if someone is on IG, we should them IG profiles
|
124 |
+
Ability to opt in / opt out
|
125 |
+
Log in through your social platform
|
126 |
+
Bring their interests into the app and then personalization based on that
|
127 |
+
|
128 |
+
|
129 |
+
|
130 |
+
Features 1-5 allow the user to enter a query in natural language regarding the topic of interest (Team, Players, Games, Rules, Fan Communities). Results will be retrieved from the web or the knowledge graph and displayed to the user in a dynamic layout depending on what they are searching for.
|
131 |
+
|
132 |
+
Team Discovery
|
133 |
+
|
134 |
+
Types of searches and format::
|
135 |
+
Overview [Novice]
|
136 |
+
Tell me about the 49ers
|
137 |
+
Season Recap [Novice]
|
138 |
+
How did the 49ers do last year
|
139 |
+
Trending News / Key Stories [Intermediate]
|
140 |
+
How did the 49ers do in the offseason
|
141 |
+
Outlook for next year [Intermediate]
|
142 |
+
Are the 49ers going to be good this year
|
143 |
+
Best plays of last year [Novice]
|
144 |
+
Show me top plays from last year
|
145 |
+
[Advanced queries TBD – something about the draft?]
|
146 |
+
|
147 |
+
Component:
|
148 |
+
Web search to pull content
|
149 |
+
Display as Text widget with image / video previews
|
150 |
+
|
151 |
+
Players Search
|
152 |
+
Types of Searches:
|
153 |
+
Overview (Default) [Novice]
|
154 |
+
Who is the starting QB?
|
155 |
+
Who are the best players?
|
156 |
+
Stats by Game [Intermediate]
|
157 |
+
What was Deebo Samuels best game last year?
|
158 |
+
Performance by Game [Advanced]
|
159 |
+
How accurate is Brock Purdy's deep ball?
|
160 |
+
Component:
|
161 |
+
Graph search to pull content
|
162 |
+
Display as Text widget with image / video previews
|
163 |
+
|
164 |
+
Game Search
|
165 |
+
|
166 |
+
Types of Searches:
|
167 |
+
Overview (Default) [Novice]
|
168 |
+
What was the 49ers record last season?
|
169 |
+
Search for a Specific Game [Intermediate]
|
170 |
+
How did the 49ers do against the Dolphins?
|
171 |
+
Game Results/ Top Plays [Advanced]
|
172 |
+
How did the 49ers passing attack perform against man vs zone coverage?
|
173 |
+
What was the critical moment in the 49ers loss to the Seahawks?
|
174 |
+
[reference – Key moments / highlights / analysis (see feed sort of like this article) https://sports.yahoo.com/live/49ers-dominate-early-turn-aside-seahawks-comeback-with-key-int-of-geno-smith-in-36-24-win-231533727.html – but keep it to one key play per game ]
|
175 |
+
|
176 |
+
Rule Search
|
177 |
+
RAG from the rulebook + general LLM reasoning
|
178 |
+
TBD / low priority on this feature
|
179 |
+
|
180 |
+
Fan Community search
|
181 |
+
Info about location and contact info for chapter lead
|
182 |
+
I live in Iowa, any fan communities near me?
|
183 |
+
Technology Approach
|
184 |
+
|
185 |
+
App Structure, Approach & Key Questions
|
186 |
+
The app is hosted on HuggingFace Spaces.
|
187 |
+
The underlying data structure is a graph database in neo4j
|
188 |
+
App user session management
|
189 |
+
The user will select pre-set personas (e.g. with attributes and preferences implemented through a Zep knowledge graph)
|
190 |
+
The user will click on a persona (button select) to set global variables that will influence application behavior
|
191 |
+
Key question – how to create a multi-screen app in gradio so that once the clicks the persona button that screen will disappear enabling them to experience the chat app?
|
192 |
+
Building dynamic UI components will happen as follows:
|
193 |
+
Foundation layer is a graph database (Neo4j)
|
194 |
+
Most of the features and use cases will be supported by calling the graph and its linked assets and displaying that to the user
|
195 |
+
Key question – what is the best way to integrate images with a neo4j database? E.g. a player database that also can display a headshot (image) of each player
|
196 |
+
MCP Server integration
|
197 |
+
For modularity I’m imagining that that ‘back end’ functions (e.g. retrieve data from neo4j) would be
|
198 |
+
Can MCP servers be called client-side by the Gradio application?
|
199 |
+
There is a POC on github with 54 stars 😅
|
200 |
+
Key question – how to integrate an MCP serveer into a web app specifically in gradio?
|
201 |
+
Building custom Gradio UI
|
202 |
+
Once i retrieve data payload from MCP functions I want to display as a front-end html component in gradio in response design
|
203 |
+
Key question – how to build responsive design HTML components in gradio?
|
204 |
+
|
205 |
+
Asset Requirements for Multi-modal output
|
206 |
+
Team Search
|
207 |
+
Team highlights – e.g. best plays of the season, best plays against NFC west https://www.youtube.com/playlist?list=PLBB205pkCsyvZ6tjCh_m5s21D0eeYJ8Ly
|
208 |
+
Player Search
|
209 |
+
Player headshots, e.g. from this page https://www.49ers.com/team/players-roster/
|
210 |
+
Player highlight videos / Top 10 Plays e.g. from this page https://www.youtube.com/playlist?list=PLBB205pkCsyvZ6tjCh_m5s21D0eeYJ8Ly
|
211 |
+
Play social media handles and follower counts, MIGHT be as easy as pulling from Google Search links but need a robust process / pattern for that
|
212 |
+
Game Search
|
213 |
+
Game recap videos e.g. from this page https://www.youtube.com/playlist?list=PLBB205pkCsyvZ6tjCh_m5s21D0eeYJ8Ly
|
214 |
+
|
215 |
+
|
216 |
+
|
217 |
+
|
218 |
+
Phases of Development
|
219 |
+
Initial POC being built on Streamlit (v1 is live here)
|
220 |
+
Replatform to Gradio to build custom UI components
|
221 |
+
Create Dynamic UI components as MCP servers
|
222 |
+
Leverage GradioUI as presentation layer to user
|
223 |
+
Consider mobile-native ‘frame’ within demo site
|
224 |
+
Apply Huge Branding for the last mile
|
225 |
+
|
226 |
+
|
227 |
+
|
228 |
+
Backlog
|
229 |
+
Improve performance of gradio (revisit after replatforms back end)
|
230 |
+
Source data to enable features
|
231 |
+
Rebuild langchain tools as MCP servers
|
232 |
+
Build dynamic widgets for F/E display
|
233 |
+
Test with freeplay and ship 👀‼️
|
234 |
+
|
235 |
+
|
236 |
+
|
237 |
+
Success Criteria
|
238 |
+
First Release / stabilizing inital deployment
|
239 |
+
Scaling strategy (think of sales funnel)
|
240 |
+
# of clients registered for white paper paper
|
241 |
+
# of clients logged in to AI experience
|
242 |
+
|
243 |
+
Timeline
|
244 |
+
Desired public release: May 29, 2025
|
245 |
+
|
246 |
+
|
247 |
+
Proposed phases of work:
|
248 |
+
Sprint 1: Needfinding, Feature Definition and Prioritization (March 31 - April 11)
|
249 |
+
Product work
|
250 |
+
Heuristic Analysis / Beacons completed ✅
|
251 |
+
initial feature set and and map out workload for rest of Sprint 1 ✅
|
252 |
+
Define what experience ‘slices’ and features makes an IFX (from a fan experience problem -> technology solution) ✅
|
253 |
+
Prioritize those features ✅
|
254 |
+
Define requirements and acceptance criteria ✅
|
255 |
+
Data discovery + collection ✅
|
256 |
+
Sprint 2: Back End Data Staging & Display (April 14 - April 25)
|
257 |
+
🔃 Get all data needed
|
258 |
+
Build new features
|
259 |
+
✅Team Search
|
260 |
+
✅Player Search
|
261 |
+
✅Game Recap Search
|
262 |
+
Work on displaying through dynamic components in gradio
|
263 |
+
Next demo April 22
|
264 |
+
Implement Persona and memory
|
265 |
+
Sprint 3: Graph Performance Upgrade (April 28 - May 9)
|
266 |
+
Refactor graph search for speed
|
267 |
+
Integrate with MCP
|
268 |
+
Work on displaying through dynamic components in gradio
|
269 |
+
Next demo May 6
|
270 |
+
Sprint 4: Integration and Polishing (May 12 - May 23)
|
271 |
+
Testing / code freeze (except for fixes)
|
272 |
+
Stabilization and integration with marketing platforms team
|
273 |
+
F/E support
|
274 |
+
Next demo May 20
|
275 |
+
Sprint 5: INTERNAL Release Week (May 26 - May 30)
|
276 |
+
Set up analytics tracking
|
277 |
+
Soft launch and stress testing
|
278 |
+
Go live
|
279 |
+
Capture feedback
|
280 |
+
|
281 |
+
|
282 |
+
Progress Tracker Component / Task Build Order
|
283 |
+
Data collection
|
284 |
+
Enable multimedia links and images
|
285 |
+
Scrape / collect data and images for:
|
286 |
+
🆗 player headshots
|
287 |
+
🆗 Play social handles (start with IG pending SERP API status)
|
288 |
+
YT highlight links: game + player + team
|
289 |
+
🆗Step 1: extracted all video links from YT api
|
290 |
+
🆗Step 2: parse and assign videos to games, players
|
291 |
+
🆗Step 3: pasrse and assign headshots to players
|
292 |
+
Generate new data files integrating old and new columns
|
293 |
+
🆗Roster - ok for now
|
294 |
+
roster has changed since first ingest 😓 might be better to build new one
|
295 |
+
Stats & Recaps
|
296 |
+
🆗 Game & Player stats
|
297 |
+
Do this next Game recaps
|
298 |
+
Integration
|
299 |
+
Integrate / re-upload to Neo4j
|
300 |
+
(run now vs later?)
|
301 |
+
Acceptance criteria – include in output displayed to user in legacy build using LangChain + Gradio
|
302 |
+
Build dynamic component in gradio
|
303 |
+
Display output to user in a ‘smart widget’
|
304 |
+
Player bio (with headshot)
|
305 |
+
Player highlights
|
306 |
+
Game recap
|
307 |
+
Refactor graph search
|
308 |
+
Option 1: use Zep to accelerate retrieval time with deterministic entity mapping
|
309 |
+
Option 2: use MCP encapsulate graph search
|
310 |
+
(still nervous about this – how do i troubleshoot if it works?)
|
311 |
+
Build personalization component
|
312 |
+
Develop fan personas and data attributes
|
313 |
+
Build selection pane in gradio
|
314 |
+
Pass those variables and content mapping for app personalization
|
315 |
+
Test prompts with Freeplay
|
316 |
+
Deploy
|
317 |
+
|
318 |
+
|
319 |
+
Question Backlog
|
320 |
+
Scope of the project (e.g. what team / what dataset)
|
321 |
+
At what point does this become an Initiative that needs a team to take it across the finish line for deployment
|
322 |
+
|
323 |
+
|
docs/Phase 1/Task 1.3 Memory & Persona Implementation.md
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Task 1.3 Memory & Persona Implementation
|
2 |
+
|
3 |
+
---
|
4 |
+
## Context
|
5 |
+
|
6 |
+
You are an expert at UI/UX design and software front-end development and architecture. You are allowed to NOT know an answer. You are allowed to be uncertain. You are allowed to disagree with your task. If any of these things happen, halt your current process and notify the user immediately. You should not hallucinate. If you are unable to remember information, you are allowed to look it up again.
|
7 |
+
|
8 |
+
You are not allowed to hallucinate. You may only use data that exists in the files specified. You are not allowed to create new data if it does not exist in those files.
|
9 |
+
|
10 |
+
You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.
|
11 |
+
|
12 |
+
When writing code, your focus should be on creating new functionality that builds on the existing code base without breaking things that are already working. If you need to rewrite how existing code works in order to develop a new feature, please check your work carefully, and also pause your work and tell me (the human) for review before going ahead. We want to avoid software regression as much as possible.
|
13 |
+
|
14 |
+
I WILL REPEAT, WHEN UPDATING EXISTING CODE FILES, PLEASE DO NOT OVERWRITE EXISTING CODE, PLEASE ADD OR MODIFY COMPONENTS TO ALIGN WITH THE NEW FUNCTIONALITY. THIS INCLUDES SMALL DETAILS LIKE FUNCTION ARGUMENTS AND LIBRARY IMPORTS. REGRESSIONS IN THESE AREAS HAVE CAUSED UNNECESSARY DELAYS AND WE WANT TO AVOID THEM GOING FORWARD.
|
15 |
+
|
16 |
+
When you need to modify existing code (in accordance with the instruction above), please present your recommendation to the user before taking action, and explain your rationale.
|
17 |
+
|
18 |
+
If the data files and code you need to use as inputs to complete your task do not conform to the structure you expected based on the instructions, please pause your work and ask the human for review and guidance on how to proceed.
|
19 |
+
|
20 |
+
If you have difficulty finding mission critical updates in the codebase (e.g. .env files, data files) ask the user for help in finding the path and directory.
|
21 |
+
|
22 |
+
|
23 |
+
|
24 |
+
---
|
25 |
+
|
26 |
+
## Objective
|
27 |
+
|
28 |
+
*Implement Feature 0 (Persona Selection) with a **careful, precise, surgical** approach.
|
29 |
+
The user will execute **one step at a time** and confirm each works before proceeding.*
|
30 |
+
|
31 |
+
---
|
32 |
+
|
33 |
+
## INSTRUCTION STEPS
|
34 |
+
|
35 |
+
> **Follow exactly. Do NOT improvise.**
|
36 |
+
> Steps 3 & 6 in `z_Task 1.3 Memory & Persona Implementation_Attempt 1 and 2.md` were already completed.
|
37 |
+
|
38 |
+
### 1 │ Review Documentation & Code Base
|
39 |
+
|
40 |
+
* `gradio_app.py`
|
41 |
+
* `gradio_agent.py`
|
42 |
+
* `gradio_utils.py`
|
43 |
+
|
44 |
+
---
|
45 |
+
|
46 |
+
### 2 │ Simplest Zep Test
|
47 |
+
|
48 |
+
1. Build a Python script in the z_utils folder using the code template at <https://help.getzep.com/walkthrough>.
|
49 |
+
2. Use **either** session‑ID:
|
50 |
+
* Casual fan `241b3478c7634492abee9f178b5341cb`
|
51 |
+
* Super fan `dedcf5cb0d71475f976f4f66d98d6400`
|
52 |
+
3. Confirm the script can *retrieve* chat history.
|
53 |
+
|
54 |
+
**Status Update:**
|
55 |
+
✅ Successfully created a simple Zep test script (`z_utils/zep_test.py`) that connects to Zep Cloud.
|
56 |
+
❗ Initial test showed no chat history was found for either the Casual fan or Super fan session IDs.
|
57 |
+
✅ Updated the `zep_setup.py` script to:
|
58 |
+
- Use the specific session IDs defined in the task (Casual fan: `241b3478c7634492abee9f178b5341cb`, Super fan: `dedcf5cb0d71475f976f4f66d98d6400`)
|
59 |
+
- Create conversation histories for both personas based on their knowledge profiles
|
60 |
+
- Store these conversations in Zep Cloud with the specified session IDs
|
61 |
+
- Save the session IDs to `persona_session_ids.json` for future reference
|
62 |
+
✅ Fixed an issue with message role_type formatting (changed "human" to "user" for proper Zep compatibility)
|
63 |
+
✅ Successfully executed the Zep setup script and verified both personas have:
|
64 |
+
- Correctly formatted chat histories loaded in Zep Cloud
|
65 |
+
- Knowledge profiles associated with their user IDs
|
66 |
+
- Ability to retrieve their chat histories through the test script
|
67 |
+
|
68 |
+
✅ Testing confirms both session IDs are now working as expected:
|
69 |
+
- Casual fan history includes basic team information and player highlights
|
70 |
+
- Super fan history contains more detailed strategic information and analysis
|
71 |
+
|
72 |
+
**Next Action:**
|
73 |
+
- Proceed to Step 3 with the Gradio integration using the ZepCloudChatMessageHistory
|
74 |
+
|
75 |
+
---
|
76 |
+
|
77 |
+
### 3 │ Gradio Integration ― DO‑NO‑HARM Path
|
78 |
+
|
79 |
+
1. **Research**
|
80 |
+
* <https://python.langchain.com/docs/integrations/memory/zep_memory/>
|
81 |
+
* <https://python.langchain.com/docs/integrations/memory/zep_cloud_chat_message_history/>
|
82 |
+
2. Create a `ZepMemory()` (or `ZepCloudChatMessageHistory`) object.
|
83 |
+
3. **Hard‑code** one session‑ID first to verify import works inside the agent.
|
84 |
+
4. Run the app. Ensure chat history loads without breaking existing features.
|
85 |
+
|
86 |
+
---
|
87 |
+
|
88 |
+
### 4 │ Add Radio Button (Skeleton Only)
|
89 |
+
|
90 |
+
* Insert a Gradio **Radio** with options **Casual Fan** / **Super Fan**.
|
91 |
+
* Initially the button **does nothing**—just proves the UI renders.
|
92 |
+
|
93 |
+
---
|
94 |
+
|
95 |
+
### 5 │ Wire Radio → Session ID
|
96 |
+
|
97 |
+
1. On change, map selection to its fixed session‑ID.
|
98 |
+
2. Pass that ID into the `ZepMemory()` object.
|
99 |
+
3. Re‑run app, switch personas, confirm different histories load.
|
100 |
+
|
101 |
+
---
|
102 |
+
|
103 |
+
### 6 │ Strict Gradio Rule
|
104 |
+
|
105 |
+
* **DO NOT** change any other settings or components in the app.
|
106 |
+
* Changes must be incremental and easily revertible.
|
107 |
+
|
108 |
+
---
|
109 |
+
|
110 |
+
### 7 │ Documentation Update
|
111 |
+
|
112 |
+
* Explain *why* the simple, surgical approach avoided regressions.
|
113 |
+
* Update project docs to reflect the new persona‑memory workflow.
|
114 |
+
|
115 |
+
---
|
116 |
+
|
117 |
+
## Failure Condition
|
118 |
+
|
119 |
+
> **If any step fails 3×, STOP and consult the user**.
|
120 |
+
|
121 |
+
---
|
122 |
+
|
123 |
+
## Completion Deliverables
|
124 |
+
|
125 |
+
1. **Markdown file** (this document) titled **"Task 1.3 Memory & Persona Implementation"**.
|
126 |
+
2. **List of Challenges / Potential Concerns** to hand off to the coding agent, **including explicit notes on preventing regression bugs**.
|
127 |
+
|
128 |
+
---
|
129 |
+
|
130 |
+
## Challenges & Concerns
|
131 |
+
|
132 |
+
| # | Risk / Concern | Mitigation |
|
133 |
+
|---|---------------|-----------|
|
134 |
+
| 1 | Zep integration may break async flow | Keep Zep calls isolated; wrap in try/except; validate after each call. |
|
135 |
+
| 2 | Accidentally overwriting existing code | **Only** add small helper functions or wrap logic; no deletions/ rewrites without review. |
|
136 |
+
| 3 | Radio button race conditions | Disable UI during session‑switch; re‑enable after confirmation. |
|
137 |
+
| 4 | Regression in existing features | Run smoke tests (player search, game recap, team story) after every change. |
|
138 |
+
| 5 | Missing env variables | At startup, assert `ZEP_API_KEY` is set; show clear error if not. |
|
139 |
+
| 6 | Session ID mismatch | Verify that session IDs in code match those actually created in Zep Cloud. |
|
140 |
+
| 7 | Message history creation | Ensure messages follow proper format for Zep; implement fallbacks if message history retrieval fails. |
|
141 |
+
|
142 |
+
---
|
143 |
+
|
144 |
+
> **Remember:** *One tiny change → test → commit. Repeat.*
|
145 |
+
|
docs/Phase 1/z_Task 1.3 Memory & Persona Implementation_Attempt 1 and 2.md
ADDED
@@ -0,0 +1,352 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Context
|
2 |
+
You are an expert at UI/UX design and software front-end development and architecture. You are allowed to not know an answer. You are allowed to be uncertain. You are allowed to disagree with your task. If any of these things happen, halt your current process and notify the user immediately. You should not hallucinate. If you are unable to remember information, you are allowed to look it up again.
|
3 |
+
|
4 |
+
You are not allowed to hallucinate. You may only use data that exists in the files specified. You are not allowed to create new data if it does not exist in those files.
|
5 |
+
|
6 |
+
You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.
|
7 |
+
|
8 |
+
When writing code, your focus should be on creating new functionality that builds on the existing code base without breaking things that are already working. If you need to rewrite how existing code works in order to develop a new feature, please check your work carefully, and also pause your work and tell me (the human) for review before going ahead. We want to avoid software regression as much as possible.
|
9 |
+
|
10 |
+
I WILL REPEAT, WHEN UPDATING EXISTING CODE FILES, PLEASE DO NOT OVERWRITE EXISTING CODE, PLEASE ADD OR MODIFY COMPONENTS TO ALIGN WITH THE NEW FUNCTIONALITY. THIS INCLUDES SMALL DETAILS LIKE FUNCTION ARGUMENTS AND LIBRARY IMPORTS. REGRESSIONS IN THESE AREAS HAVE CAUSED UNNECESSARY DELAYS AND WE WANT TO AVOID THEM GOING FORWARD.
|
11 |
+
|
12 |
+
When you need to modify existing code (in accordance with the instruction above), please present your recommendation to the user before taking action, and explain your rationale.
|
13 |
+
|
14 |
+
If the data files and code you need to use as inputs to complete your task do not conform to the structure you expected based on the instructions, please pause your work and ask the human for review and guidance on how to proceed.
|
15 |
+
|
16 |
+
If you have difficulty finding mission critical updates in the codebase (e.g. .env files, data files) ask the user for help in finding the path and directory.
|
17 |
+
|
18 |
+
# Task 1.3 Memory & Persona Implementation Instructions
|
19 |
+
|
20 |
+
## Objective
|
21 |
+
You are to follow the step-by-step process in order to implement Feature 0 (Persona Selection) / Task 1.3 (Memory System and UI Persona Integration with Zep) as defined in `ifx-sandbox/docs/requirements.md`. This involves creating Zep users/memory, adding UI selection in Gradio, integrating Zep memory into the agent, and performing related housekeeping. The goal is to allow users to select a persona ("Casual Fan" or "Super Fan") which loads pre-defined user profiles and memory contexts via Zep Cloud, personalizing the chat experience.
|
22 |
+
|
23 |
+
## Instruction Steps
|
24 |
+
|
25 |
+
1. **Codebase Review:** Familiarize yourself with the existing project structure:
|
26 |
+
* `gradio_app.py`: Understand the current UI structure, the `AppState` class, and how Zep is already integrated. Pay close attention to `initialize_chat()` and `process_message()` functions which handle session creation and message processing.
|
27 |
+
* `gradio_agent.py`: Examine the agent setup, tool integration, and especially the `get_memory()` function which currently uses `Neo4jChatMessageHistory` instead of Zep memory.
|
28 |
+
* `gradio_utils.py`: Review for utility functions, especially those related to session/user ID generation.
|
29 |
+
* `tools/vector.py`: Examine this file as it needs to be removed in the housekeeping phase.
|
30 |
+
* `.env`: Check for `ZEP_API_KEY` to ensure Zep Cloud access is configured. Note that Zep Cloud API keys start with "z_" prefix.
|
31 |
+
* Read `requirements.txt` to verify Zep client libraries are available (specifically `zep-cloud>=0.1.0`).
|
32 |
+
|
33 |
+
2. **Create Directory Structure:**
|
34 |
+
* **Note:** The directory `ifx-sandbox/z_utils/` already exists with other utility scripts like `set_secrets.py` and `restart_space.py`. Follow their patterns for loading environment variables and error handling.
|
35 |
+
|
36 |
+
3. **Zep User & Memory Setup Script:**
|
37 |
+
* Create a new file: `ifx-sandbox/z_utils/zep_setup.py`.
|
38 |
+
* **Implement the script:**
|
39 |
+
* Import `asyncio`, `os`, `uuid`.
|
40 |
+
* Import `AsyncZep`, `User`, `Message` from `zep_cloud`.
|
41 |
+
* Load `ZEP_API_KEY` (and optionally `ZEP_API_URL`) from environment variables using `dotenv` like other scripts. Handle missing keys gracefully (e.g., raise error).
|
42 |
+
* Initialize `AsyncZep` client.
|
43 |
+
* **Define Persona Data:** Create dictionaries mapping persona names to details:
|
44 |
+
```python
|
45 |
+
PERSONAS = {
|
46 |
+
"Casual Fan": {
|
47 |
+
"user_id": str(uuid.uuid4()), # Generate and store fixed UUIDs
|
48 |
+
"email": "[email protected]",
|
49 |
+
"first_name": "Casual",
|
50 |
+
"last_name": "Fan",
|
51 |
+
"metadata": {"fan_type": "casual"}
|
52 |
+
},
|
53 |
+
"Super Fan": {
|
54 |
+
"user_id": str(uuid.uuid4()), # Generate and store fixed UUIDs
|
55 |
+
"email": "[email protected]",
|
56 |
+
"first_name": "Super",
|
57 |
+
"last_name": "Fan",
|
58 |
+
"metadata": {"fan_type": "super"}
|
59 |
+
}
|
60 |
+
}
|
61 |
+
```
|
62 |
+
*Print these generated UUIDs to the console when the script runs.*
|
63 |
+
**IMPORTANT:** The UUIDs must be saved to a file (e.g., `z_utils/persona_uuids.json`) for later use by the application, as these will be used to link users to their persona data.
|
64 |
+
* **Define User Creation Function:** Create an `async def create_zep_user(client, user_data)` function that calls `client.user.add(**user_data)`. Include error handling (e.g., user already exists). Consider adding a `client.user.get(user_id)` check first.
|
65 |
+
* **Define Memory Pre-loading:**
|
66 |
+
* Define sample `Message` lists for each persona reflecting their interests:
|
67 |
+
* **Casual Fan**: Focus on big stars (QB, star receivers), game outcomes, simple recaps with minimal stats, excitement for big plays.
|
68 |
+
* **Super Fan**: Focus on detailed stats, role players, strategy discussion, defensive play analysis, references to historical context.
|
69 |
+
* Create an `async def preload_memory(client, user_id, memories)` function.
|
70 |
+
* **Implementation approach**: Using the Zep Cloud API, create a temporary session for the user with `await client.memory.add_session(session_id=str(uuid.uuid4()), user_id=user_id)`, then add the messages with `await client.memory.add(session_id=session_id, messages=memories)`.
|
71 |
+
* **Main Execution Block:** Create an `async def main()` function and use `asyncio.run(main())` in `if __name__ == "__main__":`.
|
72 |
+
* In `main()`, iterate through `PERSONAS`, call `create_zep_user` for each.
|
73 |
+
* Call memory pre-loading function for each persona.
|
74 |
+
* Save the persona UUIDs to a JSON file.
|
75 |
+
* Print confirmations or errors.
|
76 |
+
* **Run the Script:** Execute `python ifx-sandbox/z_utils/zep_setup.py` once to create the users in Zep Cloud. Note the generated UUIDs.
|
77 |
+
|
78 |
+
4. **Modify Gradio UI & State Management:**
|
79 |
+
* Update `gradio_app.py` to:
|
80 |
+
* Add imports: Ensure `AsyncZep`, `Message` are imported. Add `uuid` and `json` for reading the persona UUIDs file.
|
81 |
+
* Enhance `AppState` class with persona-related fields:
|
82 |
+
```python
|
83 |
+
def __init__(self):
|
84 |
+
self.chat_history = []
|
85 |
+
self.initialized = False
|
86 |
+
self.user_id = None
|
87 |
+
self.session_id = None
|
88 |
+
self.zep_client = None
|
89 |
+
self.current_persona = None # New field to track selected persona
|
90 |
+
self.persona_data = None # New field to store persona details
|
91 |
+
```
|
92 |
+
* Load persona data from the saved JSON file at application start.
|
93 |
+
* Add a Persona Selector UI component (radio buttons) at the top of the UI, above the chat interface:
|
94 |
+
```python
|
95 |
+
persona_selector = gr.Radio(
|
96 |
+
choices=["Casual Fan", "Super Fan"],
|
97 |
+
label="Select your 49ers Fan Type:",
|
98 |
+
value="Casual Fan", # Default selection
|
99 |
+
interactive=True
|
100 |
+
)
|
101 |
+
```
|
102 |
+
* Modify `initialize_chat()` to use persona-specific user IDs:
|
103 |
+
* Use the UUID corresponding to the selected persona from the saved JSON file
|
104 |
+
* Update the existing code that calls `zep.user.add` with the persona-specific data
|
105 |
+
* Implement a new `handle_persona_change(persona_name)` function for switching personas:
|
106 |
+
* Update `state.current_persona` with the new persona
|
107 |
+
* Set `state.user_id` to the corresponding UUID
|
108 |
+
* Generate a new `session_id` using `gradio_utils.reset_ids()`
|
109 |
+
* Create a new Zep session with `await zep.memory.add_session()`
|
110 |
+
* Clear the chat history display to start fresh
|
111 |
+
* Register this function as a callback for the persona selector.
|
112 |
+
* Update `process_message()` to ensure the persona-specific context is used.
|
113 |
+
|
114 |
+
5. **Integrate Zep Memory into Agent:**
|
115 |
+
* Modify `gradio_agent.py` to:
|
116 |
+
* Replace `Neo4jChatMessageHistory` import with:
|
117 |
+
```python
|
118 |
+
from langchain_community.memory.zep_cloud_memory import ZepCloudChatMessageHistory
|
119 |
+
```
|
120 |
+
* Update the `get_memory()` function to return a Zep memory instance:
|
121 |
+
```python
|
122 |
+
def get_memory(session_id):
|
123 |
+
"""Get the chat history from Zep for the given session"""
|
124 |
+
return ZepCloudChatMessageHistory(
|
125 |
+
session_id=session_id,
|
126 |
+
api_key=os.environ.get("ZEP_API_KEY"),
|
127 |
+
)
|
128 |
+
```
|
129 |
+
* Ensure all agent components correctly use the new memory interface (which should be 100% compatible with the existing code).
|
130 |
+
|
131 |
+
6. **Perform Housekeeping Tasks:**
|
132 |
+
* **Create and Execute Neo4j Cleanup Script:**
|
133 |
+
* Create a new script, `ifx-sandbox/z_utils/neo4j_cleanup.py`.
|
134 |
+
* This script should connect to Neo4j using the existing `graph` object from `gradio_graph.py` (similar to the pattern in `ifx-sandbox/data/april_11_multimedia_data_collect/neo4j_article_uploader.py`).
|
135 |
+
* The script's sole purpose is to execute the following Cypher query to safely remove only the `embedding` property from `Game` nodes:
|
136 |
+
```cypher
|
137 |
+
MATCH (g:Game)
|
138 |
+
WHERE g.embedding IS NOT NULL
|
139 |
+
REMOVE g.embedding
|
140 |
+
RETURN count(*) as removed_count
|
141 |
+
```
|
142 |
+
* Ensure the script includes appropriate logging or print statements to confirm success (reporting the number of nodes affected) or indicate errors.
|
143 |
+
* Execute the script once using `python ifx-sandbox/z_utils/neo4j_cleanup.py`.
|
144 |
+
* **Delete Obsolete File:** Delete the `ifx-sandbox/tools/vector.py` file.
|
145 |
+
* **Remove Tool from Agent:** Remove the "Game Summary Search" tool and related imports from `ifx-sandbox/gradio_agent.py`:
|
146 |
+
* Remove the import statement: `from tools.vector import get_game_summary`
|
147 |
+
* Remove the tool entry from the `tools` list:
|
148 |
+
```python
|
149 |
+
Tool.from_function(
|
150 |
+
name="Game Summary Search",
|
151 |
+
description="""ONLY use for detailed game summaries or specific match results if the 'Game Recap' tool fails or doesn't provide enough detail.
|
152 |
+
Examples: "Summarize the 49ers vs Seahawks game", "Give me details about the last playoff game results"
|
153 |
+
Do NOT use for general schedule questions.""",
|
154 |
+
func=get_game_summary,
|
155 |
+
),
|
156 |
+
```
|
157 |
+
|
158 |
+
7. **Test the Implementation:**
|
159 |
+
* Run the application and verify the persona selector is present.
|
160 |
+
* Test both personas with appropriate questions:
|
161 |
+
* For Casual Fan: "Who's the quarterback for the 49ers?" (should give basic info)
|
162 |
+
* For Super Fan: "Tell me about the 49ers offensive line strategy" (should give detailed analysis)
|
163 |
+
* Ensure switching personas correctly clears chat history and establishes a new session.
|
164 |
+
* Verify all existing functionality continues to work.
|
165 |
+
|
166 |
+
8. **Update Documentation:**
|
167 |
+
* Edit `ifx-sandbox/docs/requirements.md` to reflect completed work:
|
168 |
+
* Update the "Persona Memory Structure (Zep)" section with actual implementation details
|
169 |
+
* Mark Feature 0 (Persona Selection) as completed
|
170 |
+
|
171 |
+
## Data Flow Architecture (Simplified)
|
172 |
+
1. User selects a persona ("Casual Fan" or "Super Fan") in the Gradio UI.
|
173 |
+
2. The UI triggers the persona change callback, which:
|
174 |
+
* Updates the application state with the selected persona.
|
175 |
+
* Sets the `user_id` to the corresponding pre-generated UUID from the persona JSON file.
|
176 |
+
* Generates a new `session_id` for this interaction.
|
177 |
+
* Creates a new Zep session linked to the persona's user ID.
|
178 |
+
* Clears the chat history to start fresh.
|
179 |
+
3. User sends a message through the chat interface.
|
180 |
+
4. The message is processed by `process_message()`, which:
|
181 |
+
* Stores the message in Zep memory for the current session.
|
182 |
+
* Calls the agent with the current `session_id`.
|
183 |
+
5. The agent retrieves the chat history via `get_memory(session_id)`, which:
|
184 |
+
* Returns a Zep memory instance tied to the current session.
|
185 |
+
* This memory contains context appropriate to the selected persona.
|
186 |
+
6. The agent generates a response influenced by the persona-specific memory context.
|
187 |
+
7. The response is stored in Zep memory and displayed to the user.
|
188 |
+
|
189 |
+
## Error Handling Strategy
|
190 |
+
1. **Zep API Connection:** Implement robust error handling for Zep API calls, including fallback options if Zep Cloud is unavailable:
|
191 |
+
* For critical initialization calls, show a clear error message to the user
|
192 |
+
* For non-critical calls during operation, log the error but continue with degraded functionality
|
193 |
+
* Add retry logic for temporary network issues where appropriate
|
194 |
+
2. **UUID Management:** Store persona UUIDs in a JSON file that can be loaded by the application. Add error handling for file access issues.
|
195 |
+
3. **State Transitions:** Handle potential race conditions during persona switching:
|
196 |
+
* Disable UI interaction during the transition
|
197 |
+
* Re-enable only after all async operations are complete
|
198 |
+
* Add a loading indicator during transitions
|
199 |
+
4. **Session Management:** Properly clean up old sessions when switching personas.
|
200 |
+
5. **Missing Dependencies:** Check for required environment variables at startup and show clear error messages if missing.
|
201 |
+
|
202 |
+
## Performance Optimization
|
203 |
+
1. **Memory Context Size:** Keep pre-loaded memories concise and relevant to avoid excessive token usage.
|
204 |
+
2. **Session Creation:** Only create a new Zep session when switching personas or when a session doesn't exist.
|
205 |
+
3. **Lazy Loading:** Load persona details only when needed rather than all at application startup.
|
206 |
+
4. **Caching:** Store the persona UUIDs in the AppState to avoid repeated file access.
|
207 |
+
|
208 |
+
## Failure Conditions
|
209 |
+
* If you are unable to complete any step after 3 attempts, immediately halt the process and consult with the user on how to continue.
|
210 |
+
* Document the failure point and the reason for failure.
|
211 |
+
* Do not proceed with subsequent steps until the issue is resolved.
|
212 |
+
|
213 |
+
## Completion Criteria & Potential Concerns
|
214 |
+
|
215 |
+
**Success Criteria:**
|
216 |
+
1. Users can select between "Casual Fan" and "Super Fan" personas in the UI.
|
217 |
+
2. Switching personas correctly clears chat history and establishes a new session.
|
218 |
+
3. The LangChain agent successfully retrieves and uses Zep memory for the current persona and session.
|
219 |
+
4. Responses are contextually appropriate to the selected persona (more basic for casual fans, more detailed for super fans).
|
220 |
+
5. Neo4j game nodes have been cleaned up by removing embedding properties.
|
221 |
+
6. The vector search tool has been removed without breaking other functionality.
|
222 |
+
|
223 |
+
**Deliverables:**
|
224 |
+
* The `z_utils/zep_setup.py` script for user creation and memory initialization.
|
225 |
+
* The `z_utils/persona_uuids.json` file storing the generated UUIDs.
|
226 |
+
* Modified `gradio_app.py` with persona selector UI and appropriate state management.
|
227 |
+
* Modified `gradio_agent.py` with Zep memory integration replacing Neo4j chat history.
|
228 |
+
* Updated `docs/requirements.md` reflecting completed implementation.
|
229 |
+
|
230 |
+
**Challenges / Potential Concerns & Mitigation Strategies:**
|
231 |
+
|
232 |
+
1. **Zep Integration:**
|
233 |
+
* *Concern:* Ensuring correct usage of the Zep Cloud SDK, especially across async/sync contexts.
|
234 |
+
* *Mitigation:* Carefully review Zep documentation for best practices. Follow the `AsyncZep` usage pattern already in place in `gradio_app.py`. Ensure proper async/await handling in all Zep operations.
|
235 |
+
|
236 |
+
2. **Gradio State Management:**
|
237 |
+
* *Concern:* Properly handling persona switching and session management to avoid race conditions or context contamination.
|
238 |
+
* *Mitigation:* Use clear state management in the UI callbacks. Ensure the persona selector is disabled during transitions. Add status indicators for long operations.
|
239 |
+
|
240 |
+
3. **Regression Risks:**
|
241 |
+
* *Concern:* Changes to core files like `gradio_app.py` and `gradio_agent.py` could break existing functionality.
|
242 |
+
* *Mitigation:* Make incremental changes with thorough testing after each modification. Use version control to track changes. Follow the existing coding patterns. Present changes to core functions for review before implementing.
|
243 |
+
|
244 |
+
4. **Neo4j Cleanup:**
|
245 |
+
* *Concern:* Removing embeddings from Game nodes could affect other components relying on this data.
|
246 |
+
* *Mitigation:* Verify all dependencies before removal. The `vector.py` file is the only component using these embeddings. Run the cleanup in a separate step and test afterwards.
|
247 |
+
|
248 |
+
5. **Vector Tool Removal:**
|
249 |
+
* *Concern:* Removing the vector search tool might have unexpected dependencies.
|
250 |
+
* *Mitigation:* Check all imports and references. Verify the tool removal doesn't break any other functionality by testing all other tools after removal.
|
251 |
+
|
252 |
+
## Appendix: Zep Memory Pre-loading Implementation
|
253 |
+
|
254 |
+
Based on the Zep memory graph structure described in [Zep documentation](https://help.getzep.com/facts), here's a detailed implementation of how to pre-load memory graphs for the Casual Fan and Super Fan personas.
|
255 |
+
|
256 |
+
### Understanding Zep's Memory Model
|
257 |
+
|
258 |
+
In Zep, memory consists of:
|
259 |
+
1. **Facts** - Stored on edges, representing precise relationships with timestamps for when they are valid/invalid
|
260 |
+
2. **Summaries** - Stored on nodes, providing high-level overviews of entities
|
261 |
+
|
262 |
+
Our implementation will focus on:
|
263 |
+
1. Creating appropriate nodes (team concepts, games, general information)
|
264 |
+
2. Establishing facts via edges that connect these nodes
|
265 |
+
3. Adding fact ratings to prioritize important knowledge for each persona
|
266 |
+
|
267 |
+
### Implementation in `zep_setup.py`
|
268 |
+
|
269 |
+
# file: z_utils/zep_setup.py
|
270 |
+
"""
|
271 |
+
Create two persona users in Zep Cloud and preload their memory graphs.
|
272 |
+
Run once: python z_utils/zep_setup.py
|
273 |
+
"""
|
274 |
+
|
275 |
+
```python
|
276 |
+
// Removed the full Python code block for zep_setup.py as it exists in the actual file
|
277 |
+
// See ifx-sandbox/z_utils/zep_setup.py for the implementation.
|
278 |
+
```
|
279 |
+
|
280 |
+
### Key Features of This Implementation
|
281 |
+
|
282 |
+
1. **Graph-Based Memory Structure**
|
283 |
+
- Creates entity nodes for team information, games, and football concepts
|
284 |
+
- Establishes facts through conversations and structured JSON data
|
285 |
+
- Simulates natural knowledge through conversation-based memory
|
286 |
+
|
287 |
+
2. **Fact Rating System**
|
288 |
+
- Implements custom fact rating instructions for each persona
|
289 |
+
- Casual Fan: Prioritizes star players, major game outcomes, and memorable moments
|
290 |
+
- Super Fan: Prioritizes detailed statistics, strategic insights, and historical context
|
291 |
+
|
292 |
+
3. **Knowledge Depth Differentiation**
|
293 |
+
- **Casual Fan**: Limited to recent memorable games and basic team facts
|
294 |
+
- **Super Fan**: Includes scheme knowledge, strategic insights, salary cap information, and draft capital
|
295 |
+
|
296 |
+
4. **Temporal Accuracy**
|
297 |
+
- Facts include temporal information (game dates, current season)
|
298 |
+
- Ensures agent responses are grounded in the correct time context
|
299 |
+
|
300 |
+
5. **User-Specific Conversation History**
|
301 |
+
- Each persona has its own conversation history with appropriate depth
|
302 |
+
- Casual conversations focus on stars and outcomes
|
303 |
+
- Super fan conversations delve into schemes and personnel management
|
304 |
+
|
305 |
+
This implementation leverages Zep's memory graph to create distinctly different knowledge profiles that the agent can access based on the selected persona. Instead of storing detailed player information directly in the graph, it captures this knowledge through conversation samples, which is a more natural way that fans would recall information about their team.
|
306 |
+
|
307 |
+
---
|
308 |
+
|
309 |
+
## Implementation Summary & Notes (July 2024)
|
310 |
+
|
311 |
+
This task was completed following the steps outlined above, with some refinements and debugging along the way.
|
312 |
+
|
313 |
+
**Key Outcomes & Changes:**
|
314 |
+
|
315 |
+
1. **`z_utils/zep_setup.py` Created & Refined:**
|
316 |
+
* Implemented using the script structure provided in the Appendix.
|
317 |
+
* Required several debugging iterations to correctly handle Zep SDK exceptions (`NotFoundError` from `zep_cloud.errors` for 404 checks, generic `Exception` for others with added type logging).
|
318 |
+
* Corrected the `zep.graph.add` call to pass profile data as a JSON string using `json.dumps()`, resolving a `BadRequestError` from the Zep API.
|
319 |
+
* Script successfully creates/updates users, preloads profile data to the graph, preloads sample chat messages to memory, and generates `z_utils/persona_uuids.json`.
|
320 |
+
|
321 |
+
2. **`gradio_app.py` Modified:**
|
322 |
+
* Added imports for `json` and `uuid`.
|
323 |
+
* `AppState` class updated:
|
324 |
+
* Added `current_persona` and `persona_data` attributes.
|
325 |
+
* Integrated Zep client initialization (`_initialize_zep`) handling `ZEP_API_KEY` and optional `ZEP_API_URL`.
|
326 |
+
* Added logic (`_load_persona_data`) to load UUIDs from `persona_uuids.json` with error handling.
|
327 |
+
* `initialize_chat` refactored: Removed `zep.user.add`, sets default persona state (`current_persona`, `user_id`), generates a new `session_id`, and attempts to create the initial Zep session using the loaded `user_id`.
|
328 |
+
* `process_message` updated to use `state.zep_client` and `state.session_id` when interacting with Zep memory.
|
329 |
+
* Added `handle_persona_change` async function to update state (`current_persona`, `user_id`), generate a new `session_id`, create a new Zep session, and return an empty list to clear the Gradio chatbot UI.
|
330 |
+
* Added `gr.Radio` component (`persona_selector`) to the `create_ui` function.
|
331 |
+
* Wired UI events:
|
332 |
+
* `demo.load` calls `initialize_chat` (outputs: `chatbot`, `persona_selector`).
|
333 |
+
* `persona_selector.change` calls `handle_persona_change` (output: `chatbot`).
|
334 |
+
* Added an explicit `clear_button` wired to an updated `clear_chat` function which resets components and the persona selector.
|
335 |
+
* `msg.submit` calls `process_and_respond` which handles agent calls and component updates.
|
336 |
+
|
337 |
+
3. **`gradio_agent.py` Modified:**
|
338 |
+
* Replaced `langchain_neo4j.Neo4jChatMessageHistory` import with `langchain_community.memory.zep_cloud_memory.ZepCloudChatMessageHistory`.
|
339 |
+
* Updated `get_memory` function to instantiate `ZepCloudChatMessageHistory`, passing `session_id` and retrieving Zep API key/URL from environment variables. Added basic error handling for missing API key.
|
340 |
+
* Removed the import `from tools.vector import get_game_summary`.
|
341 |
+
* Removed the "Game Summary Search" `Tool.from_function` definition from the `tools` list.
|
342 |
+
|
343 |
+
4. **Housekeeping Performed:**
|
344 |
+
* Original plan to run manual Cypher query was updated.
|
345 |
+
* Created `z_utils/neo4j_cleanup.py` script using `gradio_graph` to connect to Neo4j.
|
346 |
+
* Executed `neo4j_cleanup.py` successfully, removing the `embedding` property from :Game nodes.
|
347 |
+
* Deleted the obsolete `ifx-sandbox/tools/vector.py` file.
|
348 |
+
|
349 |
+
5. **Documentation Updated:**
|
350 |
+
* Updated `ifx-sandbox/docs/requirements.md` (Feature 0 description, Persona Memory Structure example) and marked Task 1.3 as complete.
|
351 |
+
|
352 |
+
**Current Status:** All implementation steps are complete. The application should now support persona selection integrated with Zep memory. Manual testing (Step 7) is required to verify functionality.
|
docs/Phase 1/z_Task 1.3 Memory & Persona Implementation_Debug Plan_Attempt 1.md
ADDED
@@ -0,0 +1,401 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# AI Agent Testing Plan: Task 1.3 Memory & Persona Implementation
|
2 |
+
|
3 |
+
## Context
|
4 |
+
You are an expert at UI/UX design and software front-end development and architecture. You are allowed to not know an answer. You are allowed to be uncertain. You are allowed to disagree with your task. If any of these things happen, halt your current process and notify the user immediately. You should not hallucinate. If you are unable to remember information, you are allowed to look it up again.
|
5 |
+
You are not allowed to hallucinate. You may only use data that exists in the files specified. You are not allowed to create new data if it does not exist in those files.
|
6 |
+
You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.
|
7 |
+
When writing code, your focus should be on creating new functionality that builds on the existing code base without breaking things that are already working. If you need to rewrite how existing code works in order to develop a new feature, please check your work carefully, and also pause your work and tell the human user for review before going ahead. We want to avoid software regression as much as possible.
|
8 |
+
WHEN UPDATING EXISTING CODE FILES, PLEASE DO NOT OVERWRITE EXISTING CODE, PLEASE ADD OR MODIFY COMPONENTS TO ALIGN WITH THE NEW FUNCTIONALITY. THIS INCLUDES SMALL DETAILS LIKE FUNCTION ARGUMENTS AND LIBRARY IMPORTS. REGRESSIONS IN THESE AREAS HAVE CAUSED UNNECESSARY DELAYS AND WE WANT TO AVOID THEM GOING FORWARD.
|
9 |
+
When you need to modify existing code (in accordance with the instruction above), please present your recommendation to the user before taking action, and explain your rationale.
|
10 |
+
If the data files and code you need to use as inputs to complete your task do not conform to the structure you expected based on the instructions, please pause your work and ask the human for review and guidance on how to proceed.
|
11 |
+
If you have difficulty finding mission critical updates in the codebase (e.g. .env files, data files) ask the user for help in finding the path and directory.
|
12 |
+
|
13 |
+
## Objective
|
14 |
+
Your objective is to systematically test the implementation of Task 1.3 (Memory & Persona Implementation) as described in `ifx-sandbox/docs/Phase 1/Task 1.3 Memory & Persona Implementation.md`. This involves verifying the UI changes, persona switching logic, Zep memory integration, and ensuring no regressions were introduced. The user will perform the tests in the Gradio application and report the results.
|
15 |
+
|
16 |
+
## Instruction Steps
|
17 |
+
|
18 |
+
**Prerequisites:**
|
19 |
+
1. The Gradio application (`ifx-sandbox/gradio_app.py`) should be running.
|
20 |
+
* **Resolution Status:** Completed (App is running in the background).
|
21 |
+
* **Actions Taken:** Resolved initial errors (file path, theme, NameError) and successfully launched the app using `python gradio_app.py`.
|
22 |
+
|
23 |
+
**Test 1: Verify Persona Selector UI**
|
24 |
+
1. **Check UI:** Open the Gradio application URL in a browser.
|
25 |
+
2. **Confirm Component:** Verify that the "Select your 49ers Fan Type:" radio button component is present, with "Casual Fan" and "Super Fan" options visible. Check that "Casual Fan" is selected by default.
|
26 |
+
* **Resolution Status:** Unresolved.
|
27 |
+
* **Actions Taken:**
|
28 |
+
|
29 |
+
**Test 2: Test "Casual Fan" Persona**
|
30 |
+
1. **Ensure Persona:** Confirm "Casual Fan" is selected.
|
31 |
+
2. **Ask Question:** Enter a question suitable for a casual fan, e.g., "Who's the quarterback for the 49ers?" or "Did the 49ers win their last game?".
|
32 |
+
3. **Evaluate Response:** Check if the response is basic, focusing on high-level information or well-known players/outcomes, consistent with the persona's expected knowledge level (as defined in `zep_setup.py` preloading).
|
33 |
+
* **Resolution Status:** Unresolved.
|
34 |
+
* **Actions Taken:**
|
35 |
+
|
36 |
+
**Test 3: Test "Super Fan" Persona**
|
37 |
+
1. **Switch Persona:** Select the "Super Fan" radio button.
|
38 |
+
2. **Verify Chat Clear:** Confirm that the chat history displayed in the UI clears immediately after switching the persona. Check terminal logs for confirmation of a new Zep session being created.
|
39 |
+
3. **Ask Question:** Enter a question suitable for a super fan, e.g., "Tell me about the 49ers offensive line strategy", "What were the key defensive plays in the last game?", or "Discuss the impact of the recent draft picks".
|
40 |
+
4. **Evaluate Response:** Check if the response is more detailed, potentially including player specifics, strategic analysis, or historical context, consistent with the "Super Fan" persona's preloaded memory/profile.
|
41 |
+
* **Resolution Status:** Unresolved.
|
42 |
+
* **Actions Taken:**
|
43 |
+
|
44 |
+
**Test 4: Verify Persona Switching Robustness**
|
45 |
+
1. **Switch Back:** Switch back to "Casual Fan". Confirm chat clears again.
|
46 |
+
2. **Ask Casual Question Again:** Ask another simple question like "Who is the head coach?". Verify the response is appropriate for the Casual Fan persona.
|
47 |
+
3. **Rapid Switching (Optional):** Try switching personas back and forth a few times quickly to check for any state management issues or errors (monitor terminal logs).
|
48 |
+
* **Resolution Status:** Unresolved.
|
49 |
+
* **Actions Taken:**
|
50 |
+
|
51 |
+
**Test 5: Verify Existing Functionality (Regression Test)**
|
52 |
+
1. **Test Other Tools:** If possible, try interacting with the agent in ways that should trigger other tools (e.g., Player Search, Team Story, Game Recap if applicable).
|
53 |
+
2. **Confirm Behavior:** Verify that these tools still function as expected and that their corresponding UI components (Player Card, Team Story, Game Recap HTML) display correctly when triggered. Ensure the removal of the "Game Summary Search" tool didn't negatively impact other operations.
|
54 |
+
* **Resolution Status:** Unresolved.
|
55 |
+
* **Actions Taken:**
|
56 |
+
|
57 |
+
## Failure Condition
|
58 |
+
If any test consistently fails after attempting minor fixes (if applicable and agreed upon), or if unexpected critical errors occur, halt the testing process and consult with the user on how to proceed with debugging.
|
59 |
+
|
60 |
+
## Completion
|
61 |
+
The testing process is complete when all specified tests have been performed, the results have been documented in this file (updating 'Resolution Status' and 'Actions Taken'), and the user confirms the functionality meets the requirements outlined in Task 1.3.
|
62 |
+
|
63 |
+
## Challenges / Potential Concerns (from Task 1.3 Doc)
|
64 |
+
|
65 |
+
* **Zep Integration:** Ensuring correct usage of the Zep Cloud SDK, especially across async/sync contexts and correct session handling based on `user_id` and `session_id`.
|
66 |
+
* **Gradio State Management:** Properly handling persona switching and session management in `AppState` and UI callbacks (`handle_persona_change`) to avoid race conditions or context contamination. Verifying chat clearing works reliably.
|
67 |
+
* **Context Differentiation:** Confirming that the agent's responses noticeably differ between personas based on the Zep memory context. This relies on both successful Zep integration *and* effective memory pre-loading via `zep_setup.py`.
|
68 |
+
* **Regression Risks:** Changes to core files (`gradio_app.py`, `gradio_agent.py`) might have broken existing tool functionality or general chat behavior. Removal of the vector tool or Neo4j memory dependency could have unintended consequences.
|
69 |
+
* **Data Dependency:** Test outcomes depend on the successful execution of `zep_setup.py` (creating users, preloading memory) and the availability of the `persona_uuids.json` file.
|
70 |
+
|
71 |
+
## Appendix: Debugging Plan for "MemoryClient.get() got an unexpected keyword argument 'memory_type'" Error
|
72 |
+
|
73 |
+
This plan addresses the specific error encountered during testing: `MemoryClient.get() got an unexpected keyword argument 'memory_type'`
|
74 |
+
|
75 |
+
### Root Cause Analysis
|
76 |
+
The error indicates a mismatch between how the ZepCloudChatMessageHistory is being initialized and what its API actually accepts. The error specifically points to the `memory_type` parameter being passed to a method that doesn't accept it.
|
77 |
+
|
78 |
+
### Step-by-Step Debugging Plan
|
79 |
+
|
80 |
+
#### Step 1: Fix memory initialization in `gradio_agent.py`
|
81 |
+
1. Modify the `get_memory()` function to properly initialize ZepCloudChatMessageHistory
|
82 |
+
2. Remove any incompatible parameters (specifically `memory_type`)
|
83 |
+
3. Add error handling with appropriate fallbacks
|
84 |
+
|
85 |
+
#### Step 2: Test simplified agent without RunnableWithMessageHistory
|
86 |
+
1. Create a simplified agent using basic memory components
|
87 |
+
2. Test basic chat functionality to isolate memory issues from agent issues
|
88 |
+
|
89 |
+
#### Step 3: Update persona handling in `gradio_app.py`
|
90 |
+
1. Ensure persona switching correctly initializes new Zep sessions
|
91 |
+
2. Verify user_id and session_id are properly passed between components
|
92 |
+
|
93 |
+
#### Step 4: Check message format compatibility
|
94 |
+
1. Ensure messages sent to Zep API have the correct schema
|
95 |
+
2. Fix any format inconsistencies in how messages are stored and retrieved
|
96 |
+
|
97 |
+
#### Step 5: Test the complete integration
|
98 |
+
1. Test Casual Fan persona functionality
|
99 |
+
2. Test Super Fan persona functionality
|
100 |
+
3. Test persona switching
|
101 |
+
4. Verify persistence of chat history within sessions
|
102 |
+
|
103 |
+
### Version Compatibility Check
|
104 |
+
- Confirm that installed package versions are compatible
|
105 |
+
- Review any recent changes to the Zep Cloud SDK or LangChain that might affect integration
|
106 |
+
|
107 |
+
### Fallback Options
|
108 |
+
If Zep Cloud integration continues to be problematic:
|
109 |
+
1. Temporary fallback to in-memory ChatMessageHistory
|
110 |
+
2. Implement a simpler Redis-based memory solution
|
111 |
+
|
112 |
+
## Key Learning: Zep Memory Retrieval Architecture
|
113 |
+
|
114 |
+
### Critical Discovery: Session ID vs User ID for Memory Retrieval
|
115 |
+
|
116 |
+
The most important discovery during debugging was understanding how Zep's memory architecture works:
|
117 |
+
|
118 |
+
1. **Memory Retrieval Uses Session ID, Not User ID:**
|
119 |
+
- According to the Zep documentation, memory retrieval should be done using `session_id` rather than `user_id`
|
120 |
+
- The `memory.get()` method specifically requires a `session_id` parameter
|
121 |
+
- While each user can have multiple sessions, memory is accessed at the session level
|
122 |
+
- The documentation states: "Note that although `memory.get()` only requires a session ID, it is able to return memory derived from any session of that user. The session is just used to determine what's relevant."
|
123 |
+
|
124 |
+
2. **Memory Persistence for Personas:**
|
125 |
+
- To maintain persistent memory for different personas, we need to:
|
126 |
+
- Create fixed, persistent session IDs for each persona
|
127 |
+
- Store these session IDs alongside user IDs in our configuration
|
128 |
+
- Use the specific session ID when retrieving memory with `memory.get()`
|
129 |
+
- This ensures each persona maintains its distinct conversation history
|
130 |
+
|
131 |
+
3. **Parameter Mismatch with LangChain:**
|
132 |
+
- The `ZepCloudChatMessageHistory` implementation in LangChain was passing parameters to Zep that aren't supported:
|
133 |
+
- `memory_type` parameter was included in the LangChain wrapper but not accepted by the Zep client
|
134 |
+
- Official Zep SDK examples show no such parameter for `memory.get()`
|
135 |
+
- The current error occurs when `memory.messages` is accessed, which internally calls `_get_memory()`, which calls `self.zep_client.memory.get()`
|
136 |
+
- The solution is to remove unsupported parameters from the LangChain initialization
|
137 |
+
|
138 |
+
4. **Proper Implementation for Our App:**
|
139 |
+
- We need to modify our implementation to align with Zep's design:
|
140 |
+
- Generate and store fixed session IDs for our personas
|
141 |
+
- Use these IDs in `get_memory()` without any additional problematic parameters
|
142 |
+
- Access chat history by retrieving from the specific session associated with each persona
|
143 |
+
|
144 |
+
This understanding fundamentally changes our approach to implementing the persona memory system.
|
145 |
+
|
146 |
+
## TO-DO: Zep Memory & Fan Profile Integration Plan
|
147 |
+
|
148 |
+
### 1. Update `zep_setup.py` to Associate Fan Profiles with Session IDs
|
149 |
+
|
150 |
+
```python
|
151 |
+
# file: z_utils/zep_setup.py
|
152 |
+
"""
|
153 |
+
Create two persona users in Zep Cloud and preload their memory graphs.
|
154 |
+
Run once: python z_utils/zep_setup.py
|
155 |
+
"""
|
156 |
+
|
157 |
+
import asyncio, os, uuid, json
|
158 |
+
from datetime import datetime, timezone
|
159 |
+
from dotenv import load_dotenv
|
160 |
+
from zep_cloud.client import AsyncZep
|
161 |
+
from zep_cloud.types import Message
|
162 |
+
from zep_cloud.errors import NotFoundError
|
163 |
+
|
164 |
+
# Load environment variables from .env file
|
165 |
+
load_dotenv()
|
166 |
+
API_KEY = os.getenv("ZEP_API_KEY")
|
167 |
+
BASE_URL = os.getenv("ZEP_API_URL") # optional for self-hosted
|
168 |
+
|
169 |
+
if not API_KEY:
|
170 |
+
raise RuntimeError("ZEP_API_KEY missing in environment variables.")
|
171 |
+
|
172 |
+
# Initialize AsyncZep client, handling optional base_url
|
173 |
+
zep_client_args = {"api_key": API_KEY}
|
174 |
+
if BASE_URL:
|
175 |
+
zep_client_args["base_url"] = BASE_URL
|
176 |
+
zep = AsyncZep(**zep_client_args)
|
177 |
+
|
178 |
+
# -------- persona blue-prints -------------------------------------------------
|
179 |
+
PERSONAS = {
|
180 |
+
"Casual Fan": {
|
181 |
+
"user_id": uuid.uuid4().hex,
|
182 |
+
"session_id": uuid.uuid4().hex, # Add persistent session_id for this persona
|
183 |
+
"email": "[email protected]",
|
184 |
+
"first_name": "Casual",
|
185 |
+
"last_name": "Fan",
|
186 |
+
"metadata": {"fan_type": "casual"},
|
187 |
+
"profile": {
|
188 |
+
"entity_type": "fan_profile",
|
189 |
+
"fan_type": "casual",
|
190 |
+
"motivations": ["feel included", "see spectacular plays"],
|
191 |
+
"behaviour_patterns": ["checks scores Monday", "shares memes"],
|
192 |
+
"knowledge_depth": "surface",
|
193 |
+
"favorite_team": "49ers",
|
194 |
+
},
|
195 |
+
"sample_chat": [
|
196 |
+
("user", "Who's the quarterback for the 49ers?"),
|
197 |
+
("assistant", "It's Brock Purdy, the breakout star of 2023."),
|
198 |
+
("user", "Did we win last night?"),
|
199 |
+
("assistant", "Yes! The Niners beat Seattle 31-17."),
|
200 |
+
],
|
201 |
+
},
|
202 |
+
"Super Fan": {
|
203 |
+
"user_id": uuid.uuid4().hex,
|
204 |
+
"session_id": uuid.uuid4().hex, # Add persistent session_id for this persona
|
205 |
+
"email": "[email protected]",
|
206 |
+
"first_name": "Super",
|
207 |
+
"last_name": "Fan",
|
208 |
+
"metadata": {"fan_type": "super"},
|
209 |
+
"profile": {
|
210 |
+
"entity_type": "fan_profile",
|
211 |
+
"fan_type": "super",
|
212 |
+
"motivations": ["understand strategy", "debate roster moves"],
|
213 |
+
"behaviour_patterns": ["reads All-22", "posts long analyses"],
|
214 |
+
"knowledge_depth": "detailed",
|
215 |
+
"favorite_team": "49ers",
|
216 |
+
},
|
217 |
+
"sample_chat": [
|
218 |
+
("user", "Break down our outside-zone success rate vs top-10 fronts."),
|
219 |
+
("assistant", "49ers average 5.6 YPC on outside-zone against top-10 run Ds..."),
|
220 |
+
("user", "Any cap room left after Aiyuk's extension?"),
|
221 |
+
("assistant", "Roughly $1.2 M pre-June-1; expect a McCaffrey restructure."),
|
222 |
+
],
|
223 |
+
},
|
224 |
+
}
|
225 |
+
|
226 |
+
# -------- helper --------------------------------------------------------------
|
227 |
+
async def create_user_if_needed(p):
|
228 |
+
"""Creates a Zep user if they don't already exist."""
|
229 |
+
user_id = p["user_id"]
|
230 |
+
try:
|
231 |
+
await zep.user.get(user_id=user_id)
|
232 |
+
print(f"User '{user_id}' already exists. Skipping creation.")
|
233 |
+
except NotFoundError:
|
234 |
+
# User not found, safe to create
|
235 |
+
try:
|
236 |
+
await zep.user.add(
|
237 |
+
user_id=user_id,
|
238 |
+
email=p["email"],
|
239 |
+
first_name=p["first_name"],
|
240 |
+
last_name=p["last_name"],
|
241 |
+
metadata=p["metadata"],
|
242 |
+
)
|
243 |
+
print(f"Successfully created user '{user_id}'.")
|
244 |
+
except Exception as add_ex:
|
245 |
+
print(f"Error creating user '{user_id}'. Type: {type(add_ex).__name__}, Details: {add_ex}")
|
246 |
+
except Exception as ex:
|
247 |
+
print(f"Error checking user '{user_id}'. Type: {type(ex).__name__}, Details: {ex}")
|
248 |
+
|
249 |
+
|
250 |
+
async def preload(p):
|
251 |
+
"""Preloads user profile graph data and sample chat memory."""
|
252 |
+
user_id = p["user_id"]
|
253 |
+
# Use the persistent session_id for this persona
|
254 |
+
session_id = p["session_id"]
|
255 |
+
|
256 |
+
print(f"Preloading data for user '{user_id}' with session '{session_id}'...")
|
257 |
+
|
258 |
+
# Preload fan profile using Zep Graph API
|
259 |
+
try:
|
260 |
+
await zep.graph.add(
|
261 |
+
user_id=user_id,
|
262 |
+
type="json",
|
263 |
+
data=json.dumps(p["profile"]),
|
264 |
+
)
|
265 |
+
print(f" - Added profile graph data for user '{user_id}'.")
|
266 |
+
except Exception as graph_ex:
|
267 |
+
print(f" - Error adding profile graph data for user '{user_id}'. Type: {type(graph_ex).__name__}, Details: {graph_ex}")
|
268 |
+
|
269 |
+
# Create a persistent session specifically for this persona
|
270 |
+
try:
|
271 |
+
await zep.memory.add_session(session_id=session_id, user_id=user_id)
|
272 |
+
print(f" - Created persistent session '{session_id}' for user '{user_id}'.")
|
273 |
+
|
274 |
+
msgs = [
|
275 |
+
Message(role=role, content=text, role_type=role) # Assuming role maps directly to role_type
|
276 |
+
for role, text in p["sample_chat"]
|
277 |
+
]
|
278 |
+
await zep.memory.add(session_id=session_id, messages=msgs)
|
279 |
+
print(f" - Added {len(msgs)} sample messages to session '{session_id}'.")
|
280 |
+
except Exception as mem_ex:
|
281 |
+
print(f" - Error preloading memory for user '{user_id}' (session '{session_id}'). Type: {type(mem_ex).__name__}, Details: {mem_ex}")
|
282 |
+
|
283 |
+
|
284 |
+
# -------- main ----------------------------------------------------------------
|
285 |
+
async def main():
|
286 |
+
"""Main function to create users, preload data, and save UUIDs."""
|
287 |
+
print("Starting Zep setup process...")
|
288 |
+
for name, persona in PERSONAS.items():
|
289 |
+
print(f"\nProcessing Persona: {name}")
|
290 |
+
await create_user_if_needed(persona)
|
291 |
+
await preload(persona)
|
292 |
+
|
293 |
+
# Persist generated UUIDs and session IDs for the Gradio app
|
294 |
+
output_dir = "ifx-sandbox/z_utils"
|
295 |
+
output_file = os.path.join(output_dir, "persona_uuids.json")
|
296 |
+
try:
|
297 |
+
os.makedirs(output_dir, exist_ok=True)
|
298 |
+
with open(output_file, "w") as f:
|
299 |
+
# Include both user_id and session_id in the JSON file
|
300 |
+
json.dump({k: {"user_id": v["user_id"], "session_id": v["session_id"]} for k, v in PERSONAS.items()}, f, indent=2)
|
301 |
+
print(f"\nSuccessfully saved persona data to {output_file}")
|
302 |
+
except IOError as e:
|
303 |
+
print(f"\nError saving persona data to {output_file}: {e}")
|
304 |
+
except Exception as ex:
|
305 |
+
print(f"\nUnexpected error saving persona data: {ex}")
|
306 |
+
|
307 |
+
|
308 |
+
print("\n--- Generated Persona Data ---")
|
309 |
+
for name, p in PERSONAS.items():
|
310 |
+
print(f"{name}: user_id={p['user_id']}, session_id={p['session_id']}")
|
311 |
+
print("-----------------------------")
|
312 |
+
print("Zep setup process complete.")
|
313 |
+
|
314 |
+
if __name__ == "__main__":
|
315 |
+
# Ensure the script is run from the workspace root or adjust paths accordingly
|
316 |
+
# Basic check: Does 'ifx-sandbox' exist in the current directory?
|
317 |
+
if not os.path.isdir("ifx-sandbox"):
|
318 |
+
print("Warning: This script assumes it's run from the workspace root directory")
|
319 |
+
print("containing 'ifx-sandbox'. If running from elsewhere, paths might be incorrect.")
|
320 |
+
|
321 |
+
asyncio.run(main())
|
322 |
+
```
|
323 |
+
|
324 |
+
### 2. TO-DO: Update `gradio_app.py` to Handle New Session IDs
|
325 |
+
|
326 |
+
```python
|
327 |
+
# Update AppState to load both user_id and session_id
|
328 |
+
def _load_persona_data(self):
|
329 |
+
"""Load persona UUID mappings from file."""
|
330 |
+
persona_file = os.path.join(os.path.dirname(__file__), 'z_utils', 'persona_uuids.json')
|
331 |
+
try:
|
332 |
+
with open(persona_file, 'r') as f:
|
333 |
+
self.persona_data = json.load(f)
|
334 |
+
print(f"Loaded persona UUIDs: {self.persona_data}")
|
335 |
+
except Exception as e:
|
336 |
+
print(f"Error loading persona data: {e}")
|
337 |
+
self.persona_data = {
|
338 |
+
"Casual Fan": {"user_id": uuid.uuid4().hex, "session_id": uuid.uuid4().hex},
|
339 |
+
"Super Fan": {"user_id": uuid.uuid4().hex, "session_id": uuid.uuid4().hex}
|
340 |
+
}
|
341 |
+
print(f"Using fallback persona data: {self.persona_data}")
|
342 |
+
```
|
343 |
+
|
344 |
+
```python
|
345 |
+
# Update handle_persona_change to use the saved session_id instead of creating a new one
|
346 |
+
async def handle_persona_change(persona_name, state: AppState):
|
347 |
+
"""Handle persona switch: update state and create new Zep session."""
|
348 |
+
try:
|
349 |
+
print(f"Handling persona change to: {persona_name}")
|
350 |
+
state.current_persona = persona_name
|
351 |
+
|
352 |
+
# Use the user_id associated with this persona
|
353 |
+
if state.persona_data and persona_name in state.persona_data:
|
354 |
+
persona_info = state.persona_data[persona_name]
|
355 |
+
state.user_id = persona_info["user_id"]
|
356 |
+
state.session_id = persona_info["session_id"] # Use the persistent session_id
|
357 |
+
|
358 |
+
print(f"Using persistent session for persona '{persona_name}'. User ID: {state.user_id}, Session ID: {state.session_id}")
|
359 |
+
else:
|
360 |
+
# Fallback if persona data not available
|
361 |
+
state.user_id = str(uuid.uuid4())
|
362 |
+
state.session_id = str(uuid.uuid4())
|
363 |
+
print(f"Creating new user/session IDs. User ID: {state.user_id}, Session ID: {state.session_id}")
|
364 |
+
|
365 |
+
return [] # Return empty list to clear chatbot
|
366 |
+
except Exception as e:
|
367 |
+
print(f"Error in handle_persona_change: {e}")
|
368 |
+
return []
|
369 |
+
```
|
370 |
+
|
371 |
+
### 3. TO-DO: Fix `gradio_agent.py` ZepCloudChatMessageHistory Implementation
|
372 |
+
|
373 |
+
```python
|
374 |
+
# Update imports to use the correct path for ZepCloudChatMessageHistory
|
375 |
+
from langchain_community.chat_message_histories.zep_cloud import ZepCloudChatMessageHistory
|
376 |
+
|
377 |
+
def get_memory(session_id):
|
378 |
+
"""Get memory for a specific session, or create a new one if it doesn't exist"""
|
379 |
+
# First check if ZEP API key is available
|
380 |
+
zep_api_key = os.environ.get("ZEP_API_KEY")
|
381 |
+
|
382 |
+
if zep_api_key:
|
383 |
+
try:
|
384 |
+
# Use Zep Cloud for memory storage with only required parameters
|
385 |
+
message_history = ZepCloudChatMessageHistory(
|
386 |
+
session_id=session_id,
|
387 |
+
api_key=zep_api_key
|
388 |
+
)
|
389 |
+
print(f"Using ZepCloudChatMessageHistory for session {session_id}")
|
390 |
+
return message_history
|
391 |
+
except Exception as e:
|
392 |
+
print(f"Error initializing ZepCloudChatMessageHistory: {e}")
|
393 |
+
print("Falling back to in-memory ChatMessageHistory")
|
394 |
+
else:
|
395 |
+
print("ZEP_API_KEY not found in environment variables")
|
396 |
+
print("Falling back to in-memory ChatMessageHistory")
|
397 |
+
|
398 |
+
# Fallback to in-memory chat history
|
399 |
+
from langchain_community.chat_message_histories import ChatMessageHistory
|
400 |
+
return ChatMessageHistory()
|
401 |
+
```
|
docs/Phase 1/z_Task 1.3 Memory & Persona Implementation_Debug Plan_Attempt 2.md
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Task 1.3 Memory & Persona Implementation - Debug Plan
|
2 |
+
|
3 |
+
This document tracks the step-by-step implementation and debugging process for Task 1.3, integrating Zep Memory and Persona selection into the 49ers FanAI Hub, following a revised plan based on codebase review and Zep documentation analysis.
|
4 |
+
|
5 |
+
**Important Design Intent:** The goal is to leverage Zep Cloud to load pre-defined knowledge graphs associated with specific user personas ("Casual Fan", "Super Fan") based on the user's selection in the UI. This pre-loaded knowledge will provide long-term context for the agent. However, the messages exchanged during a specific Gradio chat session are intended to be **ephemeral** regarding Zep's persistent storage. That is, user inputs and assistant responses **should NOT be added back** to the Zep user's memory graph or session history using `memory.add`. The Zep integration focuses *only* on retrieving relevant context from the pre-loaded persona graphs.
|
6 |
+
|
7 |
+
## Revised Implementation Steps (Updated Order)
|
8 |
+
|
9 |
+
1. **Implement Zep User & Memory Setup Script (`z_utils/zep_setup.py`)**
|
10 |
+
* [X] Create `z_utils/zep_setup.py`.
|
11 |
+
* [X] Load `ZEP_API_KEY`, `ZEP_API_URL` (optional).
|
12 |
+
* [X] Initialize `AsyncZep` client.
|
13 |
+
* [X] Define `PERSONAS` dictionary.
|
14 |
+
* [X] Generate fixed UUIDs for each persona.
|
15 |
+
* [X] Save UUIDs to `z_utils/persona_uuids.json`.
|
16 |
+
* [X] Implement `async def create_zep_user(client, user_data)` (check existence first).
|
17 |
+
* [X] Define persona-specific knowledge (text/JSON suitable for `graph.add`).
|
18 |
+
* [X] Implement `async def preload_knowledge(client, user_id, persona_knowledge)` using `client.graph.add`. (Renamed from preload_memory)
|
19 |
+
* [X] Implement `async def main()` to orchestrate user creation, knowledge pre-loading, and UUID saving.
|
20 |
+
* [X] Add `if __name__ == "__main__": asyncio.run(main())`.
|
21 |
+
* **Status:** Completed
|
22 |
+
|
23 |
+
2. **Refactor ID Management & AppState (`gradio_utils.py`, `gradio_app.py`)**
|
24 |
+
* [X] Modify `gradio_utils.py`: Remove global ID state, create generator functions.
|
25 |
+
* **Details:** Removed the global variables `_session_id` and `_user_id`. Removed the `reset_ids` function. Renamed existing internal functions to `generate_session_id()` and `generate_user_id()`, making them simple wrappers around `str(uuid.uuid4())` to provide unique IDs on demand without relying on global state.
|
26 |
+
* [X] Modify `gradio_app.py`:
|
27 |
+
* [X] Enhance `AppState` (add `current_persona`, `persona_data`, ensure `user_id`, `session_id` are instance vars).
|
28 |
+
* **Details:** Added `current_persona = DEFAULT_PERSONA` and `persona_data = None` as instance attributes to the `AppState` class `__init__` method. Confirmed `user_id` and `session_id` were already instance attributes (initialized to `None`). Added helper methods `_initialize_zep` (to create the `AsyncZep` client using environment variables `ZEP_API_KEY`/`ZEP_API_URL` with error handling) and `_load_persona_data` (to load the `z_utils/persona_uuids.json` file into the `self.persona_data` attribute with error handling for file not found or JSON parsing issues).
|
29 |
+
* [X] Load persona UUIDs from JSON into `AppState.persona_data`.
|
30 |
+
* **Details:** This is handled within the `_load_persona_data` method called by `initialize_chat`. It opens `z_utils/persona_uuids.json`, uses `json.load()` to parse it, and stores the resulting dictionary in `current_state.persona_data`.
|
31 |
+
* [X] Update `initialize_chat`: Set default persona, generate initial `user_id` (from loaded data) & `session_id`, store in `state`, create initial Zep session.
|
32 |
+
* **Details:** This async function is triggered by `demo.load`. It first calls `_initialize_zep` and `_load_persona_data`. It then sets `current_state.current_persona` to `DEFAULT_PERSONA`. It retrieves the corresponding `user_id` from `current_state.persona_data` and stores it in `current_state.user_id`. It calls `generate_session_id()` (from `gradio_utils`) and stores the result in `current_state.session_id`. Finally, if the Zep client is ready and a `user_id` was successfully loaded, it calls `await current_state.zep_client.memory.add_session(...)` using the generated `session_id` and loaded `user_id` to establish the initial session context in Zep Cloud. The original logic involving `zep.user.add` was removed as user creation is now handled by the separate `zep_setup.py` script.
|
33 |
+
* [X] Update `process_message`: Use `state.session_id`, `state.zep_client`.
|
34 |
+
* **Details:** Modified the `process_message` function (which handles the core agent interaction logic) to retrieve the Zep client (`current_state.zep_client`) and the current `session_id` (`current_state.session_id`) directly from the `AppState` object passed into it. This ensures it uses the correct context set by `initialize_chat` or `handle_persona_change`. **Crucially**, the calls to `await current_state.zep_client.memory.add(...)` for both user and assistant messages within this function were **commented out** to implement the design intent of keeping the Gradio chat ephemeral and *not* persistently storing conversation history back into the Zep memory for the selected persona.
|
35 |
+
* [X] Add `handle_persona_change` async function: Update `state` (`current_persona`, `user_id`), generate *new* `session_id`, store in `state`, create new Zep session, clear UI.
|
36 |
+
* **Details:** Created a new async function `handle_persona_change` designed to be triggered by the `persona_selector.change` event. It receives the selected `persona_name` and the `current_state`. It looks up the corresponding `new_user_id` from `current_state.persona_data`. It updates `current_state.current_persona` and `current_state.user_id`. It calls `generate_session_id()` to get a *new* session ID for the new persona context and stores it in `current_state.session_id`. If the Zep client is available, it calls `await current_state.zep_client.memory.add_session(...)` with the new `session_id` and `user_id`. It also clears internal caches used by tools (like `game_recap.last_game_data`) and returns `[]` to clear the Gradio chatbot UI.
|
37 |
+
* [X] Add Persona Selector UI (`gr.Radio`).
|
38 |
+
* **Details:** Added a `gr.Radio` component named `persona_selector` within the `create_ui` function. Configured with `choices=["Casual Fan", "Super Fan"]`, a label, the `DEFAULT_PERSONA` as the initial value, and `interactive=True`.
|
39 |
+
* [X] Wire UI events (`demo.load`, `persona_selector.change`, `msg.submit`, `clear_button`).
|
40 |
+
* **Details:** Configured the event listeners within `create_ui`:
|
41 |
+
* `demo.load` was wired to call `initialize_chat`, passing the initial `gr.State(state)` and updating the `chatbot` and `persona_selector` components (and later, `gr.State(state)` itself during debugging attempts).
|
42 |
+
* `persona_selector.change` was wired to call `handle_persona_change`, passing the `persona_selector` value and `gr.State(state)`, updating the `chatbot` component.
|
43 |
+
* `msg.submit` was initially wired using `.then()` chaining involving `user_input` and `process_and_respond`. During debugging, this was simplified to directly call `process_and_respond`, passing `msg`, `chatbot`, and `gr.State(state)` (later removed/re-added `gr.State` during debugging) as inputs, and updating `msg`, `chatbot`, and the dynamic info components.
|
44 |
+
* `clear_button.click` was wired to call `clear_chat`, passing `gr.State(state)` and updating various UI components including `persona_selector` back to default.
|
45 |
+
* **Status:** Completed
|
46 |
+
|
47 |
+
3. **Integrate Zep Memory into Agent (`gradio_agent.py`)**
|
48 |
+
* [X] Replace `Neo4jChatMessageHistory` import with `ZepCloudChatMessageHistory`.
|
49 |
+
* **Details:** Changed the import statement from `from langchain_neo4j import Neo4jChatMessageHistory` to `from langchain_community.memory.zep_cloud_memory import ZepCloudChatMessageHistory`.
|
50 |
+
* [X] Update `get_memory` function to instantiate `ZepCloudChatMessageHistory` using `session_id` argument and Zep credentials from env vars. Add error handling for missing key / Zep init failure (fallback to basic history).
|
51 |
+
* **Details:** Rewritten the `get_memory(session_id)` function. It now retrieves `ZEP_API_KEY` and optionally `ZEP_API_URL` from environment variables. It includes a check: if `ZEP_API_KEY` is missing, it prints an error and returns a basic fallback `langchain.memory.ChatMessageHistory()`. Otherwise, it constructs the arguments dictionary (`session_id`, `api_key`, optional `url`) and instantiates `ZepCloudChatMessageHistory(**history_args)`. A `try...except` block was added around the instantiation to catch potential Zep client initialization errors, also falling back to `ChatMessageHistory` if an exception occurs. This ensures the agent receives a valid memory object, albeit potentially a non-persistent one if Zep connection fails.
|
52 |
+
* **Status:** Completed
|
53 |
+
|
54 |
+
4. **Perform Housekeeping Tasks** (Already Completed)
|
55 |
+
* [X] Create `z_utils/neo4j_cleanup.py` script using `gradio_graph`.
|
56 |
+
* [X] Add Cypher query `MATCH (g:Game) WHERE g.embedding IS NOT NULL REMOVE g.embedding RETURN count(*)` to script.
|
57 |
+
* [X] Add execution logic and logging/print statements to `neo4j_cleanup.py`.
|
58 |
+
* [X] *Action:* Run `python ifx-sandbox/z_utils/neo4j_cleanup.py` (User needs to run this).
|
59 |
+
* [X] Delete `ifx-sandbox/tools/vector.py`.
|
60 |
+
* [X] Remove "Game Summary Search" tool (`get_game_summary`) import and definition from `tools` list in `gradio_agent.py`. (Verified Complete)
|
61 |
+
* **Status:** Completed (Pre-existing / Verified)
|
62 |
+
|
63 |
+
5. **Update Documentation (`docs/requirements.md`)**
|
64 |
+
* [X] Update "Persona Memory Structure (Zep)" section with actual implementation details.
|
65 |
+
* [X] Mark Feature 0 (Persona Selection) as completed.
|
66 |
+
* [X] Update Task 1.3 Description/Status in Detailed Work Plan.
|
67 |
+
* **Status:** Completed
|
68 |
+
|
69 |
+
## Execution Log & Review Points
|
70 |
+
|
71 |
+
* **2024-07-27:** Created debug plan file.
|
72 |
+
* **2024-07-27:** Refactored `gradio_utils.py` to remove global state and use ID generator functions.
|
73 |
+
* **2024-07-27:** Updated plan order: Step 2 (`zep_setup.py`) moved to first, Step 4 marked as completed.
|
74 |
+
* **2024-07-27:** Implemented `zep_setup.py`, fixed import errors, fixed async context manager issue, fixed UUID loading/saving logic. Script executed successfully, creating users and preloading knowledge.
|
75 |
+
* **2024-07-27:** Modified `gradio_app.py`: Enhanced `AppState`, added persona loading/handling, integrated UI selector, updated initialization/message processing, wired events.
|
76 |
+
* **2024-07-27:** Clarified design intent: Zep context retrieval only; Gradio chat messages are ephemeral, not added back to Zep via `memory.add`.
|
77 |
+
* **2024-07-27:** Modified `gradio_app.py` again: Commented out `zep_client.memory.add` calls in `process_message` to implement ephemeral chat regarding Zep persistence.
|
78 |
+
* **2024-07-27:** Modified `gradio_agent.py`: Replaced Neo4j memory with `ZepCloudChatMessageHistory`, updated `get_memory` function with env var loading and error handling/fallback, ensured `session_id` is passed correctly to agent invocation.
|
79 |
+
* **2024-07-27:** Verified Step 4 Housekeeping: Confirmed removal of vector tool import and definition in `gradio_agent.py`.
|
80 |
+
* **2024-07-27:** Updated `docs/requirements.md` to reflect completed implementation details for Feature 0/Task 1.3 and marked as complete.
|
81 |
+
|
82 |
+
*(This section will be updated after each step)*
|
83 |
+
|
84 |
+
**Implementation Complete.**
|
85 |
+
|
86 |
+
---
|
87 |
+
|
88 |
+
## Debugging Log (Post-Implementation)
|
89 |
+
|
90 |
+
**2024-07-28:** Began testing the implementation.
|
91 |
+
|
92 |
+
* **Issue 1: Startup Error - `Attempted to process message before state initialization.`**
|
93 |
+
* **Observation:** Error occurred immediately after `Application state initialized.` log during startup.
|
94 |
+
* **Root Cause:** The `initialize_chat` function completed its setup but failed to set the `state.initialized` flag to `True` before returning.
|
95 |
+
* **Fix 1:** Added `current_state.initialized = True` before the return statement in `initialize_chat`. -> *Failed to resolve fully.*
|
96 |
+
|
97 |
+
* **Issue 2: Startup Error Persists / State Propagation Delay**
|
98 |
+
* **Observation:** After Fix 1, the same error occurred, originating from `process_and_respond` being called with an empty message immediately after initialization. Debug logs showed `process_and_respond` received a state object where `initialized`
|
z_utils/neo4j_cleanup.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
"""
|
3 |
+
Script to perform Neo4j cleanup: Remove the 'embedding' property from all :Game nodes.
|
4 |
+
This is part of Task 1.3 housekeeping.
|
5 |
+
|
6 |
+
Run once: python ifx-sandbox/z_utils/neo4j_cleanup.py
|
7 |
+
"""
|
8 |
+
|
9 |
+
import os
|
10 |
+
import sys
|
11 |
+
|
12 |
+
# Adjust path to import graph object from the parent directory (ifx-sandbox)
|
13 |
+
# Assumes script is run from the workspace root directory
|
14 |
+
workspace_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
15 |
+
ifx_sandbox_path = os.path.join(workspace_root, 'ifx-sandbox')
|
16 |
+
|
17 |
+
if ifx_sandbox_path not in sys.path:
|
18 |
+
print(f"Adding {ifx_sandbox_path} to sys.path")
|
19 |
+
sys.path.insert(0, ifx_sandbox_path)
|
20 |
+
|
21 |
+
try:
|
22 |
+
# Import the configured graph instance from ifx-sandbox directory
|
23 |
+
from gradio_graph import graph
|
24 |
+
print("Successfully imported graph object from gradio_graph.")
|
25 |
+
except ImportError as e:
|
26 |
+
print(f"Error importing gradio_graph: {e}")
|
27 |
+
print("Please ensure gradio_graph.py exists in the 'ifx-sandbox' directory and is configured correctly.")
|
28 |
+
print("Make sure you are running this script from the workspace root directory.")
|
29 |
+
sys.exit(1)
|
30 |
+
except Exception as e:
|
31 |
+
print(f"An unexpected error occurred during import: {e}")
|
32 |
+
sys.exit(1)
|
33 |
+
|
34 |
+
def cleanup_game_embeddings():
|
35 |
+
"""Removes the embedding property from all Game nodes in Neo4j."""
|
36 |
+
print("Starting Neo4j cleanup: Removing 'embedding' property from :Game nodes...")
|
37 |
+
|
38 |
+
cleanup_query = """
|
39 |
+
MATCH (g:Game)
|
40 |
+
WHERE g.embedding IS NOT NULL
|
41 |
+
REMOVE g.embedding
|
42 |
+
RETURN count(g) as removed_count
|
43 |
+
"""
|
44 |
+
|
45 |
+
try:
|
46 |
+
result = graph.query(cleanup_query)
|
47 |
+
|
48 |
+
if result and isinstance(result, list) and len(result) > 0 and 'removed_count' in result[0]:
|
49 |
+
count = result[0]['removed_count']
|
50 |
+
print(f"Successfully removed 'embedding' property from {count} :Game node(s).")
|
51 |
+
else:
|
52 |
+
# Query might return empty list if no nodes matched or had the property
|
53 |
+
print("Cleanup query executed. No 'embedding' properties found or removed from :Game nodes (or query result format unexpected).")
|
54 |
+
print(f"Raw query result: {result}")
|
55 |
+
|
56 |
+
except Exception as e:
|
57 |
+
print(f"Error executing Neo4j cleanup query: {e}")
|
58 |
+
print("Cleanup failed.")
|
59 |
+
|
60 |
+
if __name__ == "__main__":
|
61 |
+
print("Running Neo4j Game Embedding Cleanup script...")
|
62 |
+
# Basic check: Does 'ifx-sandbox' exist relative to script location?
|
63 |
+
if not os.path.isdir(ifx_sandbox_path):
|
64 |
+
print("Error: Cannot find 'ifx-sandbox' directory.")
|
65 |
+
print("Please ensure you run this script from the workspace root directory.")
|
66 |
+
sys.exit(1)
|
67 |
+
|
68 |
+
cleanup_game_embeddings()
|
69 |
+
print("Script execution complete.")
|
z_utils/persona_session_ids.json
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"Casual Fan": "241b3478c7634492abee9f178b5341cb",
|
3 |
+
"Super Fan": "dedcf5cb0d71475f976f4f66d98d6400"
|
4 |
+
}
|
z_utils/persona_uuids.json
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"Casual Fan": "bdfc78a0-069a-48bf-af11-a843b7c6844e",
|
3 |
+
"Super Fan": "2e28be0a-8e2f-4480-8caf-975f58db0bca"
|
4 |
+
}
|
z_utils/restart_space.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
import os
|
3 |
+
import time
|
4 |
+
import subprocess
|
5 |
+
from huggingface_hub import HfApi
|
6 |
+
|
7 |
+
# Get token from environment or from stored token
|
8 |
+
def get_token():
|
9 |
+
# Try to get token from cached location
|
10 |
+
try:
|
11 |
+
token_path = os.path.expanduser("~/.cache/huggingface/token")
|
12 |
+
if os.path.exists(token_path):
|
13 |
+
with open(token_path, "r") as f:
|
14 |
+
return f.read().strip()
|
15 |
+
except:
|
16 |
+
pass
|
17 |
+
|
18 |
+
# If that fails, try using the huggingface-cli to print the token
|
19 |
+
try:
|
20 |
+
result = subprocess.run(["huggingface-cli", "whoami", "--token"],
|
21 |
+
capture_output=True, text=True, check=True)
|
22 |
+
if result.stdout:
|
23 |
+
return result.stdout.strip()
|
24 |
+
except:
|
25 |
+
pass
|
26 |
+
|
27 |
+
return None
|
28 |
+
|
29 |
+
# Get the token
|
30 |
+
token = get_token()
|
31 |
+
if not token:
|
32 |
+
print("No Hugging Face token found. Please login using 'huggingface-cli login'")
|
33 |
+
exit(1)
|
34 |
+
|
35 |
+
# Hugging Face repo ID
|
36 |
+
repo_id = "aliss77777/IFX-sandbox"
|
37 |
+
|
38 |
+
# Initialize the Hugging Face API with the token
|
39 |
+
api = HfApi(token=token)
|
40 |
+
|
41 |
+
print(f"Restarting Space: {repo_id}")
|
42 |
+
|
43 |
+
try:
|
44 |
+
# Restart the Space
|
45 |
+
api.restart_space(repo_id=repo_id)
|
46 |
+
print(f"✓ Space restart request sent!")
|
47 |
+
print(f"The Space should restart shortly. You can check its status at: https://huggingface.co/spaces/{repo_id}")
|
48 |
+
except Exception as e:
|
49 |
+
print(f"Error restarting Space: {str(e)}")
|
50 |
+
exit(1)
|
z_utils/set_secrets.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
import os
|
3 |
+
import subprocess
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
from huggingface_hub import HfApi
|
6 |
+
|
7 |
+
# Load environment variables from .env file
|
8 |
+
load_dotenv()
|
9 |
+
|
10 |
+
# Get Neo4j credentials
|
11 |
+
AURA_CONNECTION_URI = os.environ.get("AURA_CONNECTION_URI")
|
12 |
+
AURA_USERNAME = os.environ.get("AURA_USERNAME")
|
13 |
+
AURA_PASSWORD = os.environ.get("AURA_PASSWORD")
|
14 |
+
|
15 |
+
# Get OpenAI credentials
|
16 |
+
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
|
17 |
+
OPENAI_MODEL = os.environ.get("OPENAI_MODEL")
|
18 |
+
|
19 |
+
# Get token from environment or from stored token
|
20 |
+
def get_token():
|
21 |
+
# Try to get token from cached location
|
22 |
+
try:
|
23 |
+
token_path = os.path.expanduser("~/.cache/huggingface/token")
|
24 |
+
if os.path.exists(token_path):
|
25 |
+
with open(token_path, "r") as f:
|
26 |
+
return f.read().strip()
|
27 |
+
except:
|
28 |
+
pass
|
29 |
+
|
30 |
+
# If that fails, try using the huggingface-cli to print the token
|
31 |
+
try:
|
32 |
+
result = subprocess.run(["huggingface-cli", "whoami", "--token"],
|
33 |
+
capture_output=True, text=True, check=True)
|
34 |
+
if result.stdout:
|
35 |
+
return result.stdout.strip()
|
36 |
+
except:
|
37 |
+
pass
|
38 |
+
|
39 |
+
return None
|
40 |
+
|
41 |
+
# Get the token
|
42 |
+
token = get_token()
|
43 |
+
if not token:
|
44 |
+
print("No Hugging Face token found. Please login using 'huggingface-cli login'")
|
45 |
+
exit(1)
|
46 |
+
|
47 |
+
# Hugging Face repo ID
|
48 |
+
repo_id = "aliss77777/IFX-sandbox"
|
49 |
+
|
50 |
+
# Initialize the Hugging Face API with the token
|
51 |
+
api = HfApi(token=token)
|
52 |
+
|
53 |
+
print("Setting secrets for", repo_id)
|
54 |
+
|
55 |
+
# Set each secret
|
56 |
+
try:
|
57 |
+
# Set Neo4j credentials
|
58 |
+
api.add_space_secret(repo_id=repo_id, key="AURA_CONNECTION_URI", value=AURA_CONNECTION_URI)
|
59 |
+
print("✓ Set AURA_CONNECTION_URI")
|
60 |
+
|
61 |
+
api.add_space_secret(repo_id=repo_id, key="AURA_USERNAME", value=AURA_USERNAME)
|
62 |
+
print("✓ Set AURA_USERNAME")
|
63 |
+
|
64 |
+
api.add_space_secret(repo_id=repo_id, key="AURA_PASSWORD", value=AURA_PASSWORD)
|
65 |
+
print("✓ Set AURA_PASSWORD")
|
66 |
+
|
67 |
+
# Set OpenAI credentials
|
68 |
+
api.add_space_secret(repo_id=repo_id, key="OPENAI_API_KEY", value=OPENAI_API_KEY)
|
69 |
+
print("✓ Set OPENAI_API_KEY")
|
70 |
+
|
71 |
+
api.add_space_secret(repo_id=repo_id, key="OPENAI_MODEL", value=OPENAI_MODEL)
|
72 |
+
print("✓ Set OPENAI_MODEL")
|
73 |
+
|
74 |
+
print("\nAll secrets set successfully!")
|
75 |
+
except Exception as e:
|
76 |
+
print(f"Error setting secrets: {str(e)}")
|
77 |
+
exit(1)
|
z_utils/zep_setup.py
ADDED
@@ -0,0 +1,445 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# file: z_utils/zep_setup.py
|
2 |
+
"""
|
3 |
+
Create two persona users in Zep Cloud and preload their memory graphs.
|
4 |
+
Run once: python z_utils/zep_setup.py
|
5 |
+
"""
|
6 |
+
|
7 |
+
import asyncio, os, uuid, json
|
8 |
+
from datetime import datetime, timezone
|
9 |
+
from dotenv import load_dotenv
|
10 |
+
from pathlib import Path
|
11 |
+
from zep_cloud.client import AsyncZep
|
12 |
+
from zep_cloud.errors import NotFoundError
|
13 |
+
from zep_cloud.types import User, Message
|
14 |
+
|
15 |
+
# Define the path for the UUIDs file relative to this script
|
16 |
+
UUID_FILE_PATH = Path(__file__).parent / "persona_uuids.json"
|
17 |
+
SESSION_ID_FILE_PATH = Path(__file__).parent / "persona_session_ids.json"
|
18 |
+
|
19 |
+
# Load environment variables from .env file
|
20 |
+
load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), '..', '.env')) # Look for .env in parent dir
|
21 |
+
API_KEY = os.getenv("ZEP_API_KEY")
|
22 |
+
BASE_URL = os.getenv("ZEP_API_URL") # optional for self-hosted
|
23 |
+
|
24 |
+
if not API_KEY:
|
25 |
+
raise RuntimeError("ZEP_API_KEY missing in environment variables. Please ensure it's in ifx-sandbox/.env")
|
26 |
+
|
27 |
+
# Initialize AsyncZep client, handling optional base_url
|
28 |
+
zep_client_args = {"api_key": API_KEY}
|
29 |
+
if BASE_URL:
|
30 |
+
zep_client_args["base_url"] = BASE_URL
|
31 |
+
zep = AsyncZep(**zep_client_args)
|
32 |
+
|
33 |
+
# -------- persona blue-prints -------------------------------------------------
|
34 |
+
# Removed FactRatingExamples definition as it's not part of the SDK
|
35 |
+
# FACT_EXAMPLES = FactRatingExamples(
|
36 |
+
# high="Brock Purdy led the 49ers to the Super Bowl in 2024.",
|
37 |
+
# medium="The 49ers play in the NFC West.",
|
38 |
+
# low="Football games are played on a field."
|
39 |
+
# )
|
40 |
+
|
41 |
+
PERSONAS = {
|
42 |
+
"Casual Fan": {
|
43 |
+
"user_id": None, # Will be generated or loaded
|
44 |
+
"session_id": None, # Will be generated or loaded
|
45 |
+
"email": "[email protected]",
|
46 |
+
"first_name": "Casual",
|
47 |
+
"last_name": "Fan",
|
48 |
+
"metadata": {"fan_type": "casual"}
|
49 |
+
},
|
50 |
+
"Super Fan": {
|
51 |
+
"user_id": None, # Will be generated or loaded
|
52 |
+
"session_id": None, # Will be generated or loaded
|
53 |
+
"email": "[email protected]",
|
54 |
+
"first_name": "Super",
|
55 |
+
"last_name": "Fan",
|
56 |
+
"metadata": {"fan_type": "super"}
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
# --- Message histories for each persona based on their knowledge ---
|
61 |
+
|
62 |
+
CASUAL_FAN_MESSAGES = [
|
63 |
+
{"role": "user", "content": "Who are the 49ers?"},
|
64 |
+
{"role": "assistant", "content": "The San Francisco 49ers are a professional football team in the NFL. They play at Levi's Stadium in Santa Clara."},
|
65 |
+
{"role": "user", "content": "Who is their quarterback?"},
|
66 |
+
{"role": "assistant", "content": "Brock Purdy is the current starting quarterback for the 49ers. He emerged as the starter after being the final pick in the 2022 NFL Draft, often called 'Mr. Irrelevant.'"},
|
67 |
+
{"role": "user", "content": "Who are their best players?"},
|
68 |
+
{"role": "assistant", "content": "The 49ers have several star players! Christian McCaffrey (often called CMC) is their key running back and an offensive weapon. George Kittle is their star tight end known for his catching and blocking abilities. On defense, Nick Bosa is a dominant defensive end who frequently pressures quarterbacks."},
|
69 |
+
{"role": "user", "content": "Did they do well last season?"},
|
70 |
+
{"role": "assistant", "content": "Yes! The 49ers have been very competitive recently, making several playoff runs. They've established themselves as one of the stronger teams in the NFC."},
|
71 |
+
{"role": "user", "content": "What was their most exciting game?"},
|
72 |
+
{"role": "assistant", "content": "While there have been several exciting games, casual fans often remember the big moments - like playoff victories and games with spectacular, highlight-reel plays. The 49ers have had many memorable comeback wins and offensive explosions that got fans on their feet!"}
|
73 |
+
]
|
74 |
+
|
75 |
+
SUPER_FAN_MESSAGES = [
|
76 |
+
{"role": "user", "content": "Explain the 49ers' offensive scheme"},
|
77 |
+
{"role": "assistant", "content": "The 49ers primarily run Kyle Shanahan's offensive scheme, which heavily features the outside zone run, play-action passing, and pre-snap motion. This system relies on precise timing, versatile players who can both block and catch, and a strong offensive line. Trent Williams, their All-Pro left tackle, is particularly crucial for their run game success, creating lanes for their zone running attack."},
|
78 |
+
{"role": "user", "content": "What defensive system do they use?"},
|
79 |
+
{"role": "assistant", "content": "Under their defensive coordinator, the 49ers utilize a scheme that emphasizes pressure from their front four, led by Nick Bosa, without excessive blitzing. This allows them to drop seven defenders into coverage while still disrupting the quarterback. Fred Warner is the key middle linebacker who orchestrates the defense, handling both run stops and pass coverage responsibilities."},
|
80 |
+
{"role": "user", "content": "What's happening with their salary cap situation?"},
|
81 |
+
{"role": "assistant", "content": "The 49ers' salary cap management is a constant challenge. Currently, they're navigating extensions for key players like Brandon Aiyuk while planning for Brock Purdy's future contract when he becomes eligible for an extension. They've become adept at structuring contracts to maintain their core while finding value in mid-round draft picks and undrafted free agents who fit their systems perfectly."},
|
82 |
+
{"role": "user", "content": "What's their draft strategy been like?"},
|
83 |
+
{"role": "assistant", "content": "Their draft strategy often focuses on finding scheme fits rather than just raw talent or conventional wisdom. They prioritize specific traits that work in their systems and aren't afraid to draft players earlier than consensus would suggest if they believe in the fit. They've been particularly good at replenishing depth on both lines and finding mid-round receivers who thrive in Shanahan's offense."},
|
84 |
+
{"role": "user", "content": "Tell me about some of their role players beyond the stars"},
|
85 |
+
{"role": "assistant", "content": "Beyond the stars, the 49ers get significant contributions from role players like their nickel cornerback, rotational defensive linemen who keep the pass rush fresh, and versatile offensive weapons. Players like Jauan Jennings (clutch third-down receiver), Tashaun Gipson (veteran safety presence), and Elijah Mitchell (change-of-pace running back) may not make headlines but are crucial to the team's success and highly valued by die-hard fans who understand their specific contributions."},
|
86 |
+
{"role": "user", "content": "How does their current team compare historically?"},
|
87 |
+
{"role": "assistant", "content": "The current 49ers team continues their tradition of innovative coaching and physical play that dates back to their dynasty years. While they haven't yet matched the Super Bowl success of the teams led by Joe Montana, Jerry Rice, and Steve Young, they've returned to being perennial contenders. The current team emphasizes a strong run game and defense, similar to their historical identity, though the specific schemes have evolved with the modern NFL."}
|
88 |
+
]
|
89 |
+
|
90 |
+
CASUAL_FAN_KNOWLEDGE = [
|
91 |
+
{
|
92 |
+
"type": "text",
|
93 |
+
"data": """
|
94 |
+
General knowledge about the San Francisco 49ers for a casual fan.
|
95 |
+
Focuses on major stars and recent performance.
|
96 |
+
- The 49ers play at Levi's Stadium in Santa Clara.
|
97 |
+
- Brock Purdy is the current starting quarterback.
|
98 |
+
- Christian McCaffrey (CMC) is a key running back and offensive weapon.
|
99 |
+
- George Kittle is a star tight end.
|
100 |
+
- Nick Bosa is a dominant defensive end.
|
101 |
+
- The team has been competitive recently, often making playoff runs.
|
102 |
+
- Big plays and game outcomes are the main interest.
|
103 |
+
"""
|
104 |
+
}
|
105 |
+
]
|
106 |
+
|
107 |
+
SUPER_FAN_KNOWLEDGE = [
|
108 |
+
{
|
109 |
+
"type": "text",
|
110 |
+
"data": """
|
111 |
+
Detailed knowledge base for a San Francisco 49ers super fan.
|
112 |
+
Includes strategic insights, player roles, and historical context.
|
113 |
+
- The 49ers primarily run a Kyle Shanahan offensive scheme, heavily featuring the outside zone run, play-action passing, and pre-snap motion.
|
114 |
+
- Key offensive line players are crucial for the run game's success (e.g., Trent Williams).
|
115 |
+
- Defensive scheme under Steve Wilks (or current DC) utilizes a strong front four, led by Nick Bosa, aiming for pressure without excessive blitzing.
|
116 |
+
- Fred Warner is the crucial middle linebacker, orchestrating the defense.
|
117 |
+
- Salary cap management is a constant discussion point, especially regarding extensions for players like Brandon Aiyuk or Brock Purdy's future contract.
|
118 |
+
- Draft strategy often focuses on finding scheme fits and replenishing depth.
|
119 |
+
- Understanding specific player roles beyond stars (e.g., slot corner, rotational defensive linemen) is important.
|
120 |
+
- Historical context, like past Super Bowl appearances or legendary players (Montana, Rice, Young), is frequently referenced.
|
121 |
+
"""
|
122 |
+
},
|
123 |
+
{
|
124 |
+
"type": "json",
|
125 |
+
"data": json.dumps({
|
126 |
+
"team_focus": "49ers Strategy and Depth",
|
127 |
+
"key_concepts": [
|
128 |
+
"Shanahan Offense",
|
129 |
+
"Outside Zone Scheme",
|
130 |
+
"Defensive Line Pressure",
|
131 |
+
"Salary Cap Implications",
|
132 |
+
"Draft Capital Management",
|
133 |
+
"Player Contract Negotiations"
|
134 |
+
],
|
135 |
+
"recent_topics": [
|
136 |
+
"Brandon Aiyuk contract situation",
|
137 |
+
"Ricky Pearsall draft pick impact",
|
138 |
+
"Defensive coordinator adjustments",
|
139 |
+
"Offensive line performance analysis"
|
140 |
+
]
|
141 |
+
})
|
142 |
+
}
|
143 |
+
]
|
144 |
+
|
145 |
+
PERSONA_KNOWLEDGE_MAP = {
|
146 |
+
"Casual Fan": CASUAL_FAN_KNOWLEDGE,
|
147 |
+
"Super Fan": SUPER_FAN_KNOWLEDGE,
|
148 |
+
}
|
149 |
+
|
150 |
+
# Add mapping for persona message histories
|
151 |
+
PERSONA_MESSAGES_MAP = {
|
152 |
+
"Casual Fan": CASUAL_FAN_MESSAGES,
|
153 |
+
"Super Fan": SUPER_FAN_MESSAGES,
|
154 |
+
}
|
155 |
+
|
156 |
+
def load_or_generate_uuids():
|
157 |
+
"""Loads existing UUIDs from file or generates new ones if file doesn't exist."""
|
158 |
+
uuids_changed = False
|
159 |
+
if UUID_FILE_PATH.exists():
|
160 |
+
try:
|
161 |
+
with open(UUID_FILE_PATH, 'r') as f:
|
162 |
+
saved_uuids = json.load(f)
|
163 |
+
# Check if saved_uuids is a dict mapping names to strings
|
164 |
+
if isinstance(saved_uuids, dict):
|
165 |
+
for name in PERSONAS:
|
166 |
+
if name in saved_uuids and isinstance(saved_uuids[name], str):
|
167 |
+
PERSONAS[name]["user_id"] = saved_uuids[name]
|
168 |
+
print(f"Loaded existing UUID for {name}: {saved_uuids[name]}")
|
169 |
+
else:
|
170 |
+
# Generate new if name missing, value isn't string, or empty
|
171 |
+
PERSONAS[name]["user_id"] = str(uuid.uuid4())
|
172 |
+
print(f"UUID for {name} not found or invalid in file, generated new: {PERSONAS[name]['user_id']}")
|
173 |
+
uuids_changed = True
|
174 |
+
else:
|
175 |
+
# Invalid format, generate all new
|
176 |
+
print(f"UUID file ({UUID_FILE_PATH}) has unexpected format. Generating new UUIDs.")
|
177 |
+
for name in PERSONAS:
|
178 |
+
PERSONAS[name]["user_id"] = str(uuid.uuid4())
|
179 |
+
print(f"Generated new UUID for {name}: {PERSONAS[name]['user_id']}")
|
180 |
+
uuids_changed = True
|
181 |
+
|
182 |
+
except (json.JSONDecodeError, IOError) as e:
|
183 |
+
print(f"Error loading UUID file ({UUID_FILE_PATH}): {e}. Generating new UUIDs.")
|
184 |
+
for name in PERSONAS:
|
185 |
+
PERSONAS[name]["user_id"] = str(uuid.uuid4())
|
186 |
+
print(f"Generated new UUID for {name}: {PERSONAS[name]['user_id']}")
|
187 |
+
uuids_changed = True
|
188 |
+
else:
|
189 |
+
# File doesn't exist, generate all new UUIDs
|
190 |
+
print(f"UUID file ({UUID_FILE_PATH}) not found. Generating new UUIDs.")
|
191 |
+
for name in PERSONAS:
|
192 |
+
PERSONAS[name]["user_id"] = str(uuid.uuid4())
|
193 |
+
print(f"Generated new UUID for {name}: {PERSONAS[name]['user_id']}")
|
194 |
+
uuids_changed = True
|
195 |
+
|
196 |
+
if uuids_changed:
|
197 |
+
save_persona_uuids() # Save if any UUIDs were generated or changed
|
198 |
+
|
199 |
+
|
200 |
+
def load_or_generate_session_ids():
|
201 |
+
"""Loads existing session IDs from file or generates new ones if file doesn't exist."""
|
202 |
+
session_ids_changed = False
|
203 |
+
|
204 |
+
# Use the specific session IDs from the task document for these personas
|
205 |
+
hardcoded_session_ids = {
|
206 |
+
"Casual Fan": "241b3478c7634492abee9f178b5341cb",
|
207 |
+
"Super Fan": "dedcf5cb0d71475f976f4f66d98d6400"
|
208 |
+
}
|
209 |
+
|
210 |
+
if SESSION_ID_FILE_PATH.exists():
|
211 |
+
try:
|
212 |
+
with open(SESSION_ID_FILE_PATH, 'r') as f:
|
213 |
+
saved_session_ids = json.load(f)
|
214 |
+
# Check if saved_session_ids is a dict mapping names to strings
|
215 |
+
if isinstance(saved_session_ids, dict):
|
216 |
+
for name in PERSONAS:
|
217 |
+
# Use hardcoded session IDs regardless of what's in the file
|
218 |
+
PERSONAS[name]["session_id"] = hardcoded_session_ids[name]
|
219 |
+
print(f"Using required session ID for {name}: {PERSONAS[name]['session_id']}")
|
220 |
+
|
221 |
+
# If the saved value differs from hardcoded, we'll need to update the file
|
222 |
+
if name not in saved_session_ids or saved_session_ids[name] != hardcoded_session_ids[name]:
|
223 |
+
session_ids_changed = True
|
224 |
+
else:
|
225 |
+
# Invalid format, use hardcoded IDs
|
226 |
+
print(f"Session ID file ({SESSION_ID_FILE_PATH}) has unexpected format. Using required session IDs.")
|
227 |
+
for name in PERSONAS:
|
228 |
+
PERSONAS[name]["session_id"] = hardcoded_session_ids[name]
|
229 |
+
print(f"Using required session ID for {name}: {PERSONAS[name]['session_id']}")
|
230 |
+
session_ids_changed = True
|
231 |
+
except (json.JSONDecodeError, IOError) as e:
|
232 |
+
print(f"Error loading session ID file ({SESSION_ID_FILE_PATH}): {e}. Using required session IDs.")
|
233 |
+
for name in PERSONAS:
|
234 |
+
PERSONAS[name]["session_id"] = hardcoded_session_ids[name]
|
235 |
+
print(f"Using required session ID for {name}: {PERSONAS[name]['session_id']}")
|
236 |
+
session_ids_changed = True
|
237 |
+
else:
|
238 |
+
# File doesn't exist, use hardcoded IDs
|
239 |
+
print(f"Session ID file ({SESSION_ID_FILE_PATH}) not found. Using required session IDs.")
|
240 |
+
for name in PERSONAS:
|
241 |
+
PERSONAS[name]["session_id"] = hardcoded_session_ids[name]
|
242 |
+
print(f"Using required session ID for {name}: {PERSONAS[name]['session_id']}")
|
243 |
+
session_ids_changed = True
|
244 |
+
|
245 |
+
if session_ids_changed:
|
246 |
+
save_persona_session_ids() # Save if any session IDs were updated
|
247 |
+
|
248 |
+
|
249 |
+
def save_persona_uuids():
|
250 |
+
"""Saves the current persona UUIDs (name -> user_id string) to the JSON file."""
|
251 |
+
# Ensure we only save the user_id string
|
252 |
+
uuids_to_save = {name: data["user_id"] for name, data in PERSONAS.items() if isinstance(data.get("user_id"), str)}
|
253 |
+
if len(uuids_to_save) != len(PERSONAS):
|
254 |
+
print("Warning: Not all personas had valid string UUIDs during save.")
|
255 |
+
# Potentially raise an error or handle more robustly
|
256 |
+
|
257 |
+
try:
|
258 |
+
with open(UUID_FILE_PATH, 'w') as f:
|
259 |
+
json.dump(uuids_to_save, f, indent=4)
|
260 |
+
print(f"Persona UUIDs saved to {UUID_FILE_PATH}")
|
261 |
+
except IOError as e:
|
262 |
+
print(f"Error saving UUIDs to {UUID_FILE_PATH}: {e}")
|
263 |
+
|
264 |
+
|
265 |
+
def save_persona_session_ids():
|
266 |
+
"""Saves the current persona session IDs (name -> session_id string) to the JSON file."""
|
267 |
+
# Ensure we only save the session_id string
|
268 |
+
session_ids_to_save = {name: data["session_id"] for name, data in PERSONAS.items() if isinstance(data.get("session_id"), str)}
|
269 |
+
if len(session_ids_to_save) != len(PERSONAS):
|
270 |
+
print("Warning: Not all personas had valid string session IDs during save.")
|
271 |
+
# Potentially raise an error or handle more robustly
|
272 |
+
|
273 |
+
try:
|
274 |
+
with open(SESSION_ID_FILE_PATH, 'w') as f:
|
275 |
+
json.dump(session_ids_to_save, f, indent=4)
|
276 |
+
print(f"Persona session IDs saved to {SESSION_ID_FILE_PATH}")
|
277 |
+
except IOError as e:
|
278 |
+
print(f"Error saving session IDs to {SESSION_ID_FILE_PATH}: {e}")
|
279 |
+
|
280 |
+
|
281 |
+
async def create_zep_user(client: AsyncZep, user_data: dict):
|
282 |
+
"""Creates or updates a Zep user, checking if they exist first."""
|
283 |
+
user_id = user_data["user_id"]
|
284 |
+
try:
|
285 |
+
# Check if user exists
|
286 |
+
await client.user.get(user_id)
|
287 |
+
print(f"User {user_id} ({user_data.get('first_name', '')}) already exists. Updating...")
|
288 |
+
# Update existing user (optional, could just skip)
|
289 |
+
await client.user.update(
|
290 |
+
user_id=user_id,
|
291 |
+
email=user_data.get("email"),
|
292 |
+
first_name=user_data.get("first_name"),
|
293 |
+
last_name=user_data.get("last_name"),
|
294 |
+
metadata=user_data.get("metadata")
|
295 |
+
)
|
296 |
+
print(f"User {user_id} updated.")
|
297 |
+
except NotFoundError:
|
298 |
+
# User does not exist, create them
|
299 |
+
print(f"User {user_id} ({user_data.get('first_name', '')}) not found. Creating...")
|
300 |
+
try:
|
301 |
+
await client.user.add(**user_data)
|
302 |
+
print(f"User {user_id} created successfully.")
|
303 |
+
except Exception as e:
|
304 |
+
print(f"Error creating user {user_id}: {e}")
|
305 |
+
except Exception as e:
|
306 |
+
print(f"Error checking or updating user {user_id}: {e}")
|
307 |
+
|
308 |
+
|
309 |
+
async def preload_knowledge(client: AsyncZep, user_id: str, knowledge_items: list):
|
310 |
+
"""Preloads foundational knowledge into the user's graph using graph.add."""
|
311 |
+
print(f"Preloading knowledge for user {user_id}...")
|
312 |
+
success_count = 0
|
313 |
+
for i, item in enumerate(knowledge_items):
|
314 |
+
try:
|
315 |
+
print(f" Adding knowledge item {i+1}/{len(knowledge_items)} (type: {item['type']})...")
|
316 |
+
await client.graph.add(
|
317 |
+
user_id=user_id,
|
318 |
+
type=item["type"],
|
319 |
+
data=item["data"] # Expects string data, JSON already dumped
|
320 |
+
)
|
321 |
+
success_count += 1
|
322 |
+
print(f" Item {i+1} added successfully.")
|
323 |
+
# Add a small delay to avoid overwhelming the API if adding many items
|
324 |
+
await asyncio.sleep(0.5)
|
325 |
+
except Exception as e:
|
326 |
+
print(f" Error adding knowledge item {i+1} for user {user_id}: {e}")
|
327 |
+
# Decide whether to continue or stop on error
|
328 |
+
# break # Uncomment to stop on first error
|
329 |
+
print(f"Knowledge preloading completed for user {user_id}. {success_count}/{len(knowledge_items)} items added.")
|
330 |
+
|
331 |
+
|
332 |
+
async def preload_message_history(client: AsyncZep, user_id: str, session_id: str, messages: list):
|
333 |
+
"""Preloads message history for a user in a specific session."""
|
334 |
+
print(f"Preloading message history for user {user_id} in session {session_id}...")
|
335 |
+
|
336 |
+
try:
|
337 |
+
# First, ensure the session exists or create it
|
338 |
+
try:
|
339 |
+
# Try to get the session to see if it exists
|
340 |
+
await client.memory.get_session(session_id=session_id)
|
341 |
+
print(f"Session {session_id} already exists.")
|
342 |
+
except NotFoundError:
|
343 |
+
# Session does not exist, create it
|
344 |
+
await client.memory.add_session(
|
345 |
+
session_id=session_id,
|
346 |
+
user_id=user_id
|
347 |
+
)
|
348 |
+
print(f"Created new session {session_id} for user {user_id}")
|
349 |
+
|
350 |
+
# Then add messages to the session
|
351 |
+
zep_messages = []
|
352 |
+
for msg in messages:
|
353 |
+
# Map 'role' field to proper Zep role_type
|
354 |
+
role_type = msg["role"]
|
355 |
+
# If role is 'user', set role_type to 'user'
|
356 |
+
# If role is 'assistant', keep role_type as 'assistant'
|
357 |
+
|
358 |
+
zep_messages.append(
|
359 |
+
Message(
|
360 |
+
role_type=role_type, # Use the role directly as role_type
|
361 |
+
content=msg["content"],
|
362 |
+
role=None # Using default role
|
363 |
+
)
|
364 |
+
)
|
365 |
+
|
366 |
+
# Add the messages to the session
|
367 |
+
await client.memory.add(
|
368 |
+
session_id=session_id,
|
369 |
+
messages=zep_messages
|
370 |
+
)
|
371 |
+
print(f"Added {len(zep_messages)} messages to session {session_id}")
|
372 |
+
|
373 |
+
except Exception as e:
|
374 |
+
print(f"Error preloading message history: {e}")
|
375 |
+
|
376 |
+
|
377 |
+
async def main():
|
378 |
+
"""Main function to set up Zep users and preload knowledge."""
|
379 |
+
print("Starting Zep setup...")
|
380 |
+
load_dotenv()
|
381 |
+
|
382 |
+
api_key = os.environ.get("ZEP_API_KEY")
|
383 |
+
api_url = os.environ.get("ZEP_API_URL") # Optional
|
384 |
+
|
385 |
+
if not api_key:
|
386 |
+
print("Error: ZEP_API_KEY environment variable not set.")
|
387 |
+
return
|
388 |
+
|
389 |
+
client_params = {"api_key": api_key}
|
390 |
+
if api_url:
|
391 |
+
client_params["api_url"] = api_url
|
392 |
+
|
393 |
+
client = None # Initialize client variable
|
394 |
+
try:
|
395 |
+
# Instantiate client directly, remove async with
|
396 |
+
client = AsyncZep(**client_params)
|
397 |
+
print("Zep client initialized.")
|
398 |
+
|
399 |
+
# Load or generate UUIDs and update PERSONAS dict
|
400 |
+
load_or_generate_uuids()
|
401 |
+
|
402 |
+
# Load or use hardcoded session IDs from the task document
|
403 |
+
load_or_generate_session_ids()
|
404 |
+
|
405 |
+
# Create/Update users
|
406 |
+
for persona_name, data in PERSONAS.items():
|
407 |
+
await create_zep_user(client, data)
|
408 |
+
|
409 |
+
# Preload knowledge for each persona
|
410 |
+
for persona_name, data in PERSONAS.items():
|
411 |
+
user_id = data["user_id"]
|
412 |
+
knowledge = PERSONA_KNOWLEDGE_MAP.get(persona_name, [])
|
413 |
+
if knowledge:
|
414 |
+
await preload_knowledge(client, user_id, knowledge)
|
415 |
+
else:
|
416 |
+
print(f"No knowledge defined for persona: {persona_name}")
|
417 |
+
|
418 |
+
# Preload message history for each persona
|
419 |
+
for persona_name, data in PERSONAS.items():
|
420 |
+
user_id = data["user_id"]
|
421 |
+
session_id = data["session_id"]
|
422 |
+
messages = PERSONA_MESSAGES_MAP.get(persona_name, [])
|
423 |
+
if messages:
|
424 |
+
await preload_message_history(client, user_id, session_id, messages)
|
425 |
+
else:
|
426 |
+
print(f"No message history defined for persona: {persona_name}")
|
427 |
+
|
428 |
+
except Exception as e:
|
429 |
+
print(f"An error occurred during Zep client initialization or operation: {e}")
|
430 |
+
# finally:
|
431 |
+
# Optional: Add any explicit cleanup if the client required it,
|
432 |
+
# but typically SDK clients manage their own connections.
|
433 |
+
# if client and hasattr(client, 'close') and asyncio.iscoroutinefunction(client.close):
|
434 |
+
# await client.close()
|
435 |
+
# print("Zep client closed.")
|
436 |
+
|
437 |
+
print("Zep setup finished.")
|
438 |
+
|
439 |
+
|
440 |
+
if __name__ == "__main__":
|
441 |
+
# Ensure the script runs in an environment where asyncio is available
|
442 |
+
# If running in a Jupyter notebook, you might need nest_asyncio
|
443 |
+
# import nest_asyncio
|
444 |
+
# nest_asyncio.apply()
|
445 |
+
asyncio.run(main())
|
z_utils/zep_test.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Simple Zep test script to retrieve chat history from a pre-defined session.
|
3 |
+
This follows step 2 of Task 1.3 Memory & Persona Implementation.
|
4 |
+
"""
|
5 |
+
import os
|
6 |
+
import json
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
from zep_cloud.client import Zep
|
9 |
+
|
10 |
+
# Load environment variables from .env file
|
11 |
+
load_dotenv()
|
12 |
+
|
13 |
+
# Get Zep API key
|
14 |
+
ZEP_API_KEY = os.environ.get("ZEP_API_KEY")
|
15 |
+
if not ZEP_API_KEY:
|
16 |
+
raise RuntimeError("ZEP_API_KEY missing in environment variables.")
|
17 |
+
|
18 |
+
# Initialize Zep client
|
19 |
+
zep = Zep(api_key=ZEP_API_KEY)
|
20 |
+
|
21 |
+
# Use one of the session IDs from the task document
|
22 |
+
# Casual fan: 241b3478c7634492abee9f178b5341cb
|
23 |
+
# Super fan: dedcf5cb0d71475f976f4f66d98d6400
|
24 |
+
SESSION_ID = "241b3478c7634492abee9f178b5341cb" # Using Casual fan session ID
|
25 |
+
|
26 |
+
def retrieve_chat_history(session_id):
|
27 |
+
"""
|
28 |
+
Retrieve chat history for a specific session from Zep.
|
29 |
+
|
30 |
+
Args:
|
31 |
+
session_id (str): The session ID to retrieve history for
|
32 |
+
|
33 |
+
Returns:
|
34 |
+
dict: The memory object containing context and messages
|
35 |
+
"""
|
36 |
+
try:
|
37 |
+
# Use Zep's memory.get API to retrieve chat history
|
38 |
+
memory = zep.memory.get(session_id=session_id)
|
39 |
+
return memory
|
40 |
+
except Exception as e:
|
41 |
+
print(f"Error retrieving chat history: {e}")
|
42 |
+
return None
|
43 |
+
|
44 |
+
def main():
|
45 |
+
print(f"Retrieving chat history for session ID: {SESSION_ID}")
|
46 |
+
|
47 |
+
# Get the memory for the session
|
48 |
+
memory = retrieve_chat_history(SESSION_ID)
|
49 |
+
|
50 |
+
if memory:
|
51 |
+
print("\n===== MEMORY CONTEXT =====")
|
52 |
+
print(memory.context)
|
53 |
+
|
54 |
+
print("\n===== CHAT MESSAGES =====")
|
55 |
+
for msg in memory.messages:
|
56 |
+
print(f"{msg.role_type} ({msg.role}): {msg.content}")
|
57 |
+
|
58 |
+
print("\nSuccessfully retrieved chat history!")
|
59 |
+
else:
|
60 |
+
print("Failed to retrieve chat history.")
|
61 |
+
|
62 |
+
if __name__ == "__main__":
|
63 |
+
main()
|