Skip to content

Commit 1428d18

Browse files
phernandezclaude
andauthored
fix: make semantic search dependencies optional extras (#566)
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 312662f commit 1428d18

13 files changed

Lines changed: 91 additions & 25 deletions

.github/workflows/test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
5656
- name: Install dependencies
5757
run: |
58-
uv pip install -e .[dev]
58+
uv pip install -e ".[dev,semantic]"
5959
6060
- name: Run type checks
6161
run: |
@@ -105,7 +105,7 @@ jobs:
105105
106106
- name: Install dependencies
107107
run: |
108-
uv pip install -e .[dev]
108+
uv pip install -e ".[dev,semantic]"
109109
110110
- name: Run tests (Postgres via testcontainers)
111111
run: |
@@ -141,7 +141,7 @@ jobs:
141141
142142
- name: Install dependencies
143143
run: |
144-
uv pip install -e .[dev]
144+
uv pip install -e ".[dev,semantic]"
145145
146146
- name: Run combined coverage (SQLite + Postgres)
147147
run: |

docs/semantic-search.md

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,70 @@ Basic Memory's default search uses full-text search (FTS) — keyword matching w
1212

1313
Semantic search is **opt-in** — existing behavior is completely unchanged unless you enable it. It works on both SQLite (local) and Postgres (cloud) backends.
1414

15+
## Installation
16+
17+
Semantic search dependencies (fastembed, sqlite-vec, openai) are **optional extras** — they are not installed with the base `basic-memory` package. Install them with:
18+
19+
```bash
20+
pip install 'basic-memory[semantic]'
21+
```
22+
23+
This keeps the base install lightweight and avoids platform-specific issues with ONNX Runtime wheels.
24+
25+
### Platform Compatibility
26+
27+
| Platform | FastEmbed (local) | OpenAI (API) |
28+
|---|---|---|
29+
| macOS ARM64 (Apple Silicon) | Yes | Yes |
30+
| macOS x86_64 (Intel Mac) | No — see workaround below | Yes |
31+
| Linux x86_64 | Yes | Yes |
32+
| Linux ARM64 | Yes | Yes |
33+
| Windows x86_64 | Yes | Yes |
34+
35+
#### Intel Mac Workaround
36+
37+
The default FastEmbed provider uses ONNX Runtime, which dropped Intel Mac (x86_64) wheels starting in v1.24. Intel Mac users have two options:
38+
39+
**Option 1: Use OpenAI embeddings (recommended)**
40+
41+
Install only the OpenAI dependency manually — no ONNX Runtime or FastEmbed needed:
42+
43+
```bash
44+
pip install openai sqlite-vec
45+
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
46+
export BASIC_MEMORY_SEMANTIC_EMBEDDING_PROVIDER=openai
47+
export OPENAI_API_KEY=sk-...
48+
```
49+
50+
**Option 2: Pin an older ONNX Runtime**
51+
52+
FastEmbed's ONNX Runtime dependency is unpinned, so you can constrain it to an older version that still ships Intel Mac wheels by passing both requirements in the same install command:
53+
54+
```bash
55+
pip install 'basic-memory[semantic]' 'onnxruntime<1.24'
56+
```
57+
1558
## Quick Start
1659

17-
1. Enable semantic search:
60+
1. Install semantic extras:
61+
62+
```bash
63+
pip install 'basic-memory[semantic]'
64+
```
65+
66+
2. Enable semantic search:
1867

1968
```bash
2069
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
2170
```
2271

23-
2. Build vector embeddings for your existing content:
72+
3. Build vector embeddings for your existing content:
2473

2574
```bash
2675
bm reindex --embeddings
2776
```
2877

29-
3. Search using semantic modes:
78+
4. Search using semantic modes:
3079

3180
```python
3281
# Pure vector similarity
@@ -63,7 +112,8 @@ FastEmbed runs entirely locally using ONNX models — no API key, no network cal
63112
- **Tradeoff**: Smaller model, fast inference, good quality for most use cases
64113

65114
```bash
66-
# FastEmbed is the default — just enable semantic search
115+
# Install semantic extras and enable
116+
pip install 'basic-memory[semantic]'
67117
export BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED=true
68118
```
69119

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ dependencies = [
4444
"sniffio>=1.3.1",
4545
"anyio>=4.10.0",
4646
"httpx>=0.28.0",
47+
]
48+
49+
[project.optional-dependencies]
50+
semantic = [
4751
"fastembed>=0.7.4",
4852
"sqlite-vec>=0.1.6",
4953
"openai>=1.100.2",

src/basic_memory/mcp/tools/search.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ def _format_search_error_response(
3838
`search_notes("{project}", "{query}", search_type="text")`
3939
""").strip()
4040

41-
if "pip install basic-memory" in error_message.lower():
41+
if "pip install" in error_message.lower() and "semantic" in error_message.lower():
4242
return dedent(f"""
4343
# Search Failed - Semantic Dependencies Missing
4444
4545
Semantic retrieval is enabled but required packages are not installed.
4646
4747
## Fix
48-
1. Reinstall basic-memory: `pip install basic-memory`
48+
1. Install semantic extras: `pip install 'basic-memory[semantic]'`
4949
2. Restart Basic Memory
5050
3. Retry your query:
5151
`search_notes("{project}", "{query}", search_type="{search_type}")`

src/basic_memory/repository/embedding_provider_factory.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
from basic_memory.config import BasicMemoryConfig
44
from basic_memory.repository.embedding_provider import EmbeddingProvider
5-
from basic_memory.repository.fastembed_provider import FastEmbedEmbeddingProvider
6-
from basic_memory.repository.openai_provider import OpenAIEmbeddingProvider
75

86

97
def create_embedding_provider(app_config: BasicMemoryConfig) -> EmbeddingProvider:
@@ -18,13 +16,19 @@ def create_embedding_provider(app_config: BasicMemoryConfig) -> EmbeddingProvide
1816
extra_kwargs["dimensions"] = app_config.semantic_embedding_dimensions
1917

2018
if provider_name == "fastembed":
19+
# Deferred import: fastembed (and its onnxruntime dep) may not be installed
20+
from basic_memory.repository.fastembed_provider import FastEmbedEmbeddingProvider
21+
2122
return FastEmbedEmbeddingProvider(
2223
model_name=app_config.semantic_embedding_model,
2324
batch_size=app_config.semantic_embedding_batch_size,
2425
**extra_kwargs,
2526
)
2627

2728
if provider_name == "openai":
29+
# Deferred import: openai may not be installed
30+
from basic_memory.repository.openai_provider import OpenAIEmbeddingProvider
31+
2832
model_name = app_config.semantic_embedding_model or "text-embedding-3-small"
2933
if model_name == "bge-small-en-v1.5":
3034
model_name = "text-embedding-3-small"

src/basic_memory/repository/fastembed_provider.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def _create_model() -> "TextEmbedding":
4848
) as exc: # pragma: no cover - exercised via tests with monkeypatch
4949
raise SemanticDependenciesMissingError(
5050
"fastembed package is missing. "
51-
"Reinstall basic-memory: pip install basic-memory"
51+
"Install semantic extras: pip install 'basic-memory[semantic]'"
5252
) from exc
5353
resolved_model_name = self._MODEL_ALIASES.get(self.model_name, self.model_name)
5454
return TextEmbedding(model_name=resolved_model_name)

src/basic_memory/repository/openai_provider.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ async def _get_client(self) -> Any:
4444
from openai import AsyncOpenAI
4545
except ImportError as exc: # pragma: no cover - covered via monkeypatch tests
4646
raise SemanticDependenciesMissingError(
47-
"OpenAI dependency is missing. Reinstall basic-memory: pip install basic-memory"
47+
"OpenAI dependency is missing. "
48+
"Install semantic extras: pip install 'basic-memory[semantic]'"
4849
) from exc
4950

5051
api_key = self._api_key or os.getenv("OPENAI_API_KEY")

src/basic_memory/repository/search_repository_base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ def _assert_semantic_available(self) -> None:
353353
if self._embedding_provider is None:
354354
raise SemanticDependenciesMissingError(
355355
"No embedding provider configured. "
356-
"Ensure semantic_search_enabled is true in your config."
356+
"Install semantic extras: pip install 'basic-memory[semantic]' "
357+
"and set semantic_search_enabled=true."
357358
)
358359

359360
def _compose_row_source_text(self, row) -> str:

src/basic_memory/repository/sqlite_search_repository.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,8 @@ async def _ensure_sqlite_vec_loaded(self, session) -> None:
339339
import sqlite_vec
340340
except ImportError as exc:
341341
raise SemanticDependenciesMissingError(
342-
"sqlite-vec package is missing. Reinstall basic-memory: pip install basic-memory"
342+
"sqlite-vec package is missing. "
343+
"Install semantic extras: pip install 'basic-memory[semantic]'"
343344
) from exc
344345

345346
async with self._sqlite_vec_lock:

tests/mcp/test_tool_search.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,13 @@ def test_format_search_error_semantic_dependencies_missing(self):
291291
"""Test formatting for missing semantic dependencies."""
292292
result = _format_search_error_response(
293293
"test-project",
294-
"fastembed package is missing. Reinstall basic-memory: pip install basic-memory",
294+
"fastembed package is missing. Install semantic extras: pip install 'basic-memory[semantic]'",
295295
"semantic query",
296296
"hybrid",
297297
)
298298

299299
assert "# Search Failed - Semantic Dependencies Missing" in result
300-
assert "pip install basic-memory" in result
300+
assert "pip install 'basic-memory[semantic]'" in result
301301

302302
def test_format_search_error_generic(self):
303303
"""Test formatting for generic errors."""

0 commit comments

Comments
 (0)