elasticsearch 8.12.1__py3-none-any.whl → 8.13.1__py3-none-any.whl

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 (105) hide show
  1. elasticsearch/__init__.py +7 -0
  2. elasticsearch/_async/client/__init__.py +477 -128
  3. elasticsearch/_async/client/_base.py +41 -1
  4. elasticsearch/_async/client/async_search.py +40 -12
  5. elasticsearch/_async/client/autoscaling.py +37 -11
  6. elasticsearch/_async/client/cat.py +260 -69
  7. elasticsearch/_async/client/ccr.py +123 -38
  8. elasticsearch/_async/client/cluster.py +153 -42
  9. elasticsearch/_async/client/dangling_indices.py +27 -8
  10. elasticsearch/_async/client/enrich.py +48 -14
  11. elasticsearch/_async/client/eql.py +38 -12
  12. elasticsearch/_async/client/esql.py +10 -2
  13. elasticsearch/_async/client/features.py +17 -4
  14. elasticsearch/_async/client/fleet.py +30 -7
  15. elasticsearch/_async/client/graph.py +11 -3
  16. elasticsearch/_async/client/ilm.py +101 -29
  17. elasticsearch/_async/client/indices.py +688 -181
  18. elasticsearch/_async/client/inference.py +111 -44
  19. elasticsearch/_async/client/ingest.py +59 -16
  20. elasticsearch/_async/client/license.py +58 -14
  21. elasticsearch/_async/client/logstash.py +31 -9
  22. elasticsearch/_async/client/migration.py +28 -7
  23. elasticsearch/_async/client/ml.py +781 -214
  24. elasticsearch/_async/client/monitoring.py +10 -2
  25. elasticsearch/_async/client/nodes.py +103 -29
  26. elasticsearch/_async/client/query_ruleset.py +37 -11
  27. elasticsearch/_async/client/rollup.py +79 -24
  28. elasticsearch/_async/client/search_application.py +76 -23
  29. elasticsearch/_async/client/searchable_snapshots.py +49 -12
  30. elasticsearch/_async/client/security.py +544 -143
  31. elasticsearch/_async/client/shutdown.py +28 -6
  32. elasticsearch/_async/client/slm.py +80 -22
  33. elasticsearch/_async/client/snapshot.py +140 -54
  34. elasticsearch/_async/client/sql.py +55 -15
  35. elasticsearch/_async/client/ssl.py +9 -2
  36. elasticsearch/_async/client/synonyms.py +75 -21
  37. elasticsearch/_async/client/tasks.py +29 -8
  38. elasticsearch/_async/client/text_structure.py +74 -2
  39. elasticsearch/_async/client/transform.py +106 -32
  40. elasticsearch/_async/client/watcher.py +110 -31
  41. elasticsearch/_async/client/xpack.py +16 -4
  42. elasticsearch/_async/helpers.py +1 -1
  43. elasticsearch/_otel.py +92 -0
  44. elasticsearch/_sync/client/__init__.py +477 -128
  45. elasticsearch/_sync/client/_base.py +41 -1
  46. elasticsearch/_sync/client/async_search.py +40 -12
  47. elasticsearch/_sync/client/autoscaling.py +37 -11
  48. elasticsearch/_sync/client/cat.py +260 -69
  49. elasticsearch/_sync/client/ccr.py +123 -38
  50. elasticsearch/_sync/client/cluster.py +153 -42
  51. elasticsearch/_sync/client/dangling_indices.py +27 -8
  52. elasticsearch/_sync/client/enrich.py +48 -14
  53. elasticsearch/_sync/client/eql.py +38 -12
  54. elasticsearch/_sync/client/esql.py +10 -2
  55. elasticsearch/_sync/client/features.py +17 -4
  56. elasticsearch/_sync/client/fleet.py +30 -7
  57. elasticsearch/_sync/client/graph.py +11 -3
  58. elasticsearch/_sync/client/ilm.py +101 -29
  59. elasticsearch/_sync/client/indices.py +688 -181
  60. elasticsearch/_sync/client/inference.py +111 -44
  61. elasticsearch/_sync/client/ingest.py +59 -16
  62. elasticsearch/_sync/client/license.py +58 -14
  63. elasticsearch/_sync/client/logstash.py +31 -9
  64. elasticsearch/_sync/client/migration.py +28 -7
  65. elasticsearch/_sync/client/ml.py +781 -214
  66. elasticsearch/_sync/client/monitoring.py +10 -2
  67. elasticsearch/_sync/client/nodes.py +103 -29
  68. elasticsearch/_sync/client/query_ruleset.py +37 -11
  69. elasticsearch/_sync/client/rollup.py +79 -24
  70. elasticsearch/_sync/client/search_application.py +76 -23
  71. elasticsearch/_sync/client/searchable_snapshots.py +49 -12
  72. elasticsearch/_sync/client/security.py +544 -143
  73. elasticsearch/_sync/client/shutdown.py +28 -6
  74. elasticsearch/_sync/client/slm.py +80 -22
  75. elasticsearch/_sync/client/snapshot.py +140 -54
  76. elasticsearch/_sync/client/sql.py +55 -15
  77. elasticsearch/_sync/client/ssl.py +9 -2
  78. elasticsearch/_sync/client/synonyms.py +75 -21
  79. elasticsearch/_sync/client/tasks.py +29 -8
  80. elasticsearch/_sync/client/text_structure.py +74 -2
  81. elasticsearch/_sync/client/transform.py +106 -32
  82. elasticsearch/_sync/client/watcher.py +110 -31
  83. elasticsearch/_sync/client/xpack.py +16 -4
  84. elasticsearch/_version.py +1 -1
  85. elasticsearch/helpers/actions.py +1 -1
  86. elasticsearch/helpers/vectorstore/__init__.py +62 -0
  87. elasticsearch/helpers/vectorstore/_async/__init__.py +16 -0
  88. elasticsearch/helpers/vectorstore/_async/_utils.py +39 -0
  89. elasticsearch/helpers/vectorstore/_async/embedding_service.py +89 -0
  90. elasticsearch/helpers/vectorstore/_async/strategies.py +466 -0
  91. elasticsearch/helpers/vectorstore/_async/vectorstore.py +391 -0
  92. elasticsearch/helpers/vectorstore/_sync/__init__.py +16 -0
  93. elasticsearch/helpers/vectorstore/_sync/_utils.py +39 -0
  94. elasticsearch/helpers/vectorstore/_sync/embedding_service.py +89 -0
  95. elasticsearch/helpers/vectorstore/_sync/strategies.py +466 -0
  96. elasticsearch/helpers/vectorstore/_sync/vectorstore.py +388 -0
  97. elasticsearch/helpers/vectorstore/_utils.py +116 -0
  98. elasticsearch/serializer.py +14 -0
  99. {elasticsearch-8.12.1.dist-info → elasticsearch-8.13.1.dist-info}/METADATA +28 -8
  100. elasticsearch-8.13.1.dist-info/RECORD +116 -0
  101. {elasticsearch-8.12.1.dist-info → elasticsearch-8.13.1.dist-info}/WHEEL +1 -1
  102. elasticsearch-8.12.1.dist-info/RECORD +0 -103
  103. {elasticsearch-8.12.1.dist-info → elasticsearch-8.13.1.dist-info}/LICENSE +0 -0
  104. {elasticsearch-8.12.1.dist-info → elasticsearch-8.13.1.dist-info}/NOTICE +0 -0
  105. {elasticsearch-8.12.1.dist-info → elasticsearch-8.13.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,466 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ from abc import ABC, abstractmethod
19
+ from typing import Any, Dict, List, Optional, Tuple, Union, cast
20
+
21
+ from elasticsearch import AsyncElasticsearch
22
+ from elasticsearch.helpers.vectorstore._async._utils import model_must_be_deployed
23
+ from elasticsearch.helpers.vectorstore._utils import DistanceMetric
24
+
25
+
26
+ class AsyncRetrievalStrategy(ABC):
27
+ @abstractmethod
28
+ def es_query(
29
+ self,
30
+ *,
31
+ query: Optional[str],
32
+ query_vector: Optional[List[float]],
33
+ text_field: str,
34
+ vector_field: str,
35
+ k: int,
36
+ num_candidates: int,
37
+ filter: List[Dict[str, Any]] = [],
38
+ ) -> Dict[str, Any]:
39
+ """
40
+ Returns the Elasticsearch query body for the given parameters.
41
+ The store will execute the query.
42
+
43
+ :param query: The text query. Can be None if query_vector is given.
44
+ :param k: The total number of results to retrieve.
45
+ :param num_candidates: The number of results to fetch initially in knn search.
46
+ :param filter: List of filter clauses to apply to the query.
47
+ :param query_vector: The query vector. Can be None if a query string is given.
48
+
49
+ :return: The Elasticsearch query body.
50
+ """
51
+
52
+ @abstractmethod
53
+ def es_mappings_settings(
54
+ self,
55
+ *,
56
+ text_field: str,
57
+ vector_field: str,
58
+ num_dimensions: Optional[int],
59
+ ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
60
+ """
61
+ Create the required index and do necessary preliminary work, like
62
+ creating inference pipelines or checking if a required model was deployed.
63
+
64
+ :param client: Elasticsearch client connection.
65
+ :param text_field: The field containing the text data in the index.
66
+ :param vector_field: The field containing the vector representations in the index.
67
+ :param num_dimensions: If vectors are indexed, how many dimensions do they have.
68
+
69
+ :return: Dictionary with field and field type pairs that describe the schema.
70
+ """
71
+
72
+ async def before_index_creation(
73
+ self, *, client: AsyncElasticsearch, text_field: str, vector_field: str
74
+ ) -> None:
75
+ """
76
+ Executes before the index is created. Used for setting up
77
+ any required Elasticsearch resources like a pipeline.
78
+ Defaults to a no-op.
79
+
80
+ :param client: The Elasticsearch client.
81
+ :param text_field: The field containing the text data in the index.
82
+ :param vector_field: The field containing the vector representations in the index.
83
+ """
84
+ pass
85
+
86
+ def needs_inference(self) -> bool:
87
+ """
88
+ Some retrieval strategies index embedding vectors and allow search by embedding
89
+ vector, for example the `DenseVectorStrategy` strategy. Mapping a user input query
90
+ string to an embedding vector is called inference. Inference can be applied
91
+ in Elasticsearch (using a `model_id`) or outside of Elasticsearch (using an
92
+ `EmbeddingService` defined on the `VectorStore`). In the latter case,
93
+ this method has to return True.
94
+ """
95
+ return False
96
+
97
+
98
+ class AsyncSparseVectorStrategy(AsyncRetrievalStrategy):
99
+ """Sparse retrieval strategy using the `text_expansion` processor."""
100
+
101
+ def __init__(self, model_id: str = ".elser_model_2"):
102
+ self.model_id = model_id
103
+ self._tokens_field = "tokens"
104
+ self._pipeline_name = f"{self.model_id}_sparse_embedding"
105
+
106
+ def es_query(
107
+ self,
108
+ *,
109
+ query: Optional[str],
110
+ query_vector: Optional[List[float]],
111
+ text_field: str,
112
+ vector_field: str,
113
+ k: int,
114
+ num_candidates: int,
115
+ filter: List[Dict[str, Any]] = [],
116
+ ) -> Dict[str, Any]:
117
+ if query_vector:
118
+ raise ValueError(
119
+ "Cannot do sparse retrieval with a query_vector. "
120
+ "Inference is currently always applied in Elasticsearch."
121
+ )
122
+ if query is None:
123
+ raise ValueError("please specify a query string")
124
+
125
+ return {
126
+ "query": {
127
+ "bool": {
128
+ "must": [
129
+ {
130
+ "text_expansion": {
131
+ f"{vector_field}.{self._tokens_field}": {
132
+ "model_id": self.model_id,
133
+ "model_text": query,
134
+ }
135
+ }
136
+ }
137
+ ],
138
+ "filter": filter,
139
+ }
140
+ }
141
+ }
142
+
143
+ def es_mappings_settings(
144
+ self,
145
+ *,
146
+ text_field: str,
147
+ vector_field: str,
148
+ num_dimensions: Optional[int],
149
+ ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
150
+ mappings: Dict[str, Any] = {
151
+ "properties": {
152
+ vector_field: {
153
+ "properties": {self._tokens_field: {"type": "rank_features"}}
154
+ }
155
+ }
156
+ }
157
+ settings = {"default_pipeline": self._pipeline_name}
158
+
159
+ return mappings, settings
160
+
161
+ async def before_index_creation(
162
+ self, *, client: AsyncElasticsearch, text_field: str, vector_field: str
163
+ ) -> None:
164
+ if self.model_id:
165
+ await model_must_be_deployed(client, self.model_id)
166
+
167
+ # Create a pipeline for the model
168
+ await client.ingest.put_pipeline(
169
+ id=self._pipeline_name,
170
+ description="Embedding pipeline for Python VectorStore",
171
+ processors=[
172
+ {
173
+ "inference": {
174
+ "model_id": self.model_id,
175
+ "target_field": vector_field,
176
+ "field_map": {text_field: "text_field"},
177
+ "inference_config": {
178
+ "text_expansion": {"results_field": self._tokens_field}
179
+ },
180
+ }
181
+ }
182
+ ],
183
+ )
184
+
185
+
186
+ class AsyncDenseVectorStrategy(AsyncRetrievalStrategy):
187
+ """K-nearest-neighbors retrieval."""
188
+
189
+ def __init__(
190
+ self,
191
+ *,
192
+ distance: DistanceMetric = DistanceMetric.COSINE,
193
+ model_id: Optional[str] = None,
194
+ hybrid: bool = False,
195
+ rrf: Union[bool, Dict[str, Any]] = True,
196
+ text_field: Optional[str] = "text_field",
197
+ ):
198
+ if hybrid and not text_field:
199
+ raise ValueError(
200
+ "to enable hybrid you have to specify a text_field (for BM25Strategy matching)"
201
+ )
202
+
203
+ self.distance = distance
204
+ self.model_id = model_id
205
+ self.hybrid = hybrid
206
+ self.rrf = rrf
207
+ self.text_field = text_field
208
+
209
+ def es_query(
210
+ self,
211
+ *,
212
+ query: Optional[str],
213
+ query_vector: Optional[List[float]],
214
+ text_field: str,
215
+ vector_field: str,
216
+ k: int,
217
+ num_candidates: int,
218
+ filter: List[Dict[str, Any]] = [],
219
+ ) -> Dict[str, Any]:
220
+ knn = {
221
+ "filter": filter,
222
+ "field": vector_field,
223
+ "k": k,
224
+ "num_candidates": num_candidates,
225
+ }
226
+
227
+ if query_vector is not None:
228
+ knn["query_vector"] = query_vector
229
+ else:
230
+ # Inference in Elasticsearch. When initializing we make sure to always have
231
+ # a model_id if don't have an embedding_service.
232
+ knn["query_vector_builder"] = {
233
+ "text_embedding": {
234
+ "model_id": self.model_id,
235
+ "model_text": query,
236
+ }
237
+ }
238
+
239
+ if self.hybrid:
240
+ return self._hybrid(query=cast(str, query), knn=knn, filter=filter)
241
+
242
+ return {"knn": knn}
243
+
244
+ def es_mappings_settings(
245
+ self,
246
+ *,
247
+ text_field: str,
248
+ vector_field: str,
249
+ num_dimensions: Optional[int],
250
+ ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
251
+ if self.distance is DistanceMetric.COSINE:
252
+ similarity = "cosine"
253
+ elif self.distance is DistanceMetric.EUCLIDEAN_DISTANCE:
254
+ similarity = "l2_norm"
255
+ elif self.distance is DistanceMetric.DOT_PRODUCT:
256
+ similarity = "dot_product"
257
+ elif self.distance is DistanceMetric.MAX_INNER_PRODUCT:
258
+ similarity = "max_inner_product"
259
+ else:
260
+ raise ValueError(f"Similarity {self.distance} not supported.")
261
+
262
+ mappings: Dict[str, Any] = {
263
+ "properties": {
264
+ vector_field: {
265
+ "type": "dense_vector",
266
+ "dims": num_dimensions,
267
+ "index": True,
268
+ "similarity": similarity,
269
+ },
270
+ }
271
+ }
272
+
273
+ return mappings, {}
274
+
275
+ async def before_index_creation(
276
+ self, *, client: AsyncElasticsearch, text_field: str, vector_field: str
277
+ ) -> None:
278
+ if self.model_id:
279
+ await model_must_be_deployed(client, self.model_id)
280
+
281
+ def _hybrid(
282
+ self, query: str, knn: Dict[str, Any], filter: List[Dict[str, Any]]
283
+ ) -> Dict[str, Any]:
284
+ # Add a query to the knn query.
285
+ # RRF is used to even the score from the knn query and text query
286
+ # RRF has two optional parameters: {'rank_constant':int, 'window_size':int}
287
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html
288
+ query_body = {
289
+ "knn": knn,
290
+ "query": {
291
+ "bool": {
292
+ "must": [
293
+ {
294
+ "match": {
295
+ self.text_field: {
296
+ "query": query,
297
+ }
298
+ }
299
+ }
300
+ ],
301
+ "filter": filter,
302
+ }
303
+ },
304
+ }
305
+
306
+ if isinstance(self.rrf, Dict):
307
+ query_body["rank"] = {"rrf": self.rrf}
308
+ elif isinstance(self.rrf, bool) and self.rrf is True:
309
+ query_body["rank"] = {"rrf": {}}
310
+
311
+ return query_body
312
+
313
+ def needs_inference(self) -> bool:
314
+ return not self.model_id
315
+
316
+
317
+ class AsyncDenseVectorScriptScoreStrategy(AsyncRetrievalStrategy):
318
+ """Exact nearest neighbors retrieval using the `script_score` query."""
319
+
320
+ def __init__(self, distance: DistanceMetric = DistanceMetric.COSINE) -> None:
321
+ self.distance = distance
322
+
323
+ def es_query(
324
+ self,
325
+ *,
326
+ query: Optional[str],
327
+ query_vector: Optional[List[float]],
328
+ text_field: str,
329
+ vector_field: str,
330
+ k: int,
331
+ num_candidates: int,
332
+ filter: List[Dict[str, Any]] = [],
333
+ ) -> Dict[str, Any]:
334
+ if not query_vector:
335
+ raise ValueError("specify a query_vector")
336
+
337
+ if self.distance is DistanceMetric.COSINE:
338
+ similarity_algo = (
339
+ f"cosineSimilarity(params.query_vector, '{vector_field}') + 1.0"
340
+ )
341
+ elif self.distance is DistanceMetric.EUCLIDEAN_DISTANCE:
342
+ similarity_algo = f"1 / (1 + l2norm(params.query_vector, '{vector_field}'))"
343
+ elif self.distance is DistanceMetric.DOT_PRODUCT:
344
+ similarity_algo = f"""
345
+ double value = dotProduct(params.query_vector, '{vector_field}');
346
+ return sigmoid(1, Math.E, -value);
347
+ """
348
+ elif self.distance is DistanceMetric.MAX_INNER_PRODUCT:
349
+ similarity_algo = f"""
350
+ double value = dotProduct(params.query_vector, '{vector_field}');
351
+ if (dotProduct < 0) {{
352
+ return 1 / (1 + -1 * dotProduct);
353
+ }}
354
+ return dotProduct + 1;
355
+ """
356
+ else:
357
+ raise ValueError(f"Similarity {self.distance} not supported.")
358
+
359
+ query_bool: Dict[str, Any] = {"match_all": {}}
360
+ if filter:
361
+ query_bool = {"bool": {"filter": filter}}
362
+
363
+ return {
364
+ "query": {
365
+ "script_score": {
366
+ "query": query_bool,
367
+ "script": {
368
+ "source": similarity_algo,
369
+ "params": {"query_vector": query_vector},
370
+ },
371
+ },
372
+ }
373
+ }
374
+
375
+ def es_mappings_settings(
376
+ self,
377
+ *,
378
+ text_field: str,
379
+ vector_field: str,
380
+ num_dimensions: Optional[int],
381
+ ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
382
+ mappings = {
383
+ "properties": {
384
+ vector_field: {
385
+ "type": "dense_vector",
386
+ "dims": num_dimensions,
387
+ "index": False,
388
+ }
389
+ }
390
+ }
391
+
392
+ return mappings, {}
393
+
394
+ def needs_inference(self) -> bool:
395
+ return True
396
+
397
+
398
+ class AsyncBM25Strategy(AsyncRetrievalStrategy):
399
+ def __init__(
400
+ self,
401
+ k1: Optional[float] = None,
402
+ b: Optional[float] = None,
403
+ ):
404
+ self.k1 = k1
405
+ self.b = b
406
+
407
+ def es_query(
408
+ self,
409
+ *,
410
+ query: Optional[str],
411
+ query_vector: Optional[List[float]],
412
+ text_field: str,
413
+ vector_field: str,
414
+ k: int,
415
+ num_candidates: int,
416
+ filter: List[Dict[str, Any]] = [],
417
+ ) -> Dict[str, Any]:
418
+ return {
419
+ "query": {
420
+ "bool": {
421
+ "must": [
422
+ {
423
+ "match": {
424
+ text_field: {
425
+ "query": query,
426
+ }
427
+ },
428
+ },
429
+ ],
430
+ "filter": filter,
431
+ },
432
+ },
433
+ }
434
+
435
+ def es_mappings_settings(
436
+ self,
437
+ *,
438
+ text_field: str,
439
+ vector_field: str,
440
+ num_dimensions: Optional[int],
441
+ ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
442
+ similarity_name = "custom_bm25"
443
+
444
+ mappings: Dict[str, Any] = {
445
+ "properties": {
446
+ text_field: {
447
+ "type": "text",
448
+ "similarity": similarity_name,
449
+ },
450
+ },
451
+ }
452
+
453
+ bm25: Dict[str, Any] = {
454
+ "type": "BM25",
455
+ }
456
+ if self.k1 is not None:
457
+ bm25["k1"] = self.k1
458
+ if self.b is not None:
459
+ bm25["b"] = self.b
460
+ settings = {
461
+ "similarity": {
462
+ similarity_name: bm25,
463
+ }
464
+ }
465
+
466
+ return mappings, settings