diff --git a/agent/embeddings/__init__.py b/agent/embeddings/__init__.py index d241230..e56b5c9 100644 --- a/agent/embeddings/__init__.py +++ b/agent/embeddings/__init__.py @@ -1,7 +1,6 @@ from .cache import ( DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILENAME, - LEGACY_CACHE_FILENAME, EmbeddingCache, default_embedding_cache_path, ) @@ -27,7 +26,6 @@ __all__ = [ "DEFAULT_CACHE_DIR", "DEFAULT_CACHE_FILENAME", - "LEGACY_CACHE_FILENAME", "DEFAULT_CHUNK_CHAR_LIMIT", "EmbeddingCache", "EmbeddingAdapter", diff --git a/agent/embeddings/cache.py b/agent/embeddings/cache.py index 4318425..ca6e1ab 100644 --- a/agent/embeddings/cache.py +++ b/agent/embeddings/cache.py @@ -1,4 +1,3 @@ -import json import os from dataclasses import dataclass, field from pathlib import Path @@ -11,7 +10,6 @@ DEFAULT_CACHE_DIR = ".code-orbit" DEFAULT_CACHE_FILENAME = "embeddings_cache.npz" -LEGACY_CACHE_FILENAME = "embeddings_cache.json" _CACHE_FORMAT_VERSION = 2 @@ -25,19 +23,12 @@ class EmbeddingCache: @classmethod def load(cls, path: Path) -> EmbeddingCache: if not path.exists(): - legacy_path = _legacy_cache_sibling_path(path) - if legacy_path.exists(): - cache = _load_legacy_json_cache(legacy_path) - if cache is not None: - return cache return cls() - if _looks_like_npz(path): - cache = _load_npz_cache(path) - if cache is not None: - return cache + if not _looks_like_npz(path): + return cls() - cache = _load_legacy_json_cache(path) + cache = _load_npz_cache(path) if cache is not None: return cache @@ -72,15 +63,6 @@ def default_embedding_cache_path(root: str | Path) -> Path: return root_path / DEFAULT_CACHE_DIR / DEFAULT_CACHE_FILENAME -def _legacy_embedding_cache_path(root: str | Path) -> Path: - root_path = Path(root).resolve() - return root_path / DEFAULT_CACHE_DIR / LEGACY_CACHE_FILENAME - - -def _legacy_cache_sibling_path(path: Path) -> Path: - return path.parent / LEGACY_CACHE_FILENAME - - def _looks_like_npz(path: Path) -> bool: try: with path.open("rb") as file_handle: @@ -148,38 +130,6 @@ def _serialize_cache_arrays( return metadata, vectors_array -def _load_legacy_json_cache(path: Path) -> EmbeddingCache | None: - try: - raw = json.loads(path.read_text(encoding="utf-8")) - except (OSError, json.JSONDecodeError): - return None - - files: dict[str, FileEmbeddingRecord] = {} - for rel_path, payload in raw.get("files", {}).items(): - chunks = tuple( - ChunkEmbedding( - index=int(chunk["index"]), - vector=tuple(float(value) for value in chunk["vector"]), - start_line=int(chunk["start_line"]), - end_line=int(chunk["end_line"]), - content_hash=str(chunk["content_hash"]), - ) - for chunk in payload.get("chunks", []) - ) - files[rel_path] = FileEmbeddingRecord( - path=rel_path, - sha256=str(payload.get("sha256", "")), - chunks=chunks, - ) - - return EmbeddingCache( - version=int(raw.get("version", _CACHE_FORMAT_VERSION)), - model=str(raw.get("model", "")), - api_base=str(raw.get("api_base", "")), - files=files, - ) - - def _load_npz_cache(path: Path) -> EmbeddingCache | None: try: with np.load(path, allow_pickle=False) as archive: diff --git a/agent/embeddings/index.py b/agent/embeddings/index.py index 851739f..21678de 100644 --- a/agent/embeddings/index.py +++ b/agent/embeddings/index.py @@ -6,11 +6,7 @@ from typing import Protocol, runtime_checkable from ..config import Config -from .cache import ( - EmbeddingCache, - _legacy_embedding_cache_path, - default_embedding_cache_path, -) +from .cache import EmbeddingCache, default_embedding_cache_path from .adapters import EmbeddingAdapter from .chunking import chunk_file from .factory import create_embedding_adapter @@ -109,13 +105,8 @@ async def build_embedding_index( """ root_path = Path(root).resolve() cache_file = cache_path or default_embedding_cache_path(root_path) - cache_load_path = cache_file - if cache_path is None and not cache_file.exists(): - legacy_cache_file = _legacy_embedding_cache_path(root_path) - if legacy_cache_file.exists(): - cache_load_path = legacy_cache_file - cache = EmbeddingCache.load(cache_load_path) + cache = EmbeddingCache.load(cache_file) if not cache.is_compatible(config): cache = EmbeddingCache( model=config.embedding_model, @@ -240,7 +231,7 @@ async def guarded_embed_batch( # Dirty-check: skip the full .npz rewrite when nothing changed and a # cache file already exists. A missing file is always (re)written so - # fresh checkouts and legacy-JSON migrations materialize it once. + # fresh checkouts materialize it once. if updated_files or stale_paths or not cache_file.exists(): cache.save(cache_file) vector_store = VectorStore(cache.files.values()) diff --git a/pyproject.toml b/pyproject.toml index 29b898c..6e68dd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "code-orbit" -version = "1.8.8" +version = "1.8.10" description = "AI-powered codebase editor using local llama.cpp models" readme = "README.md" requires-python = ">=3.14" diff --git a/tests/test_embeddings.py b/tests/test_embeddings.py index 076a7ca..97654d7 100644 --- a/tests/test_embeddings.py +++ b/tests/test_embeddings.py @@ -1,4 +1,3 @@ -import json import hashlib from pathlib import Path @@ -161,125 +160,35 @@ def test_embedding_cache_round_trips_npz(tmp_path: Path) -> None: assert loaded.files == cache.files -def test_embedding_cache_loads_legacy_json(tmp_path: Path) -> None: +def test_embedding_cache_ignores_non_npz_file(tmp_path: Path) -> None: cache_path = tmp_path / "embeddings_cache.json" - cache_path.write_text( - json.dumps( - { - "version": 1, - "model": "legacy-model", - "api_base": "http://legacy.invalid/v1", - "files": { - "src/app.py": { - "sha256": "abc123", - "chunks": [ - { - "index": 0, - "vector": [0.4, 0.5], - "start_line": 1, - "end_line": 2, - "content_hash": "legacy-chunk", - } - ], - } - }, - } - ), - encoding="utf-8", - ) + cache_path.write_text('{"version": 1, "files": {}}', encoding="utf-8") cache = EmbeddingCache.load(cache_path) - assert cache.version == 1 - assert cache.model == "legacy-model" - assert cache.api_base == "http://legacy.invalid/v1" - assert cache.files["src/app.py"].sha256 == "abc123" - assert cache.files["src/app.py"].chunks[0].vector == (0.4, 0.5) + assert cache.files == {} -def test_embedding_cache_loads_legacy_json_sibling_for_missing_npz( +def test_embedding_cache_ignores_legacy_json_sibling_for_missing_npz( tmp_path: Path, ) -> None: cache_path = tmp_path / ".code-orbit" / "embeddings_cache.npz" cache_path.parent.mkdir() legacy_path = cache_path.parent / "embeddings_cache.json" - legacy_path.write_text( - json.dumps( - { - "version": 1, - "model": "legacy-model", - "api_base": "http://legacy.invalid/v1", - "files": { - "src/app.py": { - "sha256": "abc123", - "chunks": [ - { - "index": 0, - "vector": [0.4, 0.5], - "start_line": 1, - "end_line": 2, - "content_hash": "legacy-chunk", - } - ], - } - }, - } - ), - encoding="utf-8", - ) + legacy_path.write_text('{"version": 1, "files": {}}', encoding="utf-8") cache = EmbeddingCache.load(cache_path) - assert cache.model == "legacy-model" - assert cache.files["src/app.py"].chunks[0].vector == (0.4, 0.5) + assert cache.files == {} -def test_build_embedding_sync_migrates_legacy_cache(tmp_path: Path) -> None: +def test_build_embedding_sync_does_not_reuse_legacy_json_cache( + tmp_path: Path, +) -> None: _write_codebase(tmp_path) config = Config(ignore_patterns=[".git", "node_modules"]) legacy_cache_dir = tmp_path / ".code-orbit" legacy_cache_dir.mkdir() - legacy_cache_path = legacy_cache_dir / "embeddings_cache.json" - - middleware_hash = hashlib.sha256( - (tmp_path / "src" / "auth" / "middleware.py").read_bytes() - ).hexdigest() - tests_hash = hashlib.sha256( - (tmp_path / "src" / "tests.py").read_bytes() - ).hexdigest() - legacy_cache_path.write_text( - json.dumps( - { - "version": 1, - "model": config.embedding_model, - "api_base": config.embedding_api_base, - "files": { - "src/auth/middleware.py": { - "sha256": middleware_hash, - "chunks": [ - { - "index": 0, - "vector": [0.9, 0.1], - "start_line": 1, - "end_line": 2, - "content_hash": "middleware-chunk", - } - ], - }, - "src/tests.py": { - "sha256": tests_hash, - "chunks": [ - { - "index": 0, - "vector": [0.1, 0.9], - "start_line": 1, - "end_line": 2, - "content_hash": "tests-chunk", - } - ], - }, - }, - } - ), - encoding="utf-8", + (legacy_cache_dir / "embeddings_cache.json").write_text( + '{"version": 1, "files": {}}', encoding="utf-8" ) client = FakeEmbeddingClient() @@ -292,16 +201,12 @@ def test_build_embedding_sync_migrates_legacy_cache(tmp_path: Path) -> None: ) assert result.cache_path.suffix == ".npz" - assert result.updated_files == () - assert set(result.reused_files) == { - "src/auth/middleware.py", - "src/tests.py", - } - assert client.requests == [] - assert set(EmbeddingCache.load(result.cache_path).files) == { + assert set(result.updated_files) == { "src/auth/middleware.py", "src/tests.py", } + assert result.reused_files == () + assert client.requests != [] def test_build_embedding_sync_reads_each_file_once(tmp_path: Path, monkeypatch) -> None: