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,74 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from abc import ABC, abstractmethod
5
+ from typing import Any
6
+
7
+ try:
8
+ import orjson
9
+ except ImportError: # pragma: nocover
10
+ orjson = None # type: ignore
11
+
12
+ try:
13
+ import ujson
14
+ except ImportError: # pragma: nocover
15
+ ujson = None # type: ignore
16
+
17
+
18
+ class _JsonHandler(ABC):
19
+ @staticmethod
20
+ @abstractmethod
21
+ def dumps(obj: Any) -> str: ...
22
+
23
+ @staticmethod
24
+ @abstractmethod
25
+ def loads(json_string: str | bytes | bytearray) -> Any: ...
26
+
27
+
28
+ class BuiltinHandler(_JsonHandler):
29
+ serializer: type[json.JSONEncoder] | None = None
30
+
31
+ def __init__(self, serializer: type[json.JSONEncoder] | None = None) -> None:
32
+ """Uses the json module from the Python standard library.
33
+
34
+ Args:
35
+ serializer: A custom JSONEncode to handle serializing fields that the build in
36
+ json.dumps cannot handle, for example UUID and datetime. Defaults to None.
37
+ """
38
+ BuiltinHandler.serializer = serializer
39
+
40
+ @staticmethod
41
+ def dumps(obj: Any) -> str:
42
+ return json.dumps(obj, cls=BuiltinHandler.serializer)
43
+
44
+ @staticmethod
45
+ def loads(json_string: str | bytes | bytearray) -> Any:
46
+ return json.loads(json_string)
47
+
48
+
49
+ class OrjsonHandler(_JsonHandler):
50
+ def __init__(self) -> None:
51
+ if orjson is None: # pragma: no cover
52
+ raise ValueError("orjson must be installed to use the OrjsonHandler")
53
+
54
+ @staticmethod
55
+ def dumps(obj: Any) -> str:
56
+ return orjson.dumps(obj).decode("utf-8")
57
+
58
+ @staticmethod
59
+ def loads(json_string: str | bytes | bytearray) -> Any:
60
+ return orjson.loads(json_string)
61
+
62
+
63
+ class UjsonHandler(_JsonHandler):
64
+ def __init__(self) -> None:
65
+ if ujson is None: # pragma: no cover
66
+ raise ValueError("ujson must be installed to use the UjsonHandler")
67
+
68
+ @staticmethod
69
+ def dumps(obj: Any) -> str:
70
+ return ujson.dumps(obj)
71
+
72
+ @staticmethod
73
+ def loads(json_string: str | bytes | bytearray) -> Any:
74
+ return ujson.loads(json_string)
File without changes
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ from camel_converter.pydantic_base import CamelBase
6
+ from pydantic import Field, field_validator
7
+
8
+ from meilisearch_python_sdk._utils import iso_to_date_time
9
+ from meilisearch_python_sdk.types import JsonDict
10
+
11
+
12
+ class BatchId(CamelBase):
13
+ uid: int
14
+
15
+
16
+ class Status(CamelBase):
17
+ succeeded: int | None = None
18
+ failed: int | None = None
19
+ cancelled: int | None = None
20
+ processing: int | None = None
21
+ enqueued: int | None = None
22
+
23
+
24
+ class Stats(CamelBase):
25
+ total_nb_tasks: int
26
+ status: Status
27
+ batch_types: JsonDict | None = Field(None, alias="types")
28
+ index_uids: JsonDict | None = None
29
+ progress_trace: JsonDict | None = None
30
+ write_channel_congestion: JsonDict | None = None
31
+ internal_database_sizes: JsonDict | None = None
32
+
33
+
34
+ class BatchResult(BatchId):
35
+ details: JsonDict | None = None
36
+ progress: JsonDict | None = None
37
+ stats: Stats
38
+ duration: str | None = None
39
+ started_at: datetime | None = None
40
+ finished_at: datetime | None = None
41
+
42
+ @field_validator("started_at", mode="before") # type: ignore[attr-defined]
43
+ @classmethod
44
+ def validate_started_at(cls, v: str) -> datetime | None:
45
+ return iso_to_date_time(v)
46
+
47
+ @field_validator("finished_at", mode="before") # type: ignore[attr-defined]
48
+ @classmethod
49
+ def validate_finished_at(cls, v: str) -> datetime | None:
50
+ return iso_to_date_time(v)
51
+
52
+
53
+ class BatchStatus(CamelBase):
54
+ results: list[BatchResult]
55
+ total: int
56
+ limit: int
57
+ from_: int | None = Field(None, alias="from")
58
+ next: int | None = None
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping
4
+ from datetime import datetime
5
+
6
+ import pydantic
7
+ from camel_converter.pydantic_base import CamelBase
8
+
9
+ from meilisearch_python_sdk._utils import iso_to_date_time
10
+ from meilisearch_python_sdk.models.index import IndexStats
11
+
12
+
13
+ class ClientStats(CamelBase):
14
+ database_size: int
15
+ used_database_size: int | None = None
16
+ last_update: datetime | None = None
17
+ indexes: dict[str, IndexStats] | None = None
18
+
19
+ @pydantic.field_validator("last_update", mode="before") # type: ignore[attr-defined]
20
+ @classmethod
21
+ def validate_last_update(cls, v: str) -> datetime | None:
22
+ return iso_to_date_time(v)
23
+
24
+
25
+ class _KeyBase(CamelBase):
26
+ uid: str
27
+ name: str | None = None
28
+ description: str | None = None
29
+ actions: list[str]
30
+ indexes: list[str]
31
+ expires_at: datetime | None = None
32
+
33
+ model_config = pydantic.ConfigDict(ser_json_timedelta="iso8601") # type: ignore[typeddict-unknown-key]
34
+
35
+ @pydantic.field_validator("expires_at", mode="before") # type: ignore[attr-defined]
36
+ @classmethod
37
+ def validate_expires_at(cls, v: str) -> datetime | None:
38
+ return iso_to_date_time(v)
39
+
40
+
41
+ class Key(_KeyBase):
42
+ key: str
43
+ created_at: datetime
44
+ updated_at: datetime | None = None
45
+
46
+ @pydantic.field_validator("created_at", mode="before") # type: ignore[attr-defined]
47
+ @classmethod
48
+ def validate_created_at(cls, v: str) -> datetime:
49
+ converted = iso_to_date_time(v)
50
+
51
+ if not converted: # pragma: no cover
52
+ raise ValueError("created_at is required")
53
+
54
+ return converted
55
+
56
+ @pydantic.field_validator("updated_at", mode="before") # type: ignore[attr-defined]
57
+ @classmethod
58
+ def validate_updated_at(cls, v: str) -> datetime | None:
59
+ return iso_to_date_time(v)
60
+
61
+
62
+ class KeyCreate(CamelBase):
63
+ name: str | None = None
64
+ description: str | None = None
65
+ actions: list[str]
66
+ indexes: list[str]
67
+ expires_at: datetime | None = None
68
+
69
+ model_config = pydantic.ConfigDict(ser_json_timedelta="iso8601") # type: ignore[typeddict-unknown-key]
70
+
71
+
72
+ class KeyUpdate(CamelBase):
73
+ key: str
74
+ name: str | None = None
75
+ description: str | None = None
76
+ actions: list[str] | None = None
77
+ indexes: list[str] | None = None
78
+ expires_at: datetime | None = None
79
+
80
+ model_config = pydantic.ConfigDict(ser_json_timedelta="iso8601") # type: ignore[typeddict-unknown-key]
81
+
82
+
83
+ class KeySearch(CamelBase):
84
+ results: list[Key]
85
+ offset: int
86
+ limit: int
87
+ total: int
88
+
89
+
90
+ class Remote(CamelBase):
91
+ url: str | None = None
92
+ search_api_key: str | None = None
93
+
94
+
95
+ class Network(CamelBase):
96
+ self_: str | None = pydantic.Field(None, alias="self")
97
+ remotes: Mapping[str, Remote] | None = None
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from camel_converter.pydantic_base import CamelBase
4
+
5
+ from meilisearch_python_sdk.types import JsonDict
6
+
7
+
8
+ class DocumentsInfo(CamelBase):
9
+ results: list[JsonDict]
10
+ offset: int
11
+ limit: int
12
+ total: int
@@ -0,0 +1,5 @@
1
+ from camel_converter.pydantic_base import CamelBase
2
+
3
+
4
+ class Health(CamelBase):
5
+ status: str
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ import pydantic
6
+ from camel_converter.pydantic_base import CamelBase
7
+
8
+ from meilisearch_python_sdk._utils import iso_to_date_time
9
+
10
+
11
+ class IndexBase(CamelBase):
12
+ uid: str
13
+ primary_key: str | None = None
14
+
15
+
16
+ class IndexInfo(IndexBase):
17
+ created_at: datetime
18
+ updated_at: datetime
19
+
20
+ @pydantic.field_validator("created_at", mode="before") # type: ignore[attr-defined]
21
+ @classmethod
22
+ def validate_created_at(cls, v: str) -> datetime:
23
+ converted = iso_to_date_time(v)
24
+
25
+ if not converted: # pragma: no cover
26
+ raise ValueError("created_at is required")
27
+
28
+ return converted
29
+
30
+ @pydantic.field_validator("updated_at", mode="before") # type: ignore[attr-defined]
31
+ @classmethod
32
+ def validate_updated_at(cls, v: str) -> datetime:
33
+ converted = iso_to_date_time(v)
34
+
35
+ if not converted: # pragma: no cover
36
+ raise ValueError("updated_at is required")
37
+
38
+ return converted
39
+
40
+
41
+ class IndexStats(CamelBase):
42
+ number_of_documents: int
43
+ number_of_embedded_documents: int | None = None
44
+ number_of_embeddings: int | None = None
45
+ is_indexing: bool
46
+ field_distribution: dict[str, int]
@@ -0,0 +1,126 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Generic, Literal, TypeVar
4
+
5
+ from camel_converter.pydantic_base import CamelBase
6
+ from pydantic import Field, field_validator
7
+
8
+ from meilisearch_python_sdk.errors import MeilisearchError
9
+ from meilisearch_python_sdk.types import Filter, JsonDict, JsonMapping
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ class FacetHits(CamelBase):
15
+ value: str
16
+ count: int
17
+
18
+
19
+ class FacetSearchResults(CamelBase):
20
+ facet_hits: list[FacetHits]
21
+ facet_query: str | None
22
+ processing_time_ms: int
23
+
24
+
25
+ class Hybrid(CamelBase):
26
+ semantic_ratio: float
27
+ embedder: str
28
+
29
+
30
+ class MergeFacets(CamelBase):
31
+ max_values_per_facet: int
32
+
33
+
34
+ class Federation(CamelBase):
35
+ limit: int = 20
36
+ offset: int = 0
37
+ facets_by_index: dict[str, list[str]] | None = None
38
+
39
+
40
+ class FederationMerged(CamelBase):
41
+ limit: int = 20
42
+ offset: int = 0
43
+ facets_by_index: dict[str, list[str]] | None = None
44
+ merge_facets: MergeFacets | None
45
+
46
+
47
+ class SearchParams(CamelBase):
48
+ index_uid: str
49
+ query: str | None = Field(None, alias="q")
50
+ offset: int = 0
51
+ limit: int = 20
52
+ filter: Filter | None = None
53
+ facets: list[str] | None = None
54
+ attributes_to_retrieve: list[str] = ["*"]
55
+ attributes_to_crop: list[str] | None = None
56
+ crop_length: int = 200
57
+ attributes_to_highlight: list[str] | None = None
58
+ sort: list[str] | None = None
59
+ show_matches_position: bool = False
60
+ highlight_pre_tag: str = "<em>"
61
+ highlight_post_tag: str = "</em>"
62
+ crop_marker: str = "..."
63
+ matching_strategy: Literal["all", "last", "frequency"] = "last"
64
+ hits_per_page: int | None = None
65
+ page: int | None = None
66
+ attributes_to_search_on: list[str] | None = None
67
+ show_ranking_score: bool = False
68
+ show_ranking_score_details: bool = False
69
+ ranking_score_threshold: float | None = None
70
+ vector: list[float] | None = None
71
+ hybrid: Hybrid | None = None
72
+ locales: list[str] | None = None
73
+ retrieve_vectors: bool | None = None
74
+ media: JsonMapping | None = None
75
+
76
+ @field_validator("ranking_score_threshold", mode="before") # type: ignore[attr-defined]
77
+ @classmethod
78
+ def validate_ranking_score_threshold(cls, v: float | None) -> float | None:
79
+ if v and not 0.0 <= v <= 1.0:
80
+ raise MeilisearchError("ranking_score_threshold must be between 0.0 and 1.0")
81
+
82
+ return v
83
+
84
+
85
+ class SearchResults(CamelBase, Generic[T]):
86
+ hits: list[T]
87
+ offset: int | None = None
88
+ limit: int | None = None
89
+ estimated_total_hits: int | None = None
90
+ processing_time_ms: int
91
+ query: str
92
+ facet_distribution: JsonDict | None = None
93
+ total_pages: int | None = None
94
+ total_hits: int | None = None
95
+ page: int | None = None
96
+ hits_per_page: int | None = None
97
+ semantic_hit_count: int | None = None
98
+ query_vector: list[float] | None = None
99
+
100
+
101
+ class SearchResultsWithUID(SearchResults, Generic[T]):
102
+ index_uid: str
103
+
104
+
105
+ class SearchResultsFederated(CamelBase, Generic[T]):
106
+ hits: list[T]
107
+ offset: int | None = None
108
+ limit: int | None = None
109
+ estimated_total_hits: int | None = None
110
+ processing_time_ms: int
111
+ facet_distribution: JsonDict | None = None
112
+ total_pages: int | None = None
113
+ total_hits: int | None = None
114
+ page: int | None = None
115
+ hits_per_page: int | None = None
116
+ semantic_hit_count: int | None = None
117
+ facets_by_index: JsonDict | None = None
118
+
119
+
120
+ class SimilarSearchResults(CamelBase, Generic[T]):
121
+ hits: list[T]
122
+ id: str
123
+ processing_time_ms: int
124
+ limit: int | None = None
125
+ offset: int | None = None
126
+ estimated_total_hits: int | None = None
@@ -0,0 +1,197 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import Literal
5
+
6
+ from camel_converter.pydantic_base import CamelBase
7
+ from pydantic import field_validator, model_validator
8
+
9
+ from meilisearch_python_sdk.types import JsonDict
10
+
11
+
12
+ class MinWordSizeForTypos(CamelBase):
13
+ one_typo: int | None = None
14
+ two_typos: int | None = None
15
+
16
+
17
+ class TypoTolerance(CamelBase):
18
+ enabled: bool = True
19
+ disable_on_attributes: list[str] | None = None
20
+ disable_on_words: list[str] | None = None
21
+ disable_on_numbers: bool | None = None
22
+ min_word_size_for_typos: MinWordSizeForTypos | None = None
23
+
24
+
25
+ class Faceting(CamelBase):
26
+ max_values_per_facet: int
27
+ sort_facet_values_by: dict[str, str] | None = None
28
+
29
+ @field_validator("sort_facet_values_by") # type: ignore[attr-defined]
30
+ @classmethod
31
+ def validate_facet_order(cls, v: dict[str, str] | None) -> dict[str, str] | None:
32
+ if not v: # pragma: no cover
33
+ return None
34
+
35
+ for _, value in v.items():
36
+ if value not in ("alpha", "count"):
37
+ raise ValueError('facet_order must be either "alpha" or "count"')
38
+
39
+ return v
40
+
41
+
42
+ class Pagination(CamelBase):
43
+ max_total_hits: int
44
+
45
+
46
+ class Distribution(CamelBase):
47
+ mean: float
48
+ sigma: float
49
+
50
+
51
+ class OpenAiEmbedder(CamelBase):
52
+ source: str = "openAi"
53
+ url: str | None = None
54
+ model: str | None = None
55
+ dimensions: int | None = None
56
+ api_key: str | None = None
57
+ document_template: str | None = None
58
+ document_template_max_bytes: int | None = None
59
+ distribution: Distribution | None = None
60
+ binary_quantized: bool | None = None
61
+
62
+
63
+ class HuggingFaceEmbedder(CamelBase):
64
+ source: str = "huggingFace"
65
+ model: str | None = None
66
+ revision: str | None = None
67
+ document_template: str | None = None
68
+ document_template_max_bytes: int | None = None
69
+ distribution: Distribution | None = None
70
+ dimensions: int | None = None
71
+ binary_quantized: bool | None = None
72
+ pooling: Literal["useModel", "forceMean", "forceCls"] | None = None
73
+
74
+
75
+ class OllamaEmbedder(CamelBase):
76
+ source: str = "ollama"
77
+ url: str | None = None
78
+ api_key: str | None = None
79
+ model: str
80
+ dimensions: int | None = None
81
+ document_template: str | None = None
82
+ document_template_max_bytes: int | None = None
83
+ distribution: Distribution | None = None
84
+ binary_quantized: bool | None = None
85
+
86
+
87
+ class RestEmbedder(CamelBase):
88
+ source: str = "rest"
89
+ url: str
90
+ api_key: str | None = None
91
+ dimensions: int
92
+ document_template: str | None = None
93
+ document_template_max_bytes: int | None = None
94
+ distribution: Distribution | None = None
95
+ headers: JsonDict | None = None
96
+ request: JsonDict
97
+ response: JsonDict
98
+ binary_quantized: bool | None = None
99
+ indexing_fragments: JsonDict | None = None
100
+ search_fragment: JsonDict | None = None
101
+
102
+ @model_validator(mode="after")
103
+ def check_document_template(self) -> RestEmbedder:
104
+ if self.indexing_fragments is not None and self.document_template is not None:
105
+ raise ValueError("document_template must be None when indexing_fragments is set")
106
+
107
+ return self
108
+
109
+
110
+ class UserProvidedEmbedder(CamelBase):
111
+ source: str = "userProvided"
112
+ dimensions: int
113
+ distribution: Distribution | None = None
114
+ document_template: str | None = None
115
+ document_template_max_bytes: int | None = None
116
+ binary_quantized: bool | None = None
117
+
118
+
119
+ class CompositeEmbedder(CamelBase):
120
+ source: str = "composite"
121
+ search_embedder: (
122
+ OpenAiEmbedder | HuggingFaceEmbedder | OllamaEmbedder | RestEmbedder | UserProvidedEmbedder
123
+ )
124
+ indexing_embedder: (
125
+ OpenAiEmbedder | HuggingFaceEmbedder | OllamaEmbedder | RestEmbedder | UserProvidedEmbedder
126
+ )
127
+
128
+
129
+ class Embedders(CamelBase):
130
+ embedders: dict[
131
+ str,
132
+ OpenAiEmbedder
133
+ | HuggingFaceEmbedder
134
+ | OllamaEmbedder
135
+ | RestEmbedder
136
+ | UserProvidedEmbedder
137
+ | CompositeEmbedder,
138
+ ]
139
+
140
+
141
+ class ProximityPrecision(str, Enum):
142
+ BY_WORD = "byWord"
143
+ BY_ATTRIBUTE = "byAttribute"
144
+
145
+
146
+ class LocalizedAttributes(CamelBase):
147
+ locales: list[str]
148
+ attribute_patterns: list[str]
149
+
150
+
151
+ class Filter(CamelBase):
152
+ equality: bool
153
+ comparison: bool
154
+
155
+
156
+ class FilterableAttributeFeatures(CamelBase):
157
+ facet_search: bool
158
+ filter: Filter
159
+
160
+
161
+ class FilterableAttributes(CamelBase):
162
+ attribute_patterns: list[str]
163
+ features: FilterableAttributeFeatures
164
+
165
+
166
+ class MeilisearchSettings(CamelBase):
167
+ synonyms: JsonDict | None = None
168
+ stop_words: list[str] | None = None
169
+ ranking_rules: list[str] | None = None
170
+ filterable_attributes: list[str | FilterableAttributes] | None = None
171
+ distinct_attribute: str | None = None
172
+ searchable_attributes: list[str] | None = None
173
+ displayed_attributes: list[str] | None = None
174
+ sortable_attributes: list[str] | None = None
175
+ typo_tolerance: TypoTolerance | None = None
176
+ faceting: Faceting | None = None
177
+ pagination: Pagination | None = None
178
+ proximity_precision: ProximityPrecision | None = None
179
+ separator_tokens: list[str] | None = None
180
+ non_separator_tokens: list[str] | None = None
181
+ search_cutoff_ms: int | None = None
182
+ dictionary: list[str] | None = None
183
+ embedders: (
184
+ dict[
185
+ str,
186
+ OpenAiEmbedder
187
+ | HuggingFaceEmbedder
188
+ | OllamaEmbedder
189
+ | RestEmbedder
190
+ | UserProvidedEmbedder
191
+ | CompositeEmbedder,
192
+ ]
193
+ | None
194
+ ) = None # Optional[Embedders] = None
195
+ localized_attributes: list[LocalizedAttributes] | None = None
196
+ facet_search: bool | None = None
197
+ prefix_search: Literal["disabled", "indexingTime", "searchTime"] | None = None
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+ import pydantic
6
+ from camel_converter.pydantic_base import CamelBase
7
+ from pydantic import Field
8
+
9
+ from meilisearch_python_sdk._utils import iso_to_date_time
10
+ from meilisearch_python_sdk.types import JsonDict
11
+
12
+
13
+ class TaskId(CamelBase):
14
+ uid: int
15
+
16
+
17
+ class TaskResult(TaskId):
18
+ index_uid: str | None = None
19
+ status: str
20
+ task_type: str | JsonDict = Field(..., alias="type")
21
+ details: JsonDict | None = None
22
+ error: JsonDict | None = None
23
+ canceled_by: int | None = None
24
+ duration: str | None = None
25
+ enqueued_at: datetime
26
+ started_at: datetime | None = None
27
+ finished_at: datetime | None = None
28
+ batch_uid: int | None = None
29
+ custom_metadata: str | None = None
30
+
31
+ @pydantic.field_validator("enqueued_at", mode="before") # type: ignore[attr-defined]
32
+ @classmethod
33
+ def validate_enqueued_at(cls, v: str) -> datetime:
34
+ converted = iso_to_date_time(v)
35
+
36
+ if not converted: # pragma: no cover
37
+ raise ValueError("enqueued_at is required")
38
+
39
+ return converted
40
+
41
+ @pydantic.field_validator("started_at", mode="before") # type: ignore[attr-defined]
42
+ @classmethod
43
+ def validate_started_at(cls, v: str) -> datetime | None:
44
+ return iso_to_date_time(v)
45
+
46
+ @pydantic.field_validator("finished_at", mode="before") # type: ignore[attr-defined]
47
+ @classmethod
48
+ def validate_finished_at(cls, v: str) -> datetime | None:
49
+ return iso_to_date_time(v)
50
+
51
+
52
+ class TaskStatus(CamelBase):
53
+ results: list[TaskResult]
54
+ total: int
55
+ limit: int
56
+ from_: int | None = Field(None, alias="from")
57
+ next: int | None = None
58
+
59
+
60
+ class TaskInfo(CamelBase):
61
+ task_uid: int
62
+ index_uid: str | None = None
63
+ status: str
64
+ task_type: str | JsonDict = Field(..., alias="type")
65
+ enqueued_at: datetime
66
+ batch_uid: int | None = None
67
+ custom_metadata: str | None = None
68
+
69
+ @pydantic.field_validator("enqueued_at", mode="before") # type: ignore[attr-defined]
70
+ @classmethod
71
+ def validate_enqueued_at(cls, v: str) -> datetime:
72
+ converted = iso_to_date_time(v)
73
+
74
+ if not converted: # pragma: no cover
75
+ raise ValueError("enqueued_at is required")
76
+
77
+ return converted