mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
Implement comprehensive memory system supporting: - Local backend (SQLite + FTS5 + HNSW) for zero-dependency operation - Mem0 backends (Neo4j + Qdrant) for production graph memory - DirectMem0Adapter for optimized pre-extracted data (bypasses LLM) - Memory extraction with facts, entities, and relationships - Proxy integration with --memory flag for automatic memory injection Key components: - headroom/memory/backends/: LocalBackend, Mem0Backend, DirectMem0Adapter - headroom/memory/system.py: MemorySystem with tool-based interface - headroom/memory/extraction.py: Entity and relationship extraction - headroom/proxy/memory_handler.py: Proxy integration layer - headroom/prediction/feature_extractor.py: Content analysis features Testing: - 217 new memory system tests covering all backends - LoCoMo evaluation framework for memory quality assessment - Integration tests for proxy memory functionality Also removes deprecated example files in favor of focused test coverage.
600 lines
23 KiB
Python
600 lines
23 KiB
Python
"""Tests for memory factory module.
|
|
|
|
Tests component creation via the factory functions.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from headroom.memory.config import (
|
|
EmbedderBackend,
|
|
MemoryConfig,
|
|
StoreBackend,
|
|
TextBackend,
|
|
VectorBackend,
|
|
)
|
|
from headroom.memory.factory import (
|
|
_create_cache,
|
|
_create_embedder,
|
|
_create_store,
|
|
_create_text_index,
|
|
_create_vector_index,
|
|
create_memory_system,
|
|
)
|
|
|
|
# =============================================================================
|
|
# Test MemoryConfig
|
|
# =============================================================================
|
|
|
|
|
|
class TestMemoryConfig:
|
|
"""Tests for MemoryConfig dataclass."""
|
|
|
|
def test_default_config(self):
|
|
"""Default config should have sensible defaults."""
|
|
config = MemoryConfig()
|
|
|
|
assert config.store_backend == StoreBackend.SQLITE
|
|
assert config.vector_backend == VectorBackend.HNSW
|
|
assert config.text_backend == TextBackend.FTS5
|
|
assert config.embedder_backend == EmbedderBackend.LOCAL
|
|
assert config.cache_enabled is True
|
|
assert config.cache_max_size == 1000
|
|
assert config.auto_bubble is True
|
|
assert config.bubble_threshold == 0.7
|
|
|
|
def test_config_with_custom_path(self):
|
|
"""Config should accept custom db path."""
|
|
config = MemoryConfig(db_path=Path("/custom/path.db"))
|
|
assert config.db_path == Path("/custom/path.db")
|
|
|
|
def test_config_converts_string_path(self):
|
|
"""String paths should be converted to Path objects."""
|
|
config = MemoryConfig(db_path="/custom/path.db") # type: ignore[arg-type]
|
|
assert isinstance(config.db_path, Path)
|
|
assert config.db_path == Path("/custom/path.db")
|
|
|
|
def test_config_validates_vector_dimension(self):
|
|
"""Vector dimension must be positive."""
|
|
with pytest.raises(ValueError, match="vector_dimension must be positive"):
|
|
MemoryConfig(vector_dimension=0)
|
|
|
|
with pytest.raises(ValueError, match="vector_dimension must be positive"):
|
|
MemoryConfig(vector_dimension=-1)
|
|
|
|
def test_config_validates_hnsw_params(self):
|
|
"""HNSW parameters must be positive."""
|
|
with pytest.raises(ValueError, match="hnsw_ef_construction must be positive"):
|
|
MemoryConfig(hnsw_ef_construction=0)
|
|
|
|
with pytest.raises(ValueError, match="hnsw_m must be positive"):
|
|
MemoryConfig(hnsw_m=0)
|
|
|
|
with pytest.raises(ValueError, match="hnsw_ef_search must be positive"):
|
|
MemoryConfig(hnsw_ef_search=0)
|
|
|
|
def test_config_validates_cache_size(self):
|
|
"""Cache max size must be positive."""
|
|
with pytest.raises(ValueError, match="cache_max_size must be positive"):
|
|
MemoryConfig(cache_max_size=0)
|
|
|
|
def test_config_requires_openai_key_for_openai_backend(self):
|
|
"""OpenAI backend requires API key."""
|
|
with pytest.raises(ValueError, match="openai_api_key is required"):
|
|
MemoryConfig(embedder_backend=EmbedderBackend.OPENAI)
|
|
|
|
def test_config_accepts_openai_key(self):
|
|
"""Config should accept OpenAI API key."""
|
|
config = MemoryConfig(embedder_backend=EmbedderBackend.OPENAI, openai_api_key="sk-test-key")
|
|
assert config.openai_api_key == "sk-test-key"
|
|
|
|
def test_config_custom_hnsw_params(self):
|
|
"""Config should accept custom HNSW parameters."""
|
|
config = MemoryConfig(
|
|
vector_dimension=768, hnsw_ef_construction=400, hnsw_m=32, hnsw_ef_search=100
|
|
)
|
|
assert config.vector_dimension == 768
|
|
assert config.hnsw_ef_construction == 400
|
|
assert config.hnsw_m == 32
|
|
assert config.hnsw_ef_search == 100
|
|
|
|
def test_config_cache_disabled(self):
|
|
"""Config should allow disabling cache."""
|
|
config = MemoryConfig(cache_enabled=False)
|
|
assert config.cache_enabled is False
|
|
|
|
def test_config_ollama_base_url(self):
|
|
"""Config should accept Ollama base URL."""
|
|
config = MemoryConfig(
|
|
embedder_backend=EmbedderBackend.OLLAMA, ollama_base_url="http://remote:11434"
|
|
)
|
|
assert config.ollama_base_url == "http://remote:11434"
|
|
|
|
|
|
# =============================================================================
|
|
# Test _create_store
|
|
# =============================================================================
|
|
|
|
|
|
class TestCreateStore:
|
|
"""Tests for _create_store factory function."""
|
|
|
|
def test_creates_sqlite_store(self):
|
|
"""Should create SQLiteMemoryStore for SQLITE backend."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(
|
|
store_backend=StoreBackend.SQLITE, db_path=Path(tmpdir) / "test.db"
|
|
)
|
|
store = _create_store(config)
|
|
|
|
from headroom.memory.adapters.sqlite import SQLiteMemoryStore
|
|
|
|
assert isinstance(store, SQLiteMemoryStore)
|
|
|
|
def test_sqlite_store_with_custom_path(self):
|
|
"""SQLite store should use config db_path."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
custom_path = Path(tmpdir) / "custom_memory.db"
|
|
config = MemoryConfig(db_path=custom_path)
|
|
store = _create_store(config)
|
|
|
|
from headroom.memory.adapters.sqlite import SQLiteMemoryStore
|
|
|
|
assert isinstance(store, SQLiteMemoryStore)
|
|
|
|
def test_unknown_store_backend_raises(self):
|
|
"""Unknown store backend should raise ValueError."""
|
|
config = MemoryConfig()
|
|
# Manually set an invalid backend to bypass __post_init__
|
|
config.store_backend = "invalid" # type: ignore[assignment]
|
|
|
|
with pytest.raises(ValueError, match="Unknown store backend"):
|
|
_create_store(config)
|
|
|
|
|
|
# =============================================================================
|
|
# Test _create_embedder
|
|
# =============================================================================
|
|
|
|
|
|
class TestCreateEmbedder:
|
|
"""Tests for _create_embedder factory function."""
|
|
|
|
def test_creates_local_embedder(self):
|
|
"""Should create LocalEmbedder for LOCAL backend."""
|
|
config = MemoryConfig(embedder_backend=EmbedderBackend.LOCAL)
|
|
embedder = _create_embedder(config)
|
|
|
|
from headroom.memory.adapters.embedders import LocalEmbedder
|
|
|
|
assert isinstance(embedder, LocalEmbedder)
|
|
|
|
def test_local_embedder_with_custom_model(self):
|
|
"""LocalEmbedder should use config embedder_model."""
|
|
config = MemoryConfig(
|
|
embedder_backend=EmbedderBackend.LOCAL, embedder_model="paraphrase-MiniLM-L6-v2"
|
|
)
|
|
embedder = _create_embedder(config)
|
|
|
|
from headroom.memory.adapters.embedders import LocalEmbedder
|
|
|
|
assert isinstance(embedder, LocalEmbedder)
|
|
assert embedder.model_name == "paraphrase-MiniLM-L6-v2"
|
|
|
|
def test_creates_openai_embedder(self):
|
|
"""Should create OpenAIEmbedder for OPENAI backend."""
|
|
config = MemoryConfig(embedder_backend=EmbedderBackend.OPENAI, openai_api_key="sk-test-key")
|
|
embedder = _create_embedder(config)
|
|
|
|
from headroom.memory.adapters.embedders import OpenAIEmbedder
|
|
|
|
assert isinstance(embedder, OpenAIEmbedder)
|
|
|
|
def test_openai_embedder_requires_key(self):
|
|
"""OpenAI embedder should raise if no API key."""
|
|
config = MemoryConfig(embedder_backend=EmbedderBackend.LOCAL)
|
|
# Manually override to test the factory check
|
|
config.embedder_backend = EmbedderBackend.OPENAI
|
|
config.openai_api_key = None
|
|
|
|
with pytest.raises(ValueError, match="openai_api_key is required"):
|
|
_create_embedder(config)
|
|
|
|
def test_creates_ollama_embedder(self):
|
|
"""Should create OllamaEmbedder for OLLAMA backend."""
|
|
config = MemoryConfig(embedder_backend=EmbedderBackend.OLLAMA)
|
|
embedder = _create_embedder(config)
|
|
|
|
from headroom.memory.adapters.embedders import OllamaEmbedder
|
|
|
|
assert isinstance(embedder, OllamaEmbedder)
|
|
|
|
def test_ollama_embedder_with_custom_url(self):
|
|
"""OllamaEmbedder should use config base URL and model."""
|
|
config = MemoryConfig(
|
|
embedder_backend=EmbedderBackend.OLLAMA,
|
|
ollama_base_url="http://custom:11434",
|
|
embedder_model="custom-model",
|
|
)
|
|
embedder = _create_embedder(config)
|
|
|
|
from headroom.memory.adapters.embedders import OllamaEmbedder
|
|
|
|
assert isinstance(embedder, OllamaEmbedder)
|
|
assert embedder.model_name == "custom-model"
|
|
|
|
def test_unknown_embedder_backend_raises(self):
|
|
"""Unknown embedder backend should raise ValueError."""
|
|
config = MemoryConfig()
|
|
# Manually set an invalid backend
|
|
config.embedder_backend = "invalid" # type: ignore[assignment]
|
|
|
|
with pytest.raises(ValueError, match="Unknown embedder backend"):
|
|
_create_embedder(config)
|
|
|
|
|
|
# =============================================================================
|
|
# Test _create_vector_index
|
|
# =============================================================================
|
|
|
|
|
|
class TestCreateVectorIndex:
|
|
"""Tests for _create_vector_index factory function."""
|
|
|
|
def test_creates_hnsw_index(self):
|
|
"""Should create HNSWVectorIndex for HNSW backend."""
|
|
config = MemoryConfig(vector_backend=VectorBackend.HNSW)
|
|
index = _create_vector_index(config)
|
|
|
|
from headroom.memory.adapters.hnsw import HNSWVectorIndex
|
|
|
|
assert isinstance(index, HNSWVectorIndex)
|
|
|
|
def test_hnsw_index_with_custom_params(self):
|
|
"""HNSW index should use config parameters."""
|
|
config = MemoryConfig(
|
|
vector_backend=VectorBackend.HNSW,
|
|
vector_dimension=768,
|
|
hnsw_ef_construction=400,
|
|
hnsw_m=32,
|
|
hnsw_ef_search=100,
|
|
)
|
|
index = _create_vector_index(config)
|
|
|
|
from headroom.memory.adapters.hnsw import HNSWVectorIndex
|
|
|
|
assert isinstance(index, HNSWVectorIndex)
|
|
assert index._dimension == 768
|
|
assert index._ef_construction == 400
|
|
assert index._m == 32
|
|
assert index._ef_search == 100
|
|
|
|
def test_unknown_vector_backend_raises(self):
|
|
"""Unknown vector backend should raise ValueError."""
|
|
config = MemoryConfig()
|
|
# Manually set an invalid backend
|
|
config.vector_backend = "invalid" # type: ignore[assignment]
|
|
|
|
with pytest.raises(ValueError, match="Unknown vector backend"):
|
|
_create_vector_index(config)
|
|
|
|
|
|
# =============================================================================
|
|
# Test _create_text_index
|
|
# =============================================================================
|
|
|
|
|
|
class TestCreateTextIndex:
|
|
"""Tests for _create_text_index factory function."""
|
|
|
|
def test_creates_fts5_index(self):
|
|
"""Should create FTS5TextIndex for FTS5 backend."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(text_backend=TextBackend.FTS5, db_path=Path(tmpdir) / "test.db")
|
|
index = _create_text_index(config)
|
|
|
|
from headroom.memory.adapters.fts5 import FTS5TextIndex
|
|
|
|
assert isinstance(index, FTS5TextIndex)
|
|
|
|
def test_fts5_index_uses_db_path(self):
|
|
"""FTS5 index should use config db_path."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
custom_path = Path(tmpdir) / "custom_memory.db"
|
|
config = MemoryConfig(db_path=custom_path)
|
|
index = _create_text_index(config)
|
|
|
|
from headroom.memory.adapters.fts5 import FTS5TextIndex
|
|
|
|
assert isinstance(index, FTS5TextIndex)
|
|
|
|
def test_unknown_text_backend_raises(self):
|
|
"""Unknown text backend should raise ValueError."""
|
|
config = MemoryConfig()
|
|
# Manually set an invalid backend
|
|
config.text_backend = "invalid" # type: ignore[assignment]
|
|
|
|
with pytest.raises(ValueError, match="Unknown text backend"):
|
|
_create_text_index(config)
|
|
|
|
|
|
# =============================================================================
|
|
# Test _create_cache
|
|
# =============================================================================
|
|
|
|
|
|
class TestCreateCache:
|
|
"""Tests for _create_cache factory function."""
|
|
|
|
def test_creates_lru_cache(self):
|
|
"""Should create LRUMemoryCache."""
|
|
config = MemoryConfig(cache_max_size=500)
|
|
cache = _create_cache(config)
|
|
|
|
from headroom.memory.adapters.cache import LRUMemoryCache
|
|
|
|
assert isinstance(cache, LRUMemoryCache)
|
|
assert cache.max_size == 500
|
|
|
|
def test_cache_with_default_size(self):
|
|
"""Cache should use default size from config."""
|
|
config = MemoryConfig() # Default cache_max_size=1000
|
|
cache = _create_cache(config)
|
|
|
|
from headroom.memory.adapters.cache import LRUMemoryCache
|
|
|
|
assert isinstance(cache, LRUMemoryCache)
|
|
assert cache.max_size == 1000
|
|
|
|
def test_cache_with_custom_size(self):
|
|
"""Cache should use custom max_size from config."""
|
|
config = MemoryConfig(cache_max_size=5000)
|
|
cache = _create_cache(config)
|
|
|
|
assert cache.max_size == 5000
|
|
|
|
|
|
# =============================================================================
|
|
# Test create_memory_system
|
|
# =============================================================================
|
|
|
|
|
|
class TestCreateMemorySystem:
|
|
"""Tests for create_memory_system factory function."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_creates_all_components_with_default_config(self):
|
|
"""Should create all components with default config."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(db_path=Path(tmpdir) / "test.db")
|
|
store, vector, text, embedder, cache = await create_memory_system(config)
|
|
|
|
from headroom.memory.adapters.cache import LRUMemoryCache
|
|
from headroom.memory.adapters.embedders import LocalEmbedder
|
|
from headroom.memory.adapters.fts5 import FTS5TextIndex
|
|
from headroom.memory.adapters.hnsw import HNSWVectorIndex
|
|
from headroom.memory.adapters.sqlite import SQLiteMemoryStore
|
|
|
|
assert isinstance(store, SQLiteMemoryStore)
|
|
assert isinstance(vector, HNSWVectorIndex)
|
|
assert isinstance(text, FTS5TextIndex)
|
|
assert isinstance(embedder, LocalEmbedder)
|
|
assert isinstance(cache, LRUMemoryCache)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_creates_components_without_cache(self):
|
|
"""Should return None for cache when disabled."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(db_path=Path(tmpdir) / "test.db", cache_enabled=False)
|
|
store, vector, text, embedder, cache = await create_memory_system(config)
|
|
|
|
assert cache is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_creates_components_with_none_config(self):
|
|
"""Should use default config when None is passed."""
|
|
# This will use default path, which may create file in current dir
|
|
# Just verify it doesn't raise
|
|
store, vector, text, embedder, cache = await create_memory_system(None)
|
|
|
|
from headroom.memory.adapters.cache import LRUMemoryCache
|
|
from headroom.memory.adapters.embedders import LocalEmbedder
|
|
from headroom.memory.adapters.fts5 import FTS5TextIndex
|
|
from headroom.memory.adapters.hnsw import HNSWVectorIndex
|
|
from headroom.memory.adapters.sqlite import SQLiteMemoryStore
|
|
|
|
assert isinstance(store, SQLiteMemoryStore)
|
|
assert isinstance(vector, HNSWVectorIndex)
|
|
assert isinstance(text, FTS5TextIndex)
|
|
assert isinstance(embedder, LocalEmbedder)
|
|
assert isinstance(cache, LRUMemoryCache)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_creates_ollama_embedder(self):
|
|
"""Should create OllamaEmbedder when specified."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(
|
|
db_path=Path(tmpdir) / "test.db", embedder_backend=EmbedderBackend.OLLAMA
|
|
)
|
|
store, vector, text, embedder, cache = await create_memory_system(config)
|
|
|
|
from headroom.memory.adapters.embedders import OllamaEmbedder
|
|
|
|
assert isinstance(embedder, OllamaEmbedder)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_creates_openai_embedder(self):
|
|
"""Should create OpenAIEmbedder when specified."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(
|
|
db_path=Path(tmpdir) / "test.db",
|
|
embedder_backend=EmbedderBackend.OPENAI,
|
|
openai_api_key="sk-test-key",
|
|
)
|
|
store, vector, text, embedder, cache = await create_memory_system(config)
|
|
|
|
from headroom.memory.adapters.embedders import OpenAIEmbedder
|
|
|
|
assert isinstance(embedder, OpenAIEmbedder)
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_custom_hnsw_params_propagate(self):
|
|
"""Custom HNSW params should propagate to vector index."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(
|
|
db_path=Path(tmpdir) / "test.db",
|
|
vector_dimension=512,
|
|
hnsw_ef_construction=300,
|
|
hnsw_m=24,
|
|
hnsw_ef_search=75,
|
|
)
|
|
store, vector, text, embedder, cache = await create_memory_system(config)
|
|
|
|
assert vector._dimension == 512
|
|
assert vector._ef_construction == 300
|
|
assert vector._m == 24
|
|
assert vector._ef_search == 75
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_returns_tuple_of_five(self):
|
|
"""Should return exactly 5 components."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(db_path=Path(tmpdir) / "test.db")
|
|
result = await create_memory_system(config)
|
|
|
|
assert isinstance(result, tuple)
|
|
assert len(result) == 5
|
|
|
|
|
|
# =============================================================================
|
|
# Test Backend Enums
|
|
# =============================================================================
|
|
|
|
|
|
class TestBackendEnums:
|
|
"""Tests for backend enum values."""
|
|
|
|
def test_store_backend_values(self):
|
|
"""StoreBackend should have expected values."""
|
|
assert StoreBackend.SQLITE.value == "sqlite"
|
|
|
|
def test_vector_backend_values(self):
|
|
"""VectorBackend should have expected values."""
|
|
assert VectorBackend.HNSW.value == "hnsw"
|
|
|
|
def test_text_backend_values(self):
|
|
"""TextBackend should have expected values."""
|
|
assert TextBackend.FTS5.value == "fts5"
|
|
|
|
def test_embedder_backend_values(self):
|
|
"""EmbedderBackend should have expected values."""
|
|
assert EmbedderBackend.LOCAL.value == "local"
|
|
assert EmbedderBackend.OPENAI.value == "openai"
|
|
assert EmbedderBackend.OLLAMA.value == "ollama"
|
|
|
|
|
|
# =============================================================================
|
|
# Integration Tests
|
|
# =============================================================================
|
|
|
|
|
|
class TestFactoryIntegration:
|
|
"""Integration tests for factory module."""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_created_components_are_usable(self):
|
|
"""Created components should be functional."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
config = MemoryConfig(db_path=Path(tmpdir) / "test.db")
|
|
store, vector, text, embedder, cache = await create_memory_system(config)
|
|
|
|
# Test store has expected interface (SQLiteMemoryStore uses 'save' not 'add')
|
|
assert hasattr(store, "save")
|
|
assert hasattr(store, "get")
|
|
assert hasattr(store, "delete")
|
|
assert hasattr(store, "query")
|
|
|
|
# Test vector index has expected interface (HNSWVectorIndex uses 'index')
|
|
assert hasattr(vector, "index")
|
|
assert hasattr(vector, "index_batch")
|
|
assert hasattr(vector, "search")
|
|
assert hasattr(vector, "remove")
|
|
|
|
# Test text index has expected interface (FTS5TextIndex uses 'index')
|
|
assert hasattr(text, "index")
|
|
assert hasattr(text, "index_batch")
|
|
assert hasattr(text, "search")
|
|
|
|
# Test embedder has expected interface
|
|
assert hasattr(embedder, "embed")
|
|
assert hasattr(embedder, "embed_batch")
|
|
assert hasattr(embedder, "dimension")
|
|
|
|
# Test cache has expected interface
|
|
assert cache is not None
|
|
assert hasattr(cache, "get")
|
|
assert hasattr(cache, "put")
|
|
assert hasattr(cache, "invalidate")
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_embedder_dimension_property(self):
|
|
"""Created embedder should report correct dimension."""
|
|
config = MemoryConfig(embedder_backend=EmbedderBackend.LOCAL)
|
|
embedder = _create_embedder(config)
|
|
|
|
# LocalEmbedder reports dimension before model is loaded
|
|
assert embedder.dimension == 384 # all-MiniLM-L6-v2 default
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_cache_operations(self):
|
|
"""Created cache should support basic operations."""
|
|
config = MemoryConfig(cache_max_size=10)
|
|
cache = _create_cache(config)
|
|
|
|
# Cache should be empty initially
|
|
assert cache.size == 0
|
|
assert cache.max_size == 10
|
|
|
|
# Test stats
|
|
stats = cache.stats()
|
|
assert stats["size"] == 0
|
|
assert stats["max_size"] == 10
|
|
assert stats["utilization"] == 0.0
|
|
|
|
def test_config_with_all_custom_options(self):
|
|
"""Config should accept all custom options together."""
|
|
config = MemoryConfig(
|
|
store_backend=StoreBackend.SQLITE,
|
|
db_path=Path("/custom/path.db"),
|
|
vector_backend=VectorBackend.HNSW,
|
|
vector_dimension=1024,
|
|
hnsw_ef_construction=500,
|
|
hnsw_m=48,
|
|
hnsw_ef_search=200,
|
|
text_backend=TextBackend.FTS5,
|
|
embedder_backend=EmbedderBackend.OLLAMA,
|
|
embedder_model="custom-model",
|
|
ollama_base_url="http://custom:11434",
|
|
cache_enabled=True,
|
|
cache_max_size=5000,
|
|
auto_bubble=False,
|
|
bubble_threshold=0.5,
|
|
)
|
|
|
|
assert config.store_backend == StoreBackend.SQLITE
|
|
assert config.db_path == Path("/custom/path.db")
|
|
assert config.vector_dimension == 1024
|
|
assert config.hnsw_ef_construction == 500
|
|
assert config.hnsw_m == 48
|
|
assert config.hnsw_ef_search == 200
|
|
assert config.embedder_backend == EmbedderBackend.OLLAMA
|
|
assert config.embedder_model == "custom-model"
|
|
assert config.ollama_base_url == "http://custom:11434"
|
|
assert config.cache_enabled is True
|
|
assert config.cache_max_size == 5000
|
|
assert config.auto_bubble is False
|
|
assert config.bubble_threshold == 0.5
|