Skip to content

Commit 2231321

Browse files
phernandezclaude
andcommitted
feat(config): add default_search_type setting
Add default_search_type to config.json to let users set their preferred search mode (text, vector, hybrid) without specifying search_type on every query. When unset (None), preserves existing auto-detection: hybrid if semantic search is enabled, text otherwise. Closes #671 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent 1a6a655 commit 2231321

4 files changed

Lines changed: 83 additions & 10 deletions

File tree

src/basic_memory/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ class BasicMemoryConfig(BaseSettings):
203203
ge=0.0,
204204
le=1.0,
205205
)
206+
default_search_type: Literal["text", "vector", "hybrid"] | None = Field(
207+
default=None,
208+
description="Default search type for search_notes when not specified per-query. "
209+
"Valid values: text, vector, hybrid. "
210+
"When unset, defaults to 'hybrid' if semantic search is enabled, otherwise 'text'.",
211+
)
206212

207213
# Database connection pool configuration (Postgres only)
208214
db_pool_size: int = Field(

src/basic_memory/mcp/tools/search.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,20 @@
2525
)
2626

2727

28-
def _semantic_search_enabled_for_text_search() -> bool:
29-
"""Resolve semantic-search enablement in both MCP and CLI invocation paths."""
28+
def _default_search_type() -> str:
29+
"""Pick default search mode from config, falling back to auto-detection.
30+
31+
Priority: config default_search_type > auto-detect (hybrid if semantic enabled, else text).
32+
"""
3033
try:
31-
return get_container().config.semantic_search_enabled
34+
config = get_container().config
3235
except RuntimeError:
33-
# Trigger: MCP container is not initialized (e.g., `bm tool search-notes` direct call).
34-
# Why: CLI path still needs the same semantic-default behavior as MCP server path.
35-
# Outcome: load config directly and keep text-mode retrieval behavior consistent.
36-
return ConfigManager().config.semantic_search_enabled
36+
config = ConfigManager().config
3737

38+
if config.default_search_type:
39+
return config.default_search_type
3840

39-
def _default_search_type() -> str:
40-
"""Pick default search mode from semantic-search config."""
41-
return "hybrid" if _semantic_search_enabled_for_text_search() else "text"
41+
return "hybrid" if config.semantic_search_enabled else "text"
4242

4343

4444
def _format_search_error_response(

tests/mcp/test_tool_search.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,3 +1567,54 @@ async def search(self, payload, page, page_size):
15671567

15681568
# "note_type" aliased to "type", "priority" passes through unchanged
15691569
assert captured_payload["metadata_filters"] == {"type": "spec", "priority": "high"}
1570+
1571+
1572+
def test_default_search_type_uses_config_value():
1573+
"""_default_search_type should return config.default_search_type when set."""
1574+
import sys
1575+
from unittest.mock import MagicMock, patch
1576+
1577+
search_module = sys.modules["basic_memory.mcp.tools.search"]
1578+
1579+
mock_config = MagicMock()
1580+
mock_config.default_search_type = "vector"
1581+
mock_config.semantic_search_enabled = True
1582+
mock_container = MagicMock()
1583+
mock_container.config = mock_config
1584+
1585+
with patch.object(search_module, "get_container", return_value=mock_container):
1586+
assert search_module._default_search_type() == "vector"
1587+
1588+
1589+
def test_default_search_type_falls_back_to_hybrid_when_semantic_enabled():
1590+
"""When default_search_type is None and semantic is enabled, default to hybrid."""
1591+
import sys
1592+
from unittest.mock import MagicMock, patch
1593+
1594+
search_module = sys.modules["basic_memory.mcp.tools.search"]
1595+
1596+
mock_config = MagicMock()
1597+
mock_config.default_search_type = None
1598+
mock_config.semantic_search_enabled = True
1599+
mock_container = MagicMock()
1600+
mock_container.config = mock_config
1601+
1602+
with patch.object(search_module, "get_container", return_value=mock_container):
1603+
assert search_module._default_search_type() == "hybrid"
1604+
1605+
1606+
def test_default_search_type_falls_back_to_text_when_semantic_disabled():
1607+
"""When default_search_type is None and semantic is disabled, default to text."""
1608+
import sys
1609+
from unittest.mock import MagicMock, patch
1610+
1611+
search_module = sys.modules["basic_memory.mcp.tools.search"]
1612+
1613+
mock_config = MagicMock()
1614+
mock_config.default_search_type = None
1615+
mock_config.semantic_search_enabled = False
1616+
mock_container = MagicMock()
1617+
mock_container.config = mock_config
1618+
1619+
with patch.object(search_module, "get_container", return_value=mock_container):
1620+
assert search_module._default_search_type() == "text"

tests/test_config.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,22 @@ def test_semantic_min_similarity_bounds_validation(self):
903903
with pytest.raises(Exception):
904904
BasicMemoryConfig(semantic_min_similarity=1.1)
905905

906+
def test_default_search_type_defaults_to_none(self):
907+
"""default_search_type should be None by default (auto-detect)."""
908+
config = BasicMemoryConfig()
909+
assert config.default_search_type is None
910+
911+
def test_default_search_type_accepts_valid_values(self):
912+
"""default_search_type accepts text, vector, hybrid."""
913+
for search_type in ("text", "vector", "hybrid"):
914+
config = BasicMemoryConfig(default_search_type=search_type)
915+
assert config.default_search_type == search_type
916+
917+
def test_default_search_type_rejects_invalid_values(self):
918+
"""default_search_type rejects unknown values."""
919+
with pytest.raises(Exception):
920+
BasicMemoryConfig(default_search_type="invalid")
921+
906922

907923
class TestFormattingConfig:
908924
"""Test file formatting configuration options."""

0 commit comments

Comments
 (0)