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.
- mirascope/api/_generated/functions/client.py +10 -0
- mirascope/api/_generated/functions/raw_client.py +8 -0
- mirascope/api/_generated/functions/types/functions_create_response.py +25 -8
- mirascope/api/_generated/functions/types/functions_find_by_hash_response.py +25 -10
- mirascope/api/_generated/functions/types/functions_get_by_env_response.py +1 -0
- mirascope/api/_generated/functions/types/functions_get_response.py +25 -8
- mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +1 -0
- mirascope/api/_generated/functions/types/functions_list_response_functions_item.py +22 -7
- mirascope/api/_generated/reference.md +9 -0
- mirascope/llm/__init__.py +42 -0
- mirascope/llm/calls/calls.py +38 -11
- mirascope/llm/exceptions.py +69 -0
- mirascope/llm/prompts/prompts.py +47 -9
- mirascope/llm/providers/__init__.py +3 -0
- mirascope/llm/providers/openai/completions/_utils/__init__.py +3 -0
- mirascope/llm/providers/openai/completions/_utils/encode.py +27 -32
- mirascope/llm/providers/openai/completions/_utils/feature_info.py +50 -0
- mirascope/llm/providers/openai/completions/base_provider.py +21 -0
- mirascope/llm/providers/openai/completions/provider.py +8 -2
- mirascope/llm/providers/openrouter/__init__.py +5 -0
- mirascope/llm/providers/openrouter/provider.py +67 -0
- mirascope/llm/providers/provider_id.py +2 -0
- mirascope/llm/providers/provider_registry.py +6 -0
- mirascope/llm/responses/response.py +217 -0
- mirascope/llm/responses/stream_response.py +234 -0
- mirascope/llm/retries/__init__.py +51 -0
- mirascope/llm/retries/retry_calls.py +159 -0
- mirascope/llm/retries/retry_config.py +168 -0
- mirascope/llm/retries/retry_decorator.py +258 -0
- mirascope/llm/retries/retry_models.py +1313 -0
- mirascope/llm/retries/retry_prompts.py +227 -0
- mirascope/llm/retries/retry_responses.py +340 -0
- mirascope/llm/retries/retry_stream_responses.py +571 -0
- mirascope/llm/retries/utils.py +159 -0
- mirascope/ops/_internal/versioned_calls.py +249 -9
- mirascope/ops/_internal/versioned_functions.py +2 -0
- {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/METADATA +1 -1
- {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/RECORD +40 -28
- {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/WHEEL +0 -0
- {mirascope-2.1.0.dist-info → mirascope-2.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1313 @@
|
|
|
1
|
+
"""RetryModel extends Model to add retry logic."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import AsyncIterator, Awaitable, Iterator, Sequence
|
|
6
|
+
from typing import cast, overload
|
|
7
|
+
from typing_extensions import Unpack
|
|
8
|
+
|
|
9
|
+
from ..context import Context, DepsT
|
|
10
|
+
from ..formatting import FormatSpec, FormattableT
|
|
11
|
+
from ..messages import Message, UserContent
|
|
12
|
+
from ..models import Model, Params
|
|
13
|
+
from ..providers import ModelId
|
|
14
|
+
from ..responses import (
|
|
15
|
+
AsyncContextResponse,
|
|
16
|
+
AsyncContextStreamResponse,
|
|
17
|
+
AsyncResponse,
|
|
18
|
+
AsyncStreamResponse,
|
|
19
|
+
ContextResponse,
|
|
20
|
+
ContextStreamResponse,
|
|
21
|
+
Response,
|
|
22
|
+
StreamResponse,
|
|
23
|
+
)
|
|
24
|
+
from ..tools import (
|
|
25
|
+
AsyncContextTools,
|
|
26
|
+
AsyncTools,
|
|
27
|
+
ContextTools,
|
|
28
|
+
Tools,
|
|
29
|
+
)
|
|
30
|
+
from .retry_config import RetryArgs, RetryConfig
|
|
31
|
+
from .retry_responses import (
|
|
32
|
+
AsyncContextRetryResponse,
|
|
33
|
+
AsyncRetryResponse,
|
|
34
|
+
ContextRetryResponse,
|
|
35
|
+
RetryResponse,
|
|
36
|
+
)
|
|
37
|
+
from .retry_stream_responses import (
|
|
38
|
+
AsyncContextRetryStreamResponse,
|
|
39
|
+
AsyncRetryStreamResponse,
|
|
40
|
+
ContextRetryStreamResponse,
|
|
41
|
+
RetryStreamResponse,
|
|
42
|
+
)
|
|
43
|
+
from .utils import with_retry, with_retry_async
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RetryModelParams(Params, RetryArgs, total=False):
|
|
47
|
+
"""Combined parameters for RetryModel: model params + retry config.
|
|
48
|
+
|
|
49
|
+
This TypedDict combines all fields from `Params` and `RetryArgs`, allowing
|
|
50
|
+
users to configure both the model behavior and retry logic in a single call.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_RETRY_ARG_KEYS = frozenset(RetryArgs.__annotations__.keys())
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class RetryModel(Model):
|
|
58
|
+
"""Extends Model with retry logic and optional fallback models.
|
|
59
|
+
|
|
60
|
+
RetryModel "is-a" Model - it has a primary model_id and params determined by
|
|
61
|
+
the active model. It also supports fallback models that will be tried if the
|
|
62
|
+
active model exhausts its retries.
|
|
63
|
+
|
|
64
|
+
The `_models` tuple contains all available models (primary + resolved fallbacks).
|
|
65
|
+
The `_active_index` indicates which model is currently "primary". When a call
|
|
66
|
+
succeeds on a fallback model, the returned response's `.model` property will
|
|
67
|
+
be a new RetryModel with that successful model as the active model.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
_models: tuple[Model, ...]
|
|
71
|
+
"""All available models: primary at index 0, then fallbacks."""
|
|
72
|
+
|
|
73
|
+
_active_index: int
|
|
74
|
+
"""Index into _models for the currently active model."""
|
|
75
|
+
|
|
76
|
+
retry_config: RetryConfig
|
|
77
|
+
"""The RetryConfig specifying retry behavior."""
|
|
78
|
+
|
|
79
|
+
@overload
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
model: Model,
|
|
83
|
+
retry_config: RetryConfig,
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Wrap an existing Model with retry logic."""
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
@overload
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
model: ModelId,
|
|
92
|
+
retry_config: RetryConfig,
|
|
93
|
+
**params: Unpack[Params],
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Create a RetryModel from a ModelId with optional params."""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
model: Model | ModelId,
|
|
101
|
+
retry_config: RetryConfig,
|
|
102
|
+
**params: Unpack[Params],
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Initialize a RetryModel.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
model: Either a Model instance to wrap, or a ModelId string to create
|
|
108
|
+
a new Model from.
|
|
109
|
+
retry_config: Configuration for retry behavior.
|
|
110
|
+
**params: Additional parameters for the model. Only used when `model`
|
|
111
|
+
is a ModelId string; ignored when wrapping an existing Model.
|
|
112
|
+
"""
|
|
113
|
+
# Resolve the primary model (strip to plain Model if needed)
|
|
114
|
+
if isinstance(model, Model):
|
|
115
|
+
primary = (
|
|
116
|
+
model if type(model) is Model else Model(model.model_id, **model.params)
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
primary = Model(model, **params)
|
|
120
|
+
|
|
121
|
+
# Resolve fallback models
|
|
122
|
+
resolved_fallbacks: list[Model] = []
|
|
123
|
+
for fb in retry_config.fallback_models:
|
|
124
|
+
if isinstance(fb, Model):
|
|
125
|
+
# Strip any RetryModel wrapping, just take model_id and params
|
|
126
|
+
resolved_fallbacks.append(
|
|
127
|
+
fb if type(fb) is Model else Model(fb.model_id, **fb.params)
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
# ModelId string - inherit params from primary
|
|
131
|
+
resolved_fallbacks.append(Model(fb, **primary.params))
|
|
132
|
+
|
|
133
|
+
self._models = (primary, *resolved_fallbacks)
|
|
134
|
+
self._active_index = 0
|
|
135
|
+
self.retry_config = retry_config
|
|
136
|
+
|
|
137
|
+
# Initialize _token_stack for context manager support
|
|
138
|
+
object.__setattr__(self, "_token_stack", [])
|
|
139
|
+
|
|
140
|
+
@classmethod
|
|
141
|
+
def _create_with_active(
|
|
142
|
+
cls,
|
|
143
|
+
models: tuple[Model, ...],
|
|
144
|
+
active_index: int,
|
|
145
|
+
retry_config: RetryConfig,
|
|
146
|
+
) -> "RetryModel":
|
|
147
|
+
"""Internal constructor for creating a RetryModel with pre-resolved models."""
|
|
148
|
+
instance = object.__new__(cls)
|
|
149
|
+
instance._models = models
|
|
150
|
+
instance._active_index = active_index
|
|
151
|
+
instance.retry_config = retry_config
|
|
152
|
+
object.__setattr__(instance, "_token_stack", [])
|
|
153
|
+
return instance
|
|
154
|
+
|
|
155
|
+
def get_active_model(self) -> Model:
|
|
156
|
+
"""Get the currently active model."""
|
|
157
|
+
return self._models[self._active_index]
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def model_id(self) -> ModelId: # pyright: ignore[reportIncompatibleVariableOverride]
|
|
161
|
+
"""The model_id of the currently active model."""
|
|
162
|
+
return self._models[self._active_index].model_id
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def params(self) -> Params: # pyright: ignore[reportIncompatibleVariableOverride]
|
|
166
|
+
"""The params of the currently active model."""
|
|
167
|
+
return self._models[self._active_index].params
|
|
168
|
+
|
|
169
|
+
def _with_active(self, index: int) -> "RetryModel":
|
|
170
|
+
"""Return a new RetryModel with a different active model."""
|
|
171
|
+
return self._create_with_active(
|
|
172
|
+
models=self._models,
|
|
173
|
+
active_index=index,
|
|
174
|
+
retry_config=self.retry_config,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def _attempt_variants(self) -> Iterator["RetryModel"]:
|
|
178
|
+
"""Yield RetryModels in attempt order: active model first, then others."""
|
|
179
|
+
yield self
|
|
180
|
+
for i in range(len(self._models)):
|
|
181
|
+
if i != self._active_index:
|
|
182
|
+
yield self._with_active(i)
|
|
183
|
+
|
|
184
|
+
def variants(self) -> Iterator["RetryModel"]:
|
|
185
|
+
"""Yield model variants with backoff delays between retries.
|
|
186
|
+
|
|
187
|
+
Yields the active model variant first. After each yield, if the caller
|
|
188
|
+
encountered an error and iterates again, this applies the appropriate
|
|
189
|
+
backoff delay before yielding the next attempt. Each model gets
|
|
190
|
+
max_retries additional attempts before moving to the next fallback model.
|
|
191
|
+
|
|
192
|
+
Yields:
|
|
193
|
+
RetryModel variants to attempt, with backoff delays applied between yields.
|
|
194
|
+
"""
|
|
195
|
+
from .utils import calculate_delay
|
|
196
|
+
|
|
197
|
+
config = self.retry_config
|
|
198
|
+
for variant in self._attempt_variants():
|
|
199
|
+
for attempt in range(config.max_retries + 1):
|
|
200
|
+
yield variant
|
|
201
|
+
# If caller iterates again, they hit an error - apply backoff
|
|
202
|
+
if attempt < config.max_retries:
|
|
203
|
+
delay = calculate_delay(config, attempt + 1)
|
|
204
|
+
time.sleep(delay)
|
|
205
|
+
|
|
206
|
+
async def variants_async(self) -> AsyncIterator["RetryModel"]:
|
|
207
|
+
"""Async version of variants().
|
|
208
|
+
|
|
209
|
+
Yields the active model variant first. After each yield, if the caller
|
|
210
|
+
encountered an error and iterates again, this applies the appropriate
|
|
211
|
+
backoff delay before yielding the next attempt. Each model gets
|
|
212
|
+
max_retries additional attempts before moving to the next fallback model.
|
|
213
|
+
|
|
214
|
+
Yields:
|
|
215
|
+
RetryModel variants to attempt, with backoff delays applied between yields.
|
|
216
|
+
"""
|
|
217
|
+
from .utils import calculate_delay
|
|
218
|
+
|
|
219
|
+
config = self.retry_config
|
|
220
|
+
for variant in self._attempt_variants():
|
|
221
|
+
for attempt in range(config.max_retries + 1):
|
|
222
|
+
yield variant
|
|
223
|
+
# If caller iterates again, they hit an error - apply backoff
|
|
224
|
+
if attempt < config.max_retries:
|
|
225
|
+
delay = calculate_delay(config, attempt + 1)
|
|
226
|
+
await asyncio.sleep(delay)
|
|
227
|
+
|
|
228
|
+
@overload
|
|
229
|
+
def call(
|
|
230
|
+
self,
|
|
231
|
+
content: UserContent | Sequence[Message],
|
|
232
|
+
*,
|
|
233
|
+
tools: Tools | None = None,
|
|
234
|
+
format: None = None,
|
|
235
|
+
) -> RetryResponse[None]: ...
|
|
236
|
+
|
|
237
|
+
@overload
|
|
238
|
+
def call(
|
|
239
|
+
self,
|
|
240
|
+
content: UserContent | Sequence[Message],
|
|
241
|
+
*,
|
|
242
|
+
tools: Tools | None = None,
|
|
243
|
+
format: FormatSpec[FormattableT],
|
|
244
|
+
) -> RetryResponse[FormattableT]: ...
|
|
245
|
+
|
|
246
|
+
@overload
|
|
247
|
+
def call(
|
|
248
|
+
self,
|
|
249
|
+
content: UserContent | Sequence[Message],
|
|
250
|
+
*,
|
|
251
|
+
tools: Tools | None = None,
|
|
252
|
+
format: FormatSpec[FormattableT] | None,
|
|
253
|
+
) -> RetryResponse[None] | RetryResponse[FormattableT]: ...
|
|
254
|
+
|
|
255
|
+
def call(
|
|
256
|
+
self,
|
|
257
|
+
content: UserContent | Sequence[Message],
|
|
258
|
+
*,
|
|
259
|
+
tools: Tools | None = None,
|
|
260
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
261
|
+
) -> RetryResponse[None] | RetryResponse[FormattableT]:
|
|
262
|
+
"""Generate a RetryResponse by calling this model's LLM provider.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
content: User content or LLM messages to send to the LLM.
|
|
266
|
+
tools: Optional tools that the model may invoke.
|
|
267
|
+
format: Optional response format specifier.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
A RetryResponse object containing the LLM-generated content and retry metadata.
|
|
271
|
+
|
|
272
|
+
Raises:
|
|
273
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
274
|
+
"""
|
|
275
|
+
response, failures, updated_model = with_retry(
|
|
276
|
+
fn=lambda m: m.call(content, tools=tools, format=format),
|
|
277
|
+
retry_model=self,
|
|
278
|
+
)
|
|
279
|
+
return RetryResponse(
|
|
280
|
+
response=cast("Response[FormattableT]", response),
|
|
281
|
+
retry_model=updated_model,
|
|
282
|
+
retry_failures=failures,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
@overload
|
|
286
|
+
async def call_async(
|
|
287
|
+
self,
|
|
288
|
+
content: UserContent | Sequence[Message],
|
|
289
|
+
*,
|
|
290
|
+
tools: AsyncTools | None = None,
|
|
291
|
+
format: None = None,
|
|
292
|
+
) -> AsyncRetryResponse[None]: ...
|
|
293
|
+
|
|
294
|
+
@overload
|
|
295
|
+
async def call_async(
|
|
296
|
+
self,
|
|
297
|
+
content: UserContent | Sequence[Message],
|
|
298
|
+
*,
|
|
299
|
+
tools: AsyncTools | None = None,
|
|
300
|
+
format: FormatSpec[FormattableT],
|
|
301
|
+
) -> AsyncRetryResponse[FormattableT]: ...
|
|
302
|
+
|
|
303
|
+
@overload
|
|
304
|
+
async def call_async(
|
|
305
|
+
self,
|
|
306
|
+
content: UserContent | Sequence[Message],
|
|
307
|
+
*,
|
|
308
|
+
tools: AsyncTools | None = None,
|
|
309
|
+
format: FormatSpec[FormattableT] | None,
|
|
310
|
+
) -> AsyncRetryResponse[None] | AsyncRetryResponse[FormattableT]: ...
|
|
311
|
+
|
|
312
|
+
async def call_async(
|
|
313
|
+
self,
|
|
314
|
+
content: UserContent | Sequence[Message],
|
|
315
|
+
*,
|
|
316
|
+
tools: AsyncTools | None = None,
|
|
317
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
318
|
+
) -> AsyncRetryResponse[None] | AsyncRetryResponse[FormattableT]:
|
|
319
|
+
"""Generate an AsyncRetryResponse by asynchronously calling this model's LLM provider.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
content: User content or LLM messages to send to the LLM.
|
|
323
|
+
tools: Optional tools that the model may invoke.
|
|
324
|
+
format: Optional response format specifier.
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
An AsyncRetryResponse object containing the LLM-generated content and retry metadata.
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
331
|
+
"""
|
|
332
|
+
response, failures, updated_model = await with_retry_async(
|
|
333
|
+
fn=lambda m: m.call_async(content, tools=tools, format=format),
|
|
334
|
+
retry_model=self,
|
|
335
|
+
)
|
|
336
|
+
return AsyncRetryResponse(
|
|
337
|
+
response=cast("AsyncResponse[FormattableT]", response),
|
|
338
|
+
retry_model=updated_model,
|
|
339
|
+
retry_failures=failures,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
# Resume methods
|
|
343
|
+
|
|
344
|
+
@overload
|
|
345
|
+
def resume(
|
|
346
|
+
self,
|
|
347
|
+
*,
|
|
348
|
+
response: Response[None],
|
|
349
|
+
content: UserContent,
|
|
350
|
+
) -> RetryResponse[None]: ...
|
|
351
|
+
|
|
352
|
+
@overload
|
|
353
|
+
def resume(
|
|
354
|
+
self,
|
|
355
|
+
*,
|
|
356
|
+
response: Response[FormattableT],
|
|
357
|
+
content: UserContent,
|
|
358
|
+
) -> RetryResponse[FormattableT]: ...
|
|
359
|
+
|
|
360
|
+
@overload
|
|
361
|
+
def resume(
|
|
362
|
+
self,
|
|
363
|
+
*,
|
|
364
|
+
response: Response[None] | Response[FormattableT],
|
|
365
|
+
content: UserContent,
|
|
366
|
+
) -> RetryResponse[None] | RetryResponse[FormattableT]: ...
|
|
367
|
+
|
|
368
|
+
def resume(
|
|
369
|
+
self,
|
|
370
|
+
*,
|
|
371
|
+
response: Response[None] | Response[FormattableT],
|
|
372
|
+
content: UserContent,
|
|
373
|
+
) -> RetryResponse[None] | RetryResponse[FormattableT]:
|
|
374
|
+
"""Generate a new RetryResponse by extending another response's messages.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
response: Previous response to extend.
|
|
378
|
+
content: Additional user content to append.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
A new RetryResponse containing the extended conversation.
|
|
382
|
+
|
|
383
|
+
Raises:
|
|
384
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
385
|
+
"""
|
|
386
|
+
new_response, failures, updated_model = with_retry(
|
|
387
|
+
fn=lambda m: m.resume(response=response, content=content),
|
|
388
|
+
retry_model=self,
|
|
389
|
+
)
|
|
390
|
+
return RetryResponse(
|
|
391
|
+
response=cast("Response[FormattableT]", new_response),
|
|
392
|
+
retry_model=updated_model,
|
|
393
|
+
retry_failures=failures,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
@overload
|
|
397
|
+
async def resume_async(
|
|
398
|
+
self,
|
|
399
|
+
*,
|
|
400
|
+
response: AsyncResponse[None],
|
|
401
|
+
content: UserContent,
|
|
402
|
+
) -> AsyncRetryResponse[None]: ...
|
|
403
|
+
|
|
404
|
+
@overload
|
|
405
|
+
async def resume_async(
|
|
406
|
+
self,
|
|
407
|
+
*,
|
|
408
|
+
response: AsyncResponse[FormattableT],
|
|
409
|
+
content: UserContent,
|
|
410
|
+
) -> AsyncRetryResponse[FormattableT]: ...
|
|
411
|
+
|
|
412
|
+
@overload
|
|
413
|
+
async def resume_async(
|
|
414
|
+
self,
|
|
415
|
+
*,
|
|
416
|
+
response: AsyncResponse[None] | AsyncResponse[FormattableT],
|
|
417
|
+
content: UserContent,
|
|
418
|
+
) -> AsyncRetryResponse[None] | AsyncRetryResponse[FormattableT]: ...
|
|
419
|
+
|
|
420
|
+
async def resume_async(
|
|
421
|
+
self,
|
|
422
|
+
*,
|
|
423
|
+
response: AsyncResponse[None] | AsyncResponse[FormattableT],
|
|
424
|
+
content: UserContent,
|
|
425
|
+
) -> AsyncRetryResponse[None] | AsyncRetryResponse[FormattableT]:
|
|
426
|
+
"""Generate a new AsyncRetryResponse by extending another response's messages.
|
|
427
|
+
|
|
428
|
+
Args:
|
|
429
|
+
response: Previous async response to extend.
|
|
430
|
+
content: Additional user content to append.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
A new AsyncRetryResponse containing the extended conversation.
|
|
434
|
+
|
|
435
|
+
Raises:
|
|
436
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
437
|
+
"""
|
|
438
|
+
new_response, failures, updated_model = await with_retry_async(
|
|
439
|
+
fn=lambda m: m.resume_async(response=response, content=content),
|
|
440
|
+
retry_model=self,
|
|
441
|
+
)
|
|
442
|
+
return AsyncRetryResponse(
|
|
443
|
+
response=cast("AsyncResponse[FormattableT]", new_response),
|
|
444
|
+
retry_model=updated_model,
|
|
445
|
+
retry_failures=failures,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Context call methods
|
|
449
|
+
|
|
450
|
+
@overload
|
|
451
|
+
def context_call(
|
|
452
|
+
self,
|
|
453
|
+
content: UserContent | Sequence[Message],
|
|
454
|
+
*,
|
|
455
|
+
ctx: Context[DepsT],
|
|
456
|
+
tools: ContextTools[DepsT] | None = None,
|
|
457
|
+
format: None = None,
|
|
458
|
+
) -> ContextRetryResponse[DepsT, None]: ...
|
|
459
|
+
|
|
460
|
+
@overload
|
|
461
|
+
def context_call(
|
|
462
|
+
self,
|
|
463
|
+
content: UserContent | Sequence[Message],
|
|
464
|
+
*,
|
|
465
|
+
ctx: Context[DepsT],
|
|
466
|
+
tools: ContextTools[DepsT] | None = None,
|
|
467
|
+
format: FormatSpec[FormattableT],
|
|
468
|
+
) -> ContextRetryResponse[DepsT, FormattableT]: ...
|
|
469
|
+
|
|
470
|
+
@overload
|
|
471
|
+
def context_call(
|
|
472
|
+
self,
|
|
473
|
+
content: UserContent | Sequence[Message],
|
|
474
|
+
*,
|
|
475
|
+
ctx: Context[DepsT],
|
|
476
|
+
tools: ContextTools[DepsT] | None = None,
|
|
477
|
+
format: FormatSpec[FormattableT] | None,
|
|
478
|
+
) -> (
|
|
479
|
+
ContextRetryResponse[DepsT, None] | ContextRetryResponse[DepsT, FormattableT]
|
|
480
|
+
): ...
|
|
481
|
+
|
|
482
|
+
def context_call(
|
|
483
|
+
self,
|
|
484
|
+
content: UserContent | Sequence[Message],
|
|
485
|
+
*,
|
|
486
|
+
ctx: Context[DepsT],
|
|
487
|
+
tools: ContextTools[DepsT] | None = None,
|
|
488
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
489
|
+
) -> ContextRetryResponse[DepsT, None] | ContextRetryResponse[DepsT, FormattableT]:
|
|
490
|
+
"""Generate a ContextRetryResponse by calling this model's LLM provider with context.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
content: User content or LLM messages to send to the LLM.
|
|
494
|
+
ctx: A `Context` with the required deps type.
|
|
495
|
+
tools: Optional context-aware tools that the model may invoke.
|
|
496
|
+
format: Optional response format specifier.
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
A ContextRetryResponse object containing the LLM-generated content and retry metadata.
|
|
500
|
+
|
|
501
|
+
Raises:
|
|
502
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
503
|
+
"""
|
|
504
|
+
response, failures, updated_model = with_retry(
|
|
505
|
+
fn=lambda m: m.context_call(content, ctx=ctx, tools=tools, format=format),
|
|
506
|
+
retry_model=self,
|
|
507
|
+
)
|
|
508
|
+
return ContextRetryResponse(
|
|
509
|
+
response=cast("ContextResponse[DepsT, FormattableT]", response),
|
|
510
|
+
retry_model=updated_model,
|
|
511
|
+
retry_failures=failures,
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
@overload
|
|
515
|
+
async def context_call_async(
|
|
516
|
+
self,
|
|
517
|
+
content: UserContent | Sequence[Message],
|
|
518
|
+
*,
|
|
519
|
+
ctx: Context[DepsT],
|
|
520
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
521
|
+
format: None = None,
|
|
522
|
+
) -> AsyncContextRetryResponse[DepsT, None]: ...
|
|
523
|
+
|
|
524
|
+
@overload
|
|
525
|
+
async def context_call_async(
|
|
526
|
+
self,
|
|
527
|
+
content: UserContent | Sequence[Message],
|
|
528
|
+
*,
|
|
529
|
+
ctx: Context[DepsT],
|
|
530
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
531
|
+
format: FormatSpec[FormattableT],
|
|
532
|
+
) -> AsyncContextRetryResponse[DepsT, FormattableT]: ...
|
|
533
|
+
|
|
534
|
+
@overload
|
|
535
|
+
async def context_call_async(
|
|
536
|
+
self,
|
|
537
|
+
content: UserContent | Sequence[Message],
|
|
538
|
+
*,
|
|
539
|
+
ctx: Context[DepsT],
|
|
540
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
541
|
+
format: FormatSpec[FormattableT] | None,
|
|
542
|
+
) -> (
|
|
543
|
+
AsyncContextRetryResponse[DepsT, None]
|
|
544
|
+
| AsyncContextRetryResponse[DepsT, FormattableT]
|
|
545
|
+
): ...
|
|
546
|
+
|
|
547
|
+
async def context_call_async(
|
|
548
|
+
self,
|
|
549
|
+
content: UserContent | Sequence[Message],
|
|
550
|
+
*,
|
|
551
|
+
ctx: Context[DepsT],
|
|
552
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
553
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
554
|
+
) -> (
|
|
555
|
+
AsyncContextRetryResponse[DepsT, None]
|
|
556
|
+
| AsyncContextRetryResponse[DepsT, FormattableT]
|
|
557
|
+
):
|
|
558
|
+
"""Generate an AsyncContextRetryResponse by asynchronously calling this model's LLM provider with context.
|
|
559
|
+
|
|
560
|
+
Args:
|
|
561
|
+
content: User content or LLM messages to send to the LLM.
|
|
562
|
+
ctx: A `Context` with the required deps type.
|
|
563
|
+
tools: Optional context-aware tools that the model may invoke.
|
|
564
|
+
format: Optional response format specifier.
|
|
565
|
+
|
|
566
|
+
Returns:
|
|
567
|
+
An AsyncContextRetryResponse object containing the LLM-generated content and retry metadata.
|
|
568
|
+
|
|
569
|
+
Raises:
|
|
570
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
571
|
+
"""
|
|
572
|
+
response, failures, updated_model = await with_retry_async(
|
|
573
|
+
fn=lambda m: m.context_call_async(
|
|
574
|
+
content, ctx=ctx, tools=tools, format=format
|
|
575
|
+
),
|
|
576
|
+
retry_model=self,
|
|
577
|
+
)
|
|
578
|
+
return AsyncContextRetryResponse(
|
|
579
|
+
response=cast("AsyncContextResponse[DepsT, FormattableT]", response),
|
|
580
|
+
retry_model=updated_model,
|
|
581
|
+
retry_failures=failures,
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
# Context resume methods
|
|
585
|
+
|
|
586
|
+
@overload
|
|
587
|
+
def context_resume(
|
|
588
|
+
self,
|
|
589
|
+
*,
|
|
590
|
+
ctx: Context[DepsT],
|
|
591
|
+
response: ContextResponse[DepsT, None],
|
|
592
|
+
content: UserContent,
|
|
593
|
+
) -> ContextRetryResponse[DepsT, None]: ...
|
|
594
|
+
|
|
595
|
+
@overload
|
|
596
|
+
def context_resume(
|
|
597
|
+
self,
|
|
598
|
+
*,
|
|
599
|
+
ctx: Context[DepsT],
|
|
600
|
+
response: ContextResponse[DepsT, FormattableT],
|
|
601
|
+
content: UserContent,
|
|
602
|
+
) -> ContextRetryResponse[DepsT, FormattableT]: ...
|
|
603
|
+
|
|
604
|
+
@overload
|
|
605
|
+
def context_resume(
|
|
606
|
+
self,
|
|
607
|
+
*,
|
|
608
|
+
ctx: Context[DepsT],
|
|
609
|
+
response: ContextResponse[DepsT, None] | ContextResponse[DepsT, FormattableT],
|
|
610
|
+
content: UserContent,
|
|
611
|
+
) -> (
|
|
612
|
+
ContextRetryResponse[DepsT, None] | ContextRetryResponse[DepsT, FormattableT]
|
|
613
|
+
): ...
|
|
614
|
+
|
|
615
|
+
def context_resume( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
616
|
+
self,
|
|
617
|
+
*,
|
|
618
|
+
ctx: Context[DepsT],
|
|
619
|
+
response: ContextResponse[DepsT, None] | ContextResponse[DepsT, FormattableT],
|
|
620
|
+
content: UserContent,
|
|
621
|
+
) -> ContextRetryResponse[DepsT, None] | ContextRetryResponse[DepsT, FormattableT]:
|
|
622
|
+
"""Generate a new ContextRetryResponse by extending another response's messages.
|
|
623
|
+
|
|
624
|
+
Args:
|
|
625
|
+
ctx: A `Context` with the required deps type.
|
|
626
|
+
response: Previous context response to extend.
|
|
627
|
+
content: Additional user content to append.
|
|
628
|
+
|
|
629
|
+
Returns:
|
|
630
|
+
A new ContextRetryResponse containing the extended conversation.
|
|
631
|
+
|
|
632
|
+
Raises:
|
|
633
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
634
|
+
"""
|
|
635
|
+
new_response, failures, updated_model = with_retry(
|
|
636
|
+
fn=lambda m: m.context_resume(ctx=ctx, response=response, content=content),
|
|
637
|
+
retry_model=self,
|
|
638
|
+
)
|
|
639
|
+
return ContextRetryResponse(
|
|
640
|
+
response=cast("ContextResponse[DepsT, FormattableT]", new_response),
|
|
641
|
+
retry_model=updated_model,
|
|
642
|
+
retry_failures=failures,
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
@overload
|
|
646
|
+
async def context_resume_async(
|
|
647
|
+
self,
|
|
648
|
+
*,
|
|
649
|
+
ctx: Context[DepsT],
|
|
650
|
+
response: AsyncContextResponse[DepsT, None],
|
|
651
|
+
content: UserContent,
|
|
652
|
+
) -> AsyncContextRetryResponse[DepsT, None]: ...
|
|
653
|
+
|
|
654
|
+
@overload
|
|
655
|
+
async def context_resume_async(
|
|
656
|
+
self,
|
|
657
|
+
*,
|
|
658
|
+
ctx: Context[DepsT],
|
|
659
|
+
response: AsyncContextResponse[DepsT, FormattableT],
|
|
660
|
+
content: UserContent,
|
|
661
|
+
) -> AsyncContextRetryResponse[DepsT, FormattableT]: ...
|
|
662
|
+
|
|
663
|
+
@overload
|
|
664
|
+
async def context_resume_async(
|
|
665
|
+
self,
|
|
666
|
+
*,
|
|
667
|
+
ctx: Context[DepsT],
|
|
668
|
+
response: (
|
|
669
|
+
AsyncContextResponse[DepsT, None]
|
|
670
|
+
| AsyncContextResponse[DepsT, FormattableT]
|
|
671
|
+
),
|
|
672
|
+
content: UserContent,
|
|
673
|
+
) -> (
|
|
674
|
+
AsyncContextRetryResponse[DepsT, None]
|
|
675
|
+
| AsyncContextRetryResponse[DepsT, FormattableT]
|
|
676
|
+
): ...
|
|
677
|
+
|
|
678
|
+
async def context_resume_async( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
679
|
+
self,
|
|
680
|
+
*,
|
|
681
|
+
ctx: Context[DepsT],
|
|
682
|
+
response: (
|
|
683
|
+
AsyncContextResponse[DepsT, None]
|
|
684
|
+
| AsyncContextResponse[DepsT, FormattableT]
|
|
685
|
+
),
|
|
686
|
+
content: UserContent,
|
|
687
|
+
) -> (
|
|
688
|
+
AsyncContextRetryResponse[DepsT, None]
|
|
689
|
+
| AsyncContextRetryResponse[DepsT, FormattableT]
|
|
690
|
+
):
|
|
691
|
+
"""Generate a new AsyncContextRetryResponse by extending another response's messages.
|
|
692
|
+
|
|
693
|
+
Args:
|
|
694
|
+
ctx: A `Context` with the required deps type.
|
|
695
|
+
response: Previous async context response to extend.
|
|
696
|
+
content: Additional user content to append.
|
|
697
|
+
|
|
698
|
+
Returns:
|
|
699
|
+
A new AsyncContextRetryResponse containing the extended conversation.
|
|
700
|
+
|
|
701
|
+
Raises:
|
|
702
|
+
Exception: The last exception encountered if all retry attempts fail.
|
|
703
|
+
"""
|
|
704
|
+
new_response, failures, updated_model = await with_retry_async(
|
|
705
|
+
fn=lambda m: m.context_resume_async(
|
|
706
|
+
ctx=ctx, response=response, content=content
|
|
707
|
+
),
|
|
708
|
+
retry_model=self,
|
|
709
|
+
)
|
|
710
|
+
return AsyncContextRetryResponse(
|
|
711
|
+
response=cast("AsyncContextResponse[DepsT, FormattableT]", new_response),
|
|
712
|
+
retry_model=updated_model,
|
|
713
|
+
retry_failures=failures,
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
# Stream methods
|
|
717
|
+
|
|
718
|
+
@overload
|
|
719
|
+
def stream(
|
|
720
|
+
self,
|
|
721
|
+
content: UserContent | Sequence[Message],
|
|
722
|
+
*,
|
|
723
|
+
tools: Tools | None = None,
|
|
724
|
+
format: None = None,
|
|
725
|
+
) -> RetryStreamResponse[None]: ...
|
|
726
|
+
|
|
727
|
+
@overload
|
|
728
|
+
def stream(
|
|
729
|
+
self,
|
|
730
|
+
content: UserContent | Sequence[Message],
|
|
731
|
+
*,
|
|
732
|
+
tools: Tools | None = None,
|
|
733
|
+
format: FormatSpec[FormattableT],
|
|
734
|
+
) -> RetryStreamResponse[FormattableT]: ...
|
|
735
|
+
|
|
736
|
+
@overload
|
|
737
|
+
def stream(
|
|
738
|
+
self,
|
|
739
|
+
content: UserContent | Sequence[Message],
|
|
740
|
+
*,
|
|
741
|
+
tools: Tools | None = None,
|
|
742
|
+
format: FormatSpec[FormattableT] | None,
|
|
743
|
+
) -> RetryStreamResponse[None] | RetryStreamResponse[FormattableT]: ...
|
|
744
|
+
|
|
745
|
+
def stream(
|
|
746
|
+
self,
|
|
747
|
+
content: UserContent | Sequence[Message],
|
|
748
|
+
*,
|
|
749
|
+
tools: Tools | None = None,
|
|
750
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
751
|
+
) -> RetryStreamResponse[None] | RetryStreamResponse[FormattableT]:
|
|
752
|
+
"""Generate a RetryStreamResponse by streaming from this model's LLM provider.
|
|
753
|
+
|
|
754
|
+
The returned response supports automatic retry on failure. If a retryable
|
|
755
|
+
error occurs during iteration, a `StreamRestarted` exception is raised
|
|
756
|
+
and the user can re-iterate to continue from the new attempt.
|
|
757
|
+
|
|
758
|
+
Supports fallback models - when the active model exhausts its retries,
|
|
759
|
+
the next fallback model is tried.
|
|
760
|
+
|
|
761
|
+
Args:
|
|
762
|
+
content: User content or LLM messages to send to the LLM.
|
|
763
|
+
tools: Optional tools that the model may invoke.
|
|
764
|
+
format: Optional response format specifier.
|
|
765
|
+
|
|
766
|
+
Returns:
|
|
767
|
+
A RetryStreamResponse for iterating over the LLM-generated content.
|
|
768
|
+
"""
|
|
769
|
+
return RetryStreamResponse(
|
|
770
|
+
self,
|
|
771
|
+
lambda m: cast(
|
|
772
|
+
"StreamResponse[FormattableT]",
|
|
773
|
+
m.stream(content=content, tools=tools, format=format),
|
|
774
|
+
),
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
@overload
|
|
778
|
+
async def stream_async(
|
|
779
|
+
self,
|
|
780
|
+
content: UserContent | Sequence[Message],
|
|
781
|
+
*,
|
|
782
|
+
tools: AsyncTools | None = None,
|
|
783
|
+
format: None = None,
|
|
784
|
+
) -> AsyncRetryStreamResponse[None]: ...
|
|
785
|
+
|
|
786
|
+
@overload
|
|
787
|
+
async def stream_async(
|
|
788
|
+
self,
|
|
789
|
+
content: UserContent | Sequence[Message],
|
|
790
|
+
*,
|
|
791
|
+
tools: AsyncTools | None = None,
|
|
792
|
+
format: FormatSpec[FormattableT],
|
|
793
|
+
) -> AsyncRetryStreamResponse[FormattableT]: ...
|
|
794
|
+
|
|
795
|
+
@overload
|
|
796
|
+
async def stream_async(
|
|
797
|
+
self,
|
|
798
|
+
content: UserContent | Sequence[Message],
|
|
799
|
+
*,
|
|
800
|
+
tools: AsyncTools | None = None,
|
|
801
|
+
format: FormatSpec[FormattableT] | None,
|
|
802
|
+
) -> AsyncRetryStreamResponse[None] | AsyncRetryStreamResponse[FormattableT]: ...
|
|
803
|
+
|
|
804
|
+
async def stream_async(
|
|
805
|
+
self,
|
|
806
|
+
content: UserContent | Sequence[Message],
|
|
807
|
+
*,
|
|
808
|
+
tools: AsyncTools | None = None,
|
|
809
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
810
|
+
) -> AsyncRetryStreamResponse[None] | AsyncRetryStreamResponse[FormattableT]:
|
|
811
|
+
"""Generate an AsyncRetryStreamResponse by streaming from this model's LLM provider.
|
|
812
|
+
|
|
813
|
+
The returned response supports automatic retry on failure. If a retryable
|
|
814
|
+
error occurs during iteration, a `StreamRestarted` exception is raised
|
|
815
|
+
and the user can re-iterate to continue from the new attempt.
|
|
816
|
+
|
|
817
|
+
Supports fallback models - when the active model exhausts its retries,
|
|
818
|
+
the next fallback model is tried.
|
|
819
|
+
|
|
820
|
+
Args:
|
|
821
|
+
content: User content or LLM messages to send to the LLM.
|
|
822
|
+
tools: Optional tools that the model may invoke.
|
|
823
|
+
format: Optional response format specifier.
|
|
824
|
+
|
|
825
|
+
Returns:
|
|
826
|
+
An AsyncRetryStreamResponse for asynchronously iterating over the LLM-generated content.
|
|
827
|
+
"""
|
|
828
|
+
|
|
829
|
+
def stream_fn(m: Model) -> Awaitable[AsyncStreamResponse[FormattableT]]:
|
|
830
|
+
return cast(
|
|
831
|
+
"Awaitable[AsyncStreamResponse[FormattableT]]",
|
|
832
|
+
m.stream_async(content=content, tools=tools, format=format),
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
variants_iter = self.variants_async()
|
|
836
|
+
initial_variant = await anext(variants_iter)
|
|
837
|
+
initial_stream = await stream_fn(initial_variant.get_active_model())
|
|
838
|
+
return AsyncRetryStreamResponse(
|
|
839
|
+
stream_fn, initial_stream, initial_variant, variants_iter
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
# Resume stream methods
|
|
843
|
+
|
|
844
|
+
@overload
|
|
845
|
+
def resume_stream(
|
|
846
|
+
self,
|
|
847
|
+
*,
|
|
848
|
+
response: StreamResponse,
|
|
849
|
+
content: UserContent,
|
|
850
|
+
) -> RetryStreamResponse[None]: ...
|
|
851
|
+
|
|
852
|
+
@overload
|
|
853
|
+
def resume_stream(
|
|
854
|
+
self,
|
|
855
|
+
*,
|
|
856
|
+
response: StreamResponse[FormattableT],
|
|
857
|
+
content: UserContent,
|
|
858
|
+
) -> RetryStreamResponse[FormattableT]: ...
|
|
859
|
+
|
|
860
|
+
@overload
|
|
861
|
+
def resume_stream(
|
|
862
|
+
self,
|
|
863
|
+
*,
|
|
864
|
+
response: StreamResponse | StreamResponse[FormattableT],
|
|
865
|
+
content: UserContent,
|
|
866
|
+
) -> RetryStreamResponse[None] | RetryStreamResponse[FormattableT]: ...
|
|
867
|
+
|
|
868
|
+
def resume_stream(
|
|
869
|
+
self,
|
|
870
|
+
*,
|
|
871
|
+
response: StreamResponse | StreamResponse[FormattableT],
|
|
872
|
+
content: UserContent,
|
|
873
|
+
) -> RetryStreamResponse[None] | RetryStreamResponse[FormattableT]:
|
|
874
|
+
"""Generate a new RetryStreamResponse by extending another response's messages.
|
|
875
|
+
|
|
876
|
+
Args:
|
|
877
|
+
response: Previous stream response to extend.
|
|
878
|
+
content: Additional user content to append.
|
|
879
|
+
|
|
880
|
+
Returns:
|
|
881
|
+
A new RetryStreamResponse for streaming the extended conversation.
|
|
882
|
+
"""
|
|
883
|
+
return RetryStreamResponse(
|
|
884
|
+
self,
|
|
885
|
+
lambda m: cast(
|
|
886
|
+
"StreamResponse[FormattableT]",
|
|
887
|
+
m.resume_stream(response=response, content=content),
|
|
888
|
+
),
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
@overload
|
|
892
|
+
async def resume_stream_async(
|
|
893
|
+
self,
|
|
894
|
+
*,
|
|
895
|
+
response: AsyncStreamResponse,
|
|
896
|
+
content: UserContent,
|
|
897
|
+
) -> AsyncRetryStreamResponse[None]: ...
|
|
898
|
+
|
|
899
|
+
@overload
|
|
900
|
+
async def resume_stream_async(
|
|
901
|
+
self,
|
|
902
|
+
*,
|
|
903
|
+
response: AsyncStreamResponse[FormattableT],
|
|
904
|
+
content: UserContent,
|
|
905
|
+
) -> AsyncRetryStreamResponse[FormattableT]: ...
|
|
906
|
+
|
|
907
|
+
@overload
|
|
908
|
+
async def resume_stream_async(
|
|
909
|
+
self,
|
|
910
|
+
*,
|
|
911
|
+
response: AsyncStreamResponse | AsyncStreamResponse[FormattableT],
|
|
912
|
+
content: UserContent,
|
|
913
|
+
) -> AsyncRetryStreamResponse[None] | AsyncRetryStreamResponse[FormattableT]: ...
|
|
914
|
+
|
|
915
|
+
async def resume_stream_async(
|
|
916
|
+
self,
|
|
917
|
+
*,
|
|
918
|
+
response: AsyncStreamResponse | AsyncStreamResponse[FormattableT],
|
|
919
|
+
content: UserContent,
|
|
920
|
+
) -> AsyncRetryStreamResponse[None] | AsyncRetryStreamResponse[FormattableT]:
|
|
921
|
+
"""Generate a new AsyncRetryStreamResponse by extending another response's messages.
|
|
922
|
+
|
|
923
|
+
Args:
|
|
924
|
+
response: Previous async stream response to extend.
|
|
925
|
+
content: Additional user content to append.
|
|
926
|
+
|
|
927
|
+
Returns:
|
|
928
|
+
A new AsyncRetryStreamResponse for asynchronously streaming the extended conversation.
|
|
929
|
+
"""
|
|
930
|
+
|
|
931
|
+
def stream_fn(m: Model) -> Awaitable[AsyncStreamResponse[FormattableT]]:
|
|
932
|
+
return cast(
|
|
933
|
+
"Awaitable[AsyncStreamResponse[FormattableT]]",
|
|
934
|
+
m.resume_stream_async(response=response, content=content),
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
variants_iter = self.variants_async()
|
|
938
|
+
initial_variant = await anext(variants_iter)
|
|
939
|
+
initial_stream = await stream_fn(initial_variant.get_active_model())
|
|
940
|
+
return AsyncRetryStreamResponse(
|
|
941
|
+
stream_fn, initial_stream, initial_variant, variants_iter
|
|
942
|
+
)
|
|
943
|
+
|
|
944
|
+
# Context stream methods
|
|
945
|
+
|
|
946
|
+
@overload
|
|
947
|
+
def context_stream(
|
|
948
|
+
self,
|
|
949
|
+
content: UserContent | Sequence[Message],
|
|
950
|
+
*,
|
|
951
|
+
ctx: Context[DepsT],
|
|
952
|
+
tools: ContextTools[DepsT] | None = None,
|
|
953
|
+
format: None = None,
|
|
954
|
+
) -> ContextRetryStreamResponse[DepsT, None]: ...
|
|
955
|
+
|
|
956
|
+
@overload
|
|
957
|
+
def context_stream(
|
|
958
|
+
self,
|
|
959
|
+
content: UserContent | Sequence[Message],
|
|
960
|
+
*,
|
|
961
|
+
ctx: Context[DepsT],
|
|
962
|
+
tools: ContextTools[DepsT] | None = None,
|
|
963
|
+
format: FormatSpec[FormattableT],
|
|
964
|
+
) -> ContextRetryStreamResponse[DepsT, FormattableT]: ...
|
|
965
|
+
|
|
966
|
+
@overload
|
|
967
|
+
def context_stream(
|
|
968
|
+
self,
|
|
969
|
+
content: UserContent | Sequence[Message],
|
|
970
|
+
*,
|
|
971
|
+
ctx: Context[DepsT],
|
|
972
|
+
tools: ContextTools[DepsT] | None = None,
|
|
973
|
+
format: FormatSpec[FormattableT] | None,
|
|
974
|
+
) -> (
|
|
975
|
+
ContextRetryStreamResponse[DepsT, None]
|
|
976
|
+
| ContextRetryStreamResponse[DepsT, FormattableT]
|
|
977
|
+
): ...
|
|
978
|
+
|
|
979
|
+
def context_stream(
|
|
980
|
+
self,
|
|
981
|
+
content: UserContent | Sequence[Message],
|
|
982
|
+
*,
|
|
983
|
+
ctx: Context[DepsT],
|
|
984
|
+
tools: ContextTools[DepsT] | None = None,
|
|
985
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
986
|
+
) -> (
|
|
987
|
+
ContextRetryStreamResponse[DepsT, None]
|
|
988
|
+
| ContextRetryStreamResponse[DepsT, FormattableT]
|
|
989
|
+
):
|
|
990
|
+
"""Generate a ContextRetryStreamResponse by streaming from this model's LLM provider with context.
|
|
991
|
+
|
|
992
|
+
The returned response supports automatic retry on failure. If a retryable
|
|
993
|
+
error occurs during iteration, a `StreamRestarted` exception is raised
|
|
994
|
+
and the user can re-iterate to continue from the new attempt.
|
|
995
|
+
|
|
996
|
+
Supports fallback models - when the active model exhausts its retries,
|
|
997
|
+
the next fallback model is tried.
|
|
998
|
+
|
|
999
|
+
Args:
|
|
1000
|
+
content: User content or LLM messages to send to the LLM.
|
|
1001
|
+
ctx: A `Context` with the required deps type.
|
|
1002
|
+
tools: Optional context-aware tools that the model may invoke.
|
|
1003
|
+
format: Optional response format specifier.
|
|
1004
|
+
|
|
1005
|
+
Returns:
|
|
1006
|
+
A ContextRetryStreamResponse for iterating over the LLM-generated content.
|
|
1007
|
+
"""
|
|
1008
|
+
return ContextRetryStreamResponse(
|
|
1009
|
+
self,
|
|
1010
|
+
lambda m: cast(
|
|
1011
|
+
"ContextStreamResponse[DepsT, FormattableT]",
|
|
1012
|
+
m.context_stream(content=content, ctx=ctx, tools=tools, format=format),
|
|
1013
|
+
),
|
|
1014
|
+
)
|
|
1015
|
+
|
|
1016
|
+
@overload
|
|
1017
|
+
async def context_stream_async(
|
|
1018
|
+
self,
|
|
1019
|
+
content: UserContent | Sequence[Message],
|
|
1020
|
+
*,
|
|
1021
|
+
ctx: Context[DepsT],
|
|
1022
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
1023
|
+
format: None = None,
|
|
1024
|
+
) -> AsyncContextRetryStreamResponse[DepsT, None]: ...
|
|
1025
|
+
|
|
1026
|
+
@overload
|
|
1027
|
+
async def context_stream_async(
|
|
1028
|
+
self,
|
|
1029
|
+
content: UserContent | Sequence[Message],
|
|
1030
|
+
*,
|
|
1031
|
+
ctx: Context[DepsT],
|
|
1032
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
1033
|
+
format: FormatSpec[FormattableT],
|
|
1034
|
+
) -> AsyncContextRetryStreamResponse[DepsT, FormattableT]: ...
|
|
1035
|
+
|
|
1036
|
+
@overload
|
|
1037
|
+
async def context_stream_async(
|
|
1038
|
+
self,
|
|
1039
|
+
content: UserContent | Sequence[Message],
|
|
1040
|
+
*,
|
|
1041
|
+
ctx: Context[DepsT],
|
|
1042
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
1043
|
+
format: FormatSpec[FormattableT] | None,
|
|
1044
|
+
) -> (
|
|
1045
|
+
AsyncContextRetryStreamResponse[DepsT, None]
|
|
1046
|
+
| AsyncContextRetryStreamResponse[DepsT, FormattableT]
|
|
1047
|
+
): ...
|
|
1048
|
+
|
|
1049
|
+
async def context_stream_async(
|
|
1050
|
+
self,
|
|
1051
|
+
content: UserContent | Sequence[Message],
|
|
1052
|
+
*,
|
|
1053
|
+
ctx: Context[DepsT],
|
|
1054
|
+
tools: AsyncContextTools[DepsT] | None = None,
|
|
1055
|
+
format: FormatSpec[FormattableT] | None = None,
|
|
1056
|
+
) -> (
|
|
1057
|
+
AsyncContextRetryStreamResponse[DepsT, None]
|
|
1058
|
+
| AsyncContextRetryStreamResponse[DepsT, FormattableT]
|
|
1059
|
+
):
|
|
1060
|
+
"""Generate an AsyncContextRetryStreamResponse by streaming from this model's LLM provider with context.
|
|
1061
|
+
|
|
1062
|
+
The returned response supports automatic retry on failure. If a retryable
|
|
1063
|
+
error occurs during iteration, a `StreamRestarted` exception is raised
|
|
1064
|
+
and the user can re-iterate to continue from the new attempt.
|
|
1065
|
+
|
|
1066
|
+
Supports fallback models - when the active model exhausts its retries,
|
|
1067
|
+
the next fallback model is tried.
|
|
1068
|
+
|
|
1069
|
+
Args:
|
|
1070
|
+
content: User content or LLM messages to send to the LLM.
|
|
1071
|
+
ctx: A `Context` with the required deps type.
|
|
1072
|
+
tools: Optional context-aware tools that the model may invoke.
|
|
1073
|
+
format: Optional response format specifier.
|
|
1074
|
+
|
|
1075
|
+
Returns:
|
|
1076
|
+
An AsyncContextRetryStreamResponse for asynchronously iterating over the LLM-generated content.
|
|
1077
|
+
"""
|
|
1078
|
+
|
|
1079
|
+
def stream_fn(
|
|
1080
|
+
m: Model,
|
|
1081
|
+
) -> Awaitable[AsyncContextStreamResponse[DepsT, FormattableT]]:
|
|
1082
|
+
return cast(
|
|
1083
|
+
"Awaitable[AsyncContextStreamResponse[DepsT, FormattableT]]",
|
|
1084
|
+
m.context_stream_async(
|
|
1085
|
+
content=content, ctx=ctx, tools=tools, format=format
|
|
1086
|
+
),
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
variants_iter = self.variants_async()
|
|
1090
|
+
initial_variant = await anext(variants_iter)
|
|
1091
|
+
initial_stream = await stream_fn(initial_variant.get_active_model())
|
|
1092
|
+
return AsyncContextRetryStreamResponse(
|
|
1093
|
+
stream_fn, initial_stream, initial_variant, variants_iter
|
|
1094
|
+
)
|
|
1095
|
+
|
|
1096
|
+
# Context resume stream methods
|
|
1097
|
+
|
|
1098
|
+
@overload
|
|
1099
|
+
def context_resume_stream(
|
|
1100
|
+
self,
|
|
1101
|
+
*,
|
|
1102
|
+
ctx: Context[DepsT],
|
|
1103
|
+
response: ContextStreamResponse[DepsT, None],
|
|
1104
|
+
content: UserContent,
|
|
1105
|
+
) -> ContextRetryStreamResponse[DepsT, None]: ...
|
|
1106
|
+
|
|
1107
|
+
@overload
|
|
1108
|
+
def context_resume_stream(
|
|
1109
|
+
self,
|
|
1110
|
+
*,
|
|
1111
|
+
ctx: Context[DepsT],
|
|
1112
|
+
response: ContextStreamResponse[DepsT, FormattableT],
|
|
1113
|
+
content: UserContent,
|
|
1114
|
+
) -> ContextRetryStreamResponse[DepsT, FormattableT]: ...
|
|
1115
|
+
|
|
1116
|
+
@overload
|
|
1117
|
+
def context_resume_stream(
|
|
1118
|
+
self,
|
|
1119
|
+
*,
|
|
1120
|
+
ctx: Context[DepsT],
|
|
1121
|
+
response: (
|
|
1122
|
+
ContextStreamResponse[DepsT, None]
|
|
1123
|
+
| ContextStreamResponse[DepsT, FormattableT]
|
|
1124
|
+
),
|
|
1125
|
+
content: UserContent,
|
|
1126
|
+
) -> (
|
|
1127
|
+
ContextRetryStreamResponse[DepsT, None]
|
|
1128
|
+
| ContextRetryStreamResponse[DepsT, FormattableT]
|
|
1129
|
+
): ...
|
|
1130
|
+
|
|
1131
|
+
def context_resume_stream( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
1132
|
+
self,
|
|
1133
|
+
*,
|
|
1134
|
+
ctx: Context[DepsT],
|
|
1135
|
+
response: (
|
|
1136
|
+
ContextStreamResponse[DepsT, None]
|
|
1137
|
+
| ContextStreamResponse[DepsT, FormattableT]
|
|
1138
|
+
),
|
|
1139
|
+
content: UserContent,
|
|
1140
|
+
) -> (
|
|
1141
|
+
ContextRetryStreamResponse[DepsT, None]
|
|
1142
|
+
| ContextRetryStreamResponse[DepsT, FormattableT]
|
|
1143
|
+
):
|
|
1144
|
+
"""Generate a new ContextRetryStreamResponse by extending another response's messages.
|
|
1145
|
+
|
|
1146
|
+
Args:
|
|
1147
|
+
ctx: A `Context` with the required deps type.
|
|
1148
|
+
response: Previous context stream response to extend.
|
|
1149
|
+
content: Additional user content to append.
|
|
1150
|
+
|
|
1151
|
+
Returns:
|
|
1152
|
+
A new ContextRetryStreamResponse for streaming the extended conversation.
|
|
1153
|
+
"""
|
|
1154
|
+
return ContextRetryStreamResponse(
|
|
1155
|
+
self,
|
|
1156
|
+
lambda m: cast(
|
|
1157
|
+
"ContextStreamResponse[DepsT, FormattableT]",
|
|
1158
|
+
m.context_resume_stream(ctx=ctx, response=response, content=content),
|
|
1159
|
+
),
|
|
1160
|
+
)
|
|
1161
|
+
|
|
1162
|
+
@overload
|
|
1163
|
+
async def context_resume_stream_async(
|
|
1164
|
+
self,
|
|
1165
|
+
*,
|
|
1166
|
+
ctx: Context[DepsT],
|
|
1167
|
+
response: AsyncContextStreamResponse[DepsT, None],
|
|
1168
|
+
content: UserContent,
|
|
1169
|
+
) -> AsyncContextRetryStreamResponse[DepsT, None]: ...
|
|
1170
|
+
|
|
1171
|
+
@overload
|
|
1172
|
+
async def context_resume_stream_async(
|
|
1173
|
+
self,
|
|
1174
|
+
*,
|
|
1175
|
+
ctx: Context[DepsT],
|
|
1176
|
+
response: AsyncContextStreamResponse[DepsT, FormattableT],
|
|
1177
|
+
content: UserContent,
|
|
1178
|
+
) -> AsyncContextRetryStreamResponse[DepsT, FormattableT]: ...
|
|
1179
|
+
|
|
1180
|
+
@overload
|
|
1181
|
+
async def context_resume_stream_async(
|
|
1182
|
+
self,
|
|
1183
|
+
*,
|
|
1184
|
+
ctx: Context[DepsT],
|
|
1185
|
+
response: (
|
|
1186
|
+
AsyncContextStreamResponse[DepsT, None]
|
|
1187
|
+
| AsyncContextStreamResponse[DepsT, FormattableT]
|
|
1188
|
+
),
|
|
1189
|
+
content: UserContent,
|
|
1190
|
+
) -> (
|
|
1191
|
+
AsyncContextRetryStreamResponse[DepsT, None]
|
|
1192
|
+
| AsyncContextRetryStreamResponse[DepsT, FormattableT]
|
|
1193
|
+
): ...
|
|
1194
|
+
|
|
1195
|
+
async def context_resume_stream_async( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
1196
|
+
self,
|
|
1197
|
+
*,
|
|
1198
|
+
ctx: Context[DepsT],
|
|
1199
|
+
response: (
|
|
1200
|
+
AsyncContextStreamResponse[DepsT, None]
|
|
1201
|
+
| AsyncContextStreamResponse[DepsT, FormattableT]
|
|
1202
|
+
),
|
|
1203
|
+
content: UserContent,
|
|
1204
|
+
) -> (
|
|
1205
|
+
AsyncContextRetryStreamResponse[DepsT, None]
|
|
1206
|
+
| AsyncContextRetryStreamResponse[DepsT, FormattableT]
|
|
1207
|
+
):
|
|
1208
|
+
"""Generate a new AsyncContextRetryStreamResponse by extending another response's messages.
|
|
1209
|
+
|
|
1210
|
+
Args:
|
|
1211
|
+
ctx: A `Context` with the required deps type.
|
|
1212
|
+
response: Previous async context stream response to extend.
|
|
1213
|
+
content: Additional user content to append.
|
|
1214
|
+
|
|
1215
|
+
Returns:
|
|
1216
|
+
A new AsyncContextRetryStreamResponse for asynchronously streaming the extended conversation.
|
|
1217
|
+
"""
|
|
1218
|
+
|
|
1219
|
+
def stream_fn(
|
|
1220
|
+
m: Model,
|
|
1221
|
+
) -> Awaitable[AsyncContextStreamResponse[DepsT, FormattableT]]:
|
|
1222
|
+
return cast(
|
|
1223
|
+
"Awaitable[AsyncContextStreamResponse[DepsT, FormattableT]]",
|
|
1224
|
+
m.context_resume_stream_async(
|
|
1225
|
+
ctx=ctx, response=response, content=content
|
|
1226
|
+
),
|
|
1227
|
+
)
|
|
1228
|
+
|
|
1229
|
+
variants_iter = self.variants_async()
|
|
1230
|
+
initial_variant = await anext(variants_iter)
|
|
1231
|
+
initial_stream = await stream_fn(initial_variant.get_active_model())
|
|
1232
|
+
return AsyncContextRetryStreamResponse(
|
|
1233
|
+
stream_fn, initial_stream, initial_variant, variants_iter
|
|
1234
|
+
)
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
def retry_model(
|
|
1238
|
+
model_id: ModelId,
|
|
1239
|
+
**kwargs: Unpack[RetryModelParams],
|
|
1240
|
+
) -> RetryModel:
|
|
1241
|
+
"""Helper for creating a `RetryModel` instance with retry logic.
|
|
1242
|
+
|
|
1243
|
+
This function creates a RetryModel that wraps the specified model with automatic
|
|
1244
|
+
retry functionality. It accepts both model parameters (temperature, max_tokens, etc.)
|
|
1245
|
+
and retry configuration (max_retries, initial_delay, etc.) in a single call.
|
|
1246
|
+
|
|
1247
|
+
Args:
|
|
1248
|
+
model_id: A model ID string (e.g., "openai/gpt-4").
|
|
1249
|
+
**kwargs: Combined model parameters and retry configuration. Model parameters
|
|
1250
|
+
(temperature, max_tokens, top_p, top_k, seed, stop_sequences, thinking)
|
|
1251
|
+
are passed to the underlying Model. Retry parameters (max_retries, retry_on,
|
|
1252
|
+
initial_delay, max_delay, backoff_multiplier, jitter, fallback_models)
|
|
1253
|
+
configure the retry behavior.
|
|
1254
|
+
|
|
1255
|
+
Returns:
|
|
1256
|
+
A RetryModel instance configured with the specified model and retry settings.
|
|
1257
|
+
|
|
1258
|
+
Raises:
|
|
1259
|
+
ValueError: If the model_id format is invalid.
|
|
1260
|
+
|
|
1261
|
+
Example:
|
|
1262
|
+
Basic usage with retry defaults:
|
|
1263
|
+
|
|
1264
|
+
```python
|
|
1265
|
+
import mirascope.llm as llm
|
|
1266
|
+
|
|
1267
|
+
model = llm.retry_model("openai/gpt-4o")
|
|
1268
|
+
response = model.call("Hello!")
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
Example:
|
|
1272
|
+
With model parameters and retry configuration:
|
|
1273
|
+
|
|
1274
|
+
```python
|
|
1275
|
+
import mirascope.llm as llm
|
|
1276
|
+
|
|
1277
|
+
model = llm.retry_model(
|
|
1278
|
+
"openai/gpt-4o",
|
|
1279
|
+
temperature=0.7,
|
|
1280
|
+
max_tokens=1000,
|
|
1281
|
+
max_retries=5,
|
|
1282
|
+
initial_delay=1.0,
|
|
1283
|
+
backoff_multiplier=2.0,
|
|
1284
|
+
)
|
|
1285
|
+
response = model.call("Write a story.")
|
|
1286
|
+
```
|
|
1287
|
+
|
|
1288
|
+
Example:
|
|
1289
|
+
With fallback models:
|
|
1290
|
+
|
|
1291
|
+
```python
|
|
1292
|
+
import mirascope.llm as llm
|
|
1293
|
+
|
|
1294
|
+
model = llm.retry_model(
|
|
1295
|
+
"openai/gpt-4o",
|
|
1296
|
+
max_retries=3,
|
|
1297
|
+
fallback_models=["anthropic/claude-sonnet-4-5", "google/gemini-2.0-flash"],
|
|
1298
|
+
)
|
|
1299
|
+
response = model.call("Complex task...")
|
|
1300
|
+
```
|
|
1301
|
+
"""
|
|
1302
|
+
# Separate retry args from model params
|
|
1303
|
+
retry_args: RetryArgs = {}
|
|
1304
|
+
model_params: Params = {}
|
|
1305
|
+
|
|
1306
|
+
for key, value in kwargs.items():
|
|
1307
|
+
if key in _RETRY_ARG_KEYS:
|
|
1308
|
+
retry_args[key] = value
|
|
1309
|
+
else:
|
|
1310
|
+
model_params[key] = value
|
|
1311
|
+
|
|
1312
|
+
retry_config = RetryConfig.from_args(**retry_args)
|
|
1313
|
+
return RetryModel(model_id, retry_config, **model_params)
|