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.
@@ -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")