138 lines
4.7 KiB
Python
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)
|
|
}
|