Complete guide to using sub stores for advanced memory partitioning and optimization.
Sub stores allow you to partition memory data into separate vector tables, each with:
- Independent routing rules: Automatically route data based on metadata
- Different embedding services: Use different models and dimensions
- Custom storage configuration: Different index types, database parameters, etc.
- Independent performance optimization: Optimize for different data characteristics
Note: Sub stores are currently only supported with OceanBase vector store.
from powermem import Memory
config = {
"vector_store": {
"provider": "oceanbase",
"config": {
"collection_name": "main_memories",
"embedding_model_dims": 1536,
"host": "127.0.0.1",
"port": "2881",
"user": "root@test",
"password": "password",
"db_name": "test_db",
}
},
"embedding": {
"provider": "openai",
"config": {
"api_key": "your-api-key",
"model": "text-embedding-3-large",
"embedding_dims": 1536,
}
},
# Configure sub stores
"sub_stores": [
{
# Sub store 1: Inherits main config
"routing_filter": {"type": "semantic"}
},
{
# Sub store 2: Uses different embedding
"routing_filter": {"type": "working"},
"embedding_model_dims": 768,
"embedding": {
"provider": "qwen",
"config": {
"model": "text-embedding-v4",
"embedding_dims": 768,
"api_key": "your-qwen-key",
}
}
}
]
}
memory = Memory(config=config)Important: Before using sub store routing, you must run migration at least once to activate the routing functionality, even if there's no data to migrate:
# Initialize sub store routing (required before first use)
memory.migrate_all_sub_stores(delete_source=False)This ensures that sub store migration status is properly initialized in the database, enabling automatic routing.
Once initialized, data is automatically routed to the appropriate sub store based on metadata:
# Add data - automatically routed to sub stores
memory.add(
"Python is a programming language",
metadata={"type": "semantic"} # Routes to sub store 1
)
memory.add(
"Buy milk today",
metadata={"type": "working"} # Routes to sub store 2
)
# Search from specific sub store
results = memory.search(
"programming concepts",
filters={"type": "semantic"}, # Searches in sub store 1
limit=5
)Defines which data should be routed to this sub store:
"routing_filter": {
"type": "working", # Single condition
"priority": "high" # Multiple conditions (AND logic)
}Custom table name. Defaults to {main_table_name}_sub_{index}:
"collection_name": "memories_important"Vector dimensions. Defaults to main store dimensions:
"embedding_model_dims": 768Sub store specific embedding service:
"embedding": {
"provider": "qwen",
"config": {
"model": "text-embedding-v4",
"embedding_dims": 768,
"api_key": "your-api-key",
}
}Important: embedding.config.embedding_dims must match embedding_model_dims.
Override main store's vector_store configuration:
"vector_store": {
"index_type": "HNSW", # Use HNSW index
"vector_weight": 0.7, # Vector search weight
"fts_weight": 0.3, # Full-text search weight
"host": "different-host", # Use different database
# Any vector_store parameter can be overridden
}Index type for this sub store:
"index_type": "HNSW" # Options: "HNSW", "IVF_FLAT", "IVF_PQ"# Method 1: Migrate by index
count = memory.migrate_to_sub_store(
sub_store_index=0, # Sub store index
delete_source=False # Keep source data
)
# Method 2: Migrate by name
count = memory.migrate_to_sub_store(
sub_store_name="memories_sub_0",
delete_source=True # Delete source after migration
)
# Method 3: Migrate all sub stores
count = memory.migrate_all_sub_stores(delete_source=False)# Check migration status
status = memory.get_sub_store_migration_status("memories_sub_0")
print(f"Status: {status['status']}") # PENDING/MIGRATING/COMPLETED/FAILED
print(f"Progress: {status['migrated_count']}/{status['total_count']}")
print(f"Percentage: {status['progress']}%")During migration:
- Queries matching records based on
routing_filter - Re-generates vectors using sub store's
embedding_service - Inserts new vectors and payload into sub store
- Optionally deletes source data
config = {
"vector_store": {"config": {"embedding_model_dims": 1536}},
"embedding": {
"provider": "openai",
"config": {"model": "text-embedding-3-large", "embedding_dims": 1536}
},
"sub_stores": [
{
"routing_filter": {"priority": "low"},
"embedding_model_dims": 384, # Smaller dimension
"embedding": {
"provider": "qwen",
"config": {"model": "text-embedding-v4", "embedding_dims": 384}
}
},
{
"routing_filter": {"priority": "high"},
"embedding_model_dims": 3072, # Larger dimension
"embedding": {
"provider": "openai",
"config": {"model": "text-embedding-3-large", "embedding_dims": 3072}
}
}
]
}config = {
"vector_store": {
"config": {"index_type": "IVF_FLAT"} # Main store: balanced
},
"sub_stores": [
{
"routing_filter": {"access_pattern": "hot"},
"vector_store": {
"index_type": "HNSW", # Fast retrieval
"vector_weight": 0.8,
"fts_weight": 0.2,
}
},
{
"routing_filter": {"access_pattern": "cold"},
"vector_store": {
"index_type": "IVF_PQ" # Compressed storage
}
}
]
}config = {
"vector_store": {...},
"sub_stores": [
{
"routing_filter": {"tenant": "enterprise_a"},
"collection_name": "memories_enterprise_a",
"vector_store": {
"database": "tenant_a_db", # Different database
"host": "db-a.example.com", # Different host
}
}
]
}"sub_stores": [
{
"routing_filter": {"type": "episodic"},
"embedding_model_dims": 1536,
},
{
"routing_filter": {"type": "semantic"},
"embedding_model_dims": 768,
"embedding": {...} # Different model
}
]"sub_stores": [
{
"routing_filter": {"priority": "high"},
"vector_store": {
"index_type": "HNSW" # Fast queries
}
},
{
"routing_filter": {"priority": "low"},
"vector_store": {
"index_type": "IVF_PQ" # Save resources
}
}
]"sub_stores": [
{
"routing_filter": {"frequency": "hot"},
"vector_store": {
"index_type": "HNSW",
"vector_weight": 0.7,
"fts_weight": 0.3
}
},
{
"routing_filter": {"frequency": "cold"},
"vector_store": {
"index_type": "IVF_FLAT"
}
}
]Critical: Before using sub store routing, you must initialize the migration system at least once:
memory.migrate_all_sub_stores(delete_source=False)This is required even if you have no existing data to migrate. It initializes the sub store status in the database, which enables the routing mechanism. Without this step, all data will continue to go to the main store.
Ensure both dimension parameters match:
{
"embedding_model_dims": 768, # Database table structure
"embedding": {
"config": {
"embedding_dims": 768, # API returns this dimension
}
}
}Mismatched dimensions will cause errors: "expected 1536 dimensions, not 768"
- Sub store
routing_filtermatches against memorymetadata - All filter conditions must match (AND logic)
- If no sub store matches, data goes to main store
- Only exact matches are supported (no OR/NOT logic)
- Getting Started - Quick start
- Configuration Guide - Basic configuration
- Integrations Guide - Embedding services
See complete examples:
examples/sub_store_simple.py- Basic sub store usageexamples/multi_agent.py- Multi-agent with sub stores