chroma-haystack 4.2.0__tar.gz → 4.3.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.
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/CHANGELOG.md +21 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/PKG-INFO +4 -3
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/pyproject.toml +17 -4
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/components/retrievers/chroma/retriever.py +6 -2
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/document_stores/chroma/document_store.py +97 -49
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/document_stores/chroma/filters.py +5 -5
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/document_stores/chroma/utils.py +2 -1
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/tests/test_document_store.py +159 -179
- chroma_haystack-4.3.0/tests/test_document_store_async.py +217 -0
- chroma_haystack-4.3.0/tests/test_filters.py +46 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/tests/test_retriever.py +70 -0
- chroma_haystack-4.2.0/tests/test_document_store_async.py +0 -444
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/.gitignore +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/LICENSE +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/README.md +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_01.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_02.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_03.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_04.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_05.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_06.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_07.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_08.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_09.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_10.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_11.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_12.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_20.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_21.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_22.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_23.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_24.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_25.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_26.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_27.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_28.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_29.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_30.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_31.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_32.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_40.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_41.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_42.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_43.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_44.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_45.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_46.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_50.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_51.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_52.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/data/usr_90.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/example/example.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/pydoc/config_docusaurus.yml +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/components/retrievers/chroma/__init__.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/document_stores/chroma/__init__.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/document_stores/chroma/errors.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/src/haystack_integrations/document_stores/py.typed +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/tests/__init__.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.0}/tests/conftest.py +0 -0
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/chroma-v4.2.0] - 2026-03-11
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- Add support for metadata that contains lists of supported types (#2877)
|
|
8
|
+
|
|
9
|
+
### 📚 Documentation
|
|
10
|
+
|
|
11
|
+
- Simplify pydoc configs (#2855)
|
|
12
|
+
|
|
13
|
+
### 🧪 Testing
|
|
14
|
+
|
|
15
|
+
- Replacing each `DocumentStore` specific tests and used the generalised ones from `haystack.testing.document_store` (#2812)
|
|
16
|
+
- Chroma - remove tests for invalid Settings (now ignored) (#2935)
|
|
17
|
+
|
|
18
|
+
### 🧹 Chores
|
|
19
|
+
|
|
20
|
+
- Remove unused allow-direct-references (#2866)
|
|
21
|
+
- Standardize author mentions (#2897)
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
## [integrations/chroma-v4.1.1] - 2026-02-10
|
|
4
25
|
|
|
5
26
|
### 📚 Documentation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chroma-haystack
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/chroma#readme
|
|
5
5
|
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
6
6
|
Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/chroma
|
|
@@ -14,11 +14,12 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
18
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
19
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
19
20
|
Requires-Python: >=3.10
|
|
20
|
-
Requires-Dist: chromadb>=1.5.
|
|
21
|
-
Requires-Dist: haystack-ai>=2.
|
|
21
|
+
Requires-Dist: chromadb>=1.5.4
|
|
22
|
+
Requires-Dist: haystack-ai>=2.28.0
|
|
22
23
|
Description-Content-Type: text/markdown
|
|
23
24
|
|
|
24
25
|
# chroma-haystack
|
|
@@ -19,12 +19,13 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python :: 3.11",
|
|
20
20
|
"Programming Language :: Python :: 3.12",
|
|
21
21
|
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
22
23
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
23
24
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
24
25
|
]
|
|
25
26
|
dependencies = [
|
|
26
|
-
"haystack-ai>=2.
|
|
27
|
-
"chromadb>=1.5.
|
|
27
|
+
"haystack-ai>=2.28.0",
|
|
28
|
+
"chromadb>=1.5.4"
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
[project.urls]
|
|
@@ -66,7 +67,8 @@ dependencies = [
|
|
|
66
67
|
unit = 'pytest -m "not integration" {args:tests}'
|
|
67
68
|
integration = 'pytest -m "integration" {args:tests}'
|
|
68
69
|
all = 'pytest {args:tests}'
|
|
69
|
-
cov-retry = 'pytest --cov=haystack_integrations --reruns 3 --reruns-delay 30 -x {args:tests}'
|
|
70
|
+
unit-cov-retry = 'pytest --cov=haystack_integrations --reruns 3 --reruns-delay 30 -x -m "not integration" {args:tests}'
|
|
71
|
+
integration-cov-append-retry = 'pytest --cov=haystack_integrations --cov-append --reruns 3 --reruns-delay 30 -x -m "integration" {args:tests}'
|
|
70
72
|
|
|
71
73
|
types = "mypy -p haystack_integrations.components.retrievers.chroma -p haystack_integrations.document_stores.chroma {args}"
|
|
72
74
|
|
|
@@ -83,9 +85,17 @@ line-length = 120
|
|
|
83
85
|
[tool.ruff.lint]
|
|
84
86
|
select = [
|
|
85
87
|
"A",
|
|
88
|
+
"ANN",
|
|
86
89
|
"ARG",
|
|
87
90
|
"B",
|
|
88
91
|
"C",
|
|
92
|
+
"D102", # Missing docstring in public method
|
|
93
|
+
"D103", # Missing docstring in public function
|
|
94
|
+
"D205", # 1 blank line required between summary line and description
|
|
95
|
+
"D209", # Closing triple quotes go to new line
|
|
96
|
+
"D213", # summary lines must be positioned on the second physical line of the docstring
|
|
97
|
+
"D417", # Missing argument descriptions in the docstring
|
|
98
|
+
"D419", # Docstring is empty
|
|
89
99
|
"DTZ",
|
|
90
100
|
"E",
|
|
91
101
|
"EM",
|
|
@@ -111,6 +121,8 @@ select = [
|
|
|
111
121
|
ignore = [
|
|
112
122
|
# Allow non-abstract empty methods in abstract base classes
|
|
113
123
|
"B027",
|
|
124
|
+
# Allow `Any` - used legitimately for **kwargs and dynamic metadata values
|
|
125
|
+
"ANN401",
|
|
114
126
|
# Allow boolean positional values in function calls, like `dict.get(... True)`
|
|
115
127
|
"FBT003",
|
|
116
128
|
# Ignore checks for possible passwords
|
|
@@ -138,12 +150,13 @@ ban-relative-imports = "parents"
|
|
|
138
150
|
|
|
139
151
|
[tool.ruff.lint.per-file-ignores]
|
|
140
152
|
# Tests can use magic values, assertions, and relative imports
|
|
141
|
-
"tests/**/*" = ["PLR2004", "S101", "TID252"]
|
|
153
|
+
"tests/**/*" = ["PLR2004", "S101", "TID252", "D", "ANN"]
|
|
142
154
|
"example/**/*" = ["T201"]
|
|
143
155
|
|
|
144
156
|
[tool.coverage.run]
|
|
145
157
|
source = ["haystack_integrations"]
|
|
146
158
|
branch = true
|
|
159
|
+
relative_files = true
|
|
147
160
|
parallel = false
|
|
148
161
|
|
|
149
162
|
|
|
@@ -51,8 +51,10 @@ class ChromaQueryTextRetriever:
|
|
|
51
51
|
filters: dict[str, Any] | None = None,
|
|
52
52
|
top_k: int = 10,
|
|
53
53
|
filter_policy: str | FilterPolicy = FilterPolicy.REPLACE,
|
|
54
|
-
):
|
|
54
|
+
) -> None:
|
|
55
55
|
"""
|
|
56
|
+
Initialize the ChromaQueryTextRetriever.
|
|
57
|
+
|
|
56
58
|
:param document_store: an instance of `ChromaDocumentStore`.
|
|
57
59
|
:param filters: filters to narrow down the search space.
|
|
58
60
|
:param top_k: the maximum number of documents to retrieve.
|
|
@@ -164,8 +166,10 @@ class ChromaEmbeddingRetriever:
|
|
|
164
166
|
filters: dict[str, Any] | None = None,
|
|
165
167
|
top_k: int = 10,
|
|
166
168
|
filter_policy: str | FilterPolicy = FilterPolicy.REPLACE,
|
|
167
|
-
):
|
|
169
|
+
) -> None:
|
|
168
170
|
"""
|
|
171
|
+
Initialize the ChromaEmbeddingRetriever.
|
|
172
|
+
|
|
169
173
|
:param document_store: an instance of `ChromaDocumentStore`.
|
|
170
174
|
:param filters: filters to narrow down the search space.
|
|
171
175
|
:param top_k: the maximum number of documents to retrieve.
|
|
@@ -11,8 +11,9 @@ from chromadb.api.types import GetResult, Metadata, OneOrMany, QueryResult
|
|
|
11
11
|
from chromadb.config import Settings
|
|
12
12
|
from haystack import default_from_dict, default_to_dict, logging
|
|
13
13
|
from haystack.dataclasses import Document
|
|
14
|
-
from haystack.document_stores.errors import DocumentStoreError
|
|
14
|
+
from haystack.document_stores.errors import DocumentStoreError, DuplicateDocumentError
|
|
15
15
|
from haystack.document_stores.types import DuplicatePolicy
|
|
16
|
+
from haystack.utils.misc import _normalize_metadata_field_name
|
|
16
17
|
|
|
17
18
|
from .filters import _convert_filters
|
|
18
19
|
from .utils import get_embedding_function
|
|
@@ -45,9 +46,10 @@ class ChromaDocumentStore:
|
|
|
45
46
|
metadata: dict | None = None,
|
|
46
47
|
client_settings: dict[str, Any] | None = None,
|
|
47
48
|
**embedding_function_params: Any,
|
|
48
|
-
):
|
|
49
|
+
) -> None:
|
|
49
50
|
"""
|
|
50
51
|
Creates a new ChromaDocumentStore instance.
|
|
52
|
+
|
|
51
53
|
It is meant to be connected to a Chroma collection.
|
|
52
54
|
|
|
53
55
|
Note: for the component to be part of a serializable pipeline, the __init__
|
|
@@ -102,7 +104,7 @@ class ChromaDocumentStore:
|
|
|
102
104
|
self._collection: chromadb.Collection | None = None
|
|
103
105
|
self._async_collection: AsyncCollection | None = None
|
|
104
106
|
|
|
105
|
-
def _ensure_initialized(self):
|
|
107
|
+
def _ensure_initialized(self) -> None:
|
|
106
108
|
if not self._collection:
|
|
107
109
|
# Create the client instance
|
|
108
110
|
if self._persist_path and (self._host or self._port is not None):
|
|
@@ -160,7 +162,7 @@ class ChromaDocumentStore:
|
|
|
160
162
|
embedding_function=self._embedding_func,
|
|
161
163
|
)
|
|
162
164
|
|
|
163
|
-
async def _ensure_initialized_async(self):
|
|
165
|
+
async def _ensure_initialized_async(self) -> None:
|
|
164
166
|
if not self._async_collection:
|
|
165
167
|
if self._host is None or self._port is None:
|
|
166
168
|
error_message = (
|
|
@@ -242,16 +244,6 @@ class ChromaDocumentStore:
|
|
|
242
244
|
"include": ["embeddings", "documents", "metadatas", "distances"],
|
|
243
245
|
}
|
|
244
246
|
|
|
245
|
-
@staticmethod
|
|
246
|
-
def _normalize_metadata_field_name(metadata_field: str) -> str:
|
|
247
|
-
"""
|
|
248
|
-
Normalizes a metadata field name by removing the "meta." prefix if present.
|
|
249
|
-
|
|
250
|
-
:param metadata_field: The metadata field name to normalize.
|
|
251
|
-
:returns: The normalized field name without "meta." prefix.
|
|
252
|
-
"""
|
|
253
|
-
return metadata_field[5:] if metadata_field.startswith("meta.") else metadata_field
|
|
254
|
-
|
|
255
247
|
@staticmethod
|
|
256
248
|
def _infer_type_from_value(value: Any) -> str:
|
|
257
249
|
"""
|
|
@@ -341,7 +333,7 @@ class ChromaDocumentStore:
|
|
|
341
333
|
for meta in metadatas:
|
|
342
334
|
if meta and field_name in meta:
|
|
343
335
|
val = meta.get(field_name)
|
|
344
|
-
if isinstance(val,
|
|
336
|
+
if isinstance(val, str | int | float):
|
|
345
337
|
values.append(val)
|
|
346
338
|
|
|
347
339
|
if not values:
|
|
@@ -581,62 +573,123 @@ class ChromaDocumentStore:
|
|
|
581
573
|
def write_documents(
|
|
582
574
|
self,
|
|
583
575
|
documents: list[Document],
|
|
584
|
-
policy: DuplicatePolicy = DuplicatePolicy.
|
|
576
|
+
policy: DuplicatePolicy = DuplicatePolicy.NONE,
|
|
585
577
|
) -> int:
|
|
586
578
|
"""
|
|
587
|
-
Writes
|
|
579
|
+
Writes documents into the store.
|
|
588
580
|
|
|
589
581
|
:param documents:
|
|
590
582
|
A list of documents to write into the document store.
|
|
591
583
|
:param policy:
|
|
592
|
-
|
|
584
|
+
How to handle documents whose `id` already exists in the store:
|
|
585
|
+
- `NONE` (default): treated as `FAIL`.
|
|
586
|
+
- `OVERWRITE`: replace the existing document.
|
|
587
|
+
- `SKIP`: keep the existing document and skip the new one.
|
|
588
|
+
- `FAIL`: raise `DuplicateDocumentError`.
|
|
593
589
|
|
|
594
590
|
:raises ValueError:
|
|
595
591
|
When input is not valid.
|
|
592
|
+
:raises DuplicateDocumentError:
|
|
593
|
+
When `policy` is `FAIL` (or `NONE`) and any document `id` already exists.
|
|
596
594
|
|
|
597
595
|
:returns:
|
|
598
|
-
The number of documents written
|
|
596
|
+
The number of documents written.
|
|
599
597
|
"""
|
|
600
598
|
self._ensure_initialized()
|
|
601
599
|
assert self._collection is not None
|
|
602
600
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
601
|
+
if policy == DuplicatePolicy.NONE:
|
|
602
|
+
policy = DuplicatePolicy.FAIL
|
|
603
|
+
|
|
604
|
+
chroma_payloads: list[dict[str, Any]] = [
|
|
605
|
+
p for p in (ChromaDocumentStore._convert_document_to_chroma(doc) for doc in documents) if p is not None
|
|
606
|
+
]
|
|
607
|
+
if not chroma_payloads:
|
|
608
|
+
return 0
|
|
609
|
+
|
|
610
|
+
if policy in (DuplicatePolicy.FAIL, DuplicatePolicy.SKIP):
|
|
611
|
+
existing_ids = set(self._collection.get(ids=[p["ids"][0] for p in chroma_payloads])["ids"])
|
|
612
|
+
payloads_to_write = self._apply_duplicate_policy(chroma_payloads, existing_ids, policy)
|
|
613
|
+
else:
|
|
614
|
+
payloads_to_write = chroma_payloads
|
|
615
|
+
|
|
616
|
+
for payload in payloads_to_write:
|
|
617
|
+
if policy == DuplicatePolicy.OVERWRITE:
|
|
618
|
+
self._collection.upsert(**payload)
|
|
619
|
+
else:
|
|
620
|
+
self._collection.add(**payload)
|
|
607
621
|
|
|
608
|
-
return len(
|
|
622
|
+
return len(payloads_to_write)
|
|
609
623
|
|
|
610
624
|
async def write_documents_async(
|
|
611
625
|
self,
|
|
612
626
|
documents: list[Document],
|
|
613
|
-
policy: DuplicatePolicy = DuplicatePolicy.
|
|
627
|
+
policy: DuplicatePolicy = DuplicatePolicy.NONE,
|
|
614
628
|
) -> int:
|
|
615
629
|
"""
|
|
616
|
-
Asynchronously writes
|
|
630
|
+
Asynchronously writes documents into the store.
|
|
617
631
|
|
|
618
632
|
Asynchronous methods are only supported for HTTP connections.
|
|
619
633
|
|
|
620
634
|
:param documents:
|
|
621
635
|
A list of documents to write into the document store.
|
|
622
636
|
:param policy:
|
|
623
|
-
|
|
637
|
+
How to handle documents whose `id` already exists in the store:
|
|
638
|
+
- `NONE` (default): treated as `FAIL`.
|
|
639
|
+
- `OVERWRITE`: replace the existing document.
|
|
640
|
+
- `SKIP`: keep the existing document and skip the new one.
|
|
641
|
+
- `FAIL`: raise `DuplicateDocumentError`.
|
|
624
642
|
|
|
625
643
|
:raises ValueError:
|
|
626
644
|
When input is not valid.
|
|
645
|
+
:raises DuplicateDocumentError:
|
|
646
|
+
When `policy` is `FAIL` (or `NONE`) and any document `id` already exists.
|
|
627
647
|
|
|
628
648
|
:returns:
|
|
629
|
-
The number of documents written
|
|
649
|
+
The number of documents written.
|
|
630
650
|
"""
|
|
631
651
|
await self._ensure_initialized_async()
|
|
632
652
|
assert self._async_collection is not None
|
|
633
653
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
654
|
+
if policy == DuplicatePolicy.NONE:
|
|
655
|
+
policy = DuplicatePolicy.FAIL
|
|
656
|
+
|
|
657
|
+
chroma_payloads: list[dict[str, Any]] = [
|
|
658
|
+
p for p in (ChromaDocumentStore._convert_document_to_chroma(doc) for doc in documents) if p is not None
|
|
659
|
+
]
|
|
660
|
+
if not chroma_payloads:
|
|
661
|
+
return 0
|
|
662
|
+
|
|
663
|
+
if policy in (DuplicatePolicy.FAIL, DuplicatePolicy.SKIP):
|
|
664
|
+
existing = await self._async_collection.get(ids=[p["ids"][0] for p in chroma_payloads])
|
|
665
|
+
existing_ids = set(existing["ids"])
|
|
666
|
+
payloads_to_write = self._apply_duplicate_policy(chroma_payloads, existing_ids, policy)
|
|
667
|
+
else:
|
|
668
|
+
payloads_to_write = chroma_payloads
|
|
638
669
|
|
|
639
|
-
|
|
670
|
+
for payload in payloads_to_write:
|
|
671
|
+
if policy == DuplicatePolicy.OVERWRITE:
|
|
672
|
+
await self._async_collection.upsert(**payload)
|
|
673
|
+
else:
|
|
674
|
+
await self._async_collection.add(**payload)
|
|
675
|
+
|
|
676
|
+
return len(payloads_to_write)
|
|
677
|
+
|
|
678
|
+
@staticmethod
|
|
679
|
+
def _apply_duplicate_policy(
|
|
680
|
+
payloads: list[dict[str, Any]],
|
|
681
|
+
existing_ids: set[str],
|
|
682
|
+
policy: DuplicatePolicy,
|
|
683
|
+
) -> list[dict[str, Any]]:
|
|
684
|
+
if policy == DuplicatePolicy.FAIL:
|
|
685
|
+
duplicates = [p["ids"][0] for p in payloads if p["ids"][0] in existing_ids]
|
|
686
|
+
if duplicates:
|
|
687
|
+
msg = f"Documents with ids {duplicates} already exist in the document store."
|
|
688
|
+
raise DuplicateDocumentError(msg)
|
|
689
|
+
return payloads
|
|
690
|
+
if policy == DuplicatePolicy.SKIP:
|
|
691
|
+
return [p for p in payloads if p["ids"][0] not in existing_ids]
|
|
692
|
+
return payloads
|
|
640
693
|
|
|
641
694
|
def delete_documents(self, document_ids: list[str]) -> None:
|
|
642
695
|
"""
|
|
@@ -1008,8 +1061,7 @@ class ChromaDocumentStore:
|
|
|
1008
1061
|
filters: dict[str, Any] | None = None,
|
|
1009
1062
|
) -> list[list[Document]]:
|
|
1010
1063
|
"""
|
|
1011
|
-
Asynchronously perform vector search
|
|
1012
|
-
their text.
|
|
1064
|
+
Asynchronously perform vector search using query embeddings instead of text.
|
|
1013
1065
|
|
|
1014
1066
|
Asynchronous methods are only supported for HTTP connections.
|
|
1015
1067
|
|
|
@@ -1074,8 +1126,7 @@ class ChromaDocumentStore:
|
|
|
1074
1126
|
|
|
1075
1127
|
def count_unique_metadata_by_filter(self, filters: dict[str, Any], metadata_fields: list[str]) -> dict[str, int]:
|
|
1076
1128
|
"""
|
|
1077
|
-
|
|
1078
|
-
of the documents that match the provided filters.
|
|
1129
|
+
Return unique value counts for metadata fields of documents matching the provided filters.
|
|
1079
1130
|
|
|
1080
1131
|
:param filters: The filters to apply to count documents.
|
|
1081
1132
|
For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
|
|
@@ -1087,7 +1138,7 @@ class ChromaDocumentStore:
|
|
|
1087
1138
|
self._ensure_initialized()
|
|
1088
1139
|
assert self._collection is not None
|
|
1089
1140
|
|
|
1090
|
-
normalized_fields = [
|
|
1141
|
+
normalized_fields = [_normalize_metadata_field_name(field) for field in metadata_fields]
|
|
1091
1142
|
|
|
1092
1143
|
kwargs = ChromaDocumentStore._prepare_get_kwargs(filters)
|
|
1093
1144
|
kwargs["include"] = ["metadatas"]
|
|
@@ -1099,8 +1150,7 @@ class ChromaDocumentStore:
|
|
|
1099
1150
|
self, filters: dict[str, Any], metadata_fields: list[str]
|
|
1100
1151
|
) -> dict[str, int]:
|
|
1101
1152
|
"""
|
|
1102
|
-
Asynchronously
|
|
1103
|
-
of the documents that match the provided filters.
|
|
1153
|
+
Asynchronously return unique value counts for metadata fields of documents matching the provided filters.
|
|
1104
1154
|
|
|
1105
1155
|
Asynchronous methods are only supported for HTTP connections.
|
|
1106
1156
|
|
|
@@ -1114,7 +1164,7 @@ class ChromaDocumentStore:
|
|
|
1114
1164
|
await self._ensure_initialized_async()
|
|
1115
1165
|
assert self._async_collection is not None
|
|
1116
1166
|
|
|
1117
|
-
normalized_fields = [
|
|
1167
|
+
normalized_fields = [_normalize_metadata_field_name(field) for field in metadata_fields]
|
|
1118
1168
|
|
|
1119
1169
|
kwargs = ChromaDocumentStore._prepare_get_kwargs(filters)
|
|
1120
1170
|
kwargs["include"] = ["metadatas"]
|
|
@@ -1205,7 +1255,7 @@ class ChromaDocumentStore:
|
|
|
1205
1255
|
self._ensure_initialized()
|
|
1206
1256
|
assert self._collection is not None
|
|
1207
1257
|
|
|
1208
|
-
field_name =
|
|
1258
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1209
1259
|
|
|
1210
1260
|
result = self._collection.get(include=["metadatas"])
|
|
1211
1261
|
return self._compute_field_min_max(result.get("metadatas", []), field_name)
|
|
@@ -1229,7 +1279,7 @@ class ChromaDocumentStore:
|
|
|
1229
1279
|
await self._ensure_initialized_async()
|
|
1230
1280
|
assert self._async_collection is not None
|
|
1231
1281
|
|
|
1232
|
-
field_name =
|
|
1282
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1233
1283
|
|
|
1234
1284
|
result = await self._async_collection.get(include=["metadatas"])
|
|
1235
1285
|
return self._compute_field_min_max(result.get("metadatas", []), field_name)
|
|
@@ -1242,8 +1292,7 @@ class ChromaDocumentStore:
|
|
|
1242
1292
|
size: int = 10,
|
|
1243
1293
|
) -> tuple[list[str], int]:
|
|
1244
1294
|
"""
|
|
1245
|
-
|
|
1246
|
-
a search term in the content field, with pagination support.
|
|
1295
|
+
Return unique metadata field values, optionally filtered by a content search term, with pagination.
|
|
1247
1296
|
|
|
1248
1297
|
:param metadata_field: The metadata field to get unique values for.
|
|
1249
1298
|
Can include or omit the "meta." prefix.
|
|
@@ -1256,7 +1305,7 @@ class ChromaDocumentStore:
|
|
|
1256
1305
|
self._ensure_initialized()
|
|
1257
1306
|
assert self._collection is not None
|
|
1258
1307
|
|
|
1259
|
-
field_name =
|
|
1308
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1260
1309
|
|
|
1261
1310
|
kwargs: dict[str, Any] = {"include": ["metadatas"]}
|
|
1262
1311
|
if search_term:
|
|
@@ -1273,8 +1322,7 @@ class ChromaDocumentStore:
|
|
|
1273
1322
|
size: int = 10,
|
|
1274
1323
|
) -> tuple[list[str], int]:
|
|
1275
1324
|
"""
|
|
1276
|
-
Asynchronously
|
|
1277
|
-
a search term in the content field, with pagination support.
|
|
1325
|
+
Asynchronously return unique metadata field values, optionally filtered by content, with pagination.
|
|
1278
1326
|
|
|
1279
1327
|
Asynchronous methods are only supported for HTTP connections.
|
|
1280
1328
|
|
|
@@ -1289,7 +1337,7 @@ class ChromaDocumentStore:
|
|
|
1289
1337
|
await self._ensure_initialized_async()
|
|
1290
1338
|
assert self._async_collection is not None
|
|
1291
1339
|
|
|
1292
|
-
field_name =
|
|
1340
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1293
1341
|
|
|
1294
1342
|
kwargs: dict[str, Any] = {"include": ["metadatas"]}
|
|
1295
1343
|
if search_term:
|
|
@@ -44,8 +44,10 @@ class ChromaFilter:
|
|
|
44
44
|
|
|
45
45
|
def _convert_filters(filters: dict[str, Any]) -> ChromaFilter:
|
|
46
46
|
"""
|
|
47
|
-
Converts Haystack filters into a format compatible with Chroma
|
|
48
|
-
|
|
47
|
+
Converts Haystack filters into a format compatible with Chroma.
|
|
48
|
+
|
|
49
|
+
Separates filters into ids, metadata filters, and content filters to be passed to chroma as ids, where,
|
|
50
|
+
and where_document clauses respectively.
|
|
49
51
|
"""
|
|
50
52
|
|
|
51
53
|
ids = []
|
|
@@ -103,9 +105,7 @@ def _convert_filter_clause(filters: dict[str, Any]) -> dict[str, Any]:
|
|
|
103
105
|
|
|
104
106
|
def _create_where_document_filter(field: str, value: dict[Any, Any]) -> dict[str, Any]:
|
|
105
107
|
"""
|
|
106
|
-
|
|
107
|
-
and converts it to Chroma-compatible where_document filter.
|
|
108
|
-
|
|
108
|
+
Check if a given Haystack filter is a document filter and convert it to a Chroma-compatible where_document filter.
|
|
109
109
|
"""
|
|
110
110
|
where_document: dict[str, list[Any]] = defaultdict(list)
|
|
111
111
|
|
|
@@ -37,7 +37,8 @@ FUNCTION_REGISTRY = {
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def get_embedding_function(function_name: str, **kwargs: Any) -> EmbeddingFunction:
|
|
40
|
-
"""
|
|
40
|
+
"""
|
|
41
|
+
Load an embedding function by name.
|
|
41
42
|
|
|
42
43
|
:param function_name: the name of the embedding function.
|
|
43
44
|
:param kwargs: additional arguments to pass to the embedding function.
|