DevStream Direct Client now includes automatic embedding generation for all stored content. This feature enables semantic search capabilities while maintaining backward compatibility and graceful degradation.
- Content Storage Request →
store_memory()called - Automatic Embedding Generation → OllamaEmbeddingClient generates 768-dimension vectors
- BLOB Storage → Embeddings stored as binary BLOB (70% space reduction vs JSON)
- Graceful Degradation → Storage succeeds even if embedding generation fails
- Return Metadata → Includes embedding status and metadata
- Model:
embeddinggemma:300m(768 dimensions) - Storage Format: Binary BLOB using
struct.pack()(70% space reduction) - Fallback: Content stored without embedding if Ollama fails
- Performance: ~100ms additional latency per operation
- Database Schema:
embedding_blob,embedding_model,embedding_dimensioncolumns
from direct_client import DevStreamDirectClient
client = DevStreamDirectClient()
# Store content with automatic embedding generation
result = await client.store_memory(
content="def fibonacci(n): return n if n <= 1 else fibonacci(n-1) + fibonacci(n-2)",
content_type="code",
keywords=["fibonacci", "recursion", "python"]
)
# Check embedding generation status
if result["embedding_generated"]:
print(f"Embedding stored: {result['embedding_dimension']} dimensions")
print(f"Storage format: {result['embedding_format']}") # "BLOB"
else:
print("Embedding generation failed, but content was stored")result = await client.store_memory(
content="Important code snippet",
content_type="code"
)
# Storage always succeeds
assert result["success"] is True
assert "memory_id" in result
# Embedding generation is optional
if result.get("embedding_generated"):
# Embedding was generated and stored
pass
else:
# Embedding failed, but content was still stored
pass- Without Embedding: ~50ms per operation
- With Embedding: ~150ms per operation (including Ollama call)
- Space Efficiency: BLOB format uses 70% less space than JSON
- Concurrent Operations: Fully supported with connection pooling
- Vector Search: Available when sqlite-vec is enabled
- FTS Fallback: Full-text search when vector search unavailable
- Hybrid Search: Combines semantic and keyword search
CREATE TABLE semantic_memory (
id TEXT PRIMARY KEY,
content TEXT NOT NULL,
content_type TEXT NOT NULL,
keywords TEXT,
session_id TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
access_count INTEGER DEFAULT 0,
relevance_score REAL DEFAULT 1.0,
importance_score REAL DEFAULT 0.0,
last_accessed_at TIMESTAMP,
metadata TEXT,
source TEXT,
-- New embedding columns
embedding_blob BLOB, -- Binary embedding vector
embedding_model TEXT, -- Model used for embedding
embedding_dimension INTEGER, -- Embedding dimensions (768)
-- Constraints
CHECK (content_type IN ('code', 'documentation', 'context', 'output', 'error', 'decision', 'learning')),
CHECK (embedding_blob IS NULL OR vec_length(embedding_blob) = 768)
);# Ollama configuration
OLLAMA_BASE_URL=http://localhost:11434 # Ollama service URL
EMBEDDING_MODEL=embeddinggemma:300m # Embedding model
# DevStream configuration
DEVSTREAM_MEMORY_ENABLED=true # Enable memory system
DEVSTREAM_VECTOR_SEARCH_ENABLED=true # Enable vector search- Embedding Dimensions: Must be exactly 768 dimensions
- Content Types: Restricted to enum values for data integrity
- BLOB Format: Binary storage for space efficiency
- NULL Embeddings: Allowed for graceful degradation
# Run embedding unit tests
.devstream/bin/python -m pytest tests/unit/memory/test_direct_client_embedding.py -vTest Coverage:
- ✅ Embedding generation success
- ✅ Embedding generation failure (graceful degradation)
- ✅ BLOB format validation
- ✅ Backward compatibility
- ✅ Performance requirements
# Run embedding integration tests
.devstream/bin/python -m pytest tests/integration/test_embedding_integration.py -vTest Coverage:
- ✅ Full embedding flow with real Ollama service
- ✅ Search integration with embeddings
- ✅ Performance under load
- ✅ Error recovery and system resilience
- ✅ Database constraints validation
Symptoms:
embedding_generated: Falsein response- Warning logs about Ollama unavailability
- Content stored successfully but no embedding
Solutions:
# Check Ollama service status
curl http://localhost:11434/api/tags
# Ensure embedding model is available
ollama pull embeddinggemma:300m
# Check Ollama logs
docker logs ollamaSymptoms:
CHECK constraint failederrors- Invalid content_type values
- Embedding dimension mismatches
Solutions:
# Use valid content types
valid_types = ['code', 'documentation', 'context', 'output', 'error', 'decision', 'learning']
# Check embedding dimensions (should be 768)
import struct
embedding = [0.1] * 768 # Must be exactly 768 dimensions
blob = struct.pack('768f', *embedding)
assert len(blob) == 3072 # 768 * 4 bytesSymptoms:
- Slow storage operations (>200ms)
- High memory usage
- Database timeouts
Solutions:
# Use concurrent operations for bulk storage
import asyncio
tasks = []
for content in contents:
task = client.store_memory(content, "code")
tasks.append(task)
results = await asyncio.gather(*tasks)# Get client statistics
stats = client.get_stats()
print(f"Vector search available: {stats['features']['vector_search']}")
print(f"Active connections: {stats['active_connections']}")
# Check embedding generation status
result = await client.store_memory("test", "code")
print(f"Embedding generated: {result['embedding_generated']}")
print(f"Embedding dimension: {result['embedding_dimension']}")The direct client maintains 100% API compatibility with the MCP client:
# Old MCP client code (unchanged)
from mcp_client import DevStreamMCPClient
client = DevStreamMCPClient()
result = await client.store_memory("content", "code")
# New direct client code (same API)
from direct_client import DevStreamDirectClient
client = DevStreamDirectClient()
result = await client.store_memory("content", "code")The direct client adds new return fields for embedding metadata:
result = await client.store_memory("content", "code")
# New embedding metadata fields
embedding_generated = result.get("embedding_generated", False)
embedding_format = result.get("embedding_format") # "BLOB" or None
embedding_dimension = result.get("embedding_dimension") # 768 or None- Batch Operations: Use
asyncio.gather()for concurrent storage - Content Optimization: Avoid extremely large content (>10MB)
- Connection Pooling: Reuse client instances for multiple operations
- Error Handling: Always check
embedding_generatedstatus
- Content Types: Use appropriate content types for better search
- Keywords: Provide relevant keywords for improved FTS search
- Session Tracking: Use session IDs for debugging and analytics
- Monitoring: Track embedding generation success rates
- Graceful Degradation: Design for embedding generation failures
- Retry Logic: Implement exponential backoff for transient failures
- Fallback Search: Use FTS search when vector search unavailable
- Monitoring: Alert on embedding generation failure rates
- Embedding Model Selection: Support for multiple embedding models
- Batch Embedding: Generate embeddings for multiple items efficiently
- Embedding Caching: Cache embeddings for duplicate content
- Hybrid Search: Improved semantic + keyword search ranking
- Embedding Analytics: Track embedding quality and usage patterns
- Async Embedding Generation: Non-blocking embedding generation
- Vector Index Optimization: Improved vector search performance
- Connection Pool Optimization: Better resource utilization
- Memory Management: Optimized BLOB storage and retrieval
async def store_memory(
self,
content: str,
content_type: str,
keywords: Optional[List[str]] = None,
session_id: Optional[str] = None
) -> Optional[Dict[str, Any]]:Parameters:
content(str): Content to storecontent_type(str): Type of content ('code', 'documentation', 'context', 'output', 'error', 'decision', 'learning')keywords(Optional[List[str]]): Associated keywords for searchsession_id(Optional[str]): Session ID for tracking
Returns:
success(bool): True if storage succeededmemory_id(str): Unique identifier for stored memorycontent_type(str): Type of content storedcreated_at(str): ISO timestamp of storageembedding_generated(bool): True if embedding was generated and storedembedding_format(str): "BLOB" if embedding was stored, None otherwiseembedding_dimension(int): Embedding vector dimensions (768), None if no embedding
Raises:
DatabaseException: If storage operation fails
- ✅ NEW: Automatic embedding generation for all stored content
- ✅ NEW: BLOB format storage (70% space reduction vs JSON)
- ✅ NEW: Graceful degradation for embedding generation failures
- ✅ NEW: Comprehensive unit and integration test coverage
- ✅ NEW: Enhanced documentation and examples
- ✅ IMPROVED: Performance monitoring and logging
- ✅ IMPROVED: Error handling and recovery mechanisms