Skip to content

Commit 91a5b1d

Browse files
authored
Merge branch 'main' into feat/add-url-to-paginated-response
2 parents e28a57a + 28ca00d commit 91a5b1d

File tree

5 files changed

+157
-2
lines changed

5 files changed

+157
-2
lines changed

llama_stack/providers/inline/vector_io/faiss/faiss.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ async def query_vector(
112112
if i < 0:
113113
continue
114114
chunks.append(self.chunk_by_index[int(i)])
115-
scores.append(1.0 / float(d))
115+
scores.append(1.0 / float(d) if d != 0 else float("inf"))
116116

117117
return QueryChunksResponse(chunks=chunks, scores=scores)
118118

llama_stack/providers/remote/vector_io/pgvector/pgvector.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ async def query_vector(self, embedding: NDArray, k: int, score_threshold: float)
116116
scores = []
117117
for doc, dist in results:
118118
chunks.append(Chunk(**doc))
119-
scores.append(1.0 / float(dist))
119+
scores.append(1.0 / float(dist) if dist != 0 else float("inf"))
120120

121121
return QueryChunksResponse(chunks=chunks, scores=scores)
122122

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ unit = [
8484
"sqlalchemy",
8585
"sqlalchemy[asyncio]>=2.0.41",
8686
"blobfile",
87+
"faiss-cpu"
8788
]
8889
# These are the core dependencies required for running integration tests. They are shared across all
8990
# providers. If a provider requires additional dependencies, please add them to your environment
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the terms described in the LICENSE file in
5+
# the root directory of this source tree.
6+
7+
import asyncio
8+
from unittest.mock import AsyncMock, MagicMock, patch
9+
10+
import numpy as np
11+
import pytest
12+
import pytest_asyncio
13+
14+
from llama_stack.apis.inference import EmbeddingsResponse, Inference
15+
from llama_stack.apis.vector_dbs import VectorDB
16+
from llama_stack.apis.vector_io import Chunk, QueryChunksResponse
17+
from llama_stack.providers.inline.vector_io.faiss.config import FaissVectorIOConfig
18+
from llama_stack.providers.inline.vector_io.faiss.faiss import (
19+
FaissIndex,
20+
FaissVectorIOAdapter,
21+
)
22+
23+
# This test is a unit test for the FaissVectorIOAdapter class. This should only contain
24+
# tests which are specific to this class. More general (API-level) tests should be placed in
25+
# tests/integration/vector_io/
26+
#
27+
# How to run this test:
28+
#
29+
# pytest tests/unit/providers/vector_io/test_faiss.py \
30+
# -v -s --tb=short --disable-warnings --asyncio-mode=auto
31+
32+
FAISS_PROVIDER = "faiss"
33+
34+
35+
@pytest.fixture(scope="session")
36+
def loop():
37+
return asyncio.new_event_loop()
38+
39+
40+
@pytest.fixture
41+
def embedding_dimension():
42+
return 384
43+
44+
45+
@pytest.fixture
46+
def vector_db_id():
47+
return "test_vector_db"
48+
49+
50+
@pytest.fixture
51+
def sample_chunks():
52+
return [
53+
Chunk(content="MOCK text content 1", mime_type="text/plain", metadata={"document_id": "mock-doc-1"}),
54+
Chunk(content="MOCK text content 1", mime_type="text/plain", metadata={"document_id": "mock-doc-2"}),
55+
]
56+
57+
58+
@pytest.fixture
59+
def sample_embeddings(embedding_dimension):
60+
return np.random.rand(2, embedding_dimension).astype(np.float32)
61+
62+
63+
@pytest.fixture
64+
def mock_vector_db(vector_db_id, embedding_dimension) -> MagicMock:
65+
mock_vector_db = MagicMock(spec=VectorDB)
66+
mock_vector_db.embedding_model = "mock_embedding_model"
67+
mock_vector_db.identifier = vector_db_id
68+
mock_vector_db.embedding_dimension = embedding_dimension
69+
return mock_vector_db
70+
71+
72+
@pytest.fixture
73+
def mock_inference_api(sample_embeddings):
74+
mock_api = MagicMock(spec=Inference)
75+
mock_api.embeddings = AsyncMock(return_value=EmbeddingsResponse(embeddings=sample_embeddings))
76+
return mock_api
77+
78+
79+
@pytest.fixture
80+
def faiss_config():
81+
config = MagicMock(spec=FaissVectorIOConfig)
82+
config.kvstore = None
83+
return config
84+
85+
86+
@pytest_asyncio.fixture
87+
async def faiss_index(embedding_dimension):
88+
index = await FaissIndex.create(dimension=embedding_dimension)
89+
yield index
90+
91+
92+
@pytest_asyncio.fixture
93+
async def faiss_adapter(faiss_config, mock_inference_api) -> FaissVectorIOAdapter:
94+
adapter = FaissVectorIOAdapter(config=faiss_config, inference_api=mock_inference_api)
95+
await adapter.initialize()
96+
yield adapter
97+
await adapter.shutdown()
98+
99+
100+
@pytest.mark.asyncio
101+
async def test_faiss_query_vector_returns_infinity_when_query_and_embedding_are_identical(
102+
faiss_index, sample_chunks, sample_embeddings, embedding_dimension
103+
):
104+
await faiss_index.add_chunks(sample_chunks, sample_embeddings)
105+
query_embedding = np.random.rand(embedding_dimension).astype(np.float32)
106+
107+
with patch.object(faiss_index.index, "search") as mock_search:
108+
mock_search.return_value = (np.array([[0.0, 0.1]]), np.array([[0, 1]]))
109+
110+
response = await faiss_index.query_vector(embedding=query_embedding, k=2, score_threshold=0.0)
111+
112+
assert isinstance(response, QueryChunksResponse)
113+
assert len(response.chunks) == 2
114+
assert len(response.scores) == 2
115+
116+
assert response.scores[0] == float("inf") # infinity (1.0 / 0.0)
117+
assert response.scores[1] == 10.0 # (1.0 / 0.1 = 10.0)
118+
119+
assert response.chunks[0] == sample_chunks[0]
120+
assert response.chunks[1] == sample_chunks[1]

uv.lock

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)