meilisearch-python-sdk 5.5.0__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 (32) hide show
  1. meilisearch_python_sdk/__init__.py +8 -0
  2. meilisearch_python_sdk/_batch.py +166 -0
  3. meilisearch_python_sdk/_client.py +2468 -0
  4. meilisearch_python_sdk/_http_requests.py +197 -0
  5. meilisearch_python_sdk/_task.py +368 -0
  6. meilisearch_python_sdk/_utils.py +58 -0
  7. meilisearch_python_sdk/_version.py +1 -0
  8. meilisearch_python_sdk/decorators.py +242 -0
  9. meilisearch_python_sdk/errors.py +75 -0
  10. meilisearch_python_sdk/index/__init__.py +4 -0
  11. meilisearch_python_sdk/index/_common.py +296 -0
  12. meilisearch_python_sdk/index/async_index.py +4891 -0
  13. meilisearch_python_sdk/index/index.py +3839 -0
  14. meilisearch_python_sdk/json_handler.py +74 -0
  15. meilisearch_python_sdk/models/__init__.py +0 -0
  16. meilisearch_python_sdk/models/batch.py +58 -0
  17. meilisearch_python_sdk/models/client.py +97 -0
  18. meilisearch_python_sdk/models/documents.py +12 -0
  19. meilisearch_python_sdk/models/health.py +5 -0
  20. meilisearch_python_sdk/models/index.py +46 -0
  21. meilisearch_python_sdk/models/search.py +126 -0
  22. meilisearch_python_sdk/models/settings.py +197 -0
  23. meilisearch_python_sdk/models/task.py +77 -0
  24. meilisearch_python_sdk/models/version.py +9 -0
  25. meilisearch_python_sdk/models/webhook.py +24 -0
  26. meilisearch_python_sdk/plugins.py +124 -0
  27. meilisearch_python_sdk/py.typed +0 -0
  28. meilisearch_python_sdk/types.py +8 -0
  29. meilisearch_python_sdk-5.5.0.dist-info/METADATA +279 -0
  30. meilisearch_python_sdk-5.5.0.dist-info/RECORD +32 -0
  31. meilisearch_python_sdk-5.5.0.dist-info/WHEEL +4 -0
  32. meilisearch_python_sdk-5.5.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,242 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from collections.abc import Callable
5
+ from functools import wraps
6
+ from typing import Any, NamedTuple
7
+
8
+ from meilisearch_python_sdk import AsyncClient, Client
9
+ from meilisearch_python_sdk._utils import use_task_groups
10
+
11
+
12
+ class ConnectionInfo(NamedTuple):
13
+ """Information on how to connect to Meilisearch.
14
+
15
+ url: URL for the Meilisearch server.
16
+ api_key: The API key for the server.
17
+ """
18
+
19
+ url: str
20
+ api_key: str
21
+
22
+
23
+ def async_add_documents(
24
+ *,
25
+ index_name: str,
26
+ connection_info: AsyncClient | ConnectionInfo,
27
+ batch_size: int | None = None,
28
+ primary_key: str | None = None,
29
+ wait_for_task: bool = False,
30
+ verify: bool = True,
31
+ ) -> Callable:
32
+ """Decorator that takes the returned documents from a function and asynchronously adds them to Meilisearch.
33
+
34
+ It is required that either an async_client or url is provided.
35
+
36
+ Args:
37
+ index_name: The name of the index to which the documents should be added.
38
+ connection_info: Either an AsyncClient instance ConnectionInfo with information on how to
39
+ connect to Meilisearch.
40
+ batch_size: If provided the documents will be sent in batches of the specified size.
41
+ Otherwise all documents are sent at once. Default = None.
42
+ primary_key: The primary key of the documents. This will be ignored if already set.
43
+ Defaults to None.
44
+ wait_for_task: If set to `True` the decorator will wait for the document addition to finish
45
+ indexing before returning, otherwise it will return right away. Default = False.
46
+ verify: If set to `False` the decorator will not verify the SSL certificate of the server.
47
+
48
+ Returns:
49
+ The list of documents provided by the decorated function.
50
+
51
+ Raises:
52
+ MeilisearchCommunicationError: If there was an error communicating with the server.
53
+ MeilisearchApiError: If the Meilisearch API returned an error.
54
+ ValueError: If neither an async_client nor an url is provided.
55
+
56
+ Examples
57
+ >>> from meilisearch_python_sdk import AsyncClient
58
+ >>> from meilisearch_python_sdk.decorators import async_add_documents, ConnectionInfo
59
+ >>>
60
+ >>>
61
+ >>> # with `AsyncClient`
62
+ >>> client = AsyncClient(url="http://localhost:7700", api_key="masterKey")
63
+ >>> @async_add_documents(index_name="movies", connection_info=client)
64
+ >>> async def my_function() -> list[dict[str, Any]]:
65
+ >>> return [{"id": 1, "title": "Test 1"}, {"id": 2, "title": "Test 2"}]
66
+ >>>
67
+ >>> # with `ConnectionInfo`
68
+ >>> @async_add_documents(
69
+ index_name="movies",
70
+ connection_info=ConnectionInfo(url="http://localhost:7700", api_key="masterKey"),
71
+ )
72
+ >>> async def my_function() -> list[dict[str, Any]]:
73
+ >>> return [{"id": 1, "title": "Test 1"}, {"id": 2, "title": "Test 2"}]
74
+ """
75
+
76
+ def decorator(func: Callable) -> Callable:
77
+ @wraps(func)
78
+ async def wrapper(*args: Any, **kwargs: Any) -> Any:
79
+ result = await func(*args, **kwargs)
80
+ if isinstance(connection_info, AsyncClient):
81
+ await _async_add_documents(
82
+ connection_info,
83
+ index_name,
84
+ result,
85
+ batch_size,
86
+ primary_key,
87
+ wait_for_task,
88
+ )
89
+ return result
90
+
91
+ async with AsyncClient(
92
+ connection_info.url, connection_info.api_key, verify=verify
93
+ ) as client:
94
+ await _async_add_documents(
95
+ client, index_name, result, batch_size, primary_key, wait_for_task
96
+ )
97
+
98
+ return result
99
+
100
+ return wrapper
101
+
102
+ return decorator
103
+
104
+
105
+ def add_documents(
106
+ *,
107
+ index_name: str,
108
+ connection_info: Client | ConnectionInfo,
109
+ batch_size: int | None = None,
110
+ primary_key: str | None = None,
111
+ wait_for_task: bool = False,
112
+ verify: bool = True,
113
+ ) -> Callable:
114
+ """Decorator that takes the returned documents from a function and adds them to Meilisearch.
115
+
116
+ It is required that either an client or url is provided.
117
+
118
+ Args:
119
+ index_name: The name of the index to which the documents should be added.
120
+ connection_info: Either an Client instance ConnectionInfo with information on how to
121
+ connect to Meilisearch.
122
+ batch_size: If provided the documents will be sent in batches of the specified size.
123
+ Otherwise all documents are sent at once. Default = None.
124
+ primary_key: The primary key of the documents. This will be ignored if already set.
125
+ Defaults to None.
126
+ wait_for_task: If set to `True` the decorator will wait for the document addition to finish
127
+ indexing before returning, otherwise it will return right away. Default = False.
128
+ verify: If set to `False` the decorator will not verify the SSL certificate of the server.
129
+
130
+ Returns:
131
+ The list of documents provided by the decorated function.
132
+
133
+ Raises:
134
+ MeilisearchCommunicationError: If there was an error communicating with the server.
135
+ MeilisearchApiError: If the Meilisearch API returned an error.
136
+ ValueError: If neither an async_client nor an url is provided.
137
+
138
+ Examples
139
+ >>> from meilisearch_python_sdk import Client
140
+ >>> from meilisearch_python_sdk.decorators import add_documents, ConnectionInfo
141
+ >>>
142
+ >>>
143
+ >>> # With `Client`
144
+ >>> client = Client(url="http://localhost:7700", api_key="masterKey")
145
+ >>> @add_documents(index_name="movies", connection_info=client)
146
+ >>> def my_function() -> list[dict[str, Any]]:
147
+ >>> return [{"id": 1, "title": "Test 1"}, {"id": 2, "title": "Test 2"}]
148
+ >>>
149
+ >>> # With `ConnectionInfo`
150
+ >>> @add_documents(
151
+ index_name="movies",
152
+ connection_info=ConnectionInfo(url="http://localhost:7700", api_key="masterKey"),
153
+ )
154
+ >>> def my_function() -> list[dict[str, Any]]:
155
+ >>> return [{"id": 1, "title": "Test 1"}, {"id": 2, "title": "Test 2"}]
156
+ """
157
+
158
+ def decorator(func: Callable) -> Callable:
159
+ @wraps(func)
160
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
161
+ result = func(*args, **kwargs)
162
+ if isinstance(connection_info, Client):
163
+ _add_documents(
164
+ connection_info,
165
+ index_name,
166
+ result,
167
+ batch_size,
168
+ primary_key,
169
+ wait_for_task,
170
+ )
171
+ return result
172
+
173
+ decorator_client = Client(
174
+ url=connection_info.url, api_key=connection_info.api_key, verify=verify
175
+ )
176
+ _add_documents(
177
+ decorator_client,
178
+ index_name,
179
+ result,
180
+ batch_size,
181
+ primary_key,
182
+ wait_for_task,
183
+ )
184
+
185
+ return result
186
+
187
+ return wrapper
188
+
189
+ return decorator
190
+
191
+
192
+ async def _async_add_documents(
193
+ async_client: AsyncClient,
194
+ index_name: str,
195
+ documents: Any,
196
+ batch_size: int | None,
197
+ primary_key: str | None,
198
+ wait_for_task: bool,
199
+ ) -> None:
200
+ index = async_client.index(index_name)
201
+ if not batch_size:
202
+ task = await index.add_documents(documents, primary_key)
203
+ if wait_for_task:
204
+ await async_client.wait_for_task(task.task_uid, timeout_in_ms=None)
205
+ return
206
+
207
+ tasks = await index.add_documents_in_batches(
208
+ documents, batch_size=batch_size, primary_key=primary_key
209
+ )
210
+
211
+ if wait_for_task:
212
+ if not use_task_groups():
213
+ waits = [async_client.wait_for_task(x.task_uid) for x in tasks]
214
+ await asyncio.gather(*waits)
215
+ return
216
+
217
+ async with asyncio.TaskGroup() as tg: # type: ignore[attr-defined]
218
+ [tg.create_task(async_client.wait_for_task(x.task_uid)) for x in tasks]
219
+
220
+
221
+ def _add_documents(
222
+ client: Client,
223
+ index_name: str,
224
+ documents: Any,
225
+ batch_size: int | None,
226
+ primary_key: str | None,
227
+ wait_for_task: bool,
228
+ ) -> None:
229
+ index = client.index(index_name)
230
+ if not batch_size:
231
+ task = index.add_documents(documents, primary_key)
232
+ if wait_for_task:
233
+ client.wait_for_task(task.task_uid, timeout_in_ms=None)
234
+ return
235
+
236
+ tasks = index.add_documents_in_batches(
237
+ documents, batch_size=batch_size, primary_key=primary_key
238
+ )
239
+
240
+ if wait_for_task:
241
+ for task in tasks:
242
+ client.wait_for_task(task.task_uid)
@@ -0,0 +1,75 @@
1
+ from httpx import Response
2
+
3
+
4
+ class BatchNotFoundError(Exception):
5
+ pass
6
+
7
+
8
+ class InvalidDocumentError(Exception):
9
+ """Error for documents that are not in a valid format for Meilisearch."""
10
+
11
+ pass
12
+
13
+
14
+ class InvalidRestriction(Exception):
15
+ pass
16
+
17
+
18
+ class MeilisearchError(Exception):
19
+ """Generic class for Meilisearch error handling."""
20
+
21
+ def __init__(self, message: str) -> None:
22
+ self.message = message
23
+ super().__init__(self.message)
24
+
25
+ def __str__(self) -> str:
26
+ return f"MeilisearchError. Error message: {self.message}."
27
+
28
+
29
+ class MeilisearchApiError(MeilisearchError):
30
+ """Error sent by Meilisearch API."""
31
+
32
+ def __init__(self, error: str, response: Response) -> None:
33
+ self.status_code = response.status_code
34
+ self.code = ""
35
+ self.message = ""
36
+ self.link = ""
37
+ self.error_type = ""
38
+ if response.content:
39
+ self.message = f"Error message: {response.json().get('message') or ''}"
40
+ self.code = f"{response.json().get('code') or ''}"
41
+ self.error_type = f"{response.json().get('type') or ''}"
42
+ self.link = f"Error documentation: {response.json().get('link') or ''}"
43
+ else:
44
+ self.message = error
45
+ super().__init__(self.message)
46
+
47
+ def __str__(self) -> str:
48
+ return f"MeilisearchApiError.{self.code} {self.message} {self.error_type} {self.link}"
49
+
50
+
51
+ class MeilisearchCommunicationError(MeilisearchError):
52
+ """Error when connecting to Meilisearch."""
53
+
54
+ def __str__(self) -> str:
55
+ return f"MeilisearchCommunicationError, {self.message}"
56
+
57
+
58
+ class MeilisearchTaskFailedError(MeilisearchError):
59
+ """Error when a task is in the failed status."""
60
+
61
+ def __str__(self) -> str:
62
+ return f"MeilisearchTaskFailedError, {self.message}"
63
+
64
+
65
+ class MeilisearchTimeoutError(MeilisearchError):
66
+ """Error when Meilisearch operation takes longer than expected."""
67
+
68
+ def __str__(self) -> str:
69
+ return f"MeilisearchTimeoutError, {self.message}"
70
+
71
+
72
+ class PayloadTooLarge(Exception):
73
+ """Error when the payload is larger than the allowed payload size."""
74
+
75
+ pass
@@ -0,0 +1,4 @@
1
+ from meilisearch_python_sdk.index.async_index import AsyncIndex
2
+ from meilisearch_python_sdk.index.index import Index
3
+
4
+ __all__ = ["AsyncIndex", "Index"]
@@ -0,0 +1,296 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Generator, MutableMapping, Sequence
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING, Any, Literal
7
+ from urllib.parse import urlencode
8
+
9
+ from meilisearch_python_sdk._utils import iso_to_date_time
10
+ from meilisearch_python_sdk.errors import MeilisearchError
11
+ from meilisearch_python_sdk.json_handler import BuiltinHandler, OrjsonHandler, UjsonHandler
12
+ from meilisearch_python_sdk.models.search import (
13
+ Hybrid,
14
+ )
15
+ from meilisearch_python_sdk.models.settings import (
16
+ CompositeEmbedder,
17
+ Embedders,
18
+ HuggingFaceEmbedder,
19
+ OllamaEmbedder,
20
+ OpenAiEmbedder,
21
+ RestEmbedder,
22
+ UserProvidedEmbedder,
23
+ )
24
+ from meilisearch_python_sdk.plugins import (
25
+ AsyncDocumentPlugin,
26
+ AsyncPlugin,
27
+ AsyncPostSearchPlugin,
28
+ DocumentPlugin,
29
+ Plugin,
30
+ PostSearchPlugin,
31
+ )
32
+ from meilisearch_python_sdk.types import JsonDict
33
+
34
+ if TYPE_CHECKING: # pragma: no cover
35
+ import sys
36
+
37
+ from meilisearch_python_sdk.types import Filter, JsonMapping
38
+
39
+ if sys.version_info >= (3, 11):
40
+ pass
41
+ else:
42
+ pass
43
+
44
+
45
+ class BaseIndex:
46
+ def __init__(
47
+ self,
48
+ uid: str,
49
+ primary_key: str | None = None,
50
+ created_at: str | datetime | None = None,
51
+ updated_at: str | datetime | None = None,
52
+ json_handler: BuiltinHandler | OrjsonHandler | UjsonHandler | None = None,
53
+ hits_type: Any = JsonDict,
54
+ ):
55
+ self.uid = uid
56
+ self.primary_key = primary_key
57
+ self.created_at: datetime | None = iso_to_date_time(created_at)
58
+ self.updated_at: datetime | None = iso_to_date_time(updated_at)
59
+ self.hits_type = hits_type
60
+ self._base_url = "indexes/"
61
+ self._base_url_with_uid = f"{self._base_url}{self.uid}"
62
+ self._documents_url = f"{self._base_url_with_uid}/documents"
63
+ self._stats_url = f"{self._base_url_with_uid}/stats"
64
+ self._settings_url = f"{self._base_url_with_uid}/settings"
65
+ self._json_handler = json_handler if json_handler else BuiltinHandler()
66
+
67
+ def __str__(self) -> str:
68
+ return f"{type(self).__name__}(uid={self.uid}, primary_key={self.primary_key}, created_at={self.created_at}, updated_at={self.updated_at})"
69
+
70
+ def __repr__(self) -> str:
71
+ return f"{type(self).__name__}(uid={self.uid!r}, primary_key={self.primary_key!r}, created_at={self.created_at!r}, updated_at={self.updated_at!r})"
72
+
73
+ def _set_fetch_info(
74
+ self, primary_key: str, created_at_iso_str: str, updated_at_iso_str: str
75
+ ) -> None:
76
+ self.primary_key = primary_key
77
+ self.created_at = iso_to_date_time(created_at_iso_str)
78
+ self.updated_at = iso_to_date_time(updated_at_iso_str)
79
+
80
+
81
+ def batch(
82
+ documents: Sequence[MutableMapping], batch_size: int
83
+ ) -> Generator[Sequence[MutableMapping], None, None]:
84
+ total_len = len(documents)
85
+ for i in range(0, total_len, batch_size):
86
+ yield documents[i : i + batch_size]
87
+
88
+
89
+ def combine_documents(documents: list[list[Any]]) -> list[Any]:
90
+ return [x for y in documents for x in y]
91
+
92
+
93
+ def plugin_has_method(
94
+ plugin: AsyncPlugin
95
+ | AsyncDocumentPlugin
96
+ | AsyncPostSearchPlugin
97
+ | Plugin
98
+ | DocumentPlugin
99
+ | PostSearchPlugin,
100
+ method: str,
101
+ ) -> bool:
102
+ check = getattr(plugin, method, None)
103
+ if callable(check):
104
+ return True
105
+
106
+ return False
107
+
108
+
109
+ def raise_on_no_documents(
110
+ documents: list[Any], document_type: str, directory_path: str | Path
111
+ ) -> None:
112
+ if not documents:
113
+ raise MeilisearchError(f"No {document_type} files found in {directory_path}")
114
+
115
+
116
+ def process_search_parameters(
117
+ *,
118
+ q: str | None = None,
119
+ facet_name: str | None = None,
120
+ facet_query: str | None = None,
121
+ offset: int = 0,
122
+ limit: int = 20,
123
+ filter: Filter | None = None,
124
+ facets: list[str] | None = None,
125
+ attributes_to_retrieve: list[str] | None = None,
126
+ attributes_to_crop: list[str] | None = None,
127
+ crop_length: int = 200,
128
+ attributes_to_highlight: list[str] | None = None,
129
+ sort: list[str] | None = None,
130
+ show_matches_position: bool = False,
131
+ highlight_pre_tag: str = "<em>",
132
+ highlight_post_tag: str = "</em>",
133
+ crop_marker: str = "...",
134
+ matching_strategy: Literal["all", "last", "frequency"] = "last",
135
+ hits_per_page: int | None = None,
136
+ page: int | None = None,
137
+ attributes_to_search_on: list[str] | None = None,
138
+ distinct: str | None = None,
139
+ show_ranking_score: bool = False,
140
+ show_ranking_score_details: bool = False,
141
+ ranking_score_threshold: float | None = None,
142
+ vector: list[float] | None = None,
143
+ hybrid: Hybrid | None = None,
144
+ locales: list[str] | None = None,
145
+ retrieve_vectors: bool | None = None,
146
+ exhaustive_facet_count: bool | None = None,
147
+ media: JsonMapping | None = None,
148
+ ) -> JsonDict:
149
+ if attributes_to_retrieve is None:
150
+ attributes_to_retrieve = ["*"]
151
+
152
+ body: JsonDict = {
153
+ "q": q,
154
+ "offset": offset,
155
+ "limit": limit,
156
+ "filter": filter,
157
+ "facets": facets,
158
+ "attributesToRetrieve": attributes_to_retrieve,
159
+ "attributesToCrop": attributes_to_crop,
160
+ "cropLength": crop_length,
161
+ "attributesToHighlight": attributes_to_highlight,
162
+ "sort": sort,
163
+ "showMatchesPosition": show_matches_position,
164
+ "highlightPreTag": highlight_pre_tag,
165
+ "highlightPostTag": highlight_post_tag,
166
+ "cropMarker": crop_marker,
167
+ "matchingStrategy": matching_strategy,
168
+ "hitsPerPage": hits_per_page,
169
+ "page": page,
170
+ "attributesToSearchOn": attributes_to_search_on,
171
+ "showRankingScore": show_ranking_score,
172
+ "rankingScoreThreshold": ranking_score_threshold,
173
+ }
174
+
175
+ if facet_name:
176
+ body["facetName"] = facet_name
177
+
178
+ if facet_query:
179
+ body["facetQuery"] = facet_query
180
+
181
+ if distinct:
182
+ body["distinct"] = distinct
183
+
184
+ if show_ranking_score_details:
185
+ body["showRankingScoreDetails"] = show_ranking_score_details
186
+
187
+ if vector:
188
+ body["vector"] = vector
189
+
190
+ if hybrid:
191
+ body["hybrid"] = hybrid.model_dump(by_alias=True)
192
+
193
+ if locales:
194
+ body["locales"] = locales
195
+
196
+ if retrieve_vectors is not None:
197
+ body["retrieveVectors"] = retrieve_vectors
198
+
199
+ if exhaustive_facet_count is not None:
200
+ body["exhaustivefacetCount"] = exhaustive_facet_count
201
+
202
+ if media is not None:
203
+ body["media"] = media
204
+
205
+ return body
206
+
207
+
208
+ def build_encoded_url(base_url: str, params: JsonMapping) -> str:
209
+ return f"{base_url}?{urlencode(params)}"
210
+
211
+
212
+ # TODO: Add back after embedder setting issue fixed https://github.com/meilisearch/meilisearch/issues/4585
213
+ def embedder_json_to_embedders_model( # pragma: no cover
214
+ embedder_json: JsonDict | None,
215
+ ) -> Embedders | None:
216
+ if not embedder_json: # pragma: no cover
217
+ return None
218
+
219
+ embedders: dict[
220
+ str,
221
+ OpenAiEmbedder
222
+ | HuggingFaceEmbedder
223
+ | OllamaEmbedder
224
+ | RestEmbedder
225
+ | UserProvidedEmbedder
226
+ | CompositeEmbedder,
227
+ ] = {}
228
+ for k, v in embedder_json.items():
229
+ if v.get("source") == "openAi":
230
+ embedders[k] = OpenAiEmbedder(**v)
231
+ elif v.get("source") == "huggingFace":
232
+ embedders[k] = HuggingFaceEmbedder(**v)
233
+ elif v.get("source") == "ollama":
234
+ embedders[k] = OllamaEmbedder(**v)
235
+ elif v.get("source") == "rest":
236
+ embedders[k] = RestEmbedder(**v)
237
+ elif v.get("source") == "composit":
238
+ embedders[k] = CompositeEmbedder(**v)
239
+ else:
240
+ embedders[k] = UserProvidedEmbedder(**v)
241
+
242
+ return Embedders(embedders=embedders)
243
+
244
+
245
+ # TODO: Add back after embedder setting issue fixed https://github.com/meilisearch/meilisearch/issues/4585
246
+ def embedder_json_to_settings_model( # pragma: no cover
247
+ embedder_json: JsonDict | None,
248
+ ) -> (
249
+ dict[
250
+ str,
251
+ OpenAiEmbedder
252
+ | HuggingFaceEmbedder
253
+ | OllamaEmbedder
254
+ | RestEmbedder
255
+ | UserProvidedEmbedder
256
+ | CompositeEmbedder,
257
+ ]
258
+ | None
259
+ ):
260
+ if not embedder_json: # pragma: no cover
261
+ return None
262
+
263
+ embedders: dict[
264
+ str,
265
+ OpenAiEmbedder
266
+ | HuggingFaceEmbedder
267
+ | OllamaEmbedder
268
+ | RestEmbedder
269
+ | UserProvidedEmbedder
270
+ | CompositeEmbedder,
271
+ ] = {}
272
+ for k, v in embedder_json.items():
273
+ if v.get("source") == "openAi":
274
+ embedders[k] = OpenAiEmbedder(**v)
275
+ elif v.get("source") == "huggingFace":
276
+ embedders[k] = HuggingFaceEmbedder(**v)
277
+ elif v.get("source") == "ollama":
278
+ embedders[k] = OllamaEmbedder(**v)
279
+ elif v.get("source") == "rest":
280
+ embedders[k] = RestEmbedder(**v)
281
+ elif v.get("source") == "composit":
282
+ embedders[k] = CompositeEmbedder(**v)
283
+ else:
284
+ embedders[k] = UserProvidedEmbedder(**v)
285
+
286
+ return embedders
287
+
288
+
289
+ def validate_file_type(file_path: Path) -> None:
290
+ if file_path.suffix not in (".json", ".csv", ".ndjson"):
291
+ raise MeilisearchError("File must be a json, ndjson, or csv file")
292
+
293
+
294
+ def validate_ranking_score_threshold(ranking_score_threshold: float) -> None:
295
+ if not 0.0 <= ranking_score_threshold <= 1.0:
296
+ raise MeilisearchError("ranking_score_threshold must be between 0.0 and 1.0")