zep-crewai 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.
- zep_crewai/__init__.py +45 -0
- zep_crewai/exceptions.py +12 -0
- zep_crewai/memory.py +177 -0
- zep_crewai-0.1.0.dist-info/METADATA +186 -0
- zep_crewai-0.1.0.dist-info/RECORD +6 -0
- zep_crewai-0.1.0.dist-info/WHEEL +4 -0
zep_crewai/__init__.py
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
"""
|
2
|
+
Zep CrewAI Integration.
|
3
|
+
|
4
|
+
This package provides memory integration between Zep and CrewAI agents,
|
5
|
+
enabling persistent conversation memory and context retrieval.
|
6
|
+
|
7
|
+
Installation:
|
8
|
+
pip install zep-crewai
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
from zep_crewai import ZepStorage
|
12
|
+
from zep_cloud.client import AsyncZep
|
13
|
+
from crewai.memory.external.external_memory import ExternalMemory
|
14
|
+
from crewai import Crew
|
15
|
+
|
16
|
+
# Initialize Zep client and storage
|
17
|
+
zep_client = AsyncZep(api_key="your-api-key")
|
18
|
+
zep_storage = ZepStorage(client=zep_client, user_id="user123", thread_id="thread123")
|
19
|
+
external_memory = ExternalMemory(storage=zep_storage)
|
20
|
+
|
21
|
+
# Create crew with Zep memory
|
22
|
+
crew = Crew(
|
23
|
+
agents=[...],
|
24
|
+
tasks=[...],
|
25
|
+
external_memory=external_memory
|
26
|
+
)
|
27
|
+
"""
|
28
|
+
|
29
|
+
__version__ = "0.1.0"
|
30
|
+
__author__ = "Zep AI"
|
31
|
+
__description__ = "Zep integration for CrewAI"
|
32
|
+
|
33
|
+
from .exceptions import ZepDependencyError
|
34
|
+
|
35
|
+
try:
|
36
|
+
# Check for required CrewAI dependencies - just test import
|
37
|
+
import crewai.memory.storage.interface # noqa: F401
|
38
|
+
|
39
|
+
# Import our integration
|
40
|
+
from .memory import ZepStorage
|
41
|
+
|
42
|
+
__all__ = ["ZepStorage", "ZepDependencyError"]
|
43
|
+
|
44
|
+
except ImportError as e:
|
45
|
+
raise ZepDependencyError(framework="CrewAI", install_command="pip install zep-crewai") from e
|
zep_crewai/exceptions.py
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
"""
|
2
|
+
Exception classes for CrewAI integration.
|
3
|
+
"""
|
4
|
+
|
5
|
+
|
6
|
+
class ZepDependencyError(ImportError):
|
7
|
+
"""Raised when required CrewAI dependencies are not installed."""
|
8
|
+
|
9
|
+
def __init__(self, framework: str, install_command: str):
|
10
|
+
self.framework = framework
|
11
|
+
self.install_command = install_command
|
12
|
+
super().__init__(f"{framework} dependencies not found. Install with: {install_command}")
|
zep_crewai/memory.py
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
"""
|
2
|
+
Zep Memory integration for CrewAI.
|
3
|
+
|
4
|
+
This module provides memory storage that integrates Zep with CrewAI's memory system.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from concurrent.futures import ThreadPoolExecutor
|
9
|
+
from typing import Any
|
10
|
+
|
11
|
+
from crewai.memory.storage.interface import Storage
|
12
|
+
from zep_cloud.client import Zep
|
13
|
+
from zep_cloud.types import GraphSearchResults, Message
|
14
|
+
|
15
|
+
|
16
|
+
class ZepStorage(Storage):
|
17
|
+
"""
|
18
|
+
A storage implementation that integrates with Zep for persistent storage
|
19
|
+
and retrieval of CrewAI agent memories.
|
20
|
+
"""
|
21
|
+
|
22
|
+
def __init__(self, client: Zep, user_id: str, thread_id: str, **kwargs: Any) -> None:
|
23
|
+
"""
|
24
|
+
Initialize ZepStorage with a Zep client instance.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
client: An initialized Zep instance (sync client)
|
28
|
+
user_id: User ID identifying a created Zep user (required)
|
29
|
+
thread_id: Thread ID identifying current conversation thread (required)
|
30
|
+
**kwargs: Additional configuration options
|
31
|
+
"""
|
32
|
+
if not isinstance(client, Zep):
|
33
|
+
raise TypeError("client must be an instance of Zep")
|
34
|
+
|
35
|
+
if not user_id:
|
36
|
+
raise ValueError("user_id is required")
|
37
|
+
|
38
|
+
if not thread_id:
|
39
|
+
raise ValueError("thread_id is required")
|
40
|
+
|
41
|
+
self._client = client
|
42
|
+
self._user_id = user_id
|
43
|
+
self._thread_id = thread_id
|
44
|
+
self._config = kwargs
|
45
|
+
|
46
|
+
self._logger = logging.getLogger(__name__)
|
47
|
+
|
48
|
+
def save(self, value: Any, metadata: dict[str, Any] | None = None) -> None:
|
49
|
+
"""
|
50
|
+
Save a memory entry to Zep using metadata-based routing.
|
51
|
+
|
52
|
+
Routes storage based on metadata.type:
|
53
|
+
- "message": Store as thread message with role from metadata
|
54
|
+
- "json" or "text": Store as graph data
|
55
|
+
|
56
|
+
Args:
|
57
|
+
value: The memory content to store
|
58
|
+
metadata: Metadata including type, role, name, etc.
|
59
|
+
"""
|
60
|
+
metadata = metadata or {}
|
61
|
+
|
62
|
+
content_str = str(value)
|
63
|
+
content_type = metadata.get("type", "text")
|
64
|
+
if content_type not in ["message", "json", "text"]:
|
65
|
+
content_type = "text"
|
66
|
+
|
67
|
+
try:
|
68
|
+
if content_type == "message":
|
69
|
+
message_metadata = metadata.copy()
|
70
|
+
role = message_metadata.get("role", "norole")
|
71
|
+
name = message_metadata.get("name")
|
72
|
+
|
73
|
+
message = Message(
|
74
|
+
role=role,
|
75
|
+
name=name,
|
76
|
+
content=content_str,
|
77
|
+
)
|
78
|
+
|
79
|
+
self._client.thread.add_messages(thread_id=self._thread_id, messages=[message])
|
80
|
+
|
81
|
+
self._logger.debug(
|
82
|
+
f"Saved message from {metadata.get('name', 'unknown')}: {content_str[:100]}..."
|
83
|
+
)
|
84
|
+
|
85
|
+
else:
|
86
|
+
self._client.graph.add(
|
87
|
+
user_id=self._user_id,
|
88
|
+
data=content_str,
|
89
|
+
type=content_type,
|
90
|
+
)
|
91
|
+
|
92
|
+
self._logger.debug(f"Saved {content_type} data: {content_str[:100]}...")
|
93
|
+
|
94
|
+
except Exception as e:
|
95
|
+
self._logger.error(f"Error saving to Zep: {e}")
|
96
|
+
raise
|
97
|
+
|
98
|
+
def search(
|
99
|
+
self, query: str, limit: int = 5, score_threshold: float = 0.5
|
100
|
+
) -> dict[str, Any] | list[Any]:
|
101
|
+
"""
|
102
|
+
Search Zep user graph.
|
103
|
+
|
104
|
+
This always retrieves thread-specific context and performs targeted graph search on the user graph
|
105
|
+
using the provided query, combining both sources.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
query: Search query string (truncated to 400 chars max for graph search)
|
109
|
+
limit: Maximum number of results to return from graph search
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
List of matching memory entries from both thread context and graph search
|
113
|
+
"""
|
114
|
+
results: list[dict[str, Any]] = []
|
115
|
+
|
116
|
+
# Truncate query to max 400 characters to avoid API errors
|
117
|
+
truncated_query = query[:400] if len(query) > 400 else query
|
118
|
+
|
119
|
+
# Define search functions for concurrent execution
|
120
|
+
def get_thread_context() -> Any:
|
121
|
+
try:
|
122
|
+
return self._client.thread.get_user_context(thread_id=self._thread_id)
|
123
|
+
except Exception as e:
|
124
|
+
self._logger.debug(f"Thread context not available: {e}")
|
125
|
+
return None
|
126
|
+
|
127
|
+
def search_graph_edges() -> list[str]:
|
128
|
+
try:
|
129
|
+
if not query:
|
130
|
+
return []
|
131
|
+
results: GraphSearchResults = self._client.graph.search(
|
132
|
+
user_id=self._user_id, query=truncated_query, limit=limit, scope="edges"
|
133
|
+
)
|
134
|
+
edges: list[str] = []
|
135
|
+
if results.edges:
|
136
|
+
for edge in results.edges:
|
137
|
+
edge_str = f"{edge.fact} (valid_at: {edge.valid_at}, invalid_at: {edge.invalid_at or 'current'})"
|
138
|
+
edges.append(edge_str)
|
139
|
+
return edges
|
140
|
+
except Exception as e:
|
141
|
+
self._logger.debug(f"Graph search not available: {e}")
|
142
|
+
return []
|
143
|
+
|
144
|
+
thread_context = None
|
145
|
+
edges_search_results: list[str] = []
|
146
|
+
|
147
|
+
try:
|
148
|
+
with ThreadPoolExecutor(max_workers=2) as executor:
|
149
|
+
future_thread = executor.submit(get_thread_context)
|
150
|
+
future_edges = executor.submit(search_graph_edges)
|
151
|
+
|
152
|
+
thread_context = future_thread.result()
|
153
|
+
edges_search_results = future_edges.result() or []
|
154
|
+
|
155
|
+
except Exception as e:
|
156
|
+
self._logger.debug(f"Failed to search user memories: {e}")
|
157
|
+
|
158
|
+
if thread_context and hasattr(thread_context, "context") and thread_context.context:
|
159
|
+
results.append({"memory": thread_context.context})
|
160
|
+
|
161
|
+
for result in edges_search_results:
|
162
|
+
results.append({"memory": result})
|
163
|
+
|
164
|
+
return results
|
165
|
+
|
166
|
+
def reset(self) -> None:
|
167
|
+
pass
|
168
|
+
|
169
|
+
@property
|
170
|
+
def user_id(self) -> str:
|
171
|
+
"""Get the user ID."""
|
172
|
+
return self._user_id
|
173
|
+
|
174
|
+
@property
|
175
|
+
def thread_id(self) -> str:
|
176
|
+
"""Get the thread ID."""
|
177
|
+
return self._thread_id
|
@@ -0,0 +1,186 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: zep-crewai
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: CrewAI integration for Zep
|
5
|
+
Project-URL: Homepage, https://github.com/getzep/zep
|
6
|
+
Project-URL: Documentation, https://help.getzep.com
|
7
|
+
Project-URL: Repository, https://github.com/getzep/zep
|
8
|
+
Project-URL: Bug Tracker, https://github.com/getzep/zep/issues
|
9
|
+
Requires-Python: >=3.10
|
10
|
+
Requires-Dist: aiohttp>=3.8.0
|
11
|
+
Requires-Dist: crewai>=0.80.0
|
12
|
+
Requires-Dist: python-dotenv>=1.0.0
|
13
|
+
Requires-Dist: python-slugify>=8.0.4
|
14
|
+
Requires-Dist: rich>=14.0.0
|
15
|
+
Requires-Dist: zep-cloud>=3.0.0rc1
|
16
|
+
Provides-Extra: dev
|
17
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
18
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
19
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
20
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
21
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
22
|
+
Description-Content-Type: text/markdown
|
23
|
+
|
24
|
+
# Zep CrewAI Integration Tutorial
|
25
|
+
|
26
|
+
Learn how to add persistent memory to your CrewAI agents using Zep's powerful memory platform.
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
Install the Zep CrewAI integration package:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
pip install zep-crewai
|
34
|
+
```
|
35
|
+
|
36
|
+
## Setup
|
37
|
+
|
38
|
+
### 1. Get Your API Key
|
39
|
+
|
40
|
+
Sign up at [Zep Cloud](https://app.getzep.com) and get your API key.
|
41
|
+
|
42
|
+
### 2. Set Environment Variable
|
43
|
+
|
44
|
+
```bash
|
45
|
+
export ZEP_API_KEY="your-zep-api-key"
|
46
|
+
```
|
47
|
+
|
48
|
+
## Basic Usage
|
49
|
+
|
50
|
+
### 1. Initialize Zep Client
|
51
|
+
|
52
|
+
```python
|
53
|
+
import os
|
54
|
+
from zep_cloud.client import Zep
|
55
|
+
|
56
|
+
# Initialize Zep client
|
57
|
+
zep_client = Zep(api_key=os.getenv("ZEP_API_KEY"))
|
58
|
+
```
|
59
|
+
|
60
|
+
### 2. Create User and Thread
|
61
|
+
|
62
|
+
**Important**: You must create a user and thread in Zep before using ZepStorage.
|
63
|
+
|
64
|
+
```python
|
65
|
+
# Create a user
|
66
|
+
user_id = "john_doe_123"
|
67
|
+
zep_client.user.add(
|
68
|
+
user_id=user_id,
|
69
|
+
first_name="John",
|
70
|
+
last_name="Doe",
|
71
|
+
email="john.doe@example.com"
|
72
|
+
)
|
73
|
+
|
74
|
+
# Create a thread
|
75
|
+
thread_id = "project_alpha_456"
|
76
|
+
zep_client.thread.create(
|
77
|
+
user_id=user_id,
|
78
|
+
thread_id=thread_id
|
79
|
+
)
|
80
|
+
```
|
81
|
+
|
82
|
+
### 3. Initialize ZepStorage
|
83
|
+
|
84
|
+
```python
|
85
|
+
from zep_crewai import ZepStorage
|
86
|
+
from crewai.memory.external.external_memory import ExternalMemory
|
87
|
+
|
88
|
+
# Create storage for your project
|
89
|
+
zep_storage = ZepStorage(
|
90
|
+
client=zep_client,
|
91
|
+
user_id=user_id,
|
92
|
+
thread_id=thread_id
|
93
|
+
)
|
94
|
+
|
95
|
+
# Wrap in CrewAI's external memory
|
96
|
+
external_memory = ExternalMemory(storage=zep_storage)
|
97
|
+
```
|
98
|
+
|
99
|
+
### 4. Create Crew with Persistent Memory
|
100
|
+
|
101
|
+
```python
|
102
|
+
from crewai import Agent, Crew, Task, Process
|
103
|
+
|
104
|
+
# Create your agents
|
105
|
+
research_agent = Agent(
|
106
|
+
role='Research Analyst',
|
107
|
+
goal='Analyze market trends and provide insights',
|
108
|
+
backstory='You are an expert at finding and analyzing market data...',
|
109
|
+
)
|
110
|
+
|
111
|
+
# Create crew with Zep memory
|
112
|
+
crew = Crew(
|
113
|
+
agents=[research_agent],
|
114
|
+
tasks=[...],
|
115
|
+
external_memory=external_memory, # This enables the crew to search Zep
|
116
|
+
process=Process.sequential,
|
117
|
+
)
|
118
|
+
|
119
|
+
# Run your crew - memories will be automatically saved and retrieved
|
120
|
+
result = crew.kickoff()
|
121
|
+
```
|
122
|
+
|
123
|
+
## How Memory Works
|
124
|
+
|
125
|
+
Zep stores different types of content using metadata-based routing.
|
126
|
+
|
127
|
+
### Messages (Conversation Context)
|
128
|
+
Stored in Zep threads for conversation history:
|
129
|
+
|
130
|
+
```python
|
131
|
+
external_memory.save(
|
132
|
+
"I need help planning a business trip to New York",
|
133
|
+
metadata={"type": "message", "role": "user", "name": "John Doe"}
|
134
|
+
)
|
135
|
+
|
136
|
+
external_memory.save(
|
137
|
+
"I'd be happy to help you plan your trip!",
|
138
|
+
metadata={"type": "message", "role": "assistant", "name": "Travel Agent"}
|
139
|
+
)
|
140
|
+
```
|
141
|
+
|
142
|
+
### Structured Data
|
143
|
+
Added as episodes to the user knowledge graph in Zep:
|
144
|
+
|
145
|
+
```python
|
146
|
+
# JSON data
|
147
|
+
external_memory.save(
|
148
|
+
'{"destination": "New York", "duration": "3 days", "budget": 2000}',
|
149
|
+
metadata={"type": "json"}
|
150
|
+
)
|
151
|
+
|
152
|
+
# Text facts and insights
|
153
|
+
external_memory.save(
|
154
|
+
"User prefers mid-range hotels with business amenities",
|
155
|
+
metadata={"type": "text"}
|
156
|
+
)
|
157
|
+
```
|
158
|
+
|
159
|
+
### Automatic Memory Retrieval
|
160
|
+
|
161
|
+
CrewAI automatically searches your memories when agents need context:
|
162
|
+
|
163
|
+
```python
|
164
|
+
# When agents run, they automatically get relevant context from Zep
|
165
|
+
results = crew.kickoff()
|
166
|
+
|
167
|
+
# You can also search manually
|
168
|
+
memory_results = zep_storage.search("hotel preferences", limit=5)
|
169
|
+
for result in memory_results:
|
170
|
+
print(result['memory'])
|
171
|
+
```
|
172
|
+
|
173
|
+
## Complete Example
|
174
|
+
|
175
|
+
For a full working example, check out [`examples/simple_example.py`](examples/simple_example.py) in this repository. This example demonstrates:
|
176
|
+
|
177
|
+
- Setting up Zep user and thread
|
178
|
+
- Saving different types of memory (messages, JSON data, text)
|
179
|
+
- Creating CrewAI agents with access to Zep memory
|
180
|
+
- Automatic context retrieval during agent execution
|
181
|
+
|
182
|
+
## Requirements
|
183
|
+
|
184
|
+
- Python 3.10+
|
185
|
+
- `zep-cloud>=3.0.0rc1`
|
186
|
+
- `crewai>=0.80.0`
|
@@ -0,0 +1,6 @@
|
|
1
|
+
zep_crewai/__init__.py,sha256=H8xpqfQd79JPRbd7h9wHjgSbsKrQGiTDV_CePgeo_cQ,1280
|
2
|
+
zep_crewai/exceptions.py,sha256=buRZJt5TDankN7PvLq_u0wDlQ0Sp6LKXroS1qTj6koY,403
|
3
|
+
zep_crewai/memory.py,sha256=lhoOsmJK6Yfe9OULt7OQ98Rk963pCY9_HGbHTnA22u8,6082
|
4
|
+
zep_crewai-0.1.0.dist-info/METADATA,sha256=panKSkdvw1uRiGebFgNZ8lfL_xQaPd_L5mSH6bDku-8,4470
|
5
|
+
zep_crewai-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
6
|
+
zep_crewai-0.1.0.dist-info/RECORD,,
|