mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description `headroom wrap --memory` could never import memories: the startup sync subprocess (`python -m headroom.memory.sync`) and the in-process Codex memory import both built their backend with `LocalBackendConfig(db_path=...)`, which defaults `embedder_backend` to `"local"` — sentence-transformers + PyTorch (~2 GB). On the proxy extras that dependency is absent, so sync crashed with `ImportError: sentence-transformers is required for LocalEmbedder` while the proxy itself served memory fine via the torch-free ONNX backend. This routes both paths through a shared `_build_sync_backend` helper that uses `embedder_backend="onnx"`, matching the proxy MCP server (`headroom/memory/mcp_server.py`). Closes #1092 ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) ## Changes Made - `headroom/memory/sync.py`: added `_build_sync_backend(db_path)` that constructs the backend with `embedder_backend="onnx"`; the sync CLI subprocess now uses it. - `headroom/cli/wrap.py`: the in-process Claude→DB memory import (Codex wrap path) now uses the same helper instead of the LOCAL-defaulting `LocalBackendConfig`. - `tests/test_memory_sync.py`: added `test_sync_backend_uses_onnx_embedder` regression test. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [x] New tests added for new functionality ### Test Output ```text $ python -m pytest tests/test_memory_sync.py -q 31 passed $ python -m ruff check headroom/memory/sync.py headroom/cli/wrap.py tests/test_memory_sync.py All checks passed! ``` ## Real Behavior Proof - Environment: Windows 11, Python 3.13, headroom on branch fix/1092-memory-sync-onnx-embedder - Exact command / steps: Ran the memory-sync suite + ruff, and an import smoke that builds the sync backend: `python -c "from headroom.memory.sync import _build_sync_backend; print(_build_sync_backend('x.db')._config.embedder_backend)"`. - Observed result: 31 tests pass (incl. the new regression test), ruff clean, and the smoke prints `onnx` — the sync backend no longer defaults to the sentence-transformers embedder. - Not tested: Did not run a full live `headroom wrap claude --memory` end to end (needs the ONNX model download + Claude memory files); the same-model (all-MiniLM-L6-v2, 384-dim) ONNX backend the proxy already uses keeps vectors DB-compatible, so no migration is involved. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b5f63d8fa9
commit
4f9fedaa7a
3 changed files with 30 additions and 8 deletions
|
|
@ -3719,8 +3719,7 @@ def codex(
|
|||
try:
|
||||
import asyncio
|
||||
|
||||
from headroom.memory.backends.local import LocalBackend, LocalBackendConfig
|
||||
from headroom.memory.sync import sync_import
|
||||
from headroom.memory.sync import _build_sync_backend, sync_import
|
||||
from headroom.memory.sync_adapters.claude_code import (
|
||||
ClaudeCodeAdapter,
|
||||
get_claude_memory_dir,
|
||||
|
|
@ -3729,8 +3728,7 @@ def codex(
|
|||
claude_memory_dir = get_claude_memory_dir()
|
||||
|
||||
async def _import_claude_memories() -> int:
|
||||
config = LocalBackendConfig(db_path=db_path)
|
||||
backend = LocalBackend(config)
|
||||
backend = _build_sync_backend(db_path)
|
||||
await backend._ensure_initialized()
|
||||
adapter = ClaudeCodeAdapter(claude_memory_dir)
|
||||
count = await sync_import(backend, adapter, mem_user)
|
||||
|
|
|
|||
|
|
@ -343,6 +343,21 @@ async def sync_export(
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _build_sync_backend(db_path: str) -> Any:
|
||||
"""Build the memory backend used by the sync subprocess.
|
||||
|
||||
Match the proxy MCP server (see ``headroom/memory/mcp_server.py``): use the
|
||||
torch-free ONNX embedder so ``wrap --memory`` sync works on the proxy extras
|
||||
without sentence-transformers/PyTorch (#1092). It loads the same
|
||||
``all-MiniLM-L6-v2`` 384-dim model as the local embedder, so vectors stay
|
||||
compatible with what the proxy writes — no DB migration.
|
||||
"""
|
||||
from headroom.memory.backends.local import LocalBackend, LocalBackendConfig
|
||||
|
||||
config = LocalBackendConfig(db_path=db_path, embedder_backend="onnx")
|
||||
return LocalBackend(config)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""CLI entry point for running sync from a subprocess."""
|
||||
import argparse
|
||||
|
|
@ -358,10 +373,7 @@ def main() -> None:
|
|||
import json as _json
|
||||
|
||||
async def _run() -> None:
|
||||
from headroom.memory.backends.local import LocalBackend, LocalBackendConfig
|
||||
|
||||
config = LocalBackendConfig(db_path=args.db)
|
||||
backend = LocalBackend(config)
|
||||
backend = _build_sync_backend(args.db)
|
||||
await backend._ensure_initialized()
|
||||
|
||||
if args.agent == "claude":
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from typing import Any
|
|||
import pytest
|
||||
|
||||
from headroom.memory.sync import (
|
||||
_build_sync_backend,
|
||||
sync,
|
||||
sync_export,
|
||||
sync_import,
|
||||
|
|
@ -675,3 +676,14 @@ class TestCrossAgentInterop:
|
|||
# AGENTS.md has both (from DB)
|
||||
agents_content = agents_md.read_text()
|
||||
assert "FastAPI" in agents_content or "8787" in agents_content
|
||||
|
||||
|
||||
def test_sync_backend_uses_onnx_embedder(tmp_path):
|
||||
"""#1092: the sync subprocess must pick the torch-free ONNX embedder.
|
||||
|
||||
Defaulting to the LOCAL (sentence-transformers) embedder makes
|
||||
`wrap --memory` crash with an ImportError on the proxy extras. The backend
|
||||
must match the proxy MCP server, which uses ONNX.
|
||||
"""
|
||||
backend = _build_sync_backend(str(tmp_path / "memory.db"))
|
||||
assert backend._config.embedder_backend == "onnx"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue