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