zep-livekit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ """
2
+ Zep Memory integration for LiveKit.
3
+
4
+ This module provides a memory-enabled agent that integrates Zep with LiveKit's voice AI framework.
5
+ """
6
+
7
+ from .agent import ZepGraphAgent, ZepUserAgent
8
+ from .exceptions import ZepLiveKitError
9
+
10
+ __version__ = "0.1.0"
11
+ __all__ = ["ZepUserAgent", "ZepGraphAgent", "ZepLiveKitError"]
zep_livekit/agent.py ADDED
@@ -0,0 +1,432 @@
1
+ """
2
+ Zep user memory integration for LiveKit agents.
3
+
4
+ This module provides the ZepUserAgent class that integrates Zep's memory capabilities
5
+ with LiveKit's voice AI agent framework
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ from typing import Any, Literal
11
+
12
+ from livekit import agents
13
+ from livekit.agents.llm.chat_context import ChatContext, ChatMessage
14
+ from zep_cloud import SearchFilters
15
+ from zep_cloud.client import AsyncZep
16
+ from zep_cloud.graph.utils import compose_context_string
17
+ from zep_cloud.types import Message, Reranker
18
+
19
+ from .exceptions import AgentConfigurationError
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class ZepUserAgent(agents.Agent):
25
+ """
26
+ LiveKit agent with Zep memory capabilities.
27
+
28
+ A drop-in replacement for LiveKit's Agent that adds persistent memory:
29
+ - Stores user and assistant messages in Zep threads
30
+ - Retrieves relevant context and injects it for personalized responses
31
+ - Accepts all standard LiveKit Agent parameters
32
+
33
+ Args:
34
+ zep_client: Initialized AsyncZep client for memory operations
35
+ user_id: User identifier for memory isolation and personalization
36
+ thread_id: Thread identifier for conversation continuity
37
+ user_message_name: Optional name to set on user messages in Zep
38
+ assistant_message_name: Optional name to set on assistant messages in Zep
39
+ **kwargs: All other LiveKit Agent parameters (chat_ctx, tools, stt, llm, tts, etc.)
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ *,
45
+ zep_client: AsyncZep,
46
+ user_id: str,
47
+ thread_id: str,
48
+ context_mode: Literal["basic", "summary"] | None = None,
49
+ user_message_name: str | None = None,
50
+ assistant_message_name: str | None = None,
51
+ **kwargs: Any,
52
+ ) -> None:
53
+ if not user_id:
54
+ raise AgentConfigurationError("user_id must be a non-empty string")
55
+ if not thread_id:
56
+ raise AgentConfigurationError("thread_id must be a non-empty string")
57
+
58
+ # Initialize base Agent with all parameters passed through
59
+ super().__init__(**kwargs)
60
+
61
+ self._zep_client = zep_client
62
+ self._user_id = user_id
63
+ self._thread_id = thread_id
64
+ self._context_mode = context_mode or "basic"
65
+ self._user_message_name = user_message_name
66
+ self._assistant_message_name = assistant_message_name
67
+
68
+ async def on_enter(self) -> None:
69
+ """Called when the agent enters a conversation."""
70
+ await super().on_enter()
71
+
72
+ # Hook into session events to capture assistant messages
73
+ if hasattr(self, "session"):
74
+ self._setup_session_handlers()
75
+
76
+ def _setup_session_handlers(self) -> None:
77
+ """Set up event handlers on the session to capture assistant responses."""
78
+
79
+ @self.session.on("conversation_item_added")
80
+ def on_conversation_item_added(event: Any) -> None:
81
+ """Handle conversation item addition events to capture assistant responses."""
82
+ # Schedule async storage to avoid blocking event processing
83
+ asyncio.create_task(self._handle_conversation_item(event))
84
+
85
+ async def _handle_conversation_item(self, event: Any) -> None:
86
+ """Handle conversation item from session event."""
87
+ try:
88
+ # Extract conversation item from event
89
+ if not hasattr(event, "item"):
90
+ return
91
+
92
+ item = event.item
93
+
94
+ # Validate item has required message attributes
95
+ if not (hasattr(item, "role") and hasattr(item, "content")):
96
+ return
97
+
98
+ role = item.role
99
+ content = item.content
100
+
101
+ # Only store assistant messages (user messages handled in on_user_turn_completed)
102
+ if role == "assistant":
103
+ content_text = self._extract_text_content(content)
104
+ if content_text.strip():
105
+ await self._store_assistant_message(content_text.strip(), item)
106
+
107
+ except Exception as e:
108
+ logger.error(f"Failed to handle conversation item: {e}")
109
+
110
+ def _extract_text_content(self, content: Any) -> str:
111
+ """Extract text content from various LiveKit content formats."""
112
+ if isinstance(content, str):
113
+ return content
114
+
115
+ if isinstance(content, list):
116
+ text_parts = []
117
+ for item in content:
118
+ if hasattr(item, "text"):
119
+ text_parts.append(item.text)
120
+ elif isinstance(item, str):
121
+ text_parts.append(item)
122
+ return " ".join(text_parts)
123
+
124
+ return str(content)
125
+
126
+ async def _store_assistant_message(self, content_text: str, item: Any) -> None:
127
+ """Store assistant message in Zep thread memory."""
128
+ try:
129
+ # Use custom assistant name if provided, otherwise fallback to item name
130
+ message_name = self._assistant_message_name or getattr(item, "name", None)
131
+
132
+ zep_message = Message(content=content_text, role="assistant", name=message_name)
133
+
134
+ await self._zep_client.thread.add_messages(
135
+ thread_id=self._thread_id, messages=[zep_message]
136
+ )
137
+
138
+ except Exception as e:
139
+ logger.warning(f"Failed to store assistant response: {e}")
140
+
141
+ async def on_user_turn_completed(self, turn_ctx: ChatContext, new_message: ChatMessage) -> None:
142
+ """
143
+ Handle user turn completion - store message and inject memory context.
144
+
145
+ 1. Store user message in Zep
146
+ 2. Retrieve relevant context from Zep
147
+ 3. Inject context into conversation
148
+ """
149
+ await super().on_user_turn_completed(turn_ctx, new_message)
150
+
151
+ user_text = new_message.text_content
152
+ if not user_text or not user_text.strip():
153
+ return
154
+
155
+ try:
156
+ zep_message = Message(
157
+ content=user_text.strip(), role="user", name=self._user_message_name
158
+ )
159
+
160
+ await self._zep_client.thread.add_messages(
161
+ thread_id=self._thread_id, messages=[zep_message]
162
+ )
163
+
164
+ except Exception as e:
165
+ logger.warning(f"Failed to store user message in Zep: {e}")
166
+
167
+ try:
168
+ memory_result = await self._zep_client.thread.get_user_context(
169
+ thread_id=self._thread_id, mode=self._context_mode
170
+ )
171
+
172
+ if memory_result and memory_result.context:
173
+ context = memory_result.context
174
+
175
+ turn_ctx.add_message(role="system", content=f"Relevant user context:\n{context}")
176
+
177
+ except Exception as e:
178
+ logger.warning(f"Failed to retrieve context from Zep: {e}")
179
+
180
+ async def on_exit(self) -> None:
181
+ """Called when the agent exits a conversation."""
182
+ await super().on_exit()
183
+
184
+
185
+ class ZepGraphAgent(agents.Agent):
186
+ """
187
+ LiveKit agent with Zep graph memory capabilities.
188
+
189
+ A drop-in replacement for LiveKit's Agent that adds persistent knowledge storage:
190
+ - Stores user and assistant messages in Zep graph
191
+ - Performs hybrid search to retrieve relevant context from edges, nodes, and episodes
192
+ - Uses smart context composition for comprehensive knowledge retrieval
193
+ - Optional user name prefixing for message attribution
194
+
195
+ User Identification:
196
+ - If user_name is provided, messages are stored as "[UserName]: message" and "[Assistant]: response"
197
+ - If user_name is None, messages are stored without prefixes
198
+ - Designed for per-user agent instances (typical deployment pattern)
199
+
200
+ Args:
201
+ zep_client: Initialized AsyncZep client for memory operations
202
+ graph_id: Graph identifier for knowledge storage
203
+ user_name: Optional user name for message prefixing (e.g., "Alice", "Bob")
204
+ facts_limit: Maximum number of facts/edges to retrieve (default: 20)
205
+ entity_limit: Maximum number of entities/nodes to retrieve (default: 5)
206
+ episode_limit: Maximum number of episodes to retrieve (default: 3)
207
+ search_filters: Optional filters for graph search
208
+ reranker: Optional reranker for search results
209
+ **kwargs: All other LiveKit Agent parameters
210
+ """
211
+
212
+ def __init__(
213
+ self,
214
+ *,
215
+ zep_client: AsyncZep,
216
+ graph_id: str,
217
+ user_name: str | None = None,
218
+ facts_limit: int = 15,
219
+ entity_limit: int = 5,
220
+ episode_limit: int = 2,
221
+ search_filters: SearchFilters | None = None,
222
+ reranker: Reranker | None = "rrf",
223
+ **kwargs: Any,
224
+ ) -> None:
225
+ if not graph_id:
226
+ raise AgentConfigurationError("graph_id must be a non-empty string")
227
+
228
+ # Initialize base Agent with all parameters passed through
229
+ super().__init__(**kwargs)
230
+
231
+ self._zep_client = zep_client
232
+ self._graph_id = graph_id
233
+ self._user_name = user_name
234
+ self._facts_limit = facts_limit
235
+ self._entity_limit = entity_limit
236
+ self._episode_limit = episode_limit
237
+ self._search_filters = search_filters
238
+ self._reranker = reranker
239
+
240
+ async def on_enter(self) -> None:
241
+ """Called when the agent enters a conversation."""
242
+ await super().on_enter()
243
+
244
+ # Hook into session events to capture assistant messages
245
+ if hasattr(self, "session"):
246
+ self._setup_session_handlers()
247
+
248
+ def _setup_session_handlers(self) -> None:
249
+ """Set up event handlers on the session to capture assistant responses."""
250
+
251
+ @self.session.on("conversation_item_added")
252
+ def on_conversation_item_added(event: Any) -> None:
253
+ """Handle conversation item addition events to capture assistant responses."""
254
+ # Schedule async storage to avoid blocking event processing
255
+ asyncio.create_task(self._handle_conversation_item(event))
256
+
257
+ async def _handle_conversation_item(self, event: Any) -> None:
258
+ """Handle conversation item from session event."""
259
+ try:
260
+ # Extract conversation item from event
261
+ if not hasattr(event, "item"):
262
+ return
263
+
264
+ item = event.item
265
+
266
+ # Validate item has required message attributes
267
+ if not (hasattr(item, "role") and hasattr(item, "content")):
268
+ return
269
+
270
+ role = item.role
271
+ content = item.content
272
+
273
+ # Only store assistant messages (user messages handled in on_user_turn_completed)
274
+ if role == "assistant":
275
+ content_text = self._extract_text_content(content)
276
+ if content_text.strip():
277
+ await self._store_assistant_message(content_text.strip(), item)
278
+
279
+ except Exception as e:
280
+ logger.error(f"Failed to handle conversation item: {e}")
281
+
282
+ def _extract_text_content(self, content: Any) -> str:
283
+ """Extract text content from various LiveKit content formats."""
284
+ if isinstance(content, str):
285
+ return content
286
+
287
+ if isinstance(content, list):
288
+ text_parts = []
289
+ for item in content:
290
+ if hasattr(item, "text"):
291
+ text_parts.append(item.text)
292
+ elif isinstance(item, str):
293
+ text_parts.append(item)
294
+ return " ".join(text_parts)
295
+
296
+ return str(content)
297
+
298
+ async def _store_assistant_message(self, content_text: str, item: Any) -> None:
299
+ """Store assistant message in Zep graph."""
300
+ try:
301
+ # Prefix assistant messages for consistency when user has a name
302
+ if self._user_name:
303
+ message_data = f"[Assistant]: {content_text}"
304
+ else:
305
+ message_data = content_text
306
+
307
+ await self._zep_client.graph.add(
308
+ graph_id=self._graph_id, type="message", data=message_data
309
+ )
310
+
311
+ except Exception as e:
312
+ logger.warning(f"Failed to store assistant response: {e}")
313
+
314
+ async def on_user_turn_completed(self, turn_ctx: ChatContext, new_message: ChatMessage) -> None:
315
+ """
316
+ Handle user turn completion - store message and inject memory context.
317
+
318
+ 1. Store user message in Zep graph
319
+ 2. Perform hybrid search to retrieve relevant context
320
+ 3. Inject context into conversation using smart composition
321
+ """
322
+ await super().on_user_turn_completed(turn_ctx, new_message)
323
+
324
+ user_text = new_message.text_content
325
+ if not user_text or not user_text.strip():
326
+ return
327
+
328
+ # Step 1: Store user message in Zep graph with user identification
329
+ try:
330
+ # Prefix message with user name if provided
331
+ message_data = user_text.strip()
332
+ if self._user_name:
333
+ message_data = f"[{self._user_name}]: {message_data}"
334
+
335
+ await self._zep_client.graph.add(
336
+ graph_id=self._graph_id, type="message", data=message_data
337
+ )
338
+
339
+ except Exception as e:
340
+ logger.warning(f"Failed to store user message in Zep graph: {e}")
341
+
342
+ # Step 2: Retrieve relevant context using hybrid search
343
+ try:
344
+ context = await self._retrieve_graph_context(user_text[:400]) # Limit query length
345
+
346
+ if context:
347
+ # Step 3: Inject context as system message
348
+ turn_ctx.add_message(
349
+ role="system", content=f"Relevant knowledge from memory:\n{context}"
350
+ )
351
+
352
+ except Exception as e:
353
+ logger.warning(f"Failed to retrieve context from Zep graph: {e}")
354
+
355
+ async def _retrieve_graph_context(self, query: str) -> str | None:
356
+ """
357
+ Retrieve and compose context from graph using hybrid search.
358
+
359
+ - Search for edges (facts), nodes (entities) and episodes concurrently
360
+ - Compose a context string using the graph utilities
361
+ """
362
+ try:
363
+ # Perform parallel searches like in autogen
364
+ search_functions = []
365
+
366
+ if self._facts_limit:
367
+ # Search for facts/relationships (edges)
368
+ search_functions.append(
369
+ self._zep_client.graph.search(
370
+ graph_id=self._graph_id,
371
+ query=query,
372
+ limit=self._facts_limit,
373
+ search_filters=self._search_filters,
374
+ reranker=self._reranker,
375
+ scope="edges",
376
+ ),
377
+ )
378
+
379
+ if self._entity_limit:
380
+ # Search for entities (nodes)
381
+ search_functions.append(
382
+ self._zep_client.graph.search(
383
+ graph_id=self._graph_id,
384
+ query=query,
385
+ limit=self._entity_limit,
386
+ search_filters=self._search_filters,
387
+ reranker=self._reranker,
388
+ scope="nodes",
389
+ ),
390
+ )
391
+
392
+ if self._episode_limit:
393
+ # Search for episodes
394
+ search_functions.append(
395
+ self._zep_client.graph.search(
396
+ graph_id=self._graph_id,
397
+ query=query,
398
+ limit=self._episode_limit,
399
+ search_filters=self._search_filters,
400
+ reranker=self._reranker,
401
+ scope="episodes",
402
+ ),
403
+ )
404
+
405
+ results = await asyncio.gather(*search_functions)
406
+
407
+ edges = []
408
+ nodes = []
409
+ episodes = []
410
+
411
+ # Collect all results
412
+ for result in results:
413
+ if result.edges:
414
+ edges.extend(result.edges)
415
+ if result.nodes:
416
+ nodes.extend(result.nodes)
417
+ if result.episodes:
418
+ episodes.extend(result.episodes)
419
+
420
+ if not edges and not nodes and not episodes:
421
+ return None
422
+
423
+ context = compose_context_string(edges, nodes, episodes)
424
+ return context
425
+
426
+ except Exception as e:
427
+ logger.error(f"Error retrieving graph context: {e}")
428
+ return None
429
+
430
+ async def on_exit(self) -> None:
431
+ """Called when the agent exits a conversation."""
432
+ await super().on_exit()
@@ -0,0 +1,15 @@
1
+ """
2
+ LiveKit-specific exceptions for Zep integration.
3
+ """
4
+
5
+
6
+ class ZepLiveKitError(Exception):
7
+ """Base exception for Zep LiveKit integration errors."""
8
+
9
+ pass
10
+
11
+
12
+ class AgentConfigurationError(ZepLiveKitError):
13
+ """Raised when agent configuration is invalid."""
14
+
15
+ pass
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.4
2
+ Name: zep-livekit
3
+ Version: 0.1.0
4
+ Summary: LiveKit integration for Zep
5
+ Project-URL: Homepage, https://github.com/getzep/zep
6
+ Project-URL: Documentation, https://help.getzep.com/integrations/livekit
7
+ Project-URL: Repository, https://github.com/getzep/zep
8
+ Project-URL: Bug Tracker, https://github.com/getzep/zep/issues
9
+ Project-URL: Changelog, https://github.com/getzep/zep/blob/main/integrations/python/zep_livekit/CHANGELOG.md
10
+ Requires-Python: >=3.10
11
+ Requires-Dist: livekit-agents[openai,silero]>=1.0.0
12
+ Requires-Dist: typing-extensions>=4.0.0
13
+ Requires-Dist: zep-cloud>=3.4.3
14
+ Provides-Extra: dev
15
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
16
+ Requires-Dist: pytest-asyncio; extra == 'dev'
17
+ Requires-Dist: pytest-cov; extra == 'dev'
18
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
19
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Zep LiveKit Integration
23
+
24
+ Add persistent memory to your LiveKit voice agents with [Zep's](https://www.getzep.com) memory capabilities. This integration provides both conversational memory for user sessions and shared knowledge graphs for cross-session information storage.
25
+
26
+ ## Quick Start
27
+
28
+ ### Install Dependencies
29
+
30
+ ```bash
31
+ pip install zep-livekit
32
+ ```
33
+
34
+ ### Environment Setup
35
+
36
+ Configure your environment with the required API keys and LiveKit connection details:
37
+
38
+ ```bash
39
+ # Required API keys
40
+ export OPENAI_API_KEY="your-openai-api-key"
41
+ export ZEP_API_KEY="your-zep-cloud-api-key"
42
+
43
+ # LiveKit configuration
44
+ export LIVEKIT_URL="your-livekit-url"
45
+ export LIVEKIT_API_KEY="your-livekit-api-key"
46
+ export LIVEKIT_API_SECRET="your-livekit-api-secret"
47
+ ```
48
+
49
+ ## Memory Architecture
50
+
51
+ Zep uses a unified temporal knowledge graph where all conversation data contributes to a single, dynamic graph structure. The LiveKit integration provides two complementary approaches to interact with this unified memory:
52
+
53
+ ### Thread-Based Memory Access (ZepUserAgent)
54
+ - **Purpose**: Structured conversation history and contextual retrieval
55
+ - **Storage**: Messages stored in threads that automatically contribute to the user's unified graph
56
+ - **Retrieval**: Context blocks assembled with temporal information from the graph
57
+ - **Use Case**: Personal assistants, customer support, tutoring sessions
58
+
59
+ ### Direct Graph Memory Access (ZepGraphAgent)
60
+ - **Purpose**: Direct interaction with the knowledge graph for shared information
61
+ - **Storage**: Information stored directly as facts, entities, and relationships in the graph
62
+ - **Retrieval**: Semantic search across the entire temporal knowledge graph
63
+ - **Use Case**: Knowledge bases, collaborative assistants, information systems
64
+
65
+ Both approaches work with the same underlying temporal knowledge graph - threads automatically enrich the graph with entities, relationships, and facts, while direct graph access allows for explicit knowledge management.
66
+
67
+ ## Thread-Based Memory Access
68
+
69
+ Using structured conversation threads that automatically contribute to your unified graph:
70
+
71
+ ### Basic Setup
72
+
73
+ ```python
74
+ import os
75
+ from livekit import agents
76
+ from livekit.plugins import openai, silero
77
+ from zep_cloud.client import AsyncZep
78
+ from zep_livekit import ZepUserAgent
79
+
80
+ async def entrypoint(ctx: agents.JobContext):
81
+ # Initialize Zep client
82
+ zep_client = AsyncZep(api_key=os.getenv("ZEP_API_KEY"))
83
+
84
+ # Create user and thread
85
+ user_id = "user_123"
86
+ thread_id = f"conversation_{user_id}"
87
+
88
+ try:
89
+ await zep_client.user.get(user_id=user_id)
90
+ except:
91
+ await zep_client.user.add(user_id=user_id, first_name="Alice")
92
+
93
+ await zep_client.thread.create(thread_id=thread_id, user_id=user_id)
94
+
95
+ # Connect to room
96
+ await ctx.connect()
97
+
98
+ # Create session with providers
99
+ session = agents.AgentSession(
100
+ stt=openai.STT(),
101
+ llm=openai.LLM(model="gpt-4o-mini"),
102
+ tts=openai.TTS(),
103
+ vad=silero.VAD.load(),
104
+ )
105
+
106
+ # Create memory-enabled agent
107
+ agent = ZepUserAgent(
108
+ zep_client=zep_client,
109
+ user_id=user_id,
110
+ thread_id=thread_id,
111
+ instructions="You are a helpful assistant with persistent memory."
112
+ )
113
+
114
+ # Start conversation with memory
115
+ await session.start(agent=agent, room=ctx.room)
116
+ ```
117
+
118
+ ### Advanced Configuration
119
+
120
+ ```python
121
+ # Enhanced user agent with message attribution
122
+ agent = ZepUserAgent(
123
+ zep_client=zep_client,
124
+ user_id="user_123",
125
+ thread_id="conversation_456",
126
+ context_mode="summary", # or "basic"
127
+ user_message_name="Alice", # Name for user messages in Zep
128
+ assistant_message_name="Assistant", # Name for assistant messages
129
+ instructions="You remember our previous conversations and preferences."
130
+ )
131
+ ```
132
+
133
+ ## Direct Graph Memory Access
134
+
135
+ For explicit control over what gets stored as facts, entities, and relationships in your unified graph:
136
+
137
+ ### Basic Setup
138
+
139
+ ```python
140
+ from zep_livekit import ZepGraphAgent
141
+
142
+ async def entrypoint(ctx: agents.JobContext):
143
+ # Initialize Zep client
144
+ zep_client = AsyncZep(api_key=os.getenv("ZEP_API_KEY"))
145
+
146
+ # Create or get knowledge graph
147
+ graph_id = "company_knowledge_base"
148
+ try:
149
+ await zep_client.graph.get(graph_id)
150
+ except:
151
+ await zep_client.graph.create(
152
+ graph_id=graph_id,
153
+ name="Company Knowledge Base",
154
+ description="Shared knowledge across all conversations"
155
+ )
156
+
157
+ # Connect to room
158
+ await ctx.connect()
159
+
160
+ # Create session
161
+ session = agents.AgentSession(
162
+ stt=openai.STT(),
163
+ llm=openai.LLM(model="gpt-4o-mini"),
164
+ tts=openai.TTS(),
165
+ vad=silero.VAD.load(),
166
+ )
167
+
168
+ # Create knowledge-enabled agent
169
+ agent = ZepGraphAgent(
170
+ zep_client=zep_client,
171
+ graph_id=graph_id,
172
+ user_name="Alice", # Optional: for message attribution
173
+ facts_limit=15, # Max facts to retrieve
174
+ entity_limit=5, # Max entities to retrieve
175
+ episode_limit=3, # Max episodes to retrieve
176
+ instructions="""
177
+ You have access to a shared knowledge graph.
178
+ Store important facts for future reference and
179
+ search your knowledge to answer questions accurately.
180
+ """
181
+ )
182
+
183
+ await session.start(agent=agent, room=ctx.room)
184
+ ```
185
+
186
+
187
+ ## Querying Your Unified Graph
188
+
189
+ ### Thread-Based Context Retrieval
190
+
191
+ ```python
192
+ # Get conversation context assembled from the unified graph
193
+ memory_result = await zep_client.thread.get_user_context(
194
+ thread_id="conversation_123",
195
+ mode="basic" # or "summary"
196
+ )
197
+
198
+ if memory_result and memory_result.context:
199
+ print(f"Context from unified graph: {memory_result.context}")
200
+ ```
201
+
202
+ ### Direct Graph Search
203
+
204
+ ```python
205
+ # Search directly across the temporal knowledge graph
206
+ search_results = await zep_client.graph.search(
207
+ graph_id="company_knowledge_base",
208
+ query="Python programming best practices",
209
+ limit=10,
210
+ scope="edges" # facts, or "nodes" (entities), "episodes"
211
+ )
212
+
213
+ # Use Zep's utility to compose context
214
+ from zep_cloud.graph.utils import compose_context_string
215
+ context = compose_context_string(
216
+ search_results.edges,
217
+ search_results.nodes,
218
+ search_results.episodes
219
+ )
220
+ ```
221
+
222
+ ## Agent Comparison
223
+
224
+ | Agent Type | Best For | Memory Access Method | Use Cases |
225
+ |------------|----------|-------------------|-----------|
226
+ | **ZepUserAgent** | Personal assistants | Thread-based access to unified graph | Conversation continuity, customer support, tutoring |
227
+ | **ZepGraphAgent** | Knowledge systems | Direct graph access to unified graph | Shared information, collaborative assistants, knowledge bases |
228
+
229
+ ### When to Use Each
230
+
231
+ **Use ZepUserAgent when:**
232
+ - Building personal assistants with structured conversation flow
233
+ - Need conversation history and context retrieval across sessions
234
+ - Want automatic thread-to-graph ingestion without manual management
235
+ - Prefer working with conversation-based memory access patterns
236
+
237
+ **Use ZepGraphAgent when:**
238
+ - Building knowledge management systems with explicit fact storage
239
+ - Need direct semantic search across the temporal knowledge graph
240
+ - Want to manually control what information gets stored as facts
241
+ - Building systems where information should be immediately searchable across entities
242
+
243
+ ## Complete Examples
244
+
245
+ ### Personal Assistant
246
+ ```bash
247
+ # examples/voice_assistant.py
248
+ python examples/voice_assistant.py
249
+ ```
250
+
251
+ ### Knowledge Assistant
252
+ ```bash
253
+ # examples/graph_voice_assistant.py
254
+ python examples/graph_voice_assistant.py
255
+ ```
256
+
257
+ ## API Reference
258
+
259
+ ### ZepUserAgent
260
+
261
+ ```python
262
+ class ZepUserAgent(agents.Agent):
263
+ def __init__(
264
+ self,
265
+ *,
266
+ zep_client: AsyncZep,
267
+ user_id: str,
268
+ thread_id: str,
269
+ context_mode: Literal["basic", "summary"] = "basic",
270
+ user_message_name: str | None = None,
271
+ assistant_message_name: str | None = None,
272
+ **kwargs: Any # All LiveKit Agent parameters
273
+ )
274
+ ```
275
+
276
+ ### ZepGraphAgent
277
+
278
+ ```python
279
+ class ZepGraphAgent(agents.Agent):
280
+ def __init__(
281
+ self,
282
+ *,
283
+ zep_client: AsyncZep,
284
+ graph_id: str,
285
+ user_name: str | None = None,
286
+ facts_limit: int = 15,
287
+ entity_limit: int = 5,
288
+ episode_limit: int = 2,
289
+ search_filters: SearchFilters | None = None,
290
+ reranker: Reranker | None = "rrf",
291
+ **kwargs: Any # All LiveKit Agent parameters
292
+ )
293
+ ```
294
+
295
+
296
+ ## Development
297
+
298
+ ### Setup Development Environment
299
+
300
+ ```bash
301
+ git clone https://github.com/getzep/zep
302
+ cd zep/integrations/python/zep_livekit
303
+ make install
304
+ ```
305
+
306
+ ### Development Workflow
307
+
308
+ ```bash
309
+ make format # Format code with ruff
310
+ make lint # Run linting checks
311
+ make type-check # Run MyPy type checking
312
+ make test # Run test suite
313
+ make pre-commit # Full pre-commit workflow
314
+ make ci # Strict CI-style checks
315
+ ```
316
+
317
+ ## Support
318
+
319
+ ### Zep Resources
320
+ - 📖 [Zep Documentation](https://help.getzep.com)
321
+ - 💬 [Zep Discord Community](https://discord.gg/W8xaHrqWVc)
322
+ - 🐛 [GitHub Issues](https://github.com/getzep/zep/issues)
323
+ - 📧 [Email Support](mailto:support@getzep.com)
324
+
325
+ ### LiveKit Resources
326
+ - 📖 [LiveKit Documentation](https://docs.livekit.io)
327
+
328
+ ---
329
+
330
+ Built with ❤️ by the [Zep](https://www.getzep.com) team.
@@ -0,0 +1,6 @@
1
+ zep_livekit/__init__.py,sha256=AnfAn284Ov_OOJThgXyWZ8JF8FojrSgy8B_Ijp95tMo,318
2
+ zep_livekit/agent.py,sha256=tcxCT7i7pl61N2sGRwR1zpVJOdXg7T6fIOlE0ZwjgRM,16269
3
+ zep_livekit/exceptions.py,sha256=W1JAf3gTrRX-R8ozyEDDPN3SadlYujoEcBQmXVWKbjk,278
4
+ zep_livekit-0.1.0.dist-info/METADATA,sha256=ck3rWQT8zuUzCNXOqJ_VQK_vdcIQL1cPlrpDlnn57zs,10129
5
+ zep_livekit-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
6
+ zep_livekit-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any