Aissugur Kurganbekov Claude Opus 4.8 commited on
Commit
03863fd
·
1 Parent(s): f65a6b3

Fix CI: make tests hermetic and harden hybrid_rank against attr-less chunks

Browse files

CI installs fastembed (requirements-dev -> requirements), which activated the
dense retrieval path during tests and surfaced two issues:
- hybrid_rank read chunk.embedding directly, crashing on the SimpleNamespace
test stand-ins (and any chunk-like object without the attribute). Use
getattr(c, embedding, None) so such a chunk simply stays lexical.
- The suite would download the ~130 MB embedding model on every run (slow and
flaky). conftest now disables embeddings, so tests are hermetic and
deterministic; the TF-IDF+vector fusion is already unit-tested with mocked
vectors in test_embeddings.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

backend/app/retrieval.py CHANGED
@@ -110,7 +110,9 @@ def hybrid_rank(
110
  return tfidf_scored[:top_k]
111
 
112
  # Semantic ranking over every chunk that has a stored vector (one matmul).
113
- pairs = [(c, embeddings.to_vector(c.embedding)) for c in chunks]
 
 
114
  pairs = [(c, v) for c, v in pairs if v is not None and v.shape == qvec.shape]
115
  if not pairs:
116
  return tfidf_scored[:top_k]
 
110
  return tfidf_scored[:top_k]
111
 
112
  # Semantic ranking over every chunk that has a stored vector (one matmul).
113
+ # getattr keeps this robust to any chunk-like object without an embedding
114
+ # attribute — such a chunk simply contributes no vector and stays lexical.
115
+ pairs = [(c, embeddings.to_vector(getattr(c, "embedding", None))) for c in chunks]
116
  pairs = [(c, v) for c, v in pairs if v is not None and v.shape == qvec.shape]
117
  if not pairs:
118
  return tfidf_scored[:top_k]
backend/tests/conftest.py CHANGED
@@ -14,9 +14,16 @@ from fastapi.testclient import TestClient
14
  from sqlalchemy import create_engine
15
  from sqlalchemy.orm import sessionmaker
16
 
 
17
  from app.database import Base, get_db
18
  from app.main import app
19
 
 
 
 
 
 
 
20
 
21
  @pytest.fixture()
22
  def _engine():
 
14
  from sqlalchemy import create_engine
15
  from sqlalchemy.orm import sessionmaker
16
 
17
+ from app import config
18
  from app.database import Base, get_db
19
  from app.main import app
20
 
21
+ # Keep the suite hermetic and deterministic: the optional dense-embedding model
22
+ # (fastembed) is a ~130 MB network download that would make CI slow and flaky, so
23
+ # tests run on pure TF-IDF. The hybrid TF-IDF+vector fusion itself is covered
24
+ # deterministically with mocked vectors in test_embeddings.py.
25
+ config.EMBED_ENABLED = False
26
+
27
 
28
  @pytest.fixture()
29
  def _engine():