chroma-haystack 4.2.0__tar.gz → 4.3.1__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.1}/CHANGELOG.md +45 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/PKG-INFO +4 -3
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/pyproject.toml +17 -4
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/components/retrievers/chroma/retriever.py +6 -2
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/document_stores/chroma/document_store.py +110 -59
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/document_stores/chroma/filters.py +5 -5
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/document_stores/chroma/utils.py +2 -1
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/tests/test_document_store.py +198 -179
- chroma_haystack-4.3.1/tests/test_document_store_async.py +217 -0
- chroma_haystack-4.3.1/tests/test_filters.py +46 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/tests/test_retriever.py +72 -0
- chroma_haystack-4.2.0/tests/test_document_store_async.py +0 -444
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/.gitignore +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/LICENSE +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/README.md +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_01.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_02.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_03.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_04.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_05.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_06.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_07.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_08.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_09.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_10.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_11.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_12.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_20.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_21.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_22.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_23.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_24.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_25.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_26.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_27.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_28.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_29.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_30.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_31.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_32.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_40.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_41.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_42.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_43.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_44.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_45.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_46.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_50.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_51.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_52.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/data/usr_90.txt +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/example/example.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/pydoc/config_docusaurus.yml +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/components/retrievers/chroma/__init__.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/components/retrievers/py.typed +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/document_stores/chroma/__init__.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/document_stores/chroma/errors.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/src/haystack_integrations/document_stores/py.typed +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/tests/__init__.py +0 -0
- {chroma_haystack-4.2.0 → chroma_haystack-4.3.1}/tests/conftest.py +0 -0
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/chroma-v4.3.0] - 2026-04-29
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- *(chroma)* Support DuplicatePolicy in write_documents and use async mixin tests (#3245)
|
|
8
|
+
|
|
9
|
+
### 🚜 Refactor
|
|
10
|
+
|
|
11
|
+
- Weaviate, chroma, elasticsearch, opensearch, azure_ai_search use `_normalize_metadata_field_name` from haystack.utils (#2953)
|
|
12
|
+
|
|
13
|
+
### 🧪 Testing
|
|
14
|
+
|
|
15
|
+
- `ChromaDocumentStore` use Mixin tests (#3026)
|
|
16
|
+
- Track test coverage for all integrations (#3065)
|
|
17
|
+
- Better categorize some Document Stores tests (#3085)
|
|
18
|
+
- Chroma - add unit tests (#3175)
|
|
19
|
+
|
|
20
|
+
### 🧹 Chores
|
|
21
|
+
|
|
22
|
+
- Add missing -> None return type annotations to chroma __init__ methods (#2976)
|
|
23
|
+
- Enforce ruff docstring rules (D102/D103/D205/D209/D213/D417/D419) in first 10 integrations (#3008)
|
|
24
|
+
- Increase lower pins for 3.14 support in some integrations + test with 3.14 (#3033)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## [integrations/chroma-v4.2.0] - 2026-03-11
|
|
28
|
+
|
|
29
|
+
### 🚀 Features
|
|
30
|
+
|
|
31
|
+
- Add support for metadata that contains lists of supported types (#2877)
|
|
32
|
+
|
|
33
|
+
### 📚 Documentation
|
|
34
|
+
|
|
35
|
+
- Simplify pydoc configs (#2855)
|
|
36
|
+
|
|
37
|
+
### 🧪 Testing
|
|
38
|
+
|
|
39
|
+
- Replacing each `DocumentStore` specific tests and used the generalised ones from `haystack.testing.document_store` (#2812)
|
|
40
|
+
- Chroma - remove tests for invalid Settings (now ignored) (#2935)
|
|
41
|
+
|
|
42
|
+
### 🧹 Chores
|
|
43
|
+
|
|
44
|
+
- Remove unused allow-direct-references (#2866)
|
|
45
|
+
- Standardize author mentions (#2897)
|
|
46
|
+
|
|
47
|
+
|
|
3
48
|
## [integrations/chroma-v4.1.1] - 2026-02-10
|
|
4
49
|
|
|
5
50
|
### 📚 Documentation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chroma-haystack
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.1
|
|
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):
|
|
@@ -138,9 +140,10 @@ class ChromaDocumentStore:
|
|
|
138
140
|
|
|
139
141
|
self._client = client # store client for potential future use
|
|
140
142
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
# Build the collection metadata locally so `self._metadata` stays exactly as the user passed it.
|
|
144
|
+
# This keeps `to_dict()` deterministic and avoids mutating a user-supplied dict in place.
|
|
145
|
+
collection_metadata = dict(self._metadata) if self._metadata else {}
|
|
146
|
+
collection_metadata.setdefault("hnsw:space", self._distance_function)
|
|
144
147
|
|
|
145
148
|
existing_collection_names = [c.name for c in client.list_collections()]
|
|
146
149
|
if self._collection_name in existing_collection_names:
|
|
@@ -149,18 +152,18 @@ class ChromaDocumentStore:
|
|
|
149
152
|
embedding_function=self._embedding_func,
|
|
150
153
|
)
|
|
151
154
|
|
|
152
|
-
if
|
|
155
|
+
if collection_metadata != self._collection.metadata:
|
|
153
156
|
logger.warning(
|
|
154
157
|
"Collection already exists. The `distance_function` and `metadata` parameters will be ignored."
|
|
155
158
|
)
|
|
156
159
|
else:
|
|
157
160
|
self._collection = client.create_collection(
|
|
158
161
|
name=self._collection_name,
|
|
159
|
-
metadata=
|
|
162
|
+
metadata=collection_metadata,
|
|
160
163
|
embedding_function=self._embedding_func,
|
|
161
164
|
)
|
|
162
165
|
|
|
163
|
-
async def _ensure_initialized_async(self):
|
|
166
|
+
async def _ensure_initialized_async(self) -> None:
|
|
164
167
|
if not self._async_collection:
|
|
165
168
|
if self._host is None or self._port is None:
|
|
166
169
|
error_message = (
|
|
@@ -186,9 +189,10 @@ class ChromaDocumentStore:
|
|
|
186
189
|
|
|
187
190
|
self._async_client = client # store client for potential future use
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
# Build the collection metadata locally so `self._metadata` stays exactly as the user passed it.
|
|
193
|
+
# This keeps `to_dict()` deterministic and avoids mutating a user-supplied dict in place.
|
|
194
|
+
collection_metadata = dict(self._metadata) if self._metadata else {}
|
|
195
|
+
collection_metadata.setdefault("hnsw:space", self._distance_function)
|
|
192
196
|
|
|
193
197
|
collection = await client.list_collections()
|
|
194
198
|
existing_collection_names = [c.name for c in collection]
|
|
@@ -198,14 +202,14 @@ class ChromaDocumentStore:
|
|
|
198
202
|
embedding_function=self._embedding_func,
|
|
199
203
|
)
|
|
200
204
|
|
|
201
|
-
if
|
|
205
|
+
if collection_metadata != self._async_collection.metadata:
|
|
202
206
|
logger.warning(
|
|
203
207
|
"Collection already exists. The `distance_function` and `metadata` parameters will be ignored."
|
|
204
208
|
)
|
|
205
209
|
else:
|
|
206
210
|
self._async_collection = await client.create_collection(
|
|
207
211
|
name=self._collection_name,
|
|
208
|
-
metadata=
|
|
212
|
+
metadata=collection_metadata,
|
|
209
213
|
embedding_function=self._embedding_func,
|
|
210
214
|
)
|
|
211
215
|
|
|
@@ -242,16 +246,6 @@ class ChromaDocumentStore:
|
|
|
242
246
|
"include": ["embeddings", "documents", "metadatas", "distances"],
|
|
243
247
|
}
|
|
244
248
|
|
|
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
249
|
@staticmethod
|
|
256
250
|
def _infer_type_from_value(value: Any) -> str:
|
|
257
251
|
"""
|
|
@@ -341,7 +335,7 @@ class ChromaDocumentStore:
|
|
|
341
335
|
for meta in metadatas:
|
|
342
336
|
if meta and field_name in meta:
|
|
343
337
|
val = meta.get(field_name)
|
|
344
|
-
if isinstance(val,
|
|
338
|
+
if isinstance(val, str | int | float):
|
|
345
339
|
values.append(val)
|
|
346
340
|
|
|
347
341
|
if not values:
|
|
@@ -581,62 +575,123 @@ class ChromaDocumentStore:
|
|
|
581
575
|
def write_documents(
|
|
582
576
|
self,
|
|
583
577
|
documents: list[Document],
|
|
584
|
-
policy: DuplicatePolicy = DuplicatePolicy.
|
|
578
|
+
policy: DuplicatePolicy = DuplicatePolicy.NONE,
|
|
585
579
|
) -> int:
|
|
586
580
|
"""
|
|
587
|
-
Writes
|
|
581
|
+
Writes documents into the store.
|
|
588
582
|
|
|
589
583
|
:param documents:
|
|
590
584
|
A list of documents to write into the document store.
|
|
591
585
|
:param policy:
|
|
592
|
-
|
|
586
|
+
How to handle documents whose `id` already exists in the store:
|
|
587
|
+
- `NONE` (default): treated as `FAIL`.
|
|
588
|
+
- `OVERWRITE`: replace the existing document.
|
|
589
|
+
- `SKIP`: keep the existing document and skip the new one.
|
|
590
|
+
- `FAIL`: raise `DuplicateDocumentError`.
|
|
593
591
|
|
|
594
592
|
:raises ValueError:
|
|
595
593
|
When input is not valid.
|
|
594
|
+
:raises DuplicateDocumentError:
|
|
595
|
+
When `policy` is `FAIL` (or `NONE`) and any document `id` already exists.
|
|
596
596
|
|
|
597
597
|
:returns:
|
|
598
|
-
The number of documents written
|
|
598
|
+
The number of documents written.
|
|
599
599
|
"""
|
|
600
600
|
self._ensure_initialized()
|
|
601
601
|
assert self._collection is not None
|
|
602
602
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
603
|
+
if policy == DuplicatePolicy.NONE:
|
|
604
|
+
policy = DuplicatePolicy.FAIL
|
|
605
|
+
|
|
606
|
+
chroma_payloads: list[dict[str, Any]] = [
|
|
607
|
+
p for p in (ChromaDocumentStore._convert_document_to_chroma(doc) for doc in documents) if p is not None
|
|
608
|
+
]
|
|
609
|
+
if not chroma_payloads:
|
|
610
|
+
return 0
|
|
611
|
+
|
|
612
|
+
if policy in (DuplicatePolicy.FAIL, DuplicatePolicy.SKIP):
|
|
613
|
+
existing_ids = set(self._collection.get(ids=[p["ids"][0] for p in chroma_payloads])["ids"])
|
|
614
|
+
payloads_to_write = self._apply_duplicate_policy(chroma_payloads, existing_ids, policy)
|
|
615
|
+
else:
|
|
616
|
+
payloads_to_write = chroma_payloads
|
|
617
|
+
|
|
618
|
+
for payload in payloads_to_write:
|
|
619
|
+
if policy == DuplicatePolicy.OVERWRITE:
|
|
620
|
+
self._collection.upsert(**payload)
|
|
621
|
+
else:
|
|
622
|
+
self._collection.add(**payload)
|
|
607
623
|
|
|
608
|
-
return len(
|
|
624
|
+
return len(payloads_to_write)
|
|
609
625
|
|
|
610
626
|
async def write_documents_async(
|
|
611
627
|
self,
|
|
612
628
|
documents: list[Document],
|
|
613
|
-
policy: DuplicatePolicy = DuplicatePolicy.
|
|
629
|
+
policy: DuplicatePolicy = DuplicatePolicy.NONE,
|
|
614
630
|
) -> int:
|
|
615
631
|
"""
|
|
616
|
-
Asynchronously writes
|
|
632
|
+
Asynchronously writes documents into the store.
|
|
617
633
|
|
|
618
634
|
Asynchronous methods are only supported for HTTP connections.
|
|
619
635
|
|
|
620
636
|
:param documents:
|
|
621
637
|
A list of documents to write into the document store.
|
|
622
638
|
:param policy:
|
|
623
|
-
|
|
639
|
+
How to handle documents whose `id` already exists in the store:
|
|
640
|
+
- `NONE` (default): treated as `FAIL`.
|
|
641
|
+
- `OVERWRITE`: replace the existing document.
|
|
642
|
+
- `SKIP`: keep the existing document and skip the new one.
|
|
643
|
+
- `FAIL`: raise `DuplicateDocumentError`.
|
|
624
644
|
|
|
625
645
|
:raises ValueError:
|
|
626
646
|
When input is not valid.
|
|
647
|
+
:raises DuplicateDocumentError:
|
|
648
|
+
When `policy` is `FAIL` (or `NONE`) and any document `id` already exists.
|
|
627
649
|
|
|
628
650
|
:returns:
|
|
629
|
-
The number of documents written
|
|
651
|
+
The number of documents written.
|
|
630
652
|
"""
|
|
631
653
|
await self._ensure_initialized_async()
|
|
632
654
|
assert self._async_collection is not None
|
|
633
655
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
656
|
+
if policy == DuplicatePolicy.NONE:
|
|
657
|
+
policy = DuplicatePolicy.FAIL
|
|
658
|
+
|
|
659
|
+
chroma_payloads: list[dict[str, Any]] = [
|
|
660
|
+
p for p in (ChromaDocumentStore._convert_document_to_chroma(doc) for doc in documents) if p is not None
|
|
661
|
+
]
|
|
662
|
+
if not chroma_payloads:
|
|
663
|
+
return 0
|
|
664
|
+
|
|
665
|
+
if policy in (DuplicatePolicy.FAIL, DuplicatePolicy.SKIP):
|
|
666
|
+
existing = await self._async_collection.get(ids=[p["ids"][0] for p in chroma_payloads])
|
|
667
|
+
existing_ids = set(existing["ids"])
|
|
668
|
+
payloads_to_write = self._apply_duplicate_policy(chroma_payloads, existing_ids, policy)
|
|
669
|
+
else:
|
|
670
|
+
payloads_to_write = chroma_payloads
|
|
638
671
|
|
|
639
|
-
|
|
672
|
+
for payload in payloads_to_write:
|
|
673
|
+
if policy == DuplicatePolicy.OVERWRITE:
|
|
674
|
+
await self._async_collection.upsert(**payload)
|
|
675
|
+
else:
|
|
676
|
+
await self._async_collection.add(**payload)
|
|
677
|
+
|
|
678
|
+
return len(payloads_to_write)
|
|
679
|
+
|
|
680
|
+
@staticmethod
|
|
681
|
+
def _apply_duplicate_policy(
|
|
682
|
+
payloads: list[dict[str, Any]],
|
|
683
|
+
existing_ids: set[str],
|
|
684
|
+
policy: DuplicatePolicy,
|
|
685
|
+
) -> list[dict[str, Any]]:
|
|
686
|
+
if policy == DuplicatePolicy.FAIL:
|
|
687
|
+
duplicates = [p["ids"][0] for p in payloads if p["ids"][0] in existing_ids]
|
|
688
|
+
if duplicates:
|
|
689
|
+
msg = f"Documents with ids {duplicates} already exist in the document store."
|
|
690
|
+
raise DuplicateDocumentError(msg)
|
|
691
|
+
return payloads
|
|
692
|
+
if policy == DuplicatePolicy.SKIP:
|
|
693
|
+
return [p for p in payloads if p["ids"][0] not in existing_ids]
|
|
694
|
+
return payloads
|
|
640
695
|
|
|
641
696
|
def delete_documents(self, document_ids: list[str]) -> None:
|
|
642
697
|
"""
|
|
@@ -1008,8 +1063,7 @@ class ChromaDocumentStore:
|
|
|
1008
1063
|
filters: dict[str, Any] | None = None,
|
|
1009
1064
|
) -> list[list[Document]]:
|
|
1010
1065
|
"""
|
|
1011
|
-
Asynchronously perform vector search
|
|
1012
|
-
their text.
|
|
1066
|
+
Asynchronously perform vector search using query embeddings instead of text.
|
|
1013
1067
|
|
|
1014
1068
|
Asynchronous methods are only supported for HTTP connections.
|
|
1015
1069
|
|
|
@@ -1074,8 +1128,7 @@ class ChromaDocumentStore:
|
|
|
1074
1128
|
|
|
1075
1129
|
def count_unique_metadata_by_filter(self, filters: dict[str, Any], metadata_fields: list[str]) -> dict[str, int]:
|
|
1076
1130
|
"""
|
|
1077
|
-
|
|
1078
|
-
of the documents that match the provided filters.
|
|
1131
|
+
Return unique value counts for metadata fields of documents matching the provided filters.
|
|
1079
1132
|
|
|
1080
1133
|
:param filters: The filters to apply to count documents.
|
|
1081
1134
|
For filter syntax, see [Haystack metadata filtering](https://docs.haystack.deepset.ai/docs/metadata-filtering)
|
|
@@ -1087,7 +1140,7 @@ class ChromaDocumentStore:
|
|
|
1087
1140
|
self._ensure_initialized()
|
|
1088
1141
|
assert self._collection is not None
|
|
1089
1142
|
|
|
1090
|
-
normalized_fields = [
|
|
1143
|
+
normalized_fields = [_normalize_metadata_field_name(field) for field in metadata_fields]
|
|
1091
1144
|
|
|
1092
1145
|
kwargs = ChromaDocumentStore._prepare_get_kwargs(filters)
|
|
1093
1146
|
kwargs["include"] = ["metadatas"]
|
|
@@ -1099,8 +1152,7 @@ class ChromaDocumentStore:
|
|
|
1099
1152
|
self, filters: dict[str, Any], metadata_fields: list[str]
|
|
1100
1153
|
) -> dict[str, int]:
|
|
1101
1154
|
"""
|
|
1102
|
-
Asynchronously
|
|
1103
|
-
of the documents that match the provided filters.
|
|
1155
|
+
Asynchronously return unique value counts for metadata fields of documents matching the provided filters.
|
|
1104
1156
|
|
|
1105
1157
|
Asynchronous methods are only supported for HTTP connections.
|
|
1106
1158
|
|
|
@@ -1114,7 +1166,7 @@ class ChromaDocumentStore:
|
|
|
1114
1166
|
await self._ensure_initialized_async()
|
|
1115
1167
|
assert self._async_collection is not None
|
|
1116
1168
|
|
|
1117
|
-
normalized_fields = [
|
|
1169
|
+
normalized_fields = [_normalize_metadata_field_name(field) for field in metadata_fields]
|
|
1118
1170
|
|
|
1119
1171
|
kwargs = ChromaDocumentStore._prepare_get_kwargs(filters)
|
|
1120
1172
|
kwargs["include"] = ["metadatas"]
|
|
@@ -1205,7 +1257,7 @@ class ChromaDocumentStore:
|
|
|
1205
1257
|
self._ensure_initialized()
|
|
1206
1258
|
assert self._collection is not None
|
|
1207
1259
|
|
|
1208
|
-
field_name =
|
|
1260
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1209
1261
|
|
|
1210
1262
|
result = self._collection.get(include=["metadatas"])
|
|
1211
1263
|
return self._compute_field_min_max(result.get("metadatas", []), field_name)
|
|
@@ -1229,7 +1281,7 @@ class ChromaDocumentStore:
|
|
|
1229
1281
|
await self._ensure_initialized_async()
|
|
1230
1282
|
assert self._async_collection is not None
|
|
1231
1283
|
|
|
1232
|
-
field_name =
|
|
1284
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1233
1285
|
|
|
1234
1286
|
result = await self._async_collection.get(include=["metadatas"])
|
|
1235
1287
|
return self._compute_field_min_max(result.get("metadatas", []), field_name)
|
|
@@ -1242,8 +1294,7 @@ class ChromaDocumentStore:
|
|
|
1242
1294
|
size: int = 10,
|
|
1243
1295
|
) -> tuple[list[str], int]:
|
|
1244
1296
|
"""
|
|
1245
|
-
|
|
1246
|
-
a search term in the content field, with pagination support.
|
|
1297
|
+
Return unique metadata field values, optionally filtered by a content search term, with pagination.
|
|
1247
1298
|
|
|
1248
1299
|
:param metadata_field: The metadata field to get unique values for.
|
|
1249
1300
|
Can include or omit the "meta." prefix.
|
|
@@ -1256,7 +1307,7 @@ class ChromaDocumentStore:
|
|
|
1256
1307
|
self._ensure_initialized()
|
|
1257
1308
|
assert self._collection is not None
|
|
1258
1309
|
|
|
1259
|
-
field_name =
|
|
1310
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1260
1311
|
|
|
1261
1312
|
kwargs: dict[str, Any] = {"include": ["metadatas"]}
|
|
1262
1313
|
if search_term:
|
|
@@ -1273,8 +1324,7 @@ class ChromaDocumentStore:
|
|
|
1273
1324
|
size: int = 10,
|
|
1274
1325
|
) -> tuple[list[str], int]:
|
|
1275
1326
|
"""
|
|
1276
|
-
Asynchronously
|
|
1277
|
-
a search term in the content field, with pagination support.
|
|
1327
|
+
Asynchronously return unique metadata field values, optionally filtered by content, with pagination.
|
|
1278
1328
|
|
|
1279
1329
|
Asynchronous methods are only supported for HTTP connections.
|
|
1280
1330
|
|
|
@@ -1289,7 +1339,7 @@ class ChromaDocumentStore:
|
|
|
1289
1339
|
await self._ensure_initialized_async()
|
|
1290
1340
|
assert self._async_collection is not None
|
|
1291
1341
|
|
|
1292
|
-
field_name =
|
|
1342
|
+
field_name = _normalize_metadata_field_name(metadata_field)
|
|
1293
1343
|
|
|
1294
1344
|
kwargs: dict[str, Any] = {"include": ["metadatas"]}
|
|
1295
1345
|
if search_term:
|
|
@@ -1325,6 +1375,7 @@ class ChromaDocumentStore:
|
|
|
1325
1375
|
host=self._host,
|
|
1326
1376
|
port=self._port,
|
|
1327
1377
|
distance_function=self._distance_function,
|
|
1378
|
+
metadata=self._metadata,
|
|
1328
1379
|
client_settings=self._client_settings,
|
|
1329
1380
|
**self._embedding_function_params,
|
|
1330
1381
|
)
|
|
@@ -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.
|