zep-crewai 0.1.0__tar.gz
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-0.1.0/.gitignore +88 -0
- zep_crewai-0.1.0/Makefile +72 -0
- zep_crewai-0.1.0/PKG-INFO +186 -0
- zep_crewai-0.1.0/README.md +163 -0
- zep_crewai-0.1.0/examples/simple_example.py +168 -0
- zep_crewai-0.1.0/pyproject.toml +89 -0
- zep_crewai-0.1.0/src/zep_crewai/__init__.py +45 -0
- zep_crewai-0.1.0/src/zep_crewai/exceptions.py +12 -0
- zep_crewai-0.1.0/src/zep_crewai/memory.py +177 -0
- zep_crewai-0.1.0/tests/test_basic.py +210 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
# If you prefer the allow list template instead of the deny list, see community template:
|
2
|
+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
3
|
+
#
|
4
|
+
# Binaries for programs and plugins
|
5
|
+
out/
|
6
|
+
*.exe
|
7
|
+
*.exe~
|
8
|
+
*.dll
|
9
|
+
*.so
|
10
|
+
*.dylib
|
11
|
+
|
12
|
+
# Secrets
|
13
|
+
.env
|
14
|
+
.env.local
|
15
|
+
|
16
|
+
# Test data
|
17
|
+
test_data
|
18
|
+
|
19
|
+
# Test binary, built with `go test -c`
|
20
|
+
*.test
|
21
|
+
|
22
|
+
# Output of the go coverage tool, specifically when used with LiteIDE
|
23
|
+
*.out
|
24
|
+
|
25
|
+
# Dependency directories (remove the comment below to include it)
|
26
|
+
# vendor/
|
27
|
+
|
28
|
+
# Go workspace file
|
29
|
+
.idea
|
30
|
+
.vscode
|
31
|
+
|
32
|
+
# VSCode local history
|
33
|
+
.history
|
34
|
+
|
35
|
+
# Python
|
36
|
+
__pycache__/
|
37
|
+
*.py[cod]
|
38
|
+
*$py.class
|
39
|
+
*.so
|
40
|
+
.Python
|
41
|
+
build/
|
42
|
+
develop-eggs/
|
43
|
+
dist/
|
44
|
+
downloads/
|
45
|
+
eggs/
|
46
|
+
.eggs/
|
47
|
+
lib/
|
48
|
+
lib64/
|
49
|
+
parts/
|
50
|
+
sdist/
|
51
|
+
var/
|
52
|
+
wheels/
|
53
|
+
share/python-wheels/
|
54
|
+
*.egg-info/
|
55
|
+
.installed.cfg
|
56
|
+
*.egg
|
57
|
+
MANIFEST
|
58
|
+
|
59
|
+
# Virtual environments
|
60
|
+
venv/
|
61
|
+
env/
|
62
|
+
ENV/
|
63
|
+
env.bak/
|
64
|
+
venv.bak/
|
65
|
+
.venv/
|
66
|
+
|
67
|
+
# PyCharm
|
68
|
+
.idea/
|
69
|
+
|
70
|
+
# Jupyter Notebook
|
71
|
+
.ipynb_checkpoints
|
72
|
+
|
73
|
+
# pyenv
|
74
|
+
.python-version
|
75
|
+
|
76
|
+
# pytest
|
77
|
+
.pytest_cache/
|
78
|
+
.coverage
|
79
|
+
|
80
|
+
# mypy
|
81
|
+
.mypy_cache/
|
82
|
+
.dmypy.json
|
83
|
+
dmypy.json
|
84
|
+
|
85
|
+
# UV (Python package manager)
|
86
|
+
uv.lock
|
87
|
+
.uv_cache/
|
88
|
+
__pypackages__/
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Makefile for zep-crewai development
|
2
|
+
|
3
|
+
.PHONY: help install format lint type-check test test-cov clean build all
|
4
|
+
|
5
|
+
# Default target
|
6
|
+
help:
|
7
|
+
@echo "Available commands:"
|
8
|
+
@echo " install - Install package and dependencies in development mode"
|
9
|
+
@echo " format - Format code with ruff"
|
10
|
+
@echo " lint - Run linting checks"
|
11
|
+
@echo " type-check - Run type checking with mypy"
|
12
|
+
@echo " test - Run tests"
|
13
|
+
@echo " test-cov - Run tests with coverage report"
|
14
|
+
@echo " all - Run format, lint, type-check, and test"
|
15
|
+
@echo " build - Build the package"
|
16
|
+
@echo " clean - Clean build artifacts"
|
17
|
+
|
18
|
+
# Install package in development mode
|
19
|
+
install:
|
20
|
+
uv sync --extra dev
|
21
|
+
|
22
|
+
# Format code
|
23
|
+
format:
|
24
|
+
uv run ruff format .
|
25
|
+
|
26
|
+
# Run linting checks
|
27
|
+
lint:
|
28
|
+
uv run ruff check .
|
29
|
+
|
30
|
+
# Fix linting issues automatically
|
31
|
+
lint-fix:
|
32
|
+
uv run ruff check --fix .
|
33
|
+
|
34
|
+
# Run type checking
|
35
|
+
type-check:
|
36
|
+
uv run mypy src/
|
37
|
+
|
38
|
+
# Run tests
|
39
|
+
test:
|
40
|
+
uv run pytest tests/ -v
|
41
|
+
|
42
|
+
# Run tests with coverage
|
43
|
+
test-cov:
|
44
|
+
uv run pytest tests/ -v --cov=zep_crewai --cov-report=term-missing --cov-report=xml
|
45
|
+
|
46
|
+
# Run all checks (the order matters: format first, then lint, then type-check, then test)
|
47
|
+
all: format lint type-check test
|
48
|
+
|
49
|
+
# Build the package
|
50
|
+
build:
|
51
|
+
uv build
|
52
|
+
|
53
|
+
# Clean build artifacts
|
54
|
+
clean:
|
55
|
+
rm -rf dist/
|
56
|
+
rm -rf build/
|
57
|
+
rm -rf *.egg-info/
|
58
|
+
rm -rf .pytest_cache/
|
59
|
+
rm -rf .mypy_cache/
|
60
|
+
rm -rf .ruff_cache/
|
61
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
62
|
+
find . -type f -name "*.pyc" -delete
|
63
|
+
rm -f coverage.xml
|
64
|
+
rm -f .coverage
|
65
|
+
|
66
|
+
# Development workflow - run this before committing
|
67
|
+
pre-commit: lint-fix format lint type-check test
|
68
|
+
@echo "✅ All checks passed! Ready to commit."
|
69
|
+
|
70
|
+
# CI workflow - strict checks without auto-fixing
|
71
|
+
ci: lint type-check test
|
72
|
+
@echo "✅ CI checks passed!"
|
@@ -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,163 @@
|
|
1
|
+
# Zep CrewAI Integration Tutorial
|
2
|
+
|
3
|
+
Learn how to add persistent memory to your CrewAI agents using Zep's powerful memory platform.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the Zep CrewAI integration package:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
pip install zep-crewai
|
11
|
+
```
|
12
|
+
|
13
|
+
## Setup
|
14
|
+
|
15
|
+
### 1. Get Your API Key
|
16
|
+
|
17
|
+
Sign up at [Zep Cloud](https://app.getzep.com) and get your API key.
|
18
|
+
|
19
|
+
### 2. Set Environment Variable
|
20
|
+
|
21
|
+
```bash
|
22
|
+
export ZEP_API_KEY="your-zep-api-key"
|
23
|
+
```
|
24
|
+
|
25
|
+
## Basic Usage
|
26
|
+
|
27
|
+
### 1. Initialize Zep Client
|
28
|
+
|
29
|
+
```python
|
30
|
+
import os
|
31
|
+
from zep_cloud.client import Zep
|
32
|
+
|
33
|
+
# Initialize Zep client
|
34
|
+
zep_client = Zep(api_key=os.getenv("ZEP_API_KEY"))
|
35
|
+
```
|
36
|
+
|
37
|
+
### 2. Create User and Thread
|
38
|
+
|
39
|
+
**Important**: You must create a user and thread in Zep before using ZepStorage.
|
40
|
+
|
41
|
+
```python
|
42
|
+
# Create a user
|
43
|
+
user_id = "john_doe_123"
|
44
|
+
zep_client.user.add(
|
45
|
+
user_id=user_id,
|
46
|
+
first_name="John",
|
47
|
+
last_name="Doe",
|
48
|
+
email="john.doe@example.com"
|
49
|
+
)
|
50
|
+
|
51
|
+
# Create a thread
|
52
|
+
thread_id = "project_alpha_456"
|
53
|
+
zep_client.thread.create(
|
54
|
+
user_id=user_id,
|
55
|
+
thread_id=thread_id
|
56
|
+
)
|
57
|
+
```
|
58
|
+
|
59
|
+
### 3. Initialize ZepStorage
|
60
|
+
|
61
|
+
```python
|
62
|
+
from zep_crewai import ZepStorage
|
63
|
+
from crewai.memory.external.external_memory import ExternalMemory
|
64
|
+
|
65
|
+
# Create storage for your project
|
66
|
+
zep_storage = ZepStorage(
|
67
|
+
client=zep_client,
|
68
|
+
user_id=user_id,
|
69
|
+
thread_id=thread_id
|
70
|
+
)
|
71
|
+
|
72
|
+
# Wrap in CrewAI's external memory
|
73
|
+
external_memory = ExternalMemory(storage=zep_storage)
|
74
|
+
```
|
75
|
+
|
76
|
+
### 4. Create Crew with Persistent Memory
|
77
|
+
|
78
|
+
```python
|
79
|
+
from crewai import Agent, Crew, Task, Process
|
80
|
+
|
81
|
+
# Create your agents
|
82
|
+
research_agent = Agent(
|
83
|
+
role='Research Analyst',
|
84
|
+
goal='Analyze market trends and provide insights',
|
85
|
+
backstory='You are an expert at finding and analyzing market data...',
|
86
|
+
)
|
87
|
+
|
88
|
+
# Create crew with Zep memory
|
89
|
+
crew = Crew(
|
90
|
+
agents=[research_agent],
|
91
|
+
tasks=[...],
|
92
|
+
external_memory=external_memory, # This enables the crew to search Zep
|
93
|
+
process=Process.sequential,
|
94
|
+
)
|
95
|
+
|
96
|
+
# Run your crew - memories will be automatically saved and retrieved
|
97
|
+
result = crew.kickoff()
|
98
|
+
```
|
99
|
+
|
100
|
+
## How Memory Works
|
101
|
+
|
102
|
+
Zep stores different types of content using metadata-based routing.
|
103
|
+
|
104
|
+
### Messages (Conversation Context)
|
105
|
+
Stored in Zep threads for conversation history:
|
106
|
+
|
107
|
+
```python
|
108
|
+
external_memory.save(
|
109
|
+
"I need help planning a business trip to New York",
|
110
|
+
metadata={"type": "message", "role": "user", "name": "John Doe"}
|
111
|
+
)
|
112
|
+
|
113
|
+
external_memory.save(
|
114
|
+
"I'd be happy to help you plan your trip!",
|
115
|
+
metadata={"type": "message", "role": "assistant", "name": "Travel Agent"}
|
116
|
+
)
|
117
|
+
```
|
118
|
+
|
119
|
+
### Structured Data
|
120
|
+
Added as episodes to the user knowledge graph in Zep:
|
121
|
+
|
122
|
+
```python
|
123
|
+
# JSON data
|
124
|
+
external_memory.save(
|
125
|
+
'{"destination": "New York", "duration": "3 days", "budget": 2000}',
|
126
|
+
metadata={"type": "json"}
|
127
|
+
)
|
128
|
+
|
129
|
+
# Text facts and insights
|
130
|
+
external_memory.save(
|
131
|
+
"User prefers mid-range hotels with business amenities",
|
132
|
+
metadata={"type": "text"}
|
133
|
+
)
|
134
|
+
```
|
135
|
+
|
136
|
+
### Automatic Memory Retrieval
|
137
|
+
|
138
|
+
CrewAI automatically searches your memories when agents need context:
|
139
|
+
|
140
|
+
```python
|
141
|
+
# When agents run, they automatically get relevant context from Zep
|
142
|
+
results = crew.kickoff()
|
143
|
+
|
144
|
+
# You can also search manually
|
145
|
+
memory_results = zep_storage.search("hotel preferences", limit=5)
|
146
|
+
for result in memory_results:
|
147
|
+
print(result['memory'])
|
148
|
+
```
|
149
|
+
|
150
|
+
## Complete Example
|
151
|
+
|
152
|
+
For a full working example, check out [`examples/simple_example.py`](examples/simple_example.py) in this repository. This example demonstrates:
|
153
|
+
|
154
|
+
- Setting up Zep user and thread
|
155
|
+
- Saving different types of memory (messages, JSON data, text)
|
156
|
+
- Creating CrewAI agents with access to Zep memory
|
157
|
+
- Automatic context retrieval during agent execution
|
158
|
+
|
159
|
+
## Requirements
|
160
|
+
|
161
|
+
- Python 3.10+
|
162
|
+
- `zep-cloud>=3.0.0rc1`
|
163
|
+
- `crewai>=0.80.0`
|
@@ -0,0 +1,168 @@
|
|
1
|
+
"""
|
2
|
+
Simple CrewAI + Zep integration example.
|
3
|
+
|
4
|
+
This example demonstrates the basic usage of ZepStorage with CrewAI's external memory system.
|
5
|
+
It shows how to manually save important information and let CrewAI automatically retrieve it
|
6
|
+
during agent execution.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import sys
|
11
|
+
import time
|
12
|
+
import uuid
|
13
|
+
|
14
|
+
from crewai import Agent, Crew, Process, Task
|
15
|
+
from crewai.memory.external.external_memory import ExternalMemory
|
16
|
+
from zep_cloud.client import Zep
|
17
|
+
|
18
|
+
from zep_crewai import ZepStorage
|
19
|
+
|
20
|
+
|
21
|
+
def main():
|
22
|
+
# Check for API key
|
23
|
+
api_key = os.environ.get("ZEP_API_KEY")
|
24
|
+
if not api_key:
|
25
|
+
print("❌ Error: Please set your ZEP_API_KEY environment variable")
|
26
|
+
print(" Get your API key from: https://app.getzep.com")
|
27
|
+
sys.exit(1)
|
28
|
+
|
29
|
+
# Initialize Zep client
|
30
|
+
zep_client = Zep(api_key=api_key)
|
31
|
+
|
32
|
+
print("\n🤖 CrewAI + Zep Memory Integration Example")
|
33
|
+
print("=" * 50)
|
34
|
+
|
35
|
+
# Set up user and thread
|
36
|
+
user_id = "demo_user_" + str(uuid.uuid4())
|
37
|
+
thread_id = "demo_thread_" + str(uuid.uuid4())
|
38
|
+
|
39
|
+
print(f"👤 User ID: {user_id}")
|
40
|
+
print(f"🧵 Thread ID: {thread_id}")
|
41
|
+
|
42
|
+
# Create user in Zep
|
43
|
+
try:
|
44
|
+
zep_client.user.add(
|
45
|
+
user_id=user_id, first_name="John", last_name="Doe", email="john.doe@example.com"
|
46
|
+
)
|
47
|
+
print("✅ User created successfully")
|
48
|
+
except Exception as e:
|
49
|
+
if "already exists" in str(e).lower():
|
50
|
+
print("✅ User already exists")
|
51
|
+
else:
|
52
|
+
print(f"⚠️ User creation issue: {e}")
|
53
|
+
|
54
|
+
# Create thread
|
55
|
+
try:
|
56
|
+
zep_client.thread.create(user_id=user_id, thread_id=thread_id)
|
57
|
+
print("✅ Thread created successfully")
|
58
|
+
except Exception as e:
|
59
|
+
print(f"⚠️ Thread creation issue: {e}")
|
60
|
+
|
61
|
+
# Initialize Zep storage and external memory
|
62
|
+
zep_storage = ZepStorage(client=zep_client, user_id=user_id, thread_id=thread_id)
|
63
|
+
external_memory = ExternalMemory(storage=zep_storage)
|
64
|
+
|
65
|
+
# Save conversation context and data (demonstrates metadata-based routing)
|
66
|
+
print("\n💾 Saving conversation and business context to Zep...")
|
67
|
+
|
68
|
+
# Save structured business trip data (goes to graph via json type)
|
69
|
+
external_memory.save(
|
70
|
+
'{"trip_type": "business", "destination": "New York", "duration": "3 days", "budget": 2000, "accommodation_preference": "mid-range hotels", "dietary_preference": "local cuisine"}',
|
71
|
+
metadata={"type": "json"},
|
72
|
+
)
|
73
|
+
|
74
|
+
# Save user messages (go to thread via message type)
|
75
|
+
external_memory.save(
|
76
|
+
"Hi, I need help planning a business trip to New York. I'll be there for 3 days and prefer mid-range hotels.",
|
77
|
+
metadata={"type": "message", "role": "user", "name": "John Doe"},
|
78
|
+
)
|
79
|
+
|
80
|
+
external_memory.save(
|
81
|
+
"I'd be happy to help you plan your New York business trip! Let me find some great mid-range hotel options for you.",
|
82
|
+
metadata={"type": "message", "role": "assistant", "name": "Travel Planning Assistant"},
|
83
|
+
)
|
84
|
+
|
85
|
+
# Save user preferences as text data (goes to graph via text type)
|
86
|
+
external_memory.save(
|
87
|
+
"John Doe prefers mid-range hotels with business amenities, enjoys trying local cuisine, and values convenient locations near business districts",
|
88
|
+
metadata={"type": "text"},
|
89
|
+
)
|
90
|
+
|
91
|
+
external_memory.save(
|
92
|
+
"Could you also recommend some good restaurants nearby? I love trying authentic local food when I travel for business.",
|
93
|
+
metadata={"type": "message", "role": "user", "name": "John Doe"},
|
94
|
+
)
|
95
|
+
|
96
|
+
# Save budget constraint as text data (goes to graph)
|
97
|
+
external_memory.save(
|
98
|
+
"John Doe's budget constraint: Total budget is around $2000 for the entire trip including flights and accommodation. Looking for good value rather than luxury.",
|
99
|
+
metadata={"type": "text"},
|
100
|
+
)
|
101
|
+
|
102
|
+
print("✅ Context saved to Zep memory")
|
103
|
+
print(" • Messages → Thread API (conversation context)")
|
104
|
+
print(" • JSON data → Graph API (structured trip info)")
|
105
|
+
print(" • Text data → Graph API (preferences & constraints)")
|
106
|
+
print(
|
107
|
+
" (Waiting 20 seconds until data is processed in zep)"
|
108
|
+
) # Give time for Zep to index the data
|
109
|
+
time.sleep(20)
|
110
|
+
|
111
|
+
# Create a simple agent
|
112
|
+
travel_agent = Agent(
|
113
|
+
role="Travel Planning Assistant",
|
114
|
+
goal="Help plan business trips efficiently and within budget",
|
115
|
+
backstory="""You are an experienced travel planner who specializes in business trips.
|
116
|
+
You always consider the user's preferences, budget constraints, and trip context
|
117
|
+
to provide practical recommendations.""",
|
118
|
+
verbose=True,
|
119
|
+
llm="gpt-4.1-mini",
|
120
|
+
)
|
121
|
+
|
122
|
+
# Create a simple task
|
123
|
+
planning_task = Task(
|
124
|
+
description="""Based on the user's saved preferences and context, provide 3 specific
|
125
|
+
hotel recommendations in New York that would be good for a business traveler.
|
126
|
+
|
127
|
+
Include:
|
128
|
+
- Hotel names and locations
|
129
|
+
- Price range per night
|
130
|
+
- Why each hotel fits the user's preferences
|
131
|
+
- Any special business amenities""",
|
132
|
+
expected_output="A list of 3 hotel recommendations with detailed explanations",
|
133
|
+
agent=travel_agent,
|
134
|
+
)
|
135
|
+
|
136
|
+
# Create crew with Zep memory
|
137
|
+
crew = Crew(
|
138
|
+
agents=[travel_agent],
|
139
|
+
tasks=[planning_task],
|
140
|
+
process=Process.sequential,
|
141
|
+
external_memory=external_memory, # This enables automatic memory retrieval during execution
|
142
|
+
verbose=True,
|
143
|
+
)
|
144
|
+
|
145
|
+
print("\n🚀 Starting CrewAI execution...")
|
146
|
+
print(" (The agent will automatically retrieve context from Zep memory)")
|
147
|
+
|
148
|
+
# Execute the crew - CrewAI will automatically call zep_storage.search() to get context
|
149
|
+
try:
|
150
|
+
result = crew.kickoff()
|
151
|
+
|
152
|
+
print("\n" + "=" * 60)
|
153
|
+
print("RESULT:")
|
154
|
+
print("=" * 60)
|
155
|
+
print(result)
|
156
|
+
print("=" * 60)
|
157
|
+
|
158
|
+
# Optionally save the result back to memory for future use
|
159
|
+
external_memory.save(str(result), metadata={"type": "message", "role": "assistant"})
|
160
|
+
print("\n💾 Agent result saved to memory for future reference")
|
161
|
+
|
162
|
+
except Exception as e:
|
163
|
+
print(f"\n❌ Execution failed: {e}")
|
164
|
+
return
|
165
|
+
|
166
|
+
|
167
|
+
if __name__ == "__main__":
|
168
|
+
main()
|
@@ -0,0 +1,89 @@
|
|
1
|
+
[project]
|
2
|
+
name = "zep-crewai"
|
3
|
+
version = "0.1.0"
|
4
|
+
description = "CrewAI integration for Zep"
|
5
|
+
readme = "README.md"
|
6
|
+
requires-python = ">=3.10"
|
7
|
+
dependencies = [
|
8
|
+
"crewai>=0.80.0",
|
9
|
+
"aiohttp>=3.8.0",
|
10
|
+
"python-dotenv>=1.0.0",
|
11
|
+
"python-slugify>=8.0.4",
|
12
|
+
"rich>=14.0.0",
|
13
|
+
"zep-cloud>=3.0.0rc1",
|
14
|
+
]
|
15
|
+
|
16
|
+
[project.urls]
|
17
|
+
Homepage = "https://github.com/getzep/zep"
|
18
|
+
Documentation = "https://help.getzep.com"
|
19
|
+
Repository = "https://github.com/getzep/zep"
|
20
|
+
"Bug Tracker" = "https://github.com/getzep/zep/issues"
|
21
|
+
|
22
|
+
[project.optional-dependencies]
|
23
|
+
dev = [
|
24
|
+
"pytest>=7.0.0",
|
25
|
+
"pytest-cov",
|
26
|
+
"pytest-asyncio",
|
27
|
+
"ruff>=0.1.0",
|
28
|
+
"mypy>=1.0.0",
|
29
|
+
]
|
30
|
+
|
31
|
+
[build-system]
|
32
|
+
requires = ["hatchling"]
|
33
|
+
build-backend = "hatchling.build"
|
34
|
+
|
35
|
+
[tool.hatch.build.targets.wheel]
|
36
|
+
packages = ["src/zep_crewai"]
|
37
|
+
|
38
|
+
[tool.pytest.ini_options]
|
39
|
+
testpaths = ["tests"]
|
40
|
+
python_files = ["test_*.py"]
|
41
|
+
python_classes = ["Test*"]
|
42
|
+
python_functions = ["test_*"]
|
43
|
+
markers = [
|
44
|
+
"integration: marks tests as integration tests (deselect with '-m \"not integration\"')"
|
45
|
+
]
|
46
|
+
|
47
|
+
[tool.ruff]
|
48
|
+
target-version = "py310"
|
49
|
+
line-length = 100
|
50
|
+
|
51
|
+
[tool.ruff.lint]
|
52
|
+
select = [
|
53
|
+
"E", # pycodestyle errors
|
54
|
+
"W", # pycodestyle warnings
|
55
|
+
"F", # pyflakes
|
56
|
+
"I", # isort
|
57
|
+
"B", # flake8-bugbear
|
58
|
+
"C4", # flake8-comprehensions
|
59
|
+
"UP", # pyupgrade
|
60
|
+
]
|
61
|
+
ignore = [
|
62
|
+
"E501", # line too long, handled by formatter
|
63
|
+
"B008", # do not perform function calls in argument defaults
|
64
|
+
"C901", # too complex
|
65
|
+
]
|
66
|
+
|
67
|
+
[tool.ruff.lint.isort]
|
68
|
+
known-first-party = ["zep_crewai"]
|
69
|
+
|
70
|
+
[tool.ruff.format]
|
71
|
+
quote-style = "double"
|
72
|
+
indent-style = "space"
|
73
|
+
skip-magic-trailing-comma = false
|
74
|
+
line-ending = "auto"
|
75
|
+
|
76
|
+
[tool.mypy]
|
77
|
+
python_version = "3.10"
|
78
|
+
warn_return_any = true
|
79
|
+
warn_unused_configs = true
|
80
|
+
disallow_untyped_defs = true
|
81
|
+
disallow_incomplete_defs = true
|
82
|
+
check_untyped_defs = true
|
83
|
+
|
84
|
+
[[tool.mypy.overrides]]
|
85
|
+
module = [
|
86
|
+
"crewai.*",
|
87
|
+
"zep_cloud.*",
|
88
|
+
]
|
89
|
+
ignore_missing_imports = true
|
@@ -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
|
@@ -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}")
|
@@ -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,210 @@
|
|
1
|
+
"""
|
2
|
+
Basic tests for the zep-crewai package.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from unittest.mock import MagicMock
|
6
|
+
|
7
|
+
import pytest
|
8
|
+
|
9
|
+
from zep_crewai import ZepStorage
|
10
|
+
|
11
|
+
|
12
|
+
def test_package_import():
|
13
|
+
"""Test that the package can be imported successfully."""
|
14
|
+
import zep_crewai
|
15
|
+
|
16
|
+
assert zep_crewai is not None
|
17
|
+
|
18
|
+
|
19
|
+
def test_zep_storage_import():
|
20
|
+
"""Test that ZepStorage can be imported successfully."""
|
21
|
+
assert ZepStorage is not None
|
22
|
+
|
23
|
+
|
24
|
+
class TestBasicFunctionality:
|
25
|
+
"""Basic functionality tests for the zep-crewai package."""
|
26
|
+
|
27
|
+
def test_package_structure(self):
|
28
|
+
"""Test that the package has the expected structure."""
|
29
|
+
import zep_crewai
|
30
|
+
|
31
|
+
assert hasattr(zep_crewai, "__version__")
|
32
|
+
assert hasattr(zep_crewai, "__author__")
|
33
|
+
assert hasattr(zep_crewai, "__description__")
|
34
|
+
|
35
|
+
|
36
|
+
class TestZepStorageMock:
|
37
|
+
"""Test ZepStorage with mock clients."""
|
38
|
+
|
39
|
+
def test_zep_storage_initialization_with_mock(self):
|
40
|
+
"""Test that ZepStorage can be initialized with a mock client."""
|
41
|
+
try:
|
42
|
+
from zep_cloud.client import Zep
|
43
|
+
|
44
|
+
# Create a mock Zep client
|
45
|
+
mock_client = MagicMock(spec=Zep)
|
46
|
+
storage = ZepStorage(client=mock_client, user_id="test-user", thread_id="test-thread")
|
47
|
+
assert storage is not None
|
48
|
+
assert storage._client is mock_client
|
49
|
+
assert storage._user_id == "test-user"
|
50
|
+
assert storage._thread_id == "test-thread"
|
51
|
+
|
52
|
+
except ImportError:
|
53
|
+
# If zep_cloud is not available, test with a generic mock
|
54
|
+
class MockZep:
|
55
|
+
pass
|
56
|
+
|
57
|
+
mock_client = MockZep()
|
58
|
+
with pytest.raises(TypeError, match="client must be an instance of Zep"):
|
59
|
+
ZepStorage(client=mock_client, user_id="test-user", thread_id="test-thread")
|
60
|
+
|
61
|
+
def test_zep_storage_requires_user_id_and_thread_id(self):
|
62
|
+
"""Test that ZepStorage requires both user_id and thread_id."""
|
63
|
+
try:
|
64
|
+
from zep_cloud.client import Zep
|
65
|
+
|
66
|
+
mock_client = MagicMock(spec=Zep)
|
67
|
+
|
68
|
+
with pytest.raises(ValueError, match="user_id is required"):
|
69
|
+
ZepStorage(client=mock_client, user_id="", thread_id="test-thread")
|
70
|
+
|
71
|
+
with pytest.raises(ValueError, match="thread_id is required"):
|
72
|
+
ZepStorage(client=mock_client, user_id="test-user", thread_id="")
|
73
|
+
|
74
|
+
except ImportError:
|
75
|
+
pytest.skip("zep_cloud not available")
|
76
|
+
|
77
|
+
def test_zep_storage_requires_zep_client(self):
|
78
|
+
"""Test that ZepStorage raises TypeError when client is not Zep."""
|
79
|
+
with pytest.raises(TypeError, match="client must be an instance of Zep"):
|
80
|
+
ZepStorage(client="not_a_client", user_id="test-user", thread_id="test-thread")
|
81
|
+
|
82
|
+
def test_zep_storage_save_message_sync(self):
|
83
|
+
"""Test saving memory as thread message using sync interface."""
|
84
|
+
try:
|
85
|
+
from zep_cloud.client import Zep
|
86
|
+
|
87
|
+
mock_client = MagicMock(spec=Zep)
|
88
|
+
mock_client.thread = MagicMock()
|
89
|
+
mock_client.thread.add_messages = MagicMock()
|
90
|
+
|
91
|
+
storage = ZepStorage(client=mock_client, user_id="test-user", thread_id="test-thread")
|
92
|
+
|
93
|
+
# Test saving message content to thread
|
94
|
+
storage.save(
|
95
|
+
"Test message content",
|
96
|
+
metadata={"type": "message", "role": "user", "name": "John Doe"},
|
97
|
+
)
|
98
|
+
|
99
|
+
# Verify the thread add_messages was called
|
100
|
+
mock_client.thread.add_messages.assert_called_once()
|
101
|
+
|
102
|
+
# Check the call arguments
|
103
|
+
call_args = mock_client.thread.add_messages.call_args
|
104
|
+
assert call_args[1]["thread_id"] == "test-thread"
|
105
|
+
assert len(call_args[1]["messages"]) == 1
|
106
|
+
|
107
|
+
message = call_args[1]["messages"][0]
|
108
|
+
assert message.content == "Test message content"
|
109
|
+
assert message.role == "user"
|
110
|
+
assert message.name == "John Doe"
|
111
|
+
|
112
|
+
except ImportError:
|
113
|
+
pytest.skip("zep_cloud not available")
|
114
|
+
|
115
|
+
def test_zep_storage_save_graph_sync(self):
|
116
|
+
"""Test saving memory as graph data using sync interface."""
|
117
|
+
try:
|
118
|
+
from zep_cloud.client import Zep
|
119
|
+
|
120
|
+
mock_client = MagicMock(spec=Zep)
|
121
|
+
mock_client.graph = MagicMock()
|
122
|
+
mock_client.graph.add = MagicMock()
|
123
|
+
|
124
|
+
storage = ZepStorage(client=mock_client, user_id="test-user", thread_id="test-thread")
|
125
|
+
|
126
|
+
# Test saving text content to graph
|
127
|
+
storage.save("Test text content", metadata={"type": "text", "category": "facts"})
|
128
|
+
|
129
|
+
# Verify the graph add was called
|
130
|
+
mock_client.graph.add.assert_called_once()
|
131
|
+
|
132
|
+
# Check the call arguments
|
133
|
+
call_args = mock_client.graph.add.call_args
|
134
|
+
assert call_args[1]["user_id"] == "test-user"
|
135
|
+
assert call_args[1]["data"] == "Test text content"
|
136
|
+
assert call_args[1]["type"] == "text"
|
137
|
+
|
138
|
+
except ImportError:
|
139
|
+
pytest.skip("zep_cloud not available")
|
140
|
+
|
141
|
+
def test_zep_storage_search_sync(self):
|
142
|
+
"""Test searching memory using sync interface."""
|
143
|
+
try:
|
144
|
+
from zep_cloud.client import Zep
|
145
|
+
from zep_cloud.types import EntityEdge, GraphSearchResults
|
146
|
+
|
147
|
+
mock_client = MagicMock(spec=Zep)
|
148
|
+
mock_client.thread = MagicMock()
|
149
|
+
mock_client.thread.get_user_context = MagicMock()
|
150
|
+
mock_client.graph = MagicMock()
|
151
|
+
mock_client.graph.search = MagicMock()
|
152
|
+
|
153
|
+
# Mock thread context response
|
154
|
+
mock_thread_context = MagicMock()
|
155
|
+
mock_thread_context.context = "Mock thread context summary"
|
156
|
+
mock_client.thread.get_user_context.return_value = mock_thread_context
|
157
|
+
|
158
|
+
# Mock graph search response
|
159
|
+
mock_edge = MagicMock(spec=EntityEdge)
|
160
|
+
mock_edge.fact = "Mock fact from graph"
|
161
|
+
mock_edge.valid_at = "2023-01-01"
|
162
|
+
mock_edge.invalid_at = None
|
163
|
+
|
164
|
+
mock_graph_results = MagicMock(spec=GraphSearchResults)
|
165
|
+
mock_graph_results.edges = [mock_edge]
|
166
|
+
mock_client.graph.search.return_value = mock_graph_results
|
167
|
+
|
168
|
+
storage = ZepStorage(client=mock_client, user_id="test-user", thread_id="test-thread")
|
169
|
+
|
170
|
+
results = storage.search("test query", limit=5)
|
171
|
+
|
172
|
+
# Should return a list of results
|
173
|
+
assert isinstance(results, list)
|
174
|
+
assert len(results) >= 1 # At least thread context
|
175
|
+
|
176
|
+
# Verify both thread context and graph search were called
|
177
|
+
mock_client.thread.get_user_context.assert_called_once_with(thread_id="test-thread")
|
178
|
+
mock_client.graph.search.assert_called_once()
|
179
|
+
|
180
|
+
except ImportError:
|
181
|
+
pytest.skip("zep_cloud not available")
|
182
|
+
|
183
|
+
def test_zep_storage_reset_sync(self):
|
184
|
+
"""Test resetting memory using sync interface."""
|
185
|
+
try:
|
186
|
+
from zep_cloud.client import Zep
|
187
|
+
|
188
|
+
mock_client = MagicMock(spec=Zep)
|
189
|
+
|
190
|
+
storage = ZepStorage(client=mock_client, user_id="test-user", thread_id="test-thread")
|
191
|
+
|
192
|
+
# Should not raise an exception (currently just logs a warning)
|
193
|
+
storage.reset()
|
194
|
+
|
195
|
+
except ImportError:
|
196
|
+
pytest.skip("zep_cloud not available")
|
197
|
+
|
198
|
+
def test_zep_storage_properties(self):
|
199
|
+
"""Test ZepStorage properties."""
|
200
|
+
try:
|
201
|
+
from zep_cloud.client import Zep
|
202
|
+
|
203
|
+
mock_client = MagicMock(spec=Zep)
|
204
|
+
storage = ZepStorage(client=mock_client, user_id="test-user", thread_id="test-thread")
|
205
|
+
|
206
|
+
assert storage.user_id == "test-user"
|
207
|
+
assert storage.thread_id == "test-thread"
|
208
|
+
|
209
|
+
except ImportError:
|
210
|
+
pytest.skip("zep_cloud not available")
|