beadboard/.agents/skills/rlm-mem/brain/scripts/remember_operation.py

138 lines
4.7 KiB
Python

"""
RLM-MEM - REMEMBER Operation
D3.1: High-level memory storage operation
REMEMBER is the high-level operation that:
- Takes user/agent content
- Chunks it (via ChunkingEngine)
- Stores chunks (via ChunkStore)
- Auto-links chunks (via AutoLinker)
- Returns confirmation
"""
from typing import List, Optional
try:
from .memory_store import ChunkStore, ChunkType
from .chunking_engine import ChunkingEngine
from .auto_linker import AutoLinker
except ImportError:
from memory_store import ChunkStore, ChunkType
from chunking_engine import ChunkingEngine
from auto_linker import AutoLinker
class RememberOperation:
"""
High-level REMEMBER operation.
Takes content, chunks it, stores it, auto-links it.
"""
def __init__(self, store, linker: AutoLinker = None):
"""
Initialize REMEMBER operation.
Args:
store: ChunkStore or LayeredChunkStoreAdapter
linker: Optional AutoLinker instance
"""
self.store = store
self.engine = ChunkingEngine()
# If linker is not provided, try to initialize default AutoLinker
# Note: AutoLinker expects a store that behaves like ChunkStore
self.linker = linker or AutoLinker(store)
def remember(self, content: str, conversation_id: str,
tags: list = None, confidence: float = 0.7,
chunk_type: str = None) -> dict:
"""
Remember content - chunk, store, and link.
Args:
content: Content to remember
conversation_id: Source conversation ID (required)
tags: Optional list of tags
confidence: Confidence score (0.0-1.0)
chunk_type: Optional type override (auto-detected if not provided)
Returns:
Confirmation dict with:
- success: bool
- chunk_ids: list of created chunk IDs
- total_tokens: total token count
- chunks_created: number of chunks created
Raises:
ValueError: For invalid inputs
TypeError: For None content
"""
# Validation - CRITICAL
if content is None:
raise TypeError("Content cannot be None")
if not isinstance(content, str):
raise TypeError(f"Content must be string, got {type(content).__name__}")
if not conversation_id:
raise ValueError("conversation_id is required")
# Check for empty or whitespace-only content
if not content.strip():
return {
"success": False,
"error": "Content is empty or whitespace-only",
"chunk_ids": [],
"total_tokens": 0,
"chunks_created": 0
}
# Validate confidence
if not 0.0 <= confidence <= 1.0:
raise ValueError(f"Confidence must be between 0.0 and 1.0, got {confidence}")
# Validate type override if provided
if chunk_type is not None:
valid_types = [t.value for t in ChunkType]
if chunk_type not in valid_types:
raise ValueError(f"Invalid chunk_type: {chunk_type}. Must be one of: {valid_types}")
# Step 1: Chunk the content
chunk_results = self.engine.chunk(content, conversation_id, tags)
if not chunk_results:
return {
"success": False,
"error": "Chunking produced no results",
"chunk_ids": [],
"total_tokens": 0,
"chunks_created": 0
}
# Step 2: Create chunks in store with auto-linking
created_chunks = []
for result in chunk_results:
# Use type override if provided, otherwise use detected type
final_type = chunk_type if chunk_type else result.type
chunk = self.store.create_chunk(
content=result.content,
chunk_type=final_type,
conversation_id=conversation_id,
tokens=result.tokens,
tags=result.tags,
confidence=confidence
)
# Auto-link the chunk
chunk = self.linker.link_on_create(chunk)
created_chunks.append(chunk)
total_tokens = sum(c.tokens for c in created_chunks)
return {
"success": True,
"chunk_ids": [c.id for c in created_chunks],
"total_tokens": total_tokens,
"chunks_created": len(created_chunks)
}