pgvector-haystack 3.1.0__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/CHANGELOG.md +11 -1
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/PKG-INFO +1 -1
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/src/haystack_integrations/document_stores/pgvector/converters.py +5 -1
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/src/haystack_integrations/document_stores/pgvector/document_store.py +29 -2
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/conftest.py +24 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/test_document_store.py +22 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/test_retrievers.py +2 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/.gitignore +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/LICENSE.txt +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/README.md +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/examples/embedding_retrieval.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/examples/hybrid_retrieval.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/pydoc/config.yml +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/pyproject.toml +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/src/haystack_integrations/components/retrievers/pgvector/__init__.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/src/haystack_integrations/components/retrievers/pgvector/embedding_retriever.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/src/haystack_integrations/components/retrievers/pgvector/keyword_retriever.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/src/haystack_integrations/document_stores/pgvector/__init__.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/src/haystack_integrations/document_stores/pgvector/filters.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/__init__.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/test_converters.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/test_document_store_async.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/test_filters.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/test_retrieval.py +0 -0
- {pgvector_haystack-3.1.0 → pgvector_haystack-3.2.0}/tests/test_retrieval_async.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/pgvector-v3.1.0] - 2025-03-20
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- Pgvector - async support (+ refactoring) (#1547)
|
|
8
|
+
|
|
9
|
+
### ⚙️ CI
|
|
10
|
+
|
|
11
|
+
- Review testing workflows (#1541)
|
|
12
|
+
|
|
3
13
|
## [integrations/pgvector-v3.0.1] - 2025-03-12
|
|
4
14
|
|
|
5
15
|
### 🌀 Miscellaneous
|
|
@@ -8,6 +18,7 @@
|
|
|
8
18
|
|
|
9
19
|
## [integrations/pgvector-v3.0.0] - 2025-03-11
|
|
10
20
|
|
|
21
|
+
|
|
11
22
|
### 🧹 Chores
|
|
12
23
|
|
|
13
24
|
- Remove Python 3.8 support (#1421)
|
|
@@ -20,7 +31,6 @@
|
|
|
20
31
|
|
|
21
32
|
- Pgvector - remove support for dataframe (#1370)
|
|
22
33
|
|
|
23
|
-
### 🌀 Miscellaneous
|
|
24
34
|
|
|
25
35
|
## [integrations/pgvector-v1.3.0] - 2025-02-03
|
|
26
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgvector-haystack
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: An integration of pgvector (vector search extension for Postgres) with Haystack
|
|
5
5
|
Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations
|
|
6
6
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/pgvector/README.md
|
|
@@ -51,8 +51,12 @@ def _from_pg_to_haystack_documents(documents: List[Dict[str, Any]]) -> List[Docu
|
|
|
51
51
|
blob_mime_type = haystack_dict.pop("blob_mime_type")
|
|
52
52
|
|
|
53
53
|
# convert the embedding to a list of floats
|
|
54
|
+
# for strange reasons, halfvec and vector have different methods to convert the embedding to a list
|
|
54
55
|
if document.get("embedding") is not None:
|
|
55
|
-
|
|
56
|
+
if hasattr(document["embedding"], "tolist"): # vector
|
|
57
|
+
haystack_dict["embedding"] = document["embedding"].tolist()
|
|
58
|
+
else: # halfvec
|
|
59
|
+
haystack_dict["embedding"] = document["embedding"].to_list()
|
|
56
60
|
|
|
57
61
|
haystack_document = Document.from_dict(haystack_dict)
|
|
58
62
|
|
|
@@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
|
|
|
26
26
|
CREATE_TABLE_STATEMENT = """
|
|
27
27
|
CREATE TABLE {schema_name}.{table_name} (
|
|
28
28
|
id VARCHAR(128) PRIMARY KEY,
|
|
29
|
-
embedding
|
|
29
|
+
embedding {embedding_col_type}({embedding_dimension}),
|
|
30
30
|
content TEXT,
|
|
31
31
|
blob_data BYTEA,
|
|
32
32
|
blob_meta JSONB,
|
|
@@ -64,6 +64,12 @@ VECTOR_FUNCTION_TO_POSTGRESQL_OPS = {
|
|
|
64
64
|
"l2_distance": "vector_l2_ops",
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
HALF_VECTOR_FUNCTION_TO_POSTGRESQL_OPS = {
|
|
68
|
+
"cosine_similarity": "halfvec_cosine_ops",
|
|
69
|
+
"inner_product": "halfvec_ip_ops",
|
|
70
|
+
"l2_distance": "halfvec_l2_ops",
|
|
71
|
+
}
|
|
72
|
+
|
|
67
73
|
HNSW_INDEX_CREATION_VALID_KWARGS = ["m", "ef_construction"]
|
|
68
74
|
|
|
69
75
|
|
|
@@ -81,6 +87,7 @@ class PgvectorDocumentStore:
|
|
|
81
87
|
table_name: str = "haystack_documents",
|
|
82
88
|
language: str = "english",
|
|
83
89
|
embedding_dimension: int = 768,
|
|
90
|
+
vector_type: Literal["vector", "halfvec"] = "vector",
|
|
84
91
|
vector_function: Literal["cosine_similarity", "inner_product", "l2_distance"] = "cosine_similarity",
|
|
85
92
|
recreate_table: bool = False,
|
|
86
93
|
search_strategy: Literal["exact_nearest_neighbor", "hnsw"] = "exact_nearest_neighbor",
|
|
@@ -112,6 +119,11 @@ class PgvectorDocumentStore:
|
|
|
112
119
|
`SELECT cfgname FROM pg_ts_config;`.
|
|
113
120
|
More information can be found in this [StackOverflow answer](https://stackoverflow.com/a/39752553).
|
|
114
121
|
:param embedding_dimension: The dimension of the embedding.
|
|
122
|
+
:param vector_type: The type of vector used for embedding storage.
|
|
123
|
+
"vector" is the default.
|
|
124
|
+
"halfvec" stores embeddings in half-precision, which is particularly useful for high-dimensional embeddings
|
|
125
|
+
(dimension greater than 2,000 and up to 4,000). Requires pgvector versions 0.7.0 or later. For more
|
|
126
|
+
information, see the [pgvector documentation](https://github.com/pgvector/pgvector?tab=readme-ov-file).
|
|
115
127
|
:param vector_function: The similarity function to use when searching for similar embeddings.
|
|
116
128
|
`"cosine_similarity"` and `"inner_product"` are similarity functions and
|
|
117
129
|
higher scores indicate greater similarity between the documents.
|
|
@@ -145,6 +157,10 @@ class PgvectorDocumentStore:
|
|
|
145
157
|
self.table_name = table_name
|
|
146
158
|
self.schema_name = schema_name
|
|
147
159
|
self.embedding_dimension = embedding_dimension
|
|
160
|
+
if vector_type not in ["vector", "halfvec"]:
|
|
161
|
+
msg = "vector_type must be one of ['vector', 'halfvec']"
|
|
162
|
+
raise ValueError(msg)
|
|
163
|
+
self.vector_type = vector_type
|
|
148
164
|
if vector_function not in VALID_VECTOR_FUNCTIONS:
|
|
149
165
|
msg = f"vector_function must be one of {VALID_VECTOR_FUNCTIONS}, but got {vector_function}"
|
|
150
166
|
raise ValueError(msg)
|
|
@@ -180,6 +196,7 @@ class PgvectorDocumentStore:
|
|
|
180
196
|
schema_name=self.schema_name,
|
|
181
197
|
table_name=self.table_name,
|
|
182
198
|
embedding_dimension=self.embedding_dimension,
|
|
199
|
+
vector_type=self.vector_type,
|
|
183
200
|
vector_function=self.vector_function,
|
|
184
201
|
recreate_table=self.recreate_table,
|
|
185
202
|
search_strategy=self.search_strategy,
|
|
@@ -376,6 +393,7 @@ class PgvectorDocumentStore:
|
|
|
376
393
|
schema_name=Identifier(self.schema_name),
|
|
377
394
|
table_name=Identifier(self.table_name),
|
|
378
395
|
embedding_dimension=SQLLiteral(self.embedding_dimension),
|
|
396
|
+
embedding_col_type=SQL(self.vector_type),
|
|
379
397
|
)
|
|
380
398
|
|
|
381
399
|
sql_keyword_index_exists = SQL(
|
|
@@ -516,7 +534,16 @@ class PgvectorDocumentStore:
|
|
|
516
534
|
index_name=Identifier(self.hnsw_index_name),
|
|
517
535
|
)
|
|
518
536
|
|
|
519
|
-
|
|
537
|
+
if self.vector_type == "halfvec":
|
|
538
|
+
if self.vector_function not in HALF_VECTOR_FUNCTION_TO_POSTGRESQL_OPS:
|
|
539
|
+
msg = f"Unsupported vector_function '{self.vector_function}' for halfvec type."
|
|
540
|
+
raise ValueError(msg)
|
|
541
|
+
pg_ops = HALF_VECTOR_FUNCTION_TO_POSTGRESQL_OPS[self.vector_function]
|
|
542
|
+
else:
|
|
543
|
+
if self.vector_function not in VECTOR_FUNCTION_TO_POSTGRESQL_OPS:
|
|
544
|
+
msg = f"Unsupported vector_function '{self.vector_function}' for vector type."
|
|
545
|
+
raise ValueError(msg)
|
|
546
|
+
pg_ops = VECTOR_FUNCTION_TO_POSTGRESQL_OPS[self.vector_function]
|
|
520
547
|
|
|
521
548
|
sql_create_hnsw_index = SQL(
|
|
522
549
|
"CREATE INDEX {index_name} ON {schema_name}.{table_name} USING hnsw (embedding {ops})"
|
|
@@ -54,6 +54,30 @@ def document_store_w_hnsw_index(request, monkeypatch):
|
|
|
54
54
|
store.delete_table()
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
@pytest.fixture
|
|
58
|
+
def document_store_w_halfvec_hnsw_index(request, monkeypatch):
|
|
59
|
+
monkeypatch.setenv("PG_CONN_STR", "postgresql://postgres:postgres@localhost:5432/postgres")
|
|
60
|
+
table_name = f"haystack_halfvec_hnsw_{request.node.name}"
|
|
61
|
+
embedding_dimension = 2500
|
|
62
|
+
vector_function = "cosine_similarity"
|
|
63
|
+
recreate_table = True
|
|
64
|
+
|
|
65
|
+
store = PgvectorDocumentStore(
|
|
66
|
+
table_name=table_name,
|
|
67
|
+
embedding_dimension=embedding_dimension,
|
|
68
|
+
vector_function=vector_function,
|
|
69
|
+
recreate_table=recreate_table,
|
|
70
|
+
search_strategy="hnsw",
|
|
71
|
+
vector_type="halfvec",
|
|
72
|
+
)
|
|
73
|
+
yield store
|
|
74
|
+
|
|
75
|
+
# Ensure connection just for table deletion.
|
|
76
|
+
# During test execution, the tested methods are expected to call _ensure_db_setup() themselves.
|
|
77
|
+
store._ensure_db_setup()
|
|
78
|
+
store.delete_table()
|
|
79
|
+
|
|
80
|
+
|
|
57
81
|
@pytest.fixture
|
|
58
82
|
def patches_for_unit_tests():
|
|
59
83
|
with patch("haystack_integrations.document_stores.pgvector.document_store.register_vector") as mock_register, patch(
|
|
@@ -95,6 +95,7 @@ def test_to_dict(monkeypatch):
|
|
|
95
95
|
create_extension=False,
|
|
96
96
|
table_name="my_table",
|
|
97
97
|
embedding_dimension=512,
|
|
98
|
+
vector_type="halfvec",
|
|
98
99
|
vector_function="l2_distance",
|
|
99
100
|
recreate_table=True,
|
|
100
101
|
search_strategy="hnsw",
|
|
@@ -113,6 +114,7 @@ def test_to_dict(monkeypatch):
|
|
|
113
114
|
"table_name": "my_table",
|
|
114
115
|
"schema_name": "public",
|
|
115
116
|
"embedding_dimension": 512,
|
|
117
|
+
"vector_type": "halfvec",
|
|
116
118
|
"vector_function": "l2_distance",
|
|
117
119
|
"recreate_table": True,
|
|
118
120
|
"search_strategy": "hnsw",
|
|
@@ -126,6 +128,26 @@ def test_to_dict(monkeypatch):
|
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
|
|
131
|
+
@pytest.mark.integration
|
|
132
|
+
def test_halfvec_hnsw_write_documents(document_store_w_halfvec_hnsw_index: PgvectorDocumentStore):
|
|
133
|
+
documents = [
|
|
134
|
+
Document(id="1", content="Hello, world!", embedding=[0.1] * 2500),
|
|
135
|
+
Document(id="2", content="Hello, mum!", embedding=[0.3] * 2500),
|
|
136
|
+
Document(id="3", content="Hello, dad!", embedding=[0.2] * 2500),
|
|
137
|
+
]
|
|
138
|
+
document_store_w_halfvec_hnsw_index.write_documents(documents)
|
|
139
|
+
|
|
140
|
+
retrieved_docs = document_store_w_halfvec_hnsw_index.filter_documents()
|
|
141
|
+
retrieved_docs.sort(key=lambda x: x.id)
|
|
142
|
+
|
|
143
|
+
for original_doc, retrieved_doc in zip(documents, retrieved_docs):
|
|
144
|
+
assert original_doc.id == retrieved_doc.id
|
|
145
|
+
assert original_doc.content == retrieved_doc.content
|
|
146
|
+
assert len(original_doc.embedding) == len(retrieved_doc.embedding)
|
|
147
|
+
# these embeddings are in half precision, so we increase the tolerance
|
|
148
|
+
assert original_doc.embedding == pytest.approx(retrieved_doc.embedding, abs=5e-5)
|
|
149
|
+
|
|
150
|
+
|
|
129
151
|
@pytest.mark.integration
|
|
130
152
|
def test_hnsw_index_recreation():
|
|
131
153
|
def get_index_oid(document_store, schema_name, index_name):
|
|
@@ -54,6 +54,7 @@ class TestEmbeddingRetriever:
|
|
|
54
54
|
"schema_name": "public",
|
|
55
55
|
"table_name": "haystack",
|
|
56
56
|
"embedding_dimension": 768,
|
|
57
|
+
"vector_type": "vector",
|
|
57
58
|
"vector_function": "cosine_similarity",
|
|
58
59
|
"recreate_table": True,
|
|
59
60
|
"search_strategy": "exact_nearest_neighbor",
|
|
@@ -198,6 +199,7 @@ class TestKeywordRetriever:
|
|
|
198
199
|
"schema_name": "public",
|
|
199
200
|
"table_name": "haystack",
|
|
200
201
|
"embedding_dimension": 768,
|
|
202
|
+
"vector_type": "vector",
|
|
201
203
|
"vector_function": "cosine_similarity",
|
|
202
204
|
"recreate_table": True,
|
|
203
205
|
"search_strategy": "exact_nearest_neighbor",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|