pgvector-haystack 0.5.0__tar.gz → 1.0.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.
Files changed (22) hide show
  1. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/CHANGELOG.md +15 -0
  2. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/PKG-INFO +1 -1
  3. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/examples/embedding_retrieval.py +1 -0
  4. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/examples/hybrid_retrieval.py +1 -0
  5. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/pyproject.toml +6 -4
  6. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/src/haystack_integrations/components/retrievers/pgvector/embedding_retriever.py +5 -1
  7. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/src/haystack_integrations/components/retrievers/pgvector/keyword_retriever.py +5 -1
  8. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/src/haystack_integrations/document_stores/pgvector/document_store.py +2 -2
  9. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/tests/conftest.py +1 -0
  10. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/tests/test_document_store.py +2 -1
  11. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/tests/test_embedding_retrieval.py +2 -1
  12. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/tests/test_filters.py +4 -3
  13. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/tests/test_keyword_retrieval.py +1 -0
  14. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/tests/test_retrievers.py +50 -1
  15. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/.gitignore +0 -0
  16. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/LICENSE.txt +0 -0
  17. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/README.md +0 -0
  18. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/pydoc/config.yml +0 -0
  19. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/src/haystack_integrations/components/retrievers/pgvector/__init__.py +0 -0
  20. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/src/haystack_integrations/document_stores/pgvector/__init__.py +0 -0
  21. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/src/haystack_integrations/document_stores/pgvector/filters.py +0 -0
  22. {pgvector_haystack-0.5.0 → pgvector_haystack-1.0.0}/tests/__init__.py +0 -0
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [unreleased]
4
+
5
+ ### 🚀 Features
6
+
7
+ - Add filter_policy to pgvector integration (#820)
8
+
9
+ ### 🐛 Bug Fixes
10
+
11
+ - `PgVector` - Fallback to default filter policy when deserializing retrievers without the init parameter (#900)
12
+
13
+ ### ⚙️ Miscellaneous Tasks
14
+
15
+ - Retry tests to reduce flakyness (#836)
16
+ - Update ruff invocation to include check parameter (#853)
17
+
3
18
  ## [integrations/pgvector-v0.4.0] - 2024-06-20
4
19
 
5
20
  ### 🚀 Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pgvector-haystack
3
- Version: 0.5.0
3
+ Version: 1.0.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
@@ -17,6 +17,7 @@ from haystack.components.converters import MarkdownToDocument
17
17
  from haystack.components.embedders import SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedder
18
18
  from haystack.components.preprocessors import DocumentSplitter
19
19
  from haystack.components.writers import DocumentWriter
20
+
20
21
  from haystack_integrations.components.retrievers.pgvector import PgvectorEmbeddingRetriever
21
22
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
22
23
 
@@ -18,6 +18,7 @@ from haystack.components.embedders import SentenceTransformersDocumentEmbedder,
18
18
  from haystack.components.joiners import DocumentJoiner
19
19
  from haystack.components.preprocessors import DocumentSplitter
20
20
  from haystack.components.writers import DocumentWriter
21
+
21
22
  from haystack_integrations.components.retrievers.pgvector import PgvectorEmbeddingRetriever, PgvectorKeywordRetriever
22
23
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
23
24
 
@@ -50,10 +50,12 @@ dependencies = [
50
50
  "haystack-pydoc-tools",
51
51
  ]
52
52
  [tool.hatch.envs.default.scripts]
53
- test = "pytest --reruns 3 --reruns-delay 30 -x {args:tests}"
54
- test-cov = "coverage run -m pytest --reruns 3 --reruns-delay 30 -x {args:tests}"
53
+ test = "pytest {args:tests}"
54
+ test-cov = "coverage run -m pytest {args:tests}"
55
+ test-cov-retry = "test-cov --reruns 3 --reruns-delay 30 -x"
55
56
  cov-report = ["- coverage combine", "coverage report"]
56
57
  cov = ["test-cov", "cov-report"]
58
+ cov-retry = ["test-cov-retry", "cov-report"]
57
59
  docs = ["pydoc-markdown pydoc/config.yml"]
58
60
 
59
61
  [[tool.hatch.envs.all.matrix]]
@@ -64,8 +66,8 @@ detached = true
64
66
  dependencies = ["black>=23.1.0", "mypy>=1.0.0", "ruff>=0.0.243"]
65
67
  [tool.hatch.envs.lint.scripts]
66
68
  typing = "mypy --install-types --non-interactive --explicit-package-bases {args:src/ tests}"
67
- style = ["ruff check {args:.}", "black --check --diff {args:.}"]
68
- fmt = ["black {args:.}", "ruff --fix {args:.}", "style"]
69
+ style = ["ruff check {args:. --exclude tests/, examples/}", "black --check --diff {args:.}"]
70
+ fmt = ["black {args:.}", "ruff --fix {args:. --exclude tests/, examples/}", "style"]
69
71
  all = ["style", "typing"]
70
72
 
71
73
  [tool.black]
@@ -7,6 +7,7 @@ from haystack import component, default_from_dict, default_to_dict
7
7
  from haystack.dataclasses import Document
8
8
  from haystack.document_stores.types import FilterPolicy
9
9
  from haystack.document_stores.types.filter_policy import apply_filter_policy
10
+
10
11
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
11
12
  from haystack_integrations.document_stores.pgvector.document_store import VALID_VECTOR_FUNCTIONS
12
13
 
@@ -126,7 +127,10 @@ class PgvectorEmbeddingRetriever:
126
127
  """
127
128
  doc_store_params = data["init_parameters"]["document_store"]
128
129
  data["init_parameters"]["document_store"] = PgvectorDocumentStore.from_dict(doc_store_params)
129
- data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(data["init_parameters"]["filter_policy"])
130
+ # Pipelines serialized with old versions of the component might not
131
+ # have the filter_policy field.
132
+ if filter_policy := data["init_parameters"].get("filter_policy"):
133
+ data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(filter_policy)
130
134
  return default_from_dict(cls, data)
131
135
 
132
136
  @component.output_types(documents=List[Document])
@@ -7,6 +7,7 @@ from haystack import component, default_from_dict, default_to_dict
7
7
  from haystack.dataclasses import Document
8
8
  from haystack.document_stores.types import FilterPolicy
9
9
  from haystack.document_stores.types.filter_policy import apply_filter_policy
10
+
10
11
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
11
12
 
12
13
 
@@ -100,7 +101,10 @@ class PgvectorKeywordRetriever:
100
101
  """
101
102
  doc_store_params = data["init_parameters"]["document_store"]
102
103
  data["init_parameters"]["document_store"] = PgvectorDocumentStore.from_dict(doc_store_params)
103
- data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(data["init_parameters"]["filter_policy"])
104
+ # Pipelines serialized with old versions of the component might not
105
+ # have the filter_policy field.
106
+ if filter_policy := data["init_parameters"].get("filter_policy"):
107
+ data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(filter_policy)
104
108
  return default_from_dict(cls, data)
105
109
 
106
110
  @component.output_types(documents=List[Document])
@@ -9,7 +9,6 @@ from haystack.dataclasses.document import ByteStream, Document
9
9
  from haystack.document_stores.errors import DocumentStoreError, DuplicateDocumentError
10
10
  from haystack.document_stores.types import DuplicatePolicy
11
11
  from haystack.utils.auth import Secret, deserialize_secrets_inplace
12
- from haystack.utils.filters import convert
13
12
  from psycopg import Error, IntegrityError, connect
14
13
  from psycopg.abc import Query
15
14
  from psycopg.cursor import Cursor
@@ -389,7 +388,8 @@ class PgvectorDocumentStore:
389
388
  msg = "Filters must be a dictionary"
390
389
  raise TypeError(msg)
391
390
  if "operator" not in filters and "conditions" not in filters:
392
- filters = convert(filters)
391
+ msg = "Invalid filter syntax. See https://docs.haystack.deepset.ai/docs/metadata-filtering for details."
392
+ raise ValueError(msg)
393
393
 
394
394
  sql_filter = SQL("SELECT * FROM {table_name}").format(table_name=Identifier(self.table_name))
395
395
 
@@ -2,6 +2,7 @@ import os
2
2
  from unittest.mock import patch
3
3
 
4
4
  import pytest
5
+
5
6
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
6
7
 
7
8
 
@@ -11,9 +11,10 @@ from haystack.document_stores.errors import DuplicateDocumentError
11
11
  from haystack.document_stores.types import DuplicatePolicy
12
12
  from haystack.testing.document_store import CountDocumentsTest, DeleteDocumentsTest, WriteDocumentsTest
13
13
  from haystack.utils import Secret
14
- from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
15
14
  from pandas import DataFrame
16
15
 
16
+ from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
17
+
17
18
 
18
19
  @pytest.mark.integration
19
20
  class TestDocumentStore(CountDocumentsTest, WriteDocumentsTest, DeleteDocumentsTest):
@@ -6,9 +6,10 @@ from typing import List
6
6
 
7
7
  import pytest
8
8
  from haystack.dataclasses.document import Document
9
- from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
10
9
  from numpy.random import rand
11
10
 
11
+ from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
12
+
12
13
 
13
14
  @pytest.mark.integration
14
15
  class TestEmbeddingRetrieval:
@@ -3,6 +3,10 @@ from typing import List
3
3
  import pytest
4
4
  from haystack.dataclasses.document import Document
5
5
  from haystack.testing.document_store import FilterDocumentsTest
6
+ from pandas import DataFrame
7
+ from psycopg.sql import SQL
8
+ from psycopg.types.json import Jsonb
9
+
6
10
  from haystack_integrations.document_stores.pgvector.filters import (
7
11
  FilterError,
8
12
  _convert_filters_to_where_clause_and_params,
@@ -10,9 +14,6 @@ from haystack_integrations.document_stores.pgvector.filters import (
10
14
  _parse_logical_condition,
11
15
  _treat_meta_field,
12
16
  )
13
- from pandas import DataFrame
14
- from psycopg.sql import SQL
15
- from psycopg.types.json import Jsonb
16
17
 
17
18
 
18
19
  @pytest.mark.integration
@@ -1,5 +1,6 @@
1
1
  import pytest
2
2
  from haystack.dataclasses.document import Document
3
+
3
4
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
4
5
 
5
6
 
@@ -7,6 +7,7 @@ import pytest
7
7
  from haystack.dataclasses import Document
8
8
  from haystack.document_stores.types import FilterPolicy
9
9
  from haystack.utils.auth import EnvVarSecret
10
+
10
11
  from haystack_integrations.components.retrievers.pgvector import PgvectorEmbeddingRetriever, PgvectorKeywordRetriever
11
12
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
12
13
 
@@ -241,6 +242,54 @@ class TestKeywordRetriever:
241
242
  assert retriever.filters == {"field": "value"}
242
243
  assert retriever.top_k == 5
243
244
 
245
+ @pytest.mark.usefixtures("patches_for_unit_tests")
246
+ def test_from_dict_without_filter_policy(self, monkeypatch):
247
+ monkeypatch.setenv("PG_CONN_STR", "some-connection-string")
248
+ t = "haystack_integrations.components.retrievers.pgvector.keyword_retriever.PgvectorKeywordRetriever"
249
+ data = {
250
+ "type": t,
251
+ "init_parameters": {
252
+ "document_store": {
253
+ "type": "haystack_integrations.document_stores.pgvector.document_store.PgvectorDocumentStore",
254
+ "init_parameters": {
255
+ "connection_string": {"env_vars": ["PG_CONN_STR"], "strict": True, "type": "env_var"},
256
+ "table_name": "haystack_test_to_dict",
257
+ "embedding_dimension": 768,
258
+ "vector_function": "cosine_similarity",
259
+ "recreate_table": True,
260
+ "search_strategy": "exact_nearest_neighbor",
261
+ "hnsw_recreate_index_if_exists": False,
262
+ "hnsw_index_creation_kwargs": {},
263
+ "hnsw_index_name": "haystack_hnsw_index",
264
+ "hnsw_ef_search": None,
265
+ "keyword_index_name": "haystack_keyword_index",
266
+ },
267
+ },
268
+ "filters": {"field": "value"},
269
+ "top_k": 5,
270
+ },
271
+ }
272
+
273
+ retriever = PgvectorKeywordRetriever.from_dict(data)
274
+ document_store = retriever.document_store
275
+
276
+ assert isinstance(document_store, PgvectorDocumentStore)
277
+ assert isinstance(document_store.connection_string, EnvVarSecret)
278
+ assert document_store.table_name == "haystack_test_to_dict"
279
+ assert document_store.embedding_dimension == 768
280
+ assert document_store.vector_function == "cosine_similarity"
281
+ assert document_store.recreate_table
282
+ assert document_store.search_strategy == "exact_nearest_neighbor"
283
+ assert not document_store.hnsw_recreate_index_if_exists
284
+ assert document_store.hnsw_index_creation_kwargs == {}
285
+ assert document_store.hnsw_index_name == "haystack_hnsw_index"
286
+ assert document_store.hnsw_ef_search is None
287
+ assert document_store.keyword_index_name == "haystack_keyword_index"
288
+
289
+ assert retriever.filters == {"field": "value"}
290
+ assert retriever.filter_policy == FilterPolicy.REPLACE # defaults to REPLACE
291
+ assert retriever.top_k == 5
292
+
244
293
  def test_run(self):
245
294
  mock_store = Mock(spec=PgvectorDocumentStore)
246
295
  doc = Document(content="Test doc", embedding=[0.1, 0.2])
@@ -263,7 +312,7 @@ class TestKeywordRetriever:
263
312
  )
264
313
  res = retriever.run(query="test query", filters={"field2": "value2"})
265
314
 
266
- mock_store._keyword_retrieval.assert_called_once_with(
315
+ mock_store.mock_calls[0].assert_called_once_with(
267
316
  query="test query", filters={"field": "value", "field2": "value2"}, top_k=10
268
317
  )
269
318