zep-autogen 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_autogen-0.1.0/.gitignore +88 -0
- zep_autogen-0.1.0/CHANGELOG.md +29 -0
- zep_autogen-0.1.0/Makefile +72 -0
- zep_autogen-0.1.0/PKG-INFO +144 -0
- zep_autogen-0.1.0/README.md +118 -0
- zep_autogen-0.1.0/examples/autogen_basic.py +98 -0
- zep_autogen-0.1.0/pyproject.toml +94 -0
- zep_autogen-0.1.0/src/zep_autogen/__init__.py +44 -0
- zep_autogen-0.1.0/src/zep_autogen/exceptions.py +12 -0
- zep_autogen-0.1.0/src/zep_autogen/memory.py +304 -0
- zep_autogen-0.1.0/tests/test_basic.py +269 -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,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the zep-autogen package will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2024-01-XX
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release of zep-autogen integration package
|
|
14
|
+
- `ZepMemory` class implementing AutoGen's Memory interface
|
|
15
|
+
- Support for persistent conversation memory with Zep Cloud
|
|
16
|
+
- Async/await support for modern Python applications
|
|
17
|
+
- Comprehensive type hints and documentation
|
|
18
|
+
- Basic example demonstrating usage with AutoGen agents
|
|
19
|
+
- Error handling for missing dependencies
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
- Seamless integration with AutoGen agents
|
|
23
|
+
- Intelligent context retrieval from Zep memory
|
|
24
|
+
- Support for user-specific and thread-specific memory contexts
|
|
25
|
+
- Configurable memory retrieval limits
|
|
26
|
+
- Compatible with AutoGen 0.6.1+
|
|
27
|
+
|
|
28
|
+
[Unreleased]: https://github.com/getzep/zep/compare/v0.1.0...HEAD
|
|
29
|
+
[0.1.0]: https://github.com/getzep/zep/releases/tag/v0.1.0
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Makefile for zep-autogen 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_autogen --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,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zep-autogen
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Autogen integration for Zep
|
|
5
|
+
Project-URL: Homepage, https://github.com/getzep/zep
|
|
6
|
+
Project-URL: Documentation, https://docs.getzep.com/integrations/autogen
|
|
7
|
+
Project-URL: Repository, https://github.com/getzep/zep
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/getzep/zep/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/getzep/zep/blob/main/integrations/python/autogen/CHANGELOG.md
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
12
|
+
Requires-Dist: autogen-agentchat>=0.6.1
|
|
13
|
+
Requires-Dist: autogen-ext[azure,openai]>=0.6.1
|
|
14
|
+
Requires-Dist: azure-identity>=1.23.0
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
|
+
Requires-Dist: python-slugify>=8.0.4
|
|
17
|
+
Requires-Dist: rich>=14.0.0
|
|
18
|
+
Requires-Dist: zep-cloud>=3.0.0rc1
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# Zep AutoGen Integration
|
|
28
|
+
|
|
29
|
+
A dedicated integration package that enables [Zep](https://getzep.com) to work seamlessly with [Microsoft AutoGen](https://github.com/microsoft/autogen) agents, providing persistent conversation memory and intelligent context retrieval.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install zep-autogen
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import asyncio
|
|
41
|
+
from zep_cloud.client import AsyncZep
|
|
42
|
+
from zep_autogen import ZepMemory
|
|
43
|
+
from autogen_agentchat.agents import AssistantAgent
|
|
44
|
+
from autogen_ext.models.openai import OpenAIChatCompletionClient
|
|
45
|
+
|
|
46
|
+
async def main():
|
|
47
|
+
# Initialize Zep client
|
|
48
|
+
zep_client = AsyncZep(api_key="your-zep-api-key")
|
|
49
|
+
|
|
50
|
+
# Create Zep memory for your agent
|
|
51
|
+
memory = ZepMemory(
|
|
52
|
+
client=zep_client,
|
|
53
|
+
user_id="user_123",
|
|
54
|
+
thread_id="conversation_456"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Create AutoGen agent with Zep memory
|
|
58
|
+
agent = AssistantAgent(
|
|
59
|
+
name="MemoryAwareAssistant",
|
|
60
|
+
model_client=OpenAIChatCompletionClient(model="gpt-4.1-mini"),
|
|
61
|
+
memory=[memory] # Add Zep memory to the agent
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Your agent now has persistent memory across conversations!
|
|
65
|
+
response = await agent.run(task="What's my name again?")
|
|
66
|
+
print(response.messages[-1].content)
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Features
|
|
72
|
+
|
|
73
|
+
- **Persistent Memory**: Conversations persist across agent sessions
|
|
74
|
+
- **Intelligent Retrieval**: Zep's memory automatically provides relevant context
|
|
75
|
+
- **AutoGen Compatible**: Seamlessly integrates with AutoGen's memory interface
|
|
76
|
+
- **Async Support**: Full async/await support for modern applications
|
|
77
|
+
- **Type Safety**: Fully typed with comprehensive type hints
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
### Environment Variables
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Required: Your Zep Cloud API key
|
|
85
|
+
export ZEP_API_KEY="your-zep-api-key"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### ZepMemory Parameters
|
|
89
|
+
|
|
90
|
+
- `client` (AsyncZep): Your Zep client instance
|
|
91
|
+
- `user_id` (str): Unique identifier for the user
|
|
92
|
+
- `thread_id` (str, optional): Thread/conversation identifier
|
|
93
|
+
- `memory_type` (str, optional): Type of memory to use (default: "perpetual")
|
|
94
|
+
- `max_tokens` (int, optional): Maximum tokens to retrieve (default: 4000)
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
### Basic Usage
|
|
99
|
+
|
|
100
|
+
See [examples/autogen_basic.py](examples/autogen_basic.py) for a complete working example.
|
|
101
|
+
|
|
102
|
+
### Multi-Agent with Shared Memory
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Multiple agents can share the same memory context
|
|
106
|
+
shared_memory = ZepMemory(
|
|
107
|
+
client=zep_client,
|
|
108
|
+
user_id="team_project",
|
|
109
|
+
thread_id="brainstorm_session"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
researcher = AssistantAgent(
|
|
113
|
+
name="Researcher",
|
|
114
|
+
model_client=model_client,
|
|
115
|
+
memory=[shared_memory]
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
writer = AssistantAgent(
|
|
119
|
+
name="Writer",
|
|
120
|
+
model_client=model_client,
|
|
121
|
+
memory=[shared_memory]
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Requirements
|
|
126
|
+
|
|
127
|
+
- Python 3.10+
|
|
128
|
+
- `zep-cloud>=2.15.0`
|
|
129
|
+
- `autogen-agentchat>=0.6.1`
|
|
130
|
+
- `autogen-core>=0.6.1`
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
135
|
+
|
|
136
|
+
## Support
|
|
137
|
+
|
|
138
|
+
- [Zep Documentation](https://docs.getzep.com)
|
|
139
|
+
- [AutoGen Documentation](https://microsoft.github.io/autogen/)
|
|
140
|
+
- [GitHub Issues](https://github.com/getzep/zep/issues)
|
|
141
|
+
|
|
142
|
+
## Contributing
|
|
143
|
+
|
|
144
|
+
Contributions are welcome! Please see our [Contributing Guide](../../../CONTRIBUTING.md) for details.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Zep AutoGen Integration
|
|
2
|
+
|
|
3
|
+
A dedicated integration package that enables [Zep](https://getzep.com) to work seamlessly with [Microsoft AutoGen](https://github.com/microsoft/autogen) agents, providing persistent conversation memory and intelligent context retrieval.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install zep-autogen
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import asyncio
|
|
15
|
+
from zep_cloud.client import AsyncZep
|
|
16
|
+
from zep_autogen import ZepMemory
|
|
17
|
+
from autogen_agentchat.agents import AssistantAgent
|
|
18
|
+
from autogen_ext.models.openai import OpenAIChatCompletionClient
|
|
19
|
+
|
|
20
|
+
async def main():
|
|
21
|
+
# Initialize Zep client
|
|
22
|
+
zep_client = AsyncZep(api_key="your-zep-api-key")
|
|
23
|
+
|
|
24
|
+
# Create Zep memory for your agent
|
|
25
|
+
memory = ZepMemory(
|
|
26
|
+
client=zep_client,
|
|
27
|
+
user_id="user_123",
|
|
28
|
+
thread_id="conversation_456"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Create AutoGen agent with Zep memory
|
|
32
|
+
agent = AssistantAgent(
|
|
33
|
+
name="MemoryAwareAssistant",
|
|
34
|
+
model_client=OpenAIChatCompletionClient(model="gpt-4.1-mini"),
|
|
35
|
+
memory=[memory] # Add Zep memory to the agent
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Your agent now has persistent memory across conversations!
|
|
39
|
+
response = await agent.run(task="What's my name again?")
|
|
40
|
+
print(response.messages[-1].content)
|
|
41
|
+
|
|
42
|
+
asyncio.run(main())
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- **Persistent Memory**: Conversations persist across agent sessions
|
|
48
|
+
- **Intelligent Retrieval**: Zep's memory automatically provides relevant context
|
|
49
|
+
- **AutoGen Compatible**: Seamlessly integrates with AutoGen's memory interface
|
|
50
|
+
- **Async Support**: Full async/await support for modern applications
|
|
51
|
+
- **Type Safety**: Fully typed with comprehensive type hints
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
### Environment Variables
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Required: Your Zep Cloud API key
|
|
59
|
+
export ZEP_API_KEY="your-zep-api-key"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### ZepMemory Parameters
|
|
63
|
+
|
|
64
|
+
- `client` (AsyncZep): Your Zep client instance
|
|
65
|
+
- `user_id` (str): Unique identifier for the user
|
|
66
|
+
- `thread_id` (str, optional): Thread/conversation identifier
|
|
67
|
+
- `memory_type` (str, optional): Type of memory to use (default: "perpetual")
|
|
68
|
+
- `max_tokens` (int, optional): Maximum tokens to retrieve (default: 4000)
|
|
69
|
+
|
|
70
|
+
## Examples
|
|
71
|
+
|
|
72
|
+
### Basic Usage
|
|
73
|
+
|
|
74
|
+
See [examples/autogen_basic.py](examples/autogen_basic.py) for a complete working example.
|
|
75
|
+
|
|
76
|
+
### Multi-Agent with Shared Memory
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Multiple agents can share the same memory context
|
|
80
|
+
shared_memory = ZepMemory(
|
|
81
|
+
client=zep_client,
|
|
82
|
+
user_id="team_project",
|
|
83
|
+
thread_id="brainstorm_session"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
researcher = AssistantAgent(
|
|
87
|
+
name="Researcher",
|
|
88
|
+
model_client=model_client,
|
|
89
|
+
memory=[shared_memory]
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
writer = AssistantAgent(
|
|
93
|
+
name="Writer",
|
|
94
|
+
model_client=model_client,
|
|
95
|
+
memory=[shared_memory]
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Requirements
|
|
100
|
+
|
|
101
|
+
- Python 3.10+
|
|
102
|
+
- `zep-cloud>=2.15.0`
|
|
103
|
+
- `autogen-agentchat>=0.6.1`
|
|
104
|
+
- `autogen-core>=0.6.1`
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
109
|
+
|
|
110
|
+
## Support
|
|
111
|
+
|
|
112
|
+
- [Zep Documentation](https://docs.getzep.com)
|
|
113
|
+
- [AutoGen Documentation](https://microsoft.github.io/autogen/)
|
|
114
|
+
- [GitHub Issues](https://github.com/getzep/zep/issues)
|
|
115
|
+
|
|
116
|
+
## Contributing
|
|
117
|
+
|
|
118
|
+
Contributions are welcome! Please see our [Contributing Guide](../../../CONTRIBUTING.md) for details.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from autogen_agentchat.agents import AssistantAgent
|
|
6
|
+
from autogen_core.memory import MemoryContent, MemoryMimeType
|
|
7
|
+
from autogen_ext.models.openai import OpenAIChatCompletionClient
|
|
8
|
+
from zep_cloud.client import AsyncZep
|
|
9
|
+
|
|
10
|
+
from zep_autogen import ZepMemory
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def main():
|
|
14
|
+
# Initialize AsyncZep client
|
|
15
|
+
zep_client = AsyncZep(api_key=os.environ.get("ZEP_API_KEY"))
|
|
16
|
+
|
|
17
|
+
user_id = f"user_{uuid.uuid4().hex[:16]}"
|
|
18
|
+
thread_id = f"thread_{uuid.uuid4().hex[:16]}"
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
# Create user for the user (upfront initialization)
|
|
22
|
+
await zep_client.user.add(
|
|
23
|
+
user_id=user_id,
|
|
24
|
+
email="alice@agents.local",
|
|
25
|
+
first_name="Alice",
|
|
26
|
+
)
|
|
27
|
+
print(f"Created user: {user_id}")
|
|
28
|
+
except Exception as e:
|
|
29
|
+
print(f"User might already exist: {e}")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
# Create thread for this conversation
|
|
33
|
+
await zep_client.thread.create(thread_id=thread_id, user_id=user_id)
|
|
34
|
+
print(f"Created thread: {thread_id}")
|
|
35
|
+
except Exception as e:
|
|
36
|
+
print(f"Thread creation failed: {e}")
|
|
37
|
+
|
|
38
|
+
# Initialize Zep memory bound to the assistant
|
|
39
|
+
memory = ZepMemory(client=zep_client, thread_id=thread_id, user_id=user_id)
|
|
40
|
+
|
|
41
|
+
# Create assistant agent with Zep memory
|
|
42
|
+
agent = AssistantAgent(
|
|
43
|
+
name="MemoryAwareAssistant",
|
|
44
|
+
model_client=OpenAIChatCompletionClient(model="gpt-4.1-mini"),
|
|
45
|
+
memory=[memory],
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
print("Assistant ready with Zep memory!")
|
|
49
|
+
|
|
50
|
+
# Helper function to store individual messages in memory (AutoGen best practice)
|
|
51
|
+
async def add_message(message: str, role: str, name: str | None = None):
|
|
52
|
+
"""Store a single message in memory following AutoGen standards"""
|
|
53
|
+
metadata = {"type": "message", "role": role, "name": name}
|
|
54
|
+
|
|
55
|
+
await memory.add(
|
|
56
|
+
MemoryContent(content=message, mime_type=MemoryMimeType.TEXT, metadata=metadata)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Example conversation with proper memory management
|
|
60
|
+
try:
|
|
61
|
+
print("\n=== Starting conversation with memory persistence ===")
|
|
62
|
+
|
|
63
|
+
# First interaction
|
|
64
|
+
user_msg1 = "My name is Alice and I love hiking in the mountains."
|
|
65
|
+
print(f"User: {user_msg1}")
|
|
66
|
+
await add_message(user_msg1, "user")
|
|
67
|
+
response1 = await agent.run(task=user_msg1)
|
|
68
|
+
agent_msg1 = response1.messages[-1].content
|
|
69
|
+
print(f"Agent: {agent_msg1}")
|
|
70
|
+
|
|
71
|
+
await add_message(agent_msg1, "assistant")
|
|
72
|
+
|
|
73
|
+
# Second interaction - agent should remember Alice and her interests
|
|
74
|
+
user_msg2 = "What outdoor activities do you think I'd enjoy?"
|
|
75
|
+
print(f"\nUser: {user_msg2}")
|
|
76
|
+
response2 = await agent.run(task=user_msg2)
|
|
77
|
+
await add_message(user_msg2, "user")
|
|
78
|
+
agent_msg2 = response2.messages[-1].content
|
|
79
|
+
print(f"Agent: {agent_msg2}")
|
|
80
|
+
await add_message(agent_msg2, "assistant")
|
|
81
|
+
|
|
82
|
+
user_msg3 = "What's my name again?"
|
|
83
|
+
print(f"\nUser: {user_msg3}")
|
|
84
|
+
await add_message(user_msg3, "user")
|
|
85
|
+
response3 = await agent.run(task=user_msg3)
|
|
86
|
+
agent_msg3 = response3.messages[-1].content
|
|
87
|
+
print(f"Agent: {agent_msg3}")
|
|
88
|
+
|
|
89
|
+
await add_message(agent_msg3, "assistant")
|
|
90
|
+
|
|
91
|
+
print("\n=== Memory persistence test complete ===")
|
|
92
|
+
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"Error during conversation: {e}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "zep-autogen"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Autogen integration for Zep"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"autogen-agentchat>=0.6.1",
|
|
9
|
+
"autogen-ext[azure,openai]>=0.6.1",
|
|
10
|
+
"azure-identity>=1.23.0",
|
|
11
|
+
"aiohttp>=3.8.0",
|
|
12
|
+
"python-dotenv>=1.0.0",
|
|
13
|
+
"python-slugify>=8.0.4",
|
|
14
|
+
"rich>=14.0.0",
|
|
15
|
+
"zep-cloud>=3.0.0rc1",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://github.com/getzep/zep"
|
|
20
|
+
Documentation = "https://docs.getzep.com/integrations/autogen"
|
|
21
|
+
Repository = "https://github.com/getzep/zep"
|
|
22
|
+
"Bug Tracker" = "https://github.com/getzep/zep/issues"
|
|
23
|
+
Changelog = "https://github.com/getzep/zep/blob/main/integrations/python/autogen/CHANGELOG.md"
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
dev = [
|
|
27
|
+
"pytest>=7.0.0",
|
|
28
|
+
"pytest-cov",
|
|
29
|
+
"pytest-asyncio",
|
|
30
|
+
"ruff>=0.1.0",
|
|
31
|
+
"mypy>=1.0.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/zep_autogen"]
|
|
40
|
+
|
|
41
|
+
[tool.pytest.ini_options]
|
|
42
|
+
testpaths = ["tests"]
|
|
43
|
+
python_files = ["test_*.py"]
|
|
44
|
+
python_classes = ["Test*"]
|
|
45
|
+
python_functions = ["test_*"]
|
|
46
|
+
markers = [
|
|
47
|
+
"integration: marks tests as integration tests (deselect with '-m \"not integration\"')"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
target-version = "py310"
|
|
52
|
+
line-length = 100
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = [
|
|
56
|
+
"E", # pycodestyle errors
|
|
57
|
+
"W", # pycodestyle warnings
|
|
58
|
+
"F", # pyflakes
|
|
59
|
+
"I", # isort
|
|
60
|
+
"B", # flake8-bugbear
|
|
61
|
+
"C4", # flake8-comprehensions
|
|
62
|
+
"UP", # pyupgrade
|
|
63
|
+
]
|
|
64
|
+
ignore = [
|
|
65
|
+
"E501", # line too long, handled by formatter
|
|
66
|
+
"B008", # do not perform function calls in argument defaults
|
|
67
|
+
"C901", # too complex
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[tool.ruff.lint.isort]
|
|
71
|
+
known-first-party = ["zep_autogen"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.format]
|
|
74
|
+
quote-style = "double"
|
|
75
|
+
indent-style = "space"
|
|
76
|
+
skip-magic-trailing-comma = false
|
|
77
|
+
line-ending = "auto"
|
|
78
|
+
|
|
79
|
+
[tool.mypy]
|
|
80
|
+
python_version = "3.10"
|
|
81
|
+
warn_return_any = true
|
|
82
|
+
warn_unused_configs = true
|
|
83
|
+
disallow_untyped_defs = true
|
|
84
|
+
disallow_incomplete_defs = true
|
|
85
|
+
check_untyped_defs = true
|
|
86
|
+
|
|
87
|
+
[[tool.mypy.overrides]]
|
|
88
|
+
module = [
|
|
89
|
+
"autogen_core.*",
|
|
90
|
+
"autogen_agentchat.*",
|
|
91
|
+
"autogen_ext.*",
|
|
92
|
+
"zep_cloud.*",
|
|
93
|
+
]
|
|
94
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Zep AutoGen Integration.
|
|
3
|
+
|
|
4
|
+
This package provides memory integration between Zep and Microsoft AutoGen agents,
|
|
5
|
+
enabling persistent conversation memory and context retrieval.
|
|
6
|
+
|
|
7
|
+
Installation:
|
|
8
|
+
pip install zep-autogen
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from zep_autogen import ZepMemory
|
|
12
|
+
from zep_cloud.client import AsyncZep
|
|
13
|
+
from autogen_agentchat.agents import AssistantAgent
|
|
14
|
+
|
|
15
|
+
# Initialize Zep client and memory
|
|
16
|
+
zep_client = AsyncZep(api_key="your-api-key")
|
|
17
|
+
memory = ZepMemory(client=zep_client, user_id="user123")
|
|
18
|
+
|
|
19
|
+
# Create agent with Zep memory
|
|
20
|
+
agent = AssistantAgent(
|
|
21
|
+
name="assistant",
|
|
22
|
+
model_client=model_client,
|
|
23
|
+
memory=[memory]
|
|
24
|
+
)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
__author__ = "Zep AI"
|
|
29
|
+
__description__ = "Zep integration for Microsoft AutoGen"
|
|
30
|
+
|
|
31
|
+
from .exceptions import ZepDependencyError
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
# Check for required AutoGen dependencies - just test import
|
|
35
|
+
import autogen_core.memory # noqa: F401
|
|
36
|
+
import autogen_core.model_context # noqa: F401
|
|
37
|
+
|
|
38
|
+
# Import our integration
|
|
39
|
+
from .memory import ZepMemory
|
|
40
|
+
|
|
41
|
+
__all__ = ["ZepMemory", "ZepDependencyError"]
|
|
42
|
+
|
|
43
|
+
except ImportError as e:
|
|
44
|
+
raise ZepDependencyError(framework="AutoGen", install_command="pip install zep-autogen") from e
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exception classes for AutoGen integration.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ZepDependencyError(ImportError):
|
|
7
|
+
"""Raised when required AutoGen 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,304 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Zep Memory integration for AutoGen.
|
|
3
|
+
|
|
4
|
+
This module provides memory classes that integrate Zep with AutoGen's memory system.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import uuid
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from autogen_core import CancellationToken
|
|
12
|
+
from autogen_core.memory import (
|
|
13
|
+
Memory,
|
|
14
|
+
MemoryContent,
|
|
15
|
+
MemoryMimeType,
|
|
16
|
+
MemoryQueryResult,
|
|
17
|
+
UpdateContextResult,
|
|
18
|
+
)
|
|
19
|
+
from autogen_core.model_context import ChatCompletionContext
|
|
20
|
+
from autogen_core.models import SystemMessage
|
|
21
|
+
from zep_cloud.client import AsyncZep
|
|
22
|
+
from zep_cloud.types import Message
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ZepMemory(Memory):
|
|
26
|
+
"""
|
|
27
|
+
A memory implementation that integrates with Zep for persistent storage
|
|
28
|
+
and retrieval of conversation context and agent memories.
|
|
29
|
+
|
|
30
|
+
This class implements AutoGen's Memory interface and provides:
|
|
31
|
+
- Automatic context injection via update_context()
|
|
32
|
+
- Manual memory queries via query()
|
|
33
|
+
- Message storage in Zep threads
|
|
34
|
+
- Data storage in Zep user graphs
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self, client: AsyncZep, user_id: str, thread_id: str | None = None, **kwargs: Any
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Initialize ZepMemory with an AsyncZep client instance.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
client: An initialized AsyncZep instance
|
|
45
|
+
user_id: User ID for memory isolation (required)
|
|
46
|
+
thread_id: Optional thread ID. If not provided, will be created automatically
|
|
47
|
+
**kwargs: Additional configuration options
|
|
48
|
+
"""
|
|
49
|
+
if not isinstance(client, AsyncZep):
|
|
50
|
+
raise TypeError("client must be an instance of AsyncZep")
|
|
51
|
+
|
|
52
|
+
if not user_id:
|
|
53
|
+
raise ValueError("user_id is required")
|
|
54
|
+
|
|
55
|
+
self._client = client
|
|
56
|
+
self._user_id = user_id
|
|
57
|
+
self._thread_id = thread_id
|
|
58
|
+
self._config = kwargs
|
|
59
|
+
|
|
60
|
+
# Set up module logger
|
|
61
|
+
self._logger = logging.getLogger(__name__)
|
|
62
|
+
|
|
63
|
+
async def add(
|
|
64
|
+
self, content: MemoryContent, cancellation_token: CancellationToken | None = None
|
|
65
|
+
) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Add a memory entry to Zep storage.
|
|
68
|
+
|
|
69
|
+
Uses metadata.type to determine storage method:
|
|
70
|
+
- type="message": stores as message in thread using thread.add_messages
|
|
71
|
+
- type="data": stores as data in user's graph using graph.add (maps mime type to data type)
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
entry: The memory content to store
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValueError: If the memory content mime type or metadata type is not supported
|
|
78
|
+
"""
|
|
79
|
+
# Validate mime type - only support TEXT, MARKDOWN, and JSON
|
|
80
|
+
supported_mime_types = {MemoryMimeType.TEXT, MemoryMimeType.MARKDOWN, MemoryMimeType.JSON}
|
|
81
|
+
|
|
82
|
+
if content.mime_type not in supported_mime_types:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Unsupported mime type: {content.mime_type}. "
|
|
85
|
+
f"ZepMemory only supports: {', '.join(str(mt) for mt in supported_mime_types)}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Extract metadata
|
|
89
|
+
metadata_copy = content.metadata.copy() if content.metadata else {}
|
|
90
|
+
content_type = metadata_copy.get("type", "data") # Default to "data" if no type specified
|
|
91
|
+
|
|
92
|
+
if content_type == "message":
|
|
93
|
+
if self._thread_id:
|
|
94
|
+
# Ensure thread exists
|
|
95
|
+
await self._client.thread.get(self._thread_id)
|
|
96
|
+
|
|
97
|
+
if not self._thread_id:
|
|
98
|
+
self._thread_id = f"thread_{uuid.uuid4().hex[:16]}"
|
|
99
|
+
await self._client.thread.create(thread_id=self._thread_id, user_id=self._user_id)
|
|
100
|
+
# Store as message in thread session
|
|
101
|
+
role = metadata_copy.get("role", "user")
|
|
102
|
+
name = metadata_copy.get("name")
|
|
103
|
+
|
|
104
|
+
message = Message(name=name, content=str(content.content), role=role)
|
|
105
|
+
|
|
106
|
+
# Add message to user's thread in Zep
|
|
107
|
+
await self._client.thread.add_messages(thread_id=self._thread_id, messages=[message])
|
|
108
|
+
|
|
109
|
+
elif content_type == "data":
|
|
110
|
+
# Store as data in the user's graph - map mime type to Zep data type
|
|
111
|
+
mime_to_data_type: dict[MemoryMimeType, str] = {
|
|
112
|
+
MemoryMimeType.TEXT: "text",
|
|
113
|
+
MemoryMimeType.MARKDOWN: "text",
|
|
114
|
+
MemoryMimeType.JSON: "json",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Safely get the data type, handling both MemoryMimeType and string
|
|
118
|
+
if isinstance(content.mime_type, MemoryMimeType):
|
|
119
|
+
data_type = mime_to_data_type.get(content.mime_type, "text")
|
|
120
|
+
else:
|
|
121
|
+
data_type = "text" # Default for string or unknown types
|
|
122
|
+
|
|
123
|
+
# Add data to user's graph
|
|
124
|
+
await self._client.graph.add(
|
|
125
|
+
user_id=self._user_id, type=data_type, data=str(content.content)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
else:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
f"Unsupported metadata type: {content_type}. Supported types: 'message', 'data'"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
async def query(
|
|
134
|
+
self,
|
|
135
|
+
query: str | MemoryContent,
|
|
136
|
+
cancellation_token: CancellationToken | None = None,
|
|
137
|
+
**kwargs: Any,
|
|
138
|
+
) -> MemoryQueryResult:
|
|
139
|
+
"""
|
|
140
|
+
Query memories from Zep storage using graph.search.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
query: Search query string or MemoryContent
|
|
144
|
+
cancellation_token: Optional cancellation token
|
|
145
|
+
**kwargs: Additional query parameters
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
MemoryQueryResult containing matching memories
|
|
149
|
+
"""
|
|
150
|
+
# Convert query to string if it's MemoryContent
|
|
151
|
+
if isinstance(query, MemoryContent):
|
|
152
|
+
query_str = str(query.content)
|
|
153
|
+
else:
|
|
154
|
+
query_str = query
|
|
155
|
+
|
|
156
|
+
# Extract limit from kwargs for backward compatibility
|
|
157
|
+
limit = kwargs.pop("limit", 5)
|
|
158
|
+
|
|
159
|
+
results = []
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
# Search the user's graph
|
|
163
|
+
graph_results = await self._client.graph.search(
|
|
164
|
+
user_id=self._user_id, query=query_str, limit=limit, **kwargs
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Add graph search results
|
|
168
|
+
if graph_results.edges:
|
|
169
|
+
for edge in graph_results.edges:
|
|
170
|
+
results.append(
|
|
171
|
+
MemoryContent(
|
|
172
|
+
content=edge.fact,
|
|
173
|
+
mime_type=MemoryMimeType.TEXT,
|
|
174
|
+
metadata={
|
|
175
|
+
"source": "user_graph",
|
|
176
|
+
"edge_name": edge.name,
|
|
177
|
+
"edge_attributes": edge.attributes or {},
|
|
178
|
+
"created_at": edge.created_at,
|
|
179
|
+
"expired_at": edge.expired_at,
|
|
180
|
+
"valid_at": edge.valid_at,
|
|
181
|
+
"invalid_at": edge.invalid_at,
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
if graph_results.nodes:
|
|
186
|
+
for node in graph_results.nodes:
|
|
187
|
+
results.append(
|
|
188
|
+
MemoryContent(
|
|
189
|
+
content=f"{node.name}:\n {node.summary}",
|
|
190
|
+
mime_type=MemoryMimeType.TEXT,
|
|
191
|
+
metadata={
|
|
192
|
+
"source": "user_graph",
|
|
193
|
+
"node_name": node.name,
|
|
194
|
+
"node_attributes": node.attributes or {},
|
|
195
|
+
"created_at": node.created_at,
|
|
196
|
+
},
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
if graph_results.episodes:
|
|
200
|
+
for episode in graph_results.episodes:
|
|
201
|
+
results.append(
|
|
202
|
+
MemoryContent(
|
|
203
|
+
content=episode.content,
|
|
204
|
+
mime_type=MemoryMimeType.TEXT,
|
|
205
|
+
metadata={
|
|
206
|
+
"source": "user_graph",
|
|
207
|
+
"episode_type": episode.source,
|
|
208
|
+
"episode_role": episode.role_type,
|
|
209
|
+
"episode_name": episode.role,
|
|
210
|
+
"created_at": episode.created_at,
|
|
211
|
+
},
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
except Exception as e:
|
|
215
|
+
# Log error but don't fail completely
|
|
216
|
+
self._logger.error(f"Error querying Zep memory: {e}")
|
|
217
|
+
|
|
218
|
+
return MemoryQueryResult(results=results)
|
|
219
|
+
|
|
220
|
+
async def update_context(self, model_context: ChatCompletionContext) -> UpdateContextResult:
|
|
221
|
+
"""
|
|
222
|
+
Update the agent's model context with retrieved memories.
|
|
223
|
+
|
|
224
|
+
Gets memory from Zep, and if memory exists, includes up to 10 last messages
|
|
225
|
+
from history and adds the memory context as a system message.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
model_context: The model context to update
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
UpdateContextResult with the memories that were retrieved
|
|
232
|
+
"""
|
|
233
|
+
try:
|
|
234
|
+
# Get messages from current context
|
|
235
|
+
messages = await model_context.get_messages()
|
|
236
|
+
if not messages:
|
|
237
|
+
return UpdateContextResult(memories=MemoryQueryResult(results=[]))
|
|
238
|
+
|
|
239
|
+
# Get memory from Zep session
|
|
240
|
+
if not self._thread_id:
|
|
241
|
+
return UpdateContextResult(memories=MemoryQueryResult(results=[]))
|
|
242
|
+
memory_result = await self._client.thread.get_user_context(thread_id=self._thread_id)
|
|
243
|
+
|
|
244
|
+
memory_contents = []
|
|
245
|
+
memory_parts = []
|
|
246
|
+
|
|
247
|
+
# If we have memory context, include it
|
|
248
|
+
if memory_result.context:
|
|
249
|
+
memory_contents.append(
|
|
250
|
+
MemoryContent(
|
|
251
|
+
content=memory_result.context,
|
|
252
|
+
mime_type=MemoryMimeType.TEXT,
|
|
253
|
+
metadata={"source": "thread_context"},
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
memory_parts.append(f"Memory context: {memory_result.context}")
|
|
257
|
+
|
|
258
|
+
# Only include recent messages if we have memory
|
|
259
|
+
if memory_result.messages:
|
|
260
|
+
recent_messages = memory_result.messages[-10:] # Get last 10 messages
|
|
261
|
+
if recent_messages:
|
|
262
|
+
message_history = []
|
|
263
|
+
for msg in recent_messages:
|
|
264
|
+
message_history.append(f"{msg.role}: {msg.content}")
|
|
265
|
+
memory_parts.append("Recent conversation:\n" + "\n".join(message_history))
|
|
266
|
+
|
|
267
|
+
# If we have memory parts, add them to the context as a system message
|
|
268
|
+
if memory_parts:
|
|
269
|
+
memory_context = "\n\n".join(memory_parts)
|
|
270
|
+
await model_context.add_message(SystemMessage(content=memory_context))
|
|
271
|
+
|
|
272
|
+
return UpdateContextResult(memories=MemoryQueryResult(results=memory_contents))
|
|
273
|
+
|
|
274
|
+
except Exception as e:
|
|
275
|
+
# Log error but don't fail completely
|
|
276
|
+
self._logger.error(f"Error updating context with Zep memory: {e}")
|
|
277
|
+
return UpdateContextResult(memories=MemoryQueryResult(results=[]))
|
|
278
|
+
|
|
279
|
+
async def clear(self) -> None:
|
|
280
|
+
"""
|
|
281
|
+
Clear all memories from Zep storage by deleting the session.
|
|
282
|
+
|
|
283
|
+
This will delete the entire session and all its messages.
|
|
284
|
+
Note: This operation cannot be undone.
|
|
285
|
+
"""
|
|
286
|
+
try:
|
|
287
|
+
# Delete the session - this clears all messages and memory for this session
|
|
288
|
+
if self._thread_id:
|
|
289
|
+
await self._client.thread.delete(thread_id=self._thread_id)
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
self._logger.error(f"Error clearing Zep memory: {e}")
|
|
293
|
+
raise
|
|
294
|
+
|
|
295
|
+
async def close(self) -> None:
|
|
296
|
+
"""
|
|
297
|
+
Clean up Zep client resources.
|
|
298
|
+
|
|
299
|
+
Note: This method does not close the AsyncZep instance since it was
|
|
300
|
+
provided externally. The caller is responsible for managing the client lifecycle.
|
|
301
|
+
"""
|
|
302
|
+
# The client was provided externally, so we don't close it here
|
|
303
|
+
# The caller is responsible for closing the client when appropriate
|
|
304
|
+
pass
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Basic tests for the zep-integrations package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from autogen_core.memory import MemoryContent, MemoryMimeType
|
|
10
|
+
|
|
11
|
+
from zep_autogen import ZepMemory
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_package_import():
|
|
15
|
+
"""Test that the package can be imported successfully."""
|
|
16
|
+
import zep_autogen
|
|
17
|
+
|
|
18
|
+
assert zep_autogen is not None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_zep_memory_import():
|
|
22
|
+
"""Test that ZepMemory can be imported successfully."""
|
|
23
|
+
assert ZepMemory is not None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestBasicFunctionality:
|
|
27
|
+
"""Basic functionality tests for the zep-autogen package."""
|
|
28
|
+
|
|
29
|
+
def test_package_structure(self):
|
|
30
|
+
"""Test that the package has the expected structure."""
|
|
31
|
+
import zep_autogen
|
|
32
|
+
|
|
33
|
+
assert hasattr(zep_autogen, "__version__")
|
|
34
|
+
assert hasattr(zep_autogen, "__author__")
|
|
35
|
+
assert hasattr(zep_autogen, "__description__")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestZepMemoryMock:
|
|
39
|
+
"""Test ZepMemory with mock clients."""
|
|
40
|
+
|
|
41
|
+
def test_zep_memory_initialization_with_mock(self):
|
|
42
|
+
"""Test that ZepMemory can be initialized with a mock client."""
|
|
43
|
+
try:
|
|
44
|
+
from zep_cloud.client import AsyncZep
|
|
45
|
+
|
|
46
|
+
# Create a mock AsyncZep client
|
|
47
|
+
mock_client = MagicMock(spec=AsyncZep)
|
|
48
|
+
memory = ZepMemory(client=mock_client, thread_id="test-session", user_id="test-user")
|
|
49
|
+
assert memory is not None
|
|
50
|
+
assert memory._client is mock_client
|
|
51
|
+
assert memory._thread_id == "test-session"
|
|
52
|
+
assert memory._user_id == "test-user"
|
|
53
|
+
|
|
54
|
+
except ImportError:
|
|
55
|
+
# If zep_cloud is not available, test with a generic mock
|
|
56
|
+
class MockAsyncZep:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
mock_client = MockAsyncZep()
|
|
60
|
+
with pytest.raises(TypeError, match="client must be an instance of AsyncZep"):
|
|
61
|
+
ZepMemory(client=mock_client, thread_id="test-session")
|
|
62
|
+
|
|
63
|
+
def test_zep_memory_requires_session_id(self):
|
|
64
|
+
"""Test that ZepMemory requires a session_id."""
|
|
65
|
+
try:
|
|
66
|
+
from zep_cloud.client import AsyncZep
|
|
67
|
+
|
|
68
|
+
mock_client = MagicMock(spec=AsyncZep)
|
|
69
|
+
|
|
70
|
+
with pytest.raises(ValueError, match="user_id is required"):
|
|
71
|
+
ZepMemory(client=mock_client, user_id="")
|
|
72
|
+
|
|
73
|
+
except ImportError:
|
|
74
|
+
pytest.skip("zep_cloud not available")
|
|
75
|
+
|
|
76
|
+
def test_zep_memory_requires_async_zep_client(self):
|
|
77
|
+
"""Test that ZepMemory raises TypeError when client is not AsyncZep."""
|
|
78
|
+
with pytest.raises(TypeError, match="client must be an instance of AsyncZep"):
|
|
79
|
+
ZepMemory(client="not_a_client", user_id="test-user")
|
|
80
|
+
|
|
81
|
+
@pytest.mark.asyncio
|
|
82
|
+
async def test_zep_memory_add_message_with_mock(self):
|
|
83
|
+
"""Test adding memory as message (with user_id) with a mock client."""
|
|
84
|
+
try:
|
|
85
|
+
from zep_cloud.client import AsyncZep
|
|
86
|
+
|
|
87
|
+
mock_client = MagicMock(spec=AsyncZep)
|
|
88
|
+
mock_client.thread = MagicMock()
|
|
89
|
+
mock_client.thread.get = AsyncMock()
|
|
90
|
+
mock_client.thread.add_messages = AsyncMock()
|
|
91
|
+
|
|
92
|
+
memory = ZepMemory(client=mock_client, user_id="test-user", thread_id="test-session")
|
|
93
|
+
|
|
94
|
+
# Test adding memory content as message type
|
|
95
|
+
content = MemoryContent(
|
|
96
|
+
content="Test message",
|
|
97
|
+
mime_type=MemoryMimeType.TEXT,
|
|
98
|
+
metadata={"type": "message", "role": "user", "name": "user123"},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
await memory.add(content)
|
|
102
|
+
|
|
103
|
+
# Verify the thread.add_messages mock was called
|
|
104
|
+
mock_client.thread.add_messages.assert_called_once()
|
|
105
|
+
|
|
106
|
+
except ImportError:
|
|
107
|
+
pytest.skip("zep_cloud not available")
|
|
108
|
+
|
|
109
|
+
@pytest.mark.asyncio
|
|
110
|
+
async def test_zep_memory_add_graph_data_with_mock(self):
|
|
111
|
+
"""Test adding memory as graph data (without user_id) with a mock client."""
|
|
112
|
+
try:
|
|
113
|
+
from zep_cloud.client import AsyncZep
|
|
114
|
+
|
|
115
|
+
mock_client = MagicMock(spec=AsyncZep)
|
|
116
|
+
mock_client.graph = MagicMock()
|
|
117
|
+
mock_client.graph.add = AsyncMock()
|
|
118
|
+
|
|
119
|
+
memory = ZepMemory(
|
|
120
|
+
client=mock_client,
|
|
121
|
+
thread_id="test-session",
|
|
122
|
+
user_id="test-user", # Required for graph data
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Test adding memory content without user_id (should store as graph data)
|
|
126
|
+
content = MemoryContent(
|
|
127
|
+
content="Test data for graph",
|
|
128
|
+
mime_type=MemoryMimeType.TEXT,
|
|
129
|
+
metadata={"category": "facts"}, # No user_id
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
await memory.add(content)
|
|
133
|
+
|
|
134
|
+
# Verify the graph.add mock was called
|
|
135
|
+
mock_client.graph.add.assert_called_once()
|
|
136
|
+
|
|
137
|
+
except ImportError:
|
|
138
|
+
pytest.skip("zep_cloud not available")
|
|
139
|
+
|
|
140
|
+
@pytest.mark.asyncio
|
|
141
|
+
async def test_zep_memory_add_graph_data_requires_user_id(self):
|
|
142
|
+
"""Test that adding graph data requires user_id."""
|
|
143
|
+
try:
|
|
144
|
+
from zep_cloud.client import AsyncZep
|
|
145
|
+
|
|
146
|
+
mock_client = MagicMock(spec=AsyncZep)
|
|
147
|
+
mock_client.graph = MagicMock()
|
|
148
|
+
mock_client.graph.add = AsyncMock()
|
|
149
|
+
|
|
150
|
+
memory = ZepMemory(
|
|
151
|
+
client=mock_client,
|
|
152
|
+
user_id="test-user", # user_id is now required
|
|
153
|
+
thread_id="test-session",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Test adding memory content with unsupported type
|
|
157
|
+
content = MemoryContent(
|
|
158
|
+
content="Test data for graph",
|
|
159
|
+
mime_type=MemoryMimeType.TEXT,
|
|
160
|
+
metadata={"type": "unsupported", "category": "facts"},
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
with pytest.raises(ValueError, match="Unsupported metadata type"):
|
|
164
|
+
await memory.add(content)
|
|
165
|
+
|
|
166
|
+
except ImportError:
|
|
167
|
+
pytest.skip("zep_cloud not available")
|
|
168
|
+
|
|
169
|
+
@pytest.mark.asyncio
|
|
170
|
+
async def test_zep_memory_query_with_mock(self):
|
|
171
|
+
"""Test querying memory with a mock client."""
|
|
172
|
+
try:
|
|
173
|
+
from zep_cloud.client import AsyncZep
|
|
174
|
+
|
|
175
|
+
mock_client = MagicMock(spec=AsyncZep)
|
|
176
|
+
mock_client.graph = MagicMock()
|
|
177
|
+
mock_client.graph.search = AsyncMock()
|
|
178
|
+
|
|
179
|
+
# Mock graph search response
|
|
180
|
+
mock_graph_response = MagicMock()
|
|
181
|
+
mock_graph_response.edges = []
|
|
182
|
+
mock_graph_response.nodes = []
|
|
183
|
+
mock_graph_response.episodes = []
|
|
184
|
+
mock_client.graph.search.return_value = mock_graph_response
|
|
185
|
+
|
|
186
|
+
memory = ZepMemory(client=mock_client, user_id="test-user", thread_id="test-session")
|
|
187
|
+
|
|
188
|
+
results = await memory.query("test query")
|
|
189
|
+
|
|
190
|
+
# Verify the mock was called and results returned
|
|
191
|
+
mock_client.graph.search.assert_called_once()
|
|
192
|
+
assert hasattr(results, "results") # Should be a MemoryQueryResult
|
|
193
|
+
assert len(results.results) >= 0 # Should return some results
|
|
194
|
+
|
|
195
|
+
except ImportError:
|
|
196
|
+
pytest.skip("zep_cloud not available")
|
|
197
|
+
|
|
198
|
+
@pytest.mark.asyncio
|
|
199
|
+
async def test_zep_memory_mime_type_validation(self):
|
|
200
|
+
"""Test that ZepMemory validates mime types correctly."""
|
|
201
|
+
try:
|
|
202
|
+
from zep_cloud.client import AsyncZep
|
|
203
|
+
|
|
204
|
+
mock_client = MagicMock(spec=AsyncZep)
|
|
205
|
+
memory = ZepMemory(client=mock_client, user_id="test-user", thread_id="test-session")
|
|
206
|
+
|
|
207
|
+
# Test supported mime types - these should work (with user_id for message storage)
|
|
208
|
+
supported_types = [MemoryMimeType.TEXT, MemoryMimeType.MARKDOWN, MemoryMimeType.JSON]
|
|
209
|
+
|
|
210
|
+
mock_client.thread = MagicMock()
|
|
211
|
+
mock_client.thread.get = AsyncMock()
|
|
212
|
+
mock_client.thread.add_messages = AsyncMock()
|
|
213
|
+
|
|
214
|
+
for mime_type in supported_types:
|
|
215
|
+
content = MemoryContent(
|
|
216
|
+
content="Test content",
|
|
217
|
+
mime_type=mime_type,
|
|
218
|
+
metadata={
|
|
219
|
+
"type": "message",
|
|
220
|
+
"role": "user",
|
|
221
|
+
"name": "user123",
|
|
222
|
+
"category": "test",
|
|
223
|
+
}, # Add type for message storage
|
|
224
|
+
)
|
|
225
|
+
# Should not raise an exception
|
|
226
|
+
await memory.add(content)
|
|
227
|
+
|
|
228
|
+
# Test unsupported mime types - these should raise ValueError
|
|
229
|
+
unsupported_types = [MemoryMimeType.IMAGE, MemoryMimeType.BINARY]
|
|
230
|
+
|
|
231
|
+
for mime_type in unsupported_types:
|
|
232
|
+
unsupported_content = MemoryContent(
|
|
233
|
+
content="Test content",
|
|
234
|
+
mime_type=mime_type,
|
|
235
|
+
metadata={"user_id": "user123", "category": "test"}, # Add user_id
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
with pytest.raises(ValueError, match="Unsupported mime type"):
|
|
239
|
+
await memory.add(unsupported_content)
|
|
240
|
+
|
|
241
|
+
except ImportError:
|
|
242
|
+
pytest.skip("zep_cloud not available")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@pytest.mark.integration
|
|
246
|
+
class TestZepMemoryReal:
|
|
247
|
+
"""Integration tests with real Zep client (only run if ZEP_API_KEY is available)."""
|
|
248
|
+
|
|
249
|
+
@pytest.fixture
|
|
250
|
+
def zep_client(self):
|
|
251
|
+
"""Create a real Zep client if API key is available."""
|
|
252
|
+
api_key = os.environ.get("ZEP_API_KEY")
|
|
253
|
+
if not api_key:
|
|
254
|
+
pytest.skip("ZEP_API_KEY not set - skipping integration tests")
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
from zep_cloud.client import AsyncZep
|
|
258
|
+
|
|
259
|
+
return AsyncZep(api_key=api_key)
|
|
260
|
+
except ImportError:
|
|
261
|
+
pytest.skip("zep_cloud not available")
|
|
262
|
+
|
|
263
|
+
@pytest.mark.asyncio
|
|
264
|
+
async def test_zep_memory_real_client_initialization(self, zep_client):
|
|
265
|
+
"""Test ZepMemory with a real Zep client."""
|
|
266
|
+
memory = ZepMemory(client=zep_client, thread_id="test-session-123", user_id="test-user-123")
|
|
267
|
+
assert memory is not None
|
|
268
|
+
assert memory._thread_id == "test-session-123"
|
|
269
|
+
assert memory._user_id == "test-user-123"
|