mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-17 12:20:04 +00:00
## Summary This PR introduces comprehensive Supermemory integration for the Microsoft Agent Framework, providing three complementary approaches to add persistent memory capabilities to agents: middleware for automatic memory injection, context providers for session-based memory management, and tools for explicit memory operations. ## Key Changes - **SupermemoryChatMiddleware**: Automatic memory injection middleware that fetches relevant memories from Supermemory before LLM calls and optionally saves conversations. Supports three modes: - `"profile"`: Injects all static and dynamic profile memories - `"query"`: Searches for memories relevant to the current user message - `"full"`: Combines both profile and query modes - **SupermemoryContextProvider**: Idiomatic context provider following the Agent Framework pattern (similar to built-in Mem0 integration). Integrates with the session pipeline via `before_run()` and `after_run()` hooks for automatic memory retrieval and storage. - **SupermemoryTools**: FunctionTool-compatible tools that agents can use for explicit memory operations: - `search_memories()`: Search for specific memories - `add_memory()`: Add new memories - `get_profile()`: Retrieve user profile - **Utility Functions**: Helper functions for: - Memory deduplication across static, dynamic, and search result sources - Profile-to-markdown conversion for LLM consumption - Message extraction and conversation formatting - Logging with configurable verbosity - **Exception Hierarchy**: Custom exceptions for better error handling: - `SupermemoryConfigurationError`: Missing/invalid configuration - `SupermemoryAPIError`: API request failures - `SupermemoryNetworkError`: Network connectivity issues - `SupermemoryMemoryOperationError`: Memory operation failures - **Comprehensive Documentation**: README with quick start examples, configuration options, and API reference for all three integration approaches. - **Test Suite**: Unit tests covering middleware, context provider, tools, and utility functions with proper mocking and error scenarios. ## Implementation Details - Supports both async (aiohttp) and sync (requests) HTTP clients with automatic fallback - Handles multiple message formats (dict, objects with attributes, content arrays) - Configurable memory storage with optional conversation grouping via `conversation_id` - Environment variable fallback for API key configuration (`SUPERMEMORY_API_KEY`) - Background task management for non-blocking memory operations in middleware - Proper async/sync compatibility for the Supermemory SDK https://claude.ai/code/session_012idB5y6UGK3zmeFULgTc4z
125 lines
4 KiB
Python
125 lines
4 KiB
Python
"""Tests for Supermemory context provider."""
|
|
|
|
import pytest
|
|
|
|
from supermemory_agent_framework import AgentSupermemory, SupermemoryContextProvider
|
|
|
|
|
|
def _make_conn(**kwargs):
|
|
kwargs.setdefault("api_key", "test-key")
|
|
kwargs.setdefault("container_tag", "user-123")
|
|
return AgentSupermemory(**kwargs)
|
|
|
|
|
|
class TestContextProviderConfiguration:
|
|
def test_accepts_connection(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
assert provider._container_tag == "user-123"
|
|
assert provider.source_id == "supermemory"
|
|
|
|
def test_uses_connection_client(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
assert provider._client is conn.client
|
|
|
|
def test_custom_source_id(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(
|
|
conn, source_id="custom-source"
|
|
)
|
|
assert provider.source_id == "custom-source"
|
|
|
|
def test_default_mode(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
assert provider._mode == "full"
|
|
|
|
def test_custom_mode(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn, mode="profile")
|
|
assert provider._mode == "profile"
|
|
|
|
def test_store_conversations_default(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
assert provider._store_conversations is False
|
|
|
|
def test_conversation_id_from_connection(self) -> None:
|
|
conn = _make_conn(conversation_id="conv-xyz")
|
|
provider = SupermemoryContextProvider(conn)
|
|
assert provider._connection.conversation_id == "conv-xyz"
|
|
assert provider._connection.custom_id == "conversation_conv-xyz"
|
|
|
|
def test_entity_context_from_connection(self) -> None:
|
|
conn = _make_conn(entity_context="User prefers TypeScript")
|
|
provider = SupermemoryContextProvider(conn)
|
|
assert provider._connection.entity_context == "User prefers TypeScript"
|
|
|
|
|
|
class TestExtractQuery:
|
|
def test_dict_messages(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
|
|
class MockContext:
|
|
input_messages = [
|
|
{"role": "user", "content": "Hello!"},
|
|
{"role": "assistant", "content": "Hi!"},
|
|
{"role": "user", "content": "How are you?"},
|
|
]
|
|
|
|
result = provider._extract_query_from_context(MockContext())
|
|
assert result == "How are you?"
|
|
|
|
def test_empty_messages(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
|
|
class MockContext:
|
|
input_messages = []
|
|
|
|
result = provider._extract_query_from_context(MockContext())
|
|
assert result == ""
|
|
|
|
def test_no_messages_attr(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
|
|
class MockContext:
|
|
pass
|
|
|
|
result = provider._extract_query_from_context(MockContext())
|
|
assert result == ""
|
|
|
|
|
|
class TestExtractConversation:
|
|
def test_basic_conversation(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
|
|
class MockContext:
|
|
input_messages = [
|
|
{"role": "user", "content": "Hello!"},
|
|
]
|
|
response = None
|
|
|
|
result = provider._extract_conversation_from_context(MockContext())
|
|
assert "User: Hello!" in result
|
|
|
|
def test_with_response(self) -> None:
|
|
conn = _make_conn()
|
|
provider = SupermemoryContextProvider(conn)
|
|
|
|
class MockResponse:
|
|
text = "Hi there!"
|
|
|
|
class MockContext:
|
|
input_messages = [
|
|
{"role": "user", "content": "Hello!"},
|
|
]
|
|
response = MockResponse()
|
|
|
|
result = provider._extract_conversation_from_context(MockContext())
|
|
assert "User: Hello!" in result
|
|
assert "Assistant: Hi there!" in result
|