Skip to content

Commit 7838172

Browse files
Revert "add milvus support (#187)"
This reverts commit 8f20d00.
1 parent ad4fced commit 7838172

8 files changed

Lines changed: 175 additions & 1019 deletions

File tree

core/config.py

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ class Settings(BaseSettings):
2323
OPENAI_API_KEY: Optional[str] = None
2424
ANTHROPIC_API_KEY: Optional[str] = None
2525
ASSEMBLYAI_API_KEY: Optional[str] = None
26-
# Milvus configuration
27-
MILVUS_URI: Optional[str] = None
28-
MILVUS_API_KEY: Optional[str] = None
2926

3027
# API configuration
3128
HOST: str
@@ -110,13 +107,9 @@ class Settings(BaseSettings):
110107
S3_BUCKET: Optional[str] = None
111108

112109
# Vector store configuration
113-
VECTOR_STORE_PROVIDER: Literal["pgvector", "milvus"]
110+
VECTOR_STORE_PROVIDER: Literal["pgvector"]
114111
VECTOR_STORE_DATABASE_NAME: Optional[str] = None
115112

116-
# Multi-vector store configuration
117-
MULTIVECTOR_PROVIDER: Literal["postgres", "milvus"] = "postgres"
118-
MILVUS_BATCH_SIZE: int = 500 # Batch size for Milvus multivector insertions
119-
120113
# Colpali configuration
121114
ENABLE_COLPALI: bool
122115
# Colpali embedding mode: off, local, or api
@@ -295,54 +288,13 @@ def get_settings() -> Settings:
295288

296289
# load vector store config
297290
vector_store_config = {"VECTOR_STORE_PROVIDER": config["vector_store"]["provider"]}
298-
if vector_store_config["VECTOR_STORE_PROVIDER"] not in ["pgvector", "milvus"]:
291+
if vector_store_config["VECTOR_STORE_PROVIDER"] != "pgvector":
299292
prov = vector_store_config["VECTOR_STORE_PROVIDER"]
300293
raise ValueError(f"Unknown vector store provider selected: '{prov}'")
301294

302-
# Validate required environment variables based on vector store provider
303-
if vector_store_config["VECTOR_STORE_PROVIDER"] == "pgvector":
304-
if "POSTGRES_URI" not in os.environ:
305-
msg = em.format(missing_value="POSTGRES_URI", field="vector_store.provider", value="pgvector")
306-
raise ValueError(msg)
307-
elif vector_store_config["VECTOR_STORE_PROVIDER"] == "milvus":
308-
if "MILVUS_URI" not in os.environ:
309-
msg = em.format(missing_value="MILVUS_URI", field="vector_store.provider", value="milvus")
310-
raise ValueError(msg)
311-
vector_store_config.update(
312-
{
313-
"MILVUS_URI": os.environ["MILVUS_URI"],
314-
"MILVUS_API_KEY": os.environ.get("MILVUS_API_KEY"), # API key is optional for some Milvus setups
315-
}
316-
)
317-
318-
# load multivector store config
319-
multivector_store_config = {}
320-
if "multivector_store" in config:
321-
multivector_store_config = {
322-
"MULTIVECTOR_PROVIDER": config["multivector_store"]["provider"],
323-
"MILVUS_BATCH_SIZE": config["multivector_store"].get("milvus_batch_size", 500), # Default to 500
324-
}
325-
if multivector_store_config["MULTIVECTOR_PROVIDER"] not in ["postgres", "milvus"]:
326-
prov = multivector_store_config["MULTIVECTOR_PROVIDER"]
327-
raise ValueError(f"Unknown multivector store provider selected: '{prov}'")
328-
329-
# Validate required environment variables based on multivector store provider
330-
if multivector_store_config["MULTIVECTOR_PROVIDER"] == "postgres":
331-
if "POSTGRES_URI" not in os.environ:
332-
msg = em.format(missing_value="POSTGRES_URI", field="multivector_store.provider", value="postgres")
333-
raise ValueError(msg)
334-
elif multivector_store_config["MULTIVECTOR_PROVIDER"] == "milvus":
335-
if "MILVUS_URI" not in os.environ:
336-
msg = em.format(missing_value="MILVUS_URI", field="multivector_store.provider", value="milvus")
337-
raise ValueError(msg)
338-
# Add Milvus credentials to config if not already added
339-
if "MILVUS_URI" not in vector_store_config:
340-
multivector_store_config.update(
341-
{
342-
"MILVUS_URI": os.environ["MILVUS_URI"],
343-
"MILVUS_API_KEY": os.environ.get("MILVUS_API_KEY"),
344-
}
345-
)
295+
if "POSTGRES_URI" not in os.environ:
296+
msg = em.format(missing_value="POSTGRES_URI", field="vector_store.provider", value="pgvector")
297+
raise ValueError(msg)
346298

347299
# load rules config
348300
rules_config = {
@@ -430,7 +382,6 @@ def get_settings() -> Settings:
430382
reranker_config,
431383
storage_config,
432384
vector_store_config,
433-
multivector_store_config,
434385
rules_config,
435386
morphik_config,
436387
redis_config,

core/services_init.py

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
from core.services.document_service import DocumentService
2929
from core.storage.local_storage import LocalStorage
3030
from core.storage.s3_storage import S3Storage
31-
from core.vector_store.milvus_multivector_store import MilvusMultiVectorStore
32-
from core.vector_store.milvus_vector_store import MilvusVectorStore
3331
from core.vector_store.multi_vector_store import MultiVectorStore
3432
from core.vector_store.pgvector_store import PGVectorStore
3533

@@ -51,18 +49,8 @@
5149
database = PostgresDatabase(uri=settings.POSTGRES_URI)
5250
logger.debug("Created PostgresDatabase singleton")
5351

54-
# Initialize vector store based on configuration
55-
match settings.VECTOR_STORE_PROVIDER:
56-
case "pgvector":
57-
vector_store = PGVectorStore(uri=settings.POSTGRES_URI)
58-
logger.info("Using PGVector for main vector storage")
59-
case "milvus":
60-
vector_store = MilvusVectorStore()
61-
logger.info("Using Milvus for main vector storage")
62-
case _:
63-
raise ValueError(f"Unsupported vector store provider: {settings.VECTOR_STORE_PROVIDER}")
64-
65-
logger.debug("Created vector store singleton")
52+
vector_store = PGVectorStore(uri=settings.POSTGRES_URI)
53+
logger.debug("Created PGVectorStore singleton")
6654

6755
# ---------------------------------------------------------------------------
6856
# Object storage
@@ -133,31 +121,16 @@
133121
# ColPali multi-vector support
134122
# ---------------------------------------------------------------------------
135123

136-
colpali_embedding_model = None
137-
colpali_vector_store = None
138-
139124
match settings.COLPALI_MODE:
140125
case "off":
141126
colpali_embedding_model = None
142127
colpali_vector_store = None
143128
case "local":
144129
colpali_embedding_model = ColpaliEmbeddingModel()
145-
# Check if we should use Milvus or PostgreSQL for multi-vector storage
146-
if settings.MULTIVECTOR_PROVIDER.lower() == "milvus":
147-
colpali_vector_store = MilvusMultiVectorStore(batch_size=settings.MILVUS_BATCH_SIZE)
148-
logger.info("Using Milvus for ColPali multi-vector storage")
149-
else:
150-
colpali_vector_store = MultiVectorStore(uri=settings.POSTGRES_URI)
151-
logger.info("Using PostgreSQL for ColPali multi-vector storage")
130+
colpali_vector_store = MultiVectorStore(uri=settings.POSTGRES_URI)
152131
case "api":
153132
colpali_embedding_model = ColpaliApiEmbeddingModel()
154-
# Check if we should use Milvus or PostgreSQL for multi-vector storage
155-
if settings.MULTIVECTOR_PROVIDER.lower() == "milvus":
156-
colpali_vector_store = MilvusMultiVectorStore(batch_size=settings.MILVUS_BATCH_SIZE)
157-
logger.info("Using Milvus for ColPali multi-vector storage (API mode)")
158-
else:
159-
colpali_vector_store = MultiVectorStore(uri=settings.POSTGRES_URI)
160-
logger.info("Using PostgreSQL for ColPali multi-vector storage (API mode)")
133+
colpali_vector_store = MultiVectorStore(uri=settings.POSTGRES_URI)
161134
case _:
162135
raise ValueError(f"Unsupported COLPALI_MODE: {settings.COLPALI_MODE}")
163136

core/tests/unit/test_multivector.py

Lines changed: 91 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import numpy as np
55
import pytest
66
import torch
7+
from pgvector.psycopg import Bit
78

89
from core.models.chunk import DocumentChunk
910
from core.tests import setup_test_logging
10-
from core.vector_store.milvus_multivector_store import MilvusMultiVectorStore
11+
from core.vector_store.multi_vector_store import MultiVectorStore
1112

1213
# Set up test logging
1314
setup_test_logging()
@@ -49,115 +50,102 @@ def get_sample_document_chunks(num_chunks=3, num_vectors=3, dim=128):
4950
return chunks
5051

5152

52-
# For Milvus
53+
# Fixtures
5354
@pytest.fixture(scope="function")
5455
async def vector_store():
5556
"""Create a real MultiVectorStore instance connected to the test database"""
56-
store = MilvusMultiVectorStore(collection_name="test_collection")
57-
store.client.drop_collection(collection_name="test_collection")
58-
store._create_collection()
59-
store.client.load_collection(collection_name="test_collection")
57+
# Create the store
58+
store = MultiVectorStore(uri=TEST_DB_URI)
59+
60+
try:
61+
# Try to initialize the database
62+
store.initialize()
63+
64+
# Clean up any existing data
65+
store.conn.execute("TRUNCATE TABLE multi_vector_embeddings RESTART IDENTITY")
66+
67+
# Drop the function if it exists
68+
try:
69+
store.conn.execute("DROP FUNCTION IF EXISTS max_sim(bit[], bit[])")
70+
except Exception as e:
71+
print(f"Error dropping function: {e}")
72+
except Exception as e:
73+
print(f"Error setting up database: {e}")
74+
6075
yield store
76+
77+
# Clean up after tests
78+
try:
79+
store.conn.execute("TRUNCATE TABLE multi_vector_embeddings RESTART IDENTITY")
80+
except Exception as e:
81+
print(f"Error cleaning up: {e}")
82+
83+
# Close connection
6184
store.close()
6285

6386

64-
# For Postgres
65-
# # Fixtures
66-
# @pytest.fixture(scope="function")
67-
# async def vector_store():
68-
# """Create a real MultiVectorStore instance connected to the test database"""
69-
# # Create the store
70-
# store = MultiVectorStore(uri=TEST_DB_URI)
71-
72-
# try:
73-
# # Try to initialize the database
74-
# store.initialize()
75-
76-
# # Clean up any existing data
77-
# store.conn.execute("TRUNCATE TABLE multi_vector_embeddings RESTART IDENTITY")
78-
79-
# # Drop the function if it exists
80-
# try:
81-
# store.conn.execute("DROP FUNCTION IF EXISTS max_sim(bit[], bit[])")
82-
# except Exception as e:
83-
# print(f"Error dropping function: {e}")
84-
# except Exception as e:
85-
# print(f"Error setting up database: {e}")
86-
87-
# yield store
88-
89-
# # Clean up after tests
90-
# try:
91-
# store.conn.execute("TRUNCATE TABLE multi_vector_embeddings RESTART IDENTITY")
92-
# except Exception as e:
93-
# print(f"Error cleaning up: {e}")
94-
95-
# # Close connection
96-
# store.close()
97-
98-
99-
# # Glassbox Tests - Testing internal implementation details
100-
# @pytest.mark.asyncio
101-
# async def test_binary_quantize():
102-
# """Test the _binary_quantize method correctly converts embeddings"""
103-
# store = MultiVectorStore(uri=TEST_DB_URI)
104-
105-
# # Test with torch tensor
106-
# torch_embeddings = torch.tensor([[0.1, -0.2, 0.3], [-0.1, 0.2, -0.3]])
107-
# binary_result = store._binary_quantize(torch_embeddings)
108-
# assert len(binary_result) == 2
109-
110-
# # Check results match expected patterns
111-
# assert binary_result[0].to_text() == Bit("101").to_text() # Positive values (>0) become 1, negative/zero become 0
112-
# assert binary_result[1].to_text() == Bit("010").to_text() # First row: [0.1 (>0), -0.2 (<0), 0.3 (>0)] → "101"
113-
# # Second row: [-0.1 (<0), 0.2 (>0), -0.3 (<0)] → "010"
114-
115-
# # Test with numpy array
116-
# numpy_embeddings = np.array([[0.1, -0.2, 0.3], [-0.1, 0.2, -0.3]])
117-
# binary_result = store._binary_quantize(numpy_embeddings)
118-
# assert len(binary_result) == 2
119-
120-
# assert binary_result[0].to_text() == Bit("101").to_text()
121-
# assert binary_result[1].to_text() == Bit("010").to_text()
122-
123-
124-
# @pytest.mark.asyncio
125-
# async def test_initialize_creates_tables_and_function(vector_store):
126-
# """Test that initialize creates the necessary tables and functions"""
127-
# vector_store.initialize()
128-
# # Check if the table exists
129-
# result = vector_store.conn.execute(
130-
# "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'multi_vector_embeddings')"
131-
# ).fetchone()
132-
# table_exists = result[0]
133-
# assert table_exists is True
134-
135-
# logger.info("Table exists!")
136-
137-
# # Check if the max_sim function exists
138-
# result = vector_store.conn.execute("SELECT EXISTS (SELECT FROM pg_proc WHERE proname = 'max_sim')").fetchone()
139-
# function_exists = result[0]
140-
# logger.info(f"Function exists {function_exists}")
141-
# assert function_exists is True
142-
143-
144-
# @pytest.mark.asyncio
145-
# async def test_database_schema(vector_store):
146-
# """Test that the database schema matches our expectations"""
147-
# # Check columns in the table
148-
# result = vector_store.conn.execute(
149-
# "SELECT column_name, data_type FROM information_schema.columns " "WHERE table_name = 'multi_vector_embeddings'"
150-
# ).fetchall()
151-
152-
# # Convert to a dict for easier checking
153-
# column_dict = {col[0]: col[1] for col in result}
154-
155-
# # Check required columns
156-
# assert "id" in column_dict
157-
# assert "document_id" in column_dict
158-
# assert "chunk_number" in column_dict
159-
# assert "content" in column_dict
160-
# assert "embeddings" in column_dict
87+
# Glassbox Tests - Testing internal implementation details
88+
@pytest.mark.asyncio
89+
async def test_binary_quantize():
90+
"""Test the _binary_quantize method correctly converts embeddings"""
91+
store = MultiVectorStore(uri=TEST_DB_URI)
92+
93+
# Test with torch tensor
94+
torch_embeddings = torch.tensor([[0.1, -0.2, 0.3], [-0.1, 0.2, -0.3]])
95+
binary_result = store._binary_quantize(torch_embeddings)
96+
assert len(binary_result) == 2
97+
98+
# Check results match expected patterns
99+
assert binary_result[0].to_text() == Bit("101").to_text() # Positive values (>0) become 1, negative/zero become 0
100+
assert binary_result[1].to_text() == Bit("010").to_text() # First row: [0.1 (>0), -0.2 (<0), 0.3 (>0)] → "101"
101+
# Second row: [-0.1 (<0), 0.2 (>0), -0.3 (<0)] → "010"
102+
103+
# Test with numpy array
104+
numpy_embeddings = np.array([[0.1, -0.2, 0.3], [-0.1, 0.2, -0.3]])
105+
binary_result = store._binary_quantize(numpy_embeddings)
106+
assert len(binary_result) == 2
107+
108+
assert binary_result[0].to_text() == Bit("101").to_text()
109+
assert binary_result[1].to_text() == Bit("010").to_text()
110+
111+
112+
@pytest.mark.asyncio
113+
async def test_initialize_creates_tables_and_function(vector_store):
114+
"""Test that initialize creates the necessary tables and functions"""
115+
vector_store.initialize()
116+
# Check if the table exists
117+
result = vector_store.conn.execute(
118+
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'multi_vector_embeddings')"
119+
).fetchone()
120+
table_exists = result[0]
121+
assert table_exists is True
122+
123+
logger.info("Table exists!")
124+
125+
# Check if the max_sim function exists
126+
result = vector_store.conn.execute("SELECT EXISTS (SELECT FROM pg_proc WHERE proname = 'max_sim')").fetchone()
127+
function_exists = result[0]
128+
logger.info(f"Function exists {function_exists}")
129+
assert function_exists is True
130+
131+
132+
@pytest.mark.asyncio
133+
async def test_database_schema(vector_store):
134+
"""Test that the database schema matches our expectations"""
135+
# Check columns in the table
136+
result = vector_store.conn.execute(
137+
"SELECT column_name, data_type FROM information_schema.columns " "WHERE table_name = 'multi_vector_embeddings'"
138+
).fetchall()
139+
140+
# Convert to a dict for easier checking
141+
column_dict = {col[0]: col[1] for col in result}
142+
143+
# Check required columns
144+
assert "id" in column_dict
145+
assert "document_id" in column_dict
146+
assert "chunk_number" in column_dict
147+
assert "content" in column_dict
148+
assert "embeddings" in column_dict
161149

162150

163151
# Blackbox Tests - Testing the public API

0 commit comments

Comments
 (0)