mirascope 2.1.0__py3-none-any.whl → 2.2.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 (40) hide show
  1. mirascope/api/_generated/functions/client.py +10 -0
  2. mirascope/api/_generated/functions/raw_client.py +8 -0
  3. mirascope/api/_generated/functions/types/functions_create_response.py +25 -8
  4. mirascope/api/_generated/functions/types/functions_find_by_hash_response.py +25 -10
  5. mirascope/api/_generated/functions/types/functions_get_by_env_response.py +1 -0
  6. mirascope/api/_generated/functions/types/functions_get_response.py +25 -8
  7. mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +1 -0
  8. mirascope/api/_generated/functions/types/functions_list_response_functions_item.py +22 -7
  9. mirascope/api/_generated/reference.md +9 -0
  10. mirascope/llm/__init__.py +42 -0
  11. mirascope/llm/calls/calls.py +38 -11
  12. mirascope/llm/exceptions.py +69 -0
  13. mirascope/llm/prompts/prompts.py +47 -9
  14. mirascope/llm/providers/__init__.py +3 -0
  15. mirascope/llm/providers/openai/completions/_utils/__init__.py +3 -0
  16. mirascope/llm/providers/openai/completions/_utils/encode.py +27 -32
  17. mirascope/llm/providers/openai/completions/_utils/feature_info.py +50 -0
  18. mirascope/llm/providers/openai/completions/base_provider.py +21 -0
  19. mirascope/llm/providers/openai/completions/provider.py +8 -2
  20. mirascope/llm/providers/openrouter/__init__.py +5 -0
  21. mirascope/llm/providers/openrouter/provider.py +67 -0
  22. mirascope/llm/providers/provider_id.py +2 -0
  23. mirascope/llm/providers/provider_registry.py +6 -0
  24. mirascope/llm/responses/response.py +217 -0
  25. mirascope/llm/responses/stream_response.py +234 -0
  26. mirascope/llm/retries/__init__.py +51 -0
  27. mirascope/llm/retries/retry_calls.py +159 -0
  28. mirascope/llm/retries/retry_config.py +168 -0
  29. mirascope/llm/retries/retry_decorator.py +258 -0
  30. mirascope/llm/retries/retry_models.py +1313 -0
  31. mirascope/llm/retries/retry_prompts.py +227 -0
  32. mirascope/llm/retries/retry_responses.py +340 -0
  33. mirascope/llm/retries/retry_stream_responses.py +571 -0
  34. mirascope/llm/retries/utils.py +159 -0
  35. mirascope/ops/_internal/versioned_calls.py +249 -9
  36. mirascope/ops/_internal/versioned_functions.py +2 -0
  37. {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/METADATA +1 -1
  38. {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/RECORD +40 -28
  39. {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/WHEEL +0 -0
  40. {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -66,6 +66,7 @@ class FunctionsClient:
66
66
  signature: str,
67
67
  signature_hash: str,
68
68
  name: str,
69
+ language: str,
69
70
  description: typing.Optional[str] = OMIT,
70
71
  tags: typing.Optional[typing.Sequence[str]] = OMIT,
71
72
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
@@ -87,6 +88,8 @@ class FunctionsClient:
87
88
 
88
89
  name : str
89
90
 
91
+ language : str
92
+
90
93
  description : typing.Optional[str]
91
94
 
92
95
  tags : typing.Optional[typing.Sequence[str]]
@@ -114,6 +117,7 @@ class FunctionsClient:
114
117
  signature="signature",
115
118
  signature_hash="signatureHash",
116
119
  name="name",
120
+ language="language",
117
121
  )
118
122
  """
119
123
  _response = self._raw_client.create(
@@ -122,6 +126,7 @@ class FunctionsClient:
122
126
  signature=signature,
123
127
  signature_hash=signature_hash,
124
128
  name=name,
129
+ language=language,
125
130
  description=description,
126
131
  tags=tags,
127
132
  metadata=metadata,
@@ -359,6 +364,7 @@ class AsyncFunctionsClient:
359
364
  signature: str,
360
365
  signature_hash: str,
361
366
  name: str,
367
+ language: str,
362
368
  description: typing.Optional[str] = OMIT,
363
369
  tags: typing.Optional[typing.Sequence[str]] = OMIT,
364
370
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
@@ -380,6 +386,8 @@ class AsyncFunctionsClient:
380
386
 
381
387
  name : str
382
388
 
389
+ language : str
390
+
383
391
  description : typing.Optional[str]
384
392
 
385
393
  tags : typing.Optional[typing.Sequence[str]]
@@ -412,6 +420,7 @@ class AsyncFunctionsClient:
412
420
  signature="signature",
413
421
  signature_hash="signatureHash",
414
422
  name="name",
423
+ language="language",
415
424
  )
416
425
 
417
426
 
@@ -423,6 +432,7 @@ class AsyncFunctionsClient:
423
432
  signature=signature,
424
433
  signature_hash=signature_hash,
425
434
  name=name,
435
+ language=language,
426
436
  description=description,
427
437
  tags=tags,
428
438
  metadata=metadata,
@@ -167,6 +167,7 @@ class RawFunctionsClient:
167
167
  signature: str,
168
168
  signature_hash: str,
169
169
  name: str,
170
+ language: str,
170
171
  description: typing.Optional[str] = OMIT,
171
172
  tags: typing.Optional[typing.Sequence[str]] = OMIT,
172
173
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
@@ -188,6 +189,8 @@ class RawFunctionsClient:
188
189
 
189
190
  name : str
190
191
 
192
+ language : str
193
+
191
194
  description : typing.Optional[str]
192
195
 
193
196
  tags : typing.Optional[typing.Sequence[str]]
@@ -213,6 +216,7 @@ class RawFunctionsClient:
213
216
  "signature": signature,
214
217
  "signatureHash": signature_hash,
215
218
  "name": name,
219
+ "language": language,
216
220
  "description": description,
217
221
  "tags": tags,
218
222
  "metadata": metadata,
@@ -1094,6 +1098,7 @@ class AsyncRawFunctionsClient:
1094
1098
  signature: str,
1095
1099
  signature_hash: str,
1096
1100
  name: str,
1101
+ language: str,
1097
1102
  description: typing.Optional[str] = OMIT,
1098
1103
  tags: typing.Optional[typing.Sequence[str]] = OMIT,
1099
1104
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT,
@@ -1115,6 +1120,8 @@ class AsyncRawFunctionsClient:
1115
1120
 
1116
1121
  name : str
1117
1122
 
1123
+ language : str
1124
+
1118
1125
  description : typing.Optional[str]
1119
1126
 
1120
1127
  tags : typing.Optional[typing.Sequence[str]]
@@ -1140,6 +1147,7 @@ class AsyncRawFunctionsClient:
1140
1147
  "signature": signature,
1141
1148
  "signatureHash": signature_hash,
1142
1149
  "name": name,
1150
+ "language": language,
1143
1151
  "description": description,
1144
1152
  "tags": tags,
1145
1153
  "metadata": metadata,
@@ -6,13 +6,17 @@ import pydantic
6
6
  import typing_extensions
7
7
  from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
8
8
  from ...core.serialization import FieldMetadata
9
- from .functions_create_response_dependencies_value import FunctionsCreateResponseDependenciesValue
9
+ from .functions_create_response_dependencies_value import (
10
+ FunctionsCreateResponseDependenciesValue,
11
+ )
10
12
 
11
13
 
12
14
  class FunctionsCreateResponse(UniversalBaseModel):
13
15
  id: str
14
16
  hash: str
15
- signature_hash: typing_extensions.Annotated[str, FieldMetadata(alias="signatureHash")]
17
+ signature_hash: typing_extensions.Annotated[
18
+ str, FieldMetadata(alias="signatureHash")
19
+ ]
16
20
  name: str
17
21
  description: typing.Optional[str] = None
18
22
  version: str
@@ -20,15 +24,28 @@ class FunctionsCreateResponse(UniversalBaseModel):
20
24
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None
21
25
  code: str
22
26
  signature: str
23
- dependencies: typing.Optional[typing.Dict[str, typing.Optional[FunctionsCreateResponseDependenciesValue]]] = None
24
- environment_id: typing_extensions.Annotated[str, FieldMetadata(alias="environmentId")]
27
+ language: str
28
+ dependencies: typing.Optional[
29
+ typing.Dict[str, typing.Optional[FunctionsCreateResponseDependenciesValue]]
30
+ ] = None
31
+ environment_id: typing_extensions.Annotated[
32
+ str, FieldMetadata(alias="environmentId")
33
+ ]
25
34
  project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")]
26
- organization_id: typing_extensions.Annotated[str, FieldMetadata(alias="organizationId")]
27
- created_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="createdAt")] = None
28
- updated_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="updatedAt")] = None
35
+ organization_id: typing_extensions.Annotated[
36
+ str, FieldMetadata(alias="organizationId")
37
+ ]
38
+ created_at: typing_extensions.Annotated[
39
+ typing.Optional[str], FieldMetadata(alias="createdAt")
40
+ ] = None
41
+ updated_at: typing_extensions.Annotated[
42
+ typing.Optional[str], FieldMetadata(alias="updatedAt")
43
+ ] = None
29
44
 
30
45
  if IS_PYDANTIC_V2:
31
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
46
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
47
+ extra="allow", frozen=True
48
+ ) # type: ignore # Pydantic v2
32
49
  else:
33
50
 
34
51
  class Config:
@@ -6,13 +6,17 @@ import pydantic
6
6
  import typing_extensions
7
7
  from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
8
8
  from ...core.serialization import FieldMetadata
9
- from .functions_find_by_hash_response_dependencies_value import FunctionsFindByHashResponseDependenciesValue
9
+ from .functions_find_by_hash_response_dependencies_value import (
10
+ FunctionsFindByHashResponseDependenciesValue,
11
+ )
10
12
 
11
13
 
12
14
  class FunctionsFindByHashResponse(UniversalBaseModel):
13
15
  id: str
14
16
  hash: str
15
- signature_hash: typing_extensions.Annotated[str, FieldMetadata(alias="signatureHash")]
17
+ signature_hash: typing_extensions.Annotated[
18
+ str, FieldMetadata(alias="signatureHash")
19
+ ]
16
20
  name: str
17
21
  description: typing.Optional[str] = None
18
22
  version: str
@@ -20,17 +24,28 @@ class FunctionsFindByHashResponse(UniversalBaseModel):
20
24
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None
21
25
  code: str
22
26
  signature: str
23
- dependencies: typing.Optional[typing.Dict[str, typing.Optional[FunctionsFindByHashResponseDependenciesValue]]] = (
24
- None
25
- )
26
- environment_id: typing_extensions.Annotated[str, FieldMetadata(alias="environmentId")]
27
+ language: str
28
+ dependencies: typing.Optional[
29
+ typing.Dict[str, typing.Optional[FunctionsFindByHashResponseDependenciesValue]]
30
+ ] = None
31
+ environment_id: typing_extensions.Annotated[
32
+ str, FieldMetadata(alias="environmentId")
33
+ ]
27
34
  project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")]
28
- organization_id: typing_extensions.Annotated[str, FieldMetadata(alias="organizationId")]
29
- created_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="createdAt")] = None
30
- updated_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="updatedAt")] = None
35
+ organization_id: typing_extensions.Annotated[
36
+ str, FieldMetadata(alias="organizationId")
37
+ ]
38
+ created_at: typing_extensions.Annotated[
39
+ typing.Optional[str], FieldMetadata(alias="createdAt")
40
+ ] = None
41
+ updated_at: typing_extensions.Annotated[
42
+ typing.Optional[str], FieldMetadata(alias="updatedAt")
43
+ ] = None
31
44
 
32
45
  if IS_PYDANTIC_V2:
33
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
46
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
47
+ extra="allow", frozen=True
48
+ ) # type: ignore # Pydantic v2
34
49
  else:
35
50
 
36
51
  class Config:
@@ -24,6 +24,7 @@ class FunctionsGetByEnvResponse(UniversalBaseModel):
24
24
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None
25
25
  code: str
26
26
  signature: str
27
+ language: str
27
28
  dependencies: typing.Optional[
28
29
  typing.Dict[str, typing.Optional[FunctionsGetByEnvResponseDependenciesValue]]
29
30
  ] = None
@@ -6,13 +6,17 @@ import pydantic
6
6
  import typing_extensions
7
7
  from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
8
8
  from ...core.serialization import FieldMetadata
9
- from .functions_get_response_dependencies_value import FunctionsGetResponseDependenciesValue
9
+ from .functions_get_response_dependencies_value import (
10
+ FunctionsGetResponseDependenciesValue,
11
+ )
10
12
 
11
13
 
12
14
  class FunctionsGetResponse(UniversalBaseModel):
13
15
  id: str
14
16
  hash: str
15
- signature_hash: typing_extensions.Annotated[str, FieldMetadata(alias="signatureHash")]
17
+ signature_hash: typing_extensions.Annotated[
18
+ str, FieldMetadata(alias="signatureHash")
19
+ ]
16
20
  name: str
17
21
  description: typing.Optional[str] = None
18
22
  version: str
@@ -20,15 +24,28 @@ class FunctionsGetResponse(UniversalBaseModel):
20
24
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None
21
25
  code: str
22
26
  signature: str
23
- dependencies: typing.Optional[typing.Dict[str, typing.Optional[FunctionsGetResponseDependenciesValue]]] = None
24
- environment_id: typing_extensions.Annotated[str, FieldMetadata(alias="environmentId")]
27
+ language: str
28
+ dependencies: typing.Optional[
29
+ typing.Dict[str, typing.Optional[FunctionsGetResponseDependenciesValue]]
30
+ ] = None
31
+ environment_id: typing_extensions.Annotated[
32
+ str, FieldMetadata(alias="environmentId")
33
+ ]
25
34
  project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")]
26
- organization_id: typing_extensions.Annotated[str, FieldMetadata(alias="organizationId")]
27
- created_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="createdAt")] = None
28
- updated_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="updatedAt")] = None
35
+ organization_id: typing_extensions.Annotated[
36
+ str, FieldMetadata(alias="organizationId")
37
+ ]
38
+ created_at: typing_extensions.Annotated[
39
+ typing.Optional[str], FieldMetadata(alias="createdAt")
40
+ ] = None
41
+ updated_at: typing_extensions.Annotated[
42
+ typing.Optional[str], FieldMetadata(alias="updatedAt")
43
+ ] = None
29
44
 
30
45
  if IS_PYDANTIC_V2:
31
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
46
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
47
+ extra="allow", frozen=True
48
+ ) # type: ignore # Pydantic v2
32
49
  else:
33
50
 
34
51
  class Config:
@@ -24,6 +24,7 @@ class FunctionsListByEnvResponseFunctionsItem(UniversalBaseModel):
24
24
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None
25
25
  code: str
26
26
  signature: str
27
+ language: str
27
28
  dependencies: typing.Optional[
28
29
  typing.Dict[
29
30
  str,
@@ -14,7 +14,9 @@ from .functions_list_response_functions_item_dependencies_value import (
14
14
  class FunctionsListResponseFunctionsItem(UniversalBaseModel):
15
15
  id: str
16
16
  hash: str
17
- signature_hash: typing_extensions.Annotated[str, FieldMetadata(alias="signatureHash")]
17
+ signature_hash: typing_extensions.Annotated[
18
+ str, FieldMetadata(alias="signatureHash")
19
+ ]
18
20
  name: str
19
21
  description: typing.Optional[str] = None
20
22
  version: str
@@ -22,17 +24,30 @@ class FunctionsListResponseFunctionsItem(UniversalBaseModel):
22
24
  metadata: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None
23
25
  code: str
24
26
  signature: str
27
+ language: str
25
28
  dependencies: typing.Optional[
26
- typing.Dict[str, typing.Optional[FunctionsListResponseFunctionsItemDependenciesValue]]
29
+ typing.Dict[
30
+ str, typing.Optional[FunctionsListResponseFunctionsItemDependenciesValue]
31
+ ]
27
32
  ] = None
28
- environment_id: typing_extensions.Annotated[str, FieldMetadata(alias="environmentId")]
33
+ environment_id: typing_extensions.Annotated[
34
+ str, FieldMetadata(alias="environmentId")
35
+ ]
29
36
  project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")]
30
- organization_id: typing_extensions.Annotated[str, FieldMetadata(alias="organizationId")]
31
- created_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="createdAt")] = None
32
- updated_at: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="updatedAt")] = None
37
+ organization_id: typing_extensions.Annotated[
38
+ str, FieldMetadata(alias="organizationId")
39
+ ]
40
+ created_at: typing_extensions.Annotated[
41
+ typing.Optional[str], FieldMetadata(alias="createdAt")
42
+ ] = None
43
+ updated_at: typing_extensions.Annotated[
44
+ typing.Optional[str], FieldMetadata(alias="updatedAt")
45
+ ] = None
33
46
 
34
47
  if IS_PYDANTIC_V2:
35
- model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
48
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
49
+ extra="allow", frozen=True
50
+ ) # type: ignore # Pydantic v2
36
51
  else:
37
52
 
38
53
  class Config:
@@ -3749,6 +3749,7 @@ client.functions.create(
3749
3749
  signature="signature",
3750
3750
  signature_hash="signatureHash",
3751
3751
  name="name",
3752
+ language="language",
3752
3753
  )
3753
3754
 
3754
3755
  ```
@@ -3805,6 +3806,14 @@ client.functions.create(
3805
3806
  <dl>
3806
3807
  <dd>
3807
3808
 
3809
+ **language:** `str`
3810
+
3811
+ </dd>
3812
+ </dl>
3813
+
3814
+ <dl>
3815
+ <dd>
3816
+
3808
3817
  **description:** `typing.Optional[str]`
3809
3818
 
3810
3819
  </dd>
mirascope/llm/__init__.py CHANGED
@@ -20,6 +20,7 @@ from . import (
20
20
  prompts,
21
21
  providers,
22
22
  responses,
23
+ retries,
23
24
  tools,
24
25
  types,
25
26
  )
@@ -67,7 +68,9 @@ from .exceptions import (
67
68
  ProviderError,
68
69
  RateLimitError,
69
70
  ResponseValidationError,
71
+ RetriesExhausted,
70
72
  ServerError,
73
+ StreamRestarted,
71
74
  TimeoutError,
72
75
  ToolError,
73
76
  ToolExecutionError,
@@ -144,6 +147,25 @@ from .responses import (
144
147
  Usage,
145
148
  UsageDeltaChunk,
146
149
  )
150
+ from .retries import (
151
+ AsyncContextRetryResponse,
152
+ AsyncContextRetryStreamResponse,
153
+ AsyncRetryCall,
154
+ AsyncRetryPrompt,
155
+ AsyncRetryResponse,
156
+ AsyncRetryStreamResponse,
157
+ ContextRetryResponse,
158
+ ContextRetryStreamResponse,
159
+ RetryCall,
160
+ RetryConfig,
161
+ RetryModel,
162
+ RetryModelParams,
163
+ RetryPrompt,
164
+ RetryResponse,
165
+ RetryStreamResponse,
166
+ retry,
167
+ retry_model,
168
+ )
147
169
  from .tools import (
148
170
  AnyToolFn,
149
171
  AnyTools,
@@ -184,12 +206,18 @@ __all__ = [
184
206
  "AsyncContextCall",
185
207
  "AsyncContextPrompt",
186
208
  "AsyncContextResponse",
209
+ "AsyncContextRetryResponse",
210
+ "AsyncContextRetryStreamResponse",
187
211
  "AsyncContextStreamResponse",
188
212
  "AsyncContextTool",
189
213
  "AsyncContextToolkit",
190
214
  "AsyncContextTools",
191
215
  "AsyncPrompt",
192
216
  "AsyncResponse",
217
+ "AsyncRetryCall",
218
+ "AsyncRetryPrompt",
219
+ "AsyncRetryResponse",
220
+ "AsyncRetryStreamResponse",
193
221
  "AsyncStream",
194
222
  "AsyncStreamResponse",
195
223
  "AsyncTextStream",
@@ -212,6 +240,8 @@ __all__ = [
212
240
  "ContextCall",
213
241
  "ContextPrompt",
214
242
  "ContextResponse",
243
+ "ContextRetryResponse",
244
+ "ContextRetryStreamResponse",
215
245
  "ContextStreamResponse",
216
246
  "ContextTool",
217
247
  "ContextToolkit",
@@ -249,11 +279,20 @@ __all__ = [
249
279
  "RawMessageChunk",
250
280
  "Response",
251
281
  "ResponseValidationError",
282
+ "RetriesExhausted",
283
+ "RetryCall",
284
+ "RetryConfig",
285
+ "RetryModel",
286
+ "RetryModelParams",
287
+ "RetryPrompt",
288
+ "RetryResponse",
289
+ "RetryStreamResponse",
252
290
  "RootResponse",
253
291
  "ServerError",
254
292
  "Stream",
255
293
  "StreamResponse",
256
294
  "StreamResponseChunk",
295
+ "StreamRestarted",
257
296
  "SystemContent",
258
297
  "SystemMessage",
259
298
  "Text",
@@ -309,6 +348,9 @@ __all__ = [
309
348
  "register_provider",
310
349
  "reset_provider_registry",
311
350
  "responses",
351
+ "retries",
352
+ "retry",
353
+ "retry_model",
312
354
  "tool",
313
355
  "tools",
314
356
  "types",
@@ -1,7 +1,6 @@
1
1
  """The Call module for generating responses using LLMs."""
2
2
 
3
3
  from collections.abc import Callable
4
- from dataclasses import dataclass, field
5
4
  from typing import Any, Generic, TypeVar, overload
6
5
 
7
6
  from ..._utils import copy_function_metadata
@@ -31,7 +30,6 @@ PromptT = TypeVar("PromptT", bound=BasePrompt[Callable[..., Any]])
31
30
  CallT = TypeVar("CallT", bound="BaseCall[Any]")
32
31
 
33
32
 
34
- @dataclass(kw_only=True)
35
33
  class BaseCall(Generic[PromptT]):
36
34
  """Base class for all Call types with shared model functionality."""
37
35
 
@@ -41,20 +39,20 @@ class BaseCall(Generic[PromptT]):
41
39
  prompt: PromptT
42
40
  """The underlying Prompt instance that generates messages with tools and format."""
43
41
 
44
- __name__: str = field(init=False, repr=False, default="")
42
+ __name__: str = ""
45
43
  """The name of the underlying function (preserved for decorator stacking)."""
46
44
 
45
+ def __init__(self, *, default_model: Model, prompt: PromptT) -> None:
46
+ self.default_model = default_model
47
+ self.prompt = prompt
48
+ copy_function_metadata(self, self.prompt.fn)
49
+
47
50
  @property
48
51
  def model(self) -> Model:
49
52
  """The model used for generating responses. May be overwritten via `with llm.model(...)`."""
50
53
  return use_model(self.default_model)
51
54
 
52
- def __post_init__(self) -> None:
53
- """Preserve standard function attributes for decorator stacking."""
54
- copy_function_metadata(self, self.prompt.fn)
55
-
56
55
 
57
- @dataclass
58
56
  class Call(BaseCall[Prompt[P, FormattableT]], Generic[P, FormattableT]):
59
57
  """A call that directly generates LLM responses without requiring a model argument.
60
58
 
@@ -67,6 +65,14 @@ class Call(BaseCall[Prompt[P, FormattableT]], Generic[P, FormattableT]):
67
65
  The model can be overridden at runtime using `with llm.model(...)` context manager.
68
66
  """
69
67
 
68
+ def __init__(
69
+ self,
70
+ *,
71
+ default_model: Model,
72
+ prompt: Prompt[P, FormattableT],
73
+ ) -> None:
74
+ super().__init__(default_model=default_model, prompt=prompt)
75
+
70
76
  @overload
71
77
  def __call__(
72
78
  self: "Call[P, None]", *args: P.args, **kwargs: P.kwargs
@@ -114,7 +120,6 @@ class Call(BaseCall[Prompt[P, FormattableT]], Generic[P, FormattableT]):
114
120
  return self.prompt.stream(self.model, *args, **kwargs)
115
121
 
116
122
 
117
- @dataclass
118
123
  class AsyncCall(BaseCall[AsyncPrompt[P, FormattableT]], Generic[P, FormattableT]):
119
124
  """An async call that directly generates LLM responses without requiring a model argument.
120
125
 
@@ -127,6 +132,14 @@ class AsyncCall(BaseCall[AsyncPrompt[P, FormattableT]], Generic[P, FormattableT]
127
132
  The model can be overridden at runtime using `with llm.model(...)` context manager.
128
133
  """
129
134
 
135
+ def __init__(
136
+ self,
137
+ *,
138
+ default_model: Model,
139
+ prompt: AsyncPrompt[P, FormattableT],
140
+ ) -> None:
141
+ super().__init__(default_model=default_model, prompt=prompt)
142
+
130
143
  @overload
131
144
  async def __call__(
132
145
  self: "AsyncCall[P, None]", *args: P.args, **kwargs: P.kwargs
@@ -176,7 +189,6 @@ class AsyncCall(BaseCall[AsyncPrompt[P, FormattableT]], Generic[P, FormattableT]
176
189
  return await self.prompt.stream(self.model, *args, **kwargs)
177
190
 
178
191
 
179
- @dataclass
180
192
  class ContextCall(
181
193
  BaseCall[ContextPrompt[P, DepsT, FormattableT]], Generic[P, DepsT, FormattableT]
182
194
  ):
@@ -192,6 +204,14 @@ class ContextCall(
192
204
  The model can be overridden at runtime using `with llm.model(...)` context manager.
193
205
  """
194
206
 
207
+ def __init__(
208
+ self,
209
+ *,
210
+ default_model: Model,
211
+ prompt: ContextPrompt[P, DepsT, FormattableT],
212
+ ) -> None:
213
+ super().__init__(default_model=default_model, prompt=prompt)
214
+
195
215
  @overload
196
216
  def __call__(
197
217
  self: "ContextCall[P, DepsT, None]",
@@ -261,7 +281,6 @@ class ContextCall(
261
281
  return self.prompt.stream(self.model, ctx, *args, **kwargs)
262
282
 
263
283
 
264
- @dataclass
265
284
  class AsyncContextCall(
266
285
  BaseCall[AsyncContextPrompt[P, DepsT, FormattableT]],
267
286
  Generic[P, DepsT, FormattableT],
@@ -278,6 +297,14 @@ class AsyncContextCall(
278
297
  The model can be overridden at runtime using `with llm.model(...)` context manager.
279
298
  """
280
299
 
300
+ def __init__(
301
+ self,
302
+ *,
303
+ default_model: Model,
304
+ prompt: AsyncContextPrompt[P, DepsT, FormattableT],
305
+ ) -> None:
306
+ super().__init__(default_model=default_model, prompt=prompt)
307
+
281
308
  @overload
282
309
  async def __call__(
283
310
  self: "AsyncContextCall[P, DepsT, None]",
@@ -7,6 +7,7 @@ from pydantic import ValidationError
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from .providers import ModelId, ProviderId
10
+ from .retries.utils import RetryFailure
10
11
 
11
12
 
12
13
  class Error(Exception):
@@ -358,3 +359,71 @@ class MissingAPIKeyError(Error):
358
359
  super().__init__(message)
359
360
  self.provider_id = provider_id
360
361
  self.env_var = env_var
362
+
363
+
364
+ class RetriesExhausted(Error):
365
+ """Raised when all retry attempts (including fallback models) have been exhausted.
366
+
367
+ This exception preserves the full failure history, allowing users to inspect
368
+ what went wrong at each attempt.
369
+
370
+ Example:
371
+ ```python
372
+ try:
373
+ response = retry_model.generate("Tell me a story")
374
+ except llm.RetriesExhausted as e:
375
+ print(f"Failed after {len(e.failures)} attempts:")
376
+ for failure in e.failures:
377
+ print(f" - {failure.model.model}: {failure.exception}")
378
+ ```
379
+ """
380
+
381
+ failures: list["RetryFailure"]
382
+ """All failed attempts, in order. The last failure triggered exhaustion."""
383
+
384
+ def __init__(self, failures: list["RetryFailure"]) -> None:
385
+ model_names = [f.model.model_id for f in failures]
386
+ message = (
387
+ f"All retries exhausted after {len(failures)} attempt(s) "
388
+ f"across models: {model_names}"
389
+ )
390
+ super().__init__(message)
391
+ self.failures = failures
392
+ # Chain to the last error for standard traceback behavior
393
+ if failures:
394
+ self.__cause__ = failures[-1].exception
395
+
396
+
397
+ class StreamRestarted(Error):
398
+ """Raised when a stream restarts due to a retryable error.
399
+
400
+ This exception signals that the stream encountered an error and has been
401
+ reset for a retry attempt. Users should catch this exception and re-iterate
402
+ the response to continue streaming from the new attempt.
403
+
404
+ Example:
405
+ ```python
406
+ response = retry_call.stream("fantasy")
407
+
408
+ while True:
409
+ try:
410
+ for chunk in response.text_stream():
411
+ print(chunk, end="", flush=True)
412
+ break # Success
413
+ except llm.StreamRestarted as e:
414
+ print(e.message)
415
+ # Loop continues, re-iterates the response
416
+ ```
417
+ """
418
+
419
+ failure: "RetryFailure"
420
+ """The failure that triggered the restart."""
421
+
422
+ def __init__(
423
+ self,
424
+ failure: "RetryFailure",
425
+ ) -> None:
426
+ message = f"Stream restarted due to: {failure.exception}"
427
+ super().__init__(message)
428
+ self.failure = failure
429
+ self.__cause__ = failure.exception