Skip to content
Merged

Dev #93

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions memory/api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Agent Memory API

The `memory/api` module provides a comprehensive interface for integrating the Agent Memory System with AI agents, enabling efficient storage, retrieval, and utilization of memories to support context-aware agent reasoning and behavior.

## Overview

The Agent Memory System implements a tiered memory architecture inspired by human cognitive systems:

- **Short-Term Memory (STM)**: Recent, high-fidelity memories with detailed information
- **Intermediate Memory (IM)**: Medium-term memories with moderate compression
- **Long-Term Memory (LTM)**: Persistent, compressed memories retaining core information

This architecture allows agents to efficiently manage memories with different levels of detail and importance across varying time horizons.

## Module Components

### Main API Class

[`memory_api.py`](memory_api.py) - Provides the primary interface class `AgentMemoryAPI` for interacting with the memory system:
- Store agent states, actions, and interactions
- Retrieve memories by various criteria (ID, time range, attributes)
- Perform semantic search across memory tiers
- Manage memory lifecycle and maintenance

### Agent Integration

[`hooks.py`](hooks.py) - Offers decorators and utility functions for automatic memory integration:
- `install_memory_hooks`: Class decorator to add memory capabilities to agent classes
- `with_memory`: Instance decorator for adding memory to existing agent instances
- `BaseAgent`: Minimal interface with standard lifecycle methods for memory-aware agents

### Data Models

[`models.py`](models.py) - Defines structured representations of agent data:
- `AgentState`: Standardized representation of an agent's state
- `ActionData`: Record of an agent action with associated states and metrics
- `ActionResult`: Lightweight result of an action execution

### Type Definitions

[`types.py`](types.py) - Establishes core type definitions for the memory system:
- Memory entry structures (metadata, embeddings, content)
- Memory tiers and filtering types
- Statistics and query result types
- Protocol definitions for memory stores

## Getting Started

### Basic Usage

```python
from memory.api import AgentMemoryAPI

# Initialize the memory API
memory_api = AgentMemoryAPI()

# Store an agent state
state_data = {
"agent_id": "agent-001",
"step_number": 42,
"content": {
"observation": "User asked about weather",
"thought": "I should check the forecast"
}
}
memory_id = memory_api.store_agent_state("agent-001", state_data, step_number=42)

# Retrieve similar memories
query = "weather forecast"
similar_memories = memory_api.search_by_content("agent-001", query, k=3)

# Use memories to inform agent's response
for memory in similar_memories:
print(f"Related memory: {memory['contents']}")
```

### Automatic Memory Integration

```python
from memory.api import install_memory_hooks, BaseAgent

@install_memory_hooks
class MyAgent(BaseAgent):
def __init__(self, config=None, agent_id=None):
super().__init__(config, agent_id)
# Agent-specific initialization

def act(self, observation):
# Memory hooks automatically capture state before this method
self.step_number += 1
action_result = self._process(observation)
# Memory hooks automatically capture state after this method
return action_result

def get_state(self):
# Return current agent state
state = super().get_state()
state.extra_data["custom_field"] = self.some_internal_state
return state

# Create an agent with memory enabled
agent = MyAgent(agent_id="agent-001")

# Use the agent normally - memories are created automatically
result = agent.act({"user_input": "What's the weather today?"})
```

## Advanced Features

### Memory Maintenance

```python
# Run memory maintenance to consolidate and optimize memories
memory_api.force_memory_maintenance("agent-001")

# Get memory statistics
stats = memory_api.get_memory_statistics("agent-001")
print(f"Total memories: {stats['total_memories']}")
print(f"STM: {stats['stm_count']}, IM: {stats['im_count']}, LTM: {stats['ltm_count']}")
```

### Memory Search

```python
# Search by content similarity
similar_memories = memory_api.search_by_content(
agent_id="agent-001",
content_query="user asked about calendar appointments",
k=5
)

# Retrieve memories by time range
recent_memories = memory_api.retrieve_by_time_range(
agent_id="agent-001",
start_step=100,
end_step=120
)

# Retrieve memories by attributes
filtered_memories = memory_api.retrieve_by_attributes(
agent_id="agent-001",
attributes={"action_type": "calendar_query"}
)
```

### Memory Configuration

```python
from memory.api import AgentMemoryAPI
from memory.config import MemoryConfig

# Custom configuration
config = MemoryConfig(
stm_config={"memory_limit": 1000},
im_config={"memory_limit": 10000},
ltm_config={"memory_limit": 100000}
)

# Initialize API with custom configuration
memory_api = AgentMemoryAPI(config)

# Update configuration
memory_api.configure_memory_system({
"stm_config": {"memory_limit": 2000}
})
```

## Error Handling

```python
from memory.api import AgentMemoryAPI
from memory.api.memory_api import MemoryStoreException, MemoryRetrievalException

memory_api = AgentMemoryAPI()

try:
memory = memory_api.retrieve_state_by_id("agent-001", "non_existent_id")
except MemoryRetrievalException as e:
print(f"Memory retrieval error: {e}")

try:
memories = memory_api.search_by_content("agent-001", "query", k=-1)
except MemoryConfigException as e:
print(f"Configuration error: {e}")
```
4 changes: 2 additions & 2 deletions memory/search/strategies/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(
im_store: RedisIMStore,
ltm_store: SQLiteLTMStore,
scoring_method: str = "length_ratio",
skip_validation: bool = False,
skip_validation: bool = True,
):
"""Initialize the attribute search strategy.

Expand Down Expand Up @@ -146,7 +146,7 @@ def search(
case_sensitive: bool = False,
use_regex: bool = False,
scoring_method: Optional[str] = None,
skip_validation: Optional[bool] = None,
skip_validation: Optional[bool] = True,
**kwargs,
) -> List[Dict[str, Any]]:
"""Search for memories based on content and metadata attributes.
Expand Down
8 changes: 4 additions & 4 deletions memory/storage/redis_im.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ def _store_memory_entry(self, agent_id: str, memory_entry: MemoryEntry) -> bool:
)
return False

def get(self, agent_id: str, memory_id: str, skip_validation: bool = False) -> Optional[MemoryEntry]:
def get(self, agent_id: str, memory_id: str, skip_validation: bool = True) -> Optional[MemoryEntry]:
"""Retrieve a memory entry by ID.

Args:
Expand Down Expand Up @@ -616,7 +616,7 @@ def _hash_to_memory_entry(self, hash_data: Dict[str, Any]) -> MemoryEntry:
return memory_entry

def get_by_timerange(
self, agent_id: str, start_time: float, end_time: float, limit: int = 100, skip_validation: bool = False
self, agent_id: str, start_time: float, end_time: float, limit: int = 100, skip_validation: bool = True
) -> List[MemoryEntry]:
"""Retrieve memories within a time range.

Expand Down Expand Up @@ -713,7 +713,7 @@ def get_by_importance(
min_importance: float = 0.0,
max_importance: float = 1.0,
limit: int = 100,
skip_validation: bool = False,
skip_validation: bool = True,
) -> List[MemoryEntry]:
"""Retrieve memories by importance score range.

Expand Down Expand Up @@ -1341,7 +1341,7 @@ def get_size(self, agent_id: str) -> int:
logger.exception("Error retrieving memory size for agent %s", agent_id)
return 0

def get_all(self, agent_id: str, limit: int = 1000, skip_validation: bool = False) -> List[MemoryEntry]:
def get_all(self, agent_id: str, limit: int = 1000, skip_validation: bool = True) -> List[MemoryEntry]:
"""Get all memories for an agent.

Args:
Expand Down
8 changes: 4 additions & 4 deletions memory/storage/redis_stm.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def _store_memory_entry(self, agent_id: str, memory_entry: MemoryEntry) -> bool:
)
return False

def get(self, agent_id: str, memory_id: str, skip_validation: bool = False) -> Optional[MemoryEntry]:
def get(self, agent_id: str, memory_id: str, skip_validation: bool = True) -> Optional[MemoryEntry]:
"""Retrieve a memory entry by ID.

Args:
Expand Down Expand Up @@ -378,7 +378,7 @@ def _update_access_metadata(
)

def get_by_timerange(
self, agent_id: str, start_time: float, end_time: float, limit: int = 100, skip_validation: bool = False
self, agent_id: str, start_time: float, end_time: float, limit: int = 100, skip_validation: bool = True
) -> List[MemoryEntry]:
"""Retrieve memories within a time range.

Expand Down Expand Up @@ -425,7 +425,7 @@ def get_by_importance(
min_importance: float = 0.0,
max_importance: float = 1.0,
limit: int = 100,
skip_validation: bool = False,
skip_validation: bool = True,
) -> List[MemoryEntry]:
"""Retrieve memories by importance score.

Expand Down Expand Up @@ -641,7 +641,7 @@ def get_size(self, agent_id: str) -> int:
logger.error("Error calculating memory size: %s", e)
return 0

def get_all(self, agent_id: str, limit: int = 1000, skip_validation: bool = False) -> List[MemoryEntry]:
def get_all(self, agent_id: str, limit: int = 1000, skip_validation: bool = True) -> List[MemoryEntry]:
"""Get all memories for an agent.

Args:
Expand Down
14 changes: 7 additions & 7 deletions memory/storage/sqlite_ltm.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ def store_batch(self, memory_entries: List[Dict[str, Any]]) -> bool:
logger.error("Unexpected error storing batch of memories: %s", str(e))
return False

def get(self, memory_id: str, agent_id: str, skip_validation: bool = False) -> Optional[Dict[str, Any]]:
def get(self, memory_id: str, agent_id: str, skip_validation: bool = True) -> Optional[Dict[str, Any]]:
"""Retrieve a memory by ID.

Args:
Expand Down Expand Up @@ -711,7 +711,7 @@ def get_by_timerange(
end_time: Union[float, int, str],
agent_id: str = None,
limit: int = 100,
skip_validation: bool = False,
skip_validation: bool = True,
) -> List[Dict[str, Any]]:
"""Retrieve memories within a time range.

Expand Down Expand Up @@ -800,7 +800,7 @@ def get_by_importance(
min_importance: float = 0.0,
max_importance: float = 1.0,
limit: int = 100,
skip_validation: bool = False,
skip_validation: bool = True,
) -> List[Dict[str, Any]]:
"""Retrieve memories by importance score.

Expand Down Expand Up @@ -858,7 +858,7 @@ def get_most_similar(
query_vector: List[float],
top_k: int = 10,
agent_id: str = None,
skip_validation: bool = False,
skip_validation: bool = True,
) -> List[Tuple[Dict[str, Any], float]]:
"""Retrieve memories most similar to the query vector.

Expand Down Expand Up @@ -940,7 +940,7 @@ def search_similar(
k: int = 5,
memory_type: Optional[str] = None,
agent_id: str = None,
skip_validation: bool = False,
skip_validation: bool = True,
) -> List[Dict[str, Any]]:
"""Search for memories with similar embeddings.

Expand Down Expand Up @@ -1161,7 +1161,7 @@ def get_size(self) -> int:
logger.error("Unexpected error calculating memory size: %s", str(e))
return 0

def get_all(self, agent_id: str = None, limit: int = 1000, skip_validation: bool = False) -> List[Dict[str, Any]]:
def get_all(self, agent_id: str = None, limit: int = 1000, skip_validation: bool = True) -> List[Dict[str, Any]]:
"""Get all memories for the agent.

Args:
Expand Down Expand Up @@ -1342,7 +1342,7 @@ def search_by_step_range(
start_step: int,
end_step: int,
memory_type: Optional[str] = None,
skip_validation: bool = False,
skip_validation: bool = True,
) -> List[Dict[str, Any]]:
"""Search for memories within a specific step range.

Expand Down
Loading
Loading