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
@@ -0,0 +1,159 @@
1
+ """Utility functions for retry logic."""
2
+
3
+ import random
4
+ from collections.abc import Awaitable, Callable
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING, TypeVar
7
+
8
+ from ..exceptions import RetriesExhausted
9
+ from .retry_config import RetryConfig
10
+
11
+ if TYPE_CHECKING:
12
+ from ..models import Model
13
+ from .retry_models import RetryModel
14
+
15
+
16
+ def get_retry_model_from_context(stored_retry_model: "RetryModel") -> "RetryModel":
17
+ """Get the RetryModel to use, checking context for overrides.
18
+
19
+ If a model is set in context (via `llm.model()` or `llm.retry_model()`),
20
+ that model is used instead, wrapped with this response's retry config.
21
+
22
+ Args:
23
+ stored_retry_model: The RetryModel stored on the response.
24
+
25
+ Returns:
26
+ Either the context model (wrapped in RetryModel if needed) or the stored model.
27
+ """
28
+ from ..models import model_from_context
29
+ from .retry_models import RetryModel
30
+
31
+ context_model = model_from_context()
32
+ if context_model is not None:
33
+ if isinstance(context_model, RetryModel):
34
+ return context_model
35
+ return RetryModel(context_model, stored_retry_model.retry_config)
36
+ return stored_retry_model
37
+
38
+
39
+ _ResultT = TypeVar("_ResultT")
40
+
41
+
42
+ @dataclass
43
+ class RetryFailure:
44
+ """A failed attempt to call a model.
45
+
46
+ Attributes:
47
+ model: The model that was tried.
48
+ exception: The exception that was raised.
49
+ """
50
+
51
+ model: "Model"
52
+ exception: BaseException
53
+
54
+
55
+ def calculate_delay(config: RetryConfig, attempt_for_model: int) -> float:
56
+ """Calculate the delay before the next retry attempt.
57
+
58
+ Args:
59
+ config: The retry configuration with backoff settings.
60
+ attempt_for_model: The retry attempt number for this model (1-indexed).
61
+
62
+ Returns:
63
+ The delay in seconds, with exponential backoff, capped at max_delay,
64
+ and optionally with jitter applied.
65
+ """
66
+ # Calculate base delay with exponential backoff
67
+ delay = config.initial_delay * (
68
+ config.backoff_multiplier ** (attempt_for_model - 1)
69
+ )
70
+
71
+ # Cap at max_delay
72
+ delay = min(delay, config.max_delay)
73
+
74
+ # Apply jitter if configured
75
+ if config.jitter > 0:
76
+ jitter_range = delay * config.jitter
77
+ delay = delay + random.uniform(-jitter_range, jitter_range)
78
+ # Ensure delay doesn't go negative
79
+ delay = max(0, delay)
80
+
81
+ return delay
82
+
83
+
84
+ def with_retry(
85
+ fn: Callable[["Model"], _ResultT],
86
+ retry_model: "RetryModel",
87
+ ) -> tuple[_ResultT, list[RetryFailure], "RetryModel"]:
88
+ """Execute a function with retry logic across the RetryModel's models.
89
+
90
+ Tries the active model first, then fallbacks. Each model gets its own
91
+ full retry budget. Returns an updated RetryModel with the successful
92
+ model set as active.
93
+
94
+ Args:
95
+ fn: Function that takes a Model and returns a result.
96
+ retry_model: The RetryModel containing models and retry config.
97
+
98
+ Returns:
99
+ A tuple of (result, failures, updated_retry_model).
100
+ failures contains all failed attempts before success.
101
+ The updated_retry_model has the successful model as active.
102
+
103
+ Raises:
104
+ Exception: The last exception encountered if all models exhaust retries.
105
+ """
106
+ config = retry_model.retry_config
107
+ failures: list[RetryFailure] = []
108
+
109
+ for variant in retry_model.variants():
110
+ model = variant.get_active_model()
111
+ try:
112
+ result = fn(model)
113
+ return result, failures, variant
114
+ except config.retry_on as e:
115
+ failures.append(RetryFailure(model=model, exception=e))
116
+
117
+ # All models exhausted
118
+ if failures:
119
+ raise RetriesExhausted(failures)
120
+ raise AssertionError("Unreachable: no models provided") # pragma: no cover
121
+
122
+
123
+ async def with_retry_async(
124
+ fn: Callable[["Model"], Awaitable[_ResultT]],
125
+ retry_model: "RetryModel",
126
+ ) -> tuple[_ResultT, list[RetryFailure], "RetryModel"]:
127
+ """Execute an async function with retry logic across the RetryModel's models.
128
+
129
+ Tries the active model first, then fallbacks. Each model gets its own
130
+ full retry budget. Returns an updated RetryModel with the successful
131
+ model set as active.
132
+
133
+ Args:
134
+ fn: Async function that takes a Model and returns a result.
135
+ retry_model: The RetryModel containing models and retry config.
136
+
137
+ Returns:
138
+ A tuple of (result, failures, updated_retry_model).
139
+ failures contains all failed attempts before success.
140
+ The updated_retry_model has the successful model as active.
141
+
142
+ Raises:
143
+ Exception: The last exception encountered if all models exhaust retries.
144
+ """
145
+ config = retry_model.retry_config
146
+ failures: list[RetryFailure] = []
147
+
148
+ async for variant in retry_model.variants_async():
149
+ model = variant.get_active_model()
150
+ try:
151
+ result = await fn(model)
152
+ return result, failures, variant
153
+ except config.retry_on as e:
154
+ failures.append(RetryFailure(model=model, exception=e))
155
+
156
+ # All models exhausted
157
+ if failures:
158
+ raise RetriesExhausted(failures)
159
+ raise AssertionError("Unreachable: no models provided") # pragma: no cover
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  from dataclasses import dataclass, field
7
- from typing import Concatenate, Generic
7
+ from typing import Concatenate, Generic, cast, overload
8
8
  from typing_extensions import TypeIs
9
9
 
10
10
  from ...llm.calls import AsyncCall, AsyncContextCall, Call, ContextCall
@@ -238,23 +238,61 @@ class VersionedCall(_BaseVersionedCall, Generic[P, FormattableT]):
238
238
  )
239
239
  self._compute_closure(self._call, self.call, self.stream)
240
240
 
241
+ @overload
242
+ def __call__(
243
+ self: VersionedCall[P, None], *args: P.args, **kwargs: P.kwargs
244
+ ) -> Response: ...
245
+
246
+ @overload
247
+ def __call__(
248
+ self: VersionedCall[P, FormattableT], *args: P.args, **kwargs: P.kwargs
249
+ ) -> Response[FormattableT]: ...
250
+
241
251
  def __call__(
242
252
  self, *args: P.args, **kwargs: P.kwargs
243
253
  ) -> Response | Response[FormattableT]:
244
254
  """Call the versioned function and return Response directly."""
245
255
  return self.call(*args, **kwargs)
246
256
 
257
+ @overload
258
+ def wrapped(
259
+ self: VersionedCall[P, None], *args: P.args, **kwargs: P.kwargs
260
+ ) -> VersionedResult[Response]: ...
261
+
262
+ @overload
263
+ def wrapped(
264
+ self: VersionedCall[P, FormattableT], *args: P.args, **kwargs: P.kwargs
265
+ ) -> VersionedResult[Response[FormattableT]]: ...
266
+
247
267
  def wrapped(
248
268
  self, *args: P.args, **kwargs: P.kwargs
249
- ) -> VersionedResult[Response | Response[FormattableT]]:
269
+ ) -> VersionedResult[Response] | VersionedResult[Response[FormattableT]]:
250
270
  """Call the versioned function and return a wrapped Response."""
251
- return self.call.wrapped(*args, **kwargs)
271
+ return cast(
272
+ "VersionedResult[Response] | VersionedResult[Response[FormattableT]]",
273
+ self.call.wrapped(*args, **kwargs),
274
+ )
275
+
276
+ @overload
277
+ def wrapped_stream(
278
+ self: VersionedCall[P, None], *args: P.args, **kwargs: P.kwargs
279
+ ) -> VersionedResult[StreamResponse]: ...
280
+
281
+ @overload
282
+ def wrapped_stream(
283
+ self: VersionedCall[P, FormattableT], *args: P.args, **kwargs: P.kwargs
284
+ ) -> VersionedResult[StreamResponse[FormattableT]]: ...
252
285
 
253
286
  def wrapped_stream(
254
287
  self, *args: P.args, **kwargs: P.kwargs
255
- ) -> VersionedResult[StreamResponse | StreamResponse[FormattableT]]:
288
+ ) -> (
289
+ VersionedResult[StreamResponse] | VersionedResult[StreamResponse[FormattableT]]
290
+ ):
256
291
  """Stream the versioned function and return a wrapped StreamResponse."""
257
- return self.stream.wrapped(*args, **kwargs)
292
+ return cast(
293
+ "VersionedResult[StreamResponse] | VersionedResult[StreamResponse[FormattableT]]",
294
+ self.stream.wrapped(*args, **kwargs),
295
+ )
258
296
 
259
297
 
260
298
  @dataclass(kw_only=True)
@@ -308,23 +346,65 @@ class VersionedAsyncCall(_BaseVersionedCall, Generic[P, FormattableT]):
308
346
  )
309
347
  self._compute_closure(self._call, self.call, self.stream)
310
348
 
349
+ @overload
350
+ async def __call__(
351
+ self: VersionedAsyncCall[P, None], *args: P.args, **kwargs: P.kwargs
352
+ ) -> AsyncResponse: ...
353
+
354
+ @overload
355
+ async def __call__(
356
+ self: VersionedAsyncCall[P, FormattableT], *args: P.args, **kwargs: P.kwargs
357
+ ) -> AsyncResponse[FormattableT]: ...
358
+
311
359
  async def __call__(
312
360
  self, *args: P.args, **kwargs: P.kwargs
313
361
  ) -> AsyncResponse | AsyncResponse[FormattableT]:
314
362
  """Call the versioned function and return AsyncResponse directly."""
315
363
  return await self.call(*args, **kwargs)
316
364
 
365
+ @overload
366
+ async def wrapped(
367
+ self: VersionedAsyncCall[P, None], *args: P.args, **kwargs: P.kwargs
368
+ ) -> AsyncVersionedResult[AsyncResponse]: ...
369
+
370
+ @overload
371
+ async def wrapped(
372
+ self: VersionedAsyncCall[P, FormattableT], *args: P.args, **kwargs: P.kwargs
373
+ ) -> AsyncVersionedResult[AsyncResponse[FormattableT]]: ...
374
+
317
375
  async def wrapped(
318
376
  self, *args: P.args, **kwargs: P.kwargs
319
- ) -> AsyncVersionedResult[AsyncResponse | AsyncResponse[FormattableT]]:
377
+ ) -> (
378
+ AsyncVersionedResult[AsyncResponse]
379
+ | AsyncVersionedResult[AsyncResponse[FormattableT]]
380
+ ):
320
381
  """Call the versioned function and return a wrapped Response."""
321
- return await self.call.wrapped(*args, **kwargs)
382
+ return cast(
383
+ "AsyncVersionedResult[AsyncResponse] | AsyncVersionedResult[AsyncResponse[FormattableT]]",
384
+ await self.call.wrapped(*args, **kwargs),
385
+ )
386
+
387
+ @overload
388
+ async def wrapped_stream(
389
+ self: VersionedAsyncCall[P, None], *args: P.args, **kwargs: P.kwargs
390
+ ) -> AsyncVersionedResult[AsyncStreamResponse]: ...
391
+
392
+ @overload
393
+ async def wrapped_stream(
394
+ self: VersionedAsyncCall[P, FormattableT], *args: P.args, **kwargs: P.kwargs
395
+ ) -> AsyncVersionedResult[AsyncStreamResponse[FormattableT]]: ...
322
396
 
323
397
  async def wrapped_stream(
324
398
  self, *args: P.args, **kwargs: P.kwargs
325
- ) -> AsyncVersionedResult[AsyncStreamResponse | AsyncStreamResponse[FormattableT]]:
399
+ ) -> (
400
+ AsyncVersionedResult[AsyncStreamResponse]
401
+ | AsyncVersionedResult[AsyncStreamResponse[FormattableT]]
402
+ ):
326
403
  """Stream the versioned function and return a wrapped StreamResponse."""
327
- return await self.stream.wrapped(*args, **kwargs)
404
+ return cast(
405
+ "AsyncVersionedResult[AsyncStreamResponse] | AsyncVersionedResult[AsyncStreamResponse[FormattableT]]",
406
+ await self.stream.wrapped(*args, **kwargs),
407
+ )
328
408
 
329
409
 
330
410
  @dataclass(kw_only=True)
@@ -381,18 +461,66 @@ class VersionedContextCall(_BaseVersionedCall, Generic[P, DepsT, FormattableT]):
381
461
  )
382
462
  self._compute_closure(self._call, self._call_versioned, self._stream_versioned)
383
463
 
464
+ @overload
465
+ def call(
466
+ self: VersionedContextCall[P, DepsT, None],
467
+ ctx: Context[DepsT],
468
+ *args: P.args,
469
+ **kwargs: P.kwargs,
470
+ ) -> ContextResponse[DepsT, None]: ...
471
+
472
+ @overload
473
+ def call(
474
+ self: VersionedContextCall[P, DepsT, FormattableT],
475
+ ctx: Context[DepsT],
476
+ *args: P.args,
477
+ **kwargs: P.kwargs,
478
+ ) -> ContextResponse[DepsT, FormattableT]: ...
479
+
384
480
  def call(
385
481
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
386
482
  ) -> ContextResponse[DepsT, None] | ContextResponse[DepsT, FormattableT]:
387
483
  """Call the versioned function and return ContextResponse directly."""
388
484
  return self._call_versioned(ctx, *args, **kwargs)
389
485
 
486
+ @overload
487
+ def __call__(
488
+ self: VersionedContextCall[P, DepsT, None],
489
+ ctx: Context[DepsT],
490
+ *args: P.args,
491
+ **kwargs: P.kwargs,
492
+ ) -> ContextResponse[DepsT, None]: ...
493
+
494
+ @overload
495
+ def __call__(
496
+ self: VersionedContextCall[P, DepsT, FormattableT],
497
+ ctx: Context[DepsT],
498
+ *args: P.args,
499
+ **kwargs: P.kwargs,
500
+ ) -> ContextResponse[DepsT, FormattableT]: ...
501
+
390
502
  def __call__(
391
503
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
392
504
  ) -> ContextResponse[DepsT, None] | ContextResponse[DepsT, FormattableT]:
393
505
  """Call the versioned function and return ContextResponse directly."""
394
506
  return self.call(ctx, *args, **kwargs)
395
507
 
508
+ @overload
509
+ def stream(
510
+ self: VersionedContextCall[P, DepsT, None],
511
+ ctx: Context[DepsT],
512
+ *args: P.args,
513
+ **kwargs: P.kwargs,
514
+ ) -> ContextStreamResponse[DepsT, None]: ...
515
+
516
+ @overload
517
+ def stream(
518
+ self: VersionedContextCall[P, DepsT, FormattableT],
519
+ ctx: Context[DepsT],
520
+ *args: P.args,
521
+ **kwargs: P.kwargs,
522
+ ) -> ContextStreamResponse[DepsT, FormattableT]: ...
523
+
396
524
  def stream(
397
525
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
398
526
  ) -> (
@@ -401,6 +529,22 @@ class VersionedContextCall(_BaseVersionedCall, Generic[P, DepsT, FormattableT]):
401
529
  """Stream the versioned function and return a ContextStreamResponse."""
402
530
  return self._stream_versioned(ctx, *args, **kwargs)
403
531
 
532
+ @overload
533
+ def wrapped(
534
+ self: VersionedContextCall[P, DepsT, None],
535
+ ctx: Context[DepsT],
536
+ *args: P.args,
537
+ **kwargs: P.kwargs,
538
+ ) -> VersionedResult[ContextResponse[DepsT, None]]: ...
539
+
540
+ @overload
541
+ def wrapped(
542
+ self: VersionedContextCall[P, DepsT, FormattableT],
543
+ ctx: Context[DepsT],
544
+ *args: P.args,
545
+ **kwargs: P.kwargs,
546
+ ) -> VersionedResult[ContextResponse[DepsT, FormattableT]]: ...
547
+
404
548
  def wrapped(
405
549
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
406
550
  ) -> VersionedResult[
@@ -409,6 +553,22 @@ class VersionedContextCall(_BaseVersionedCall, Generic[P, DepsT, FormattableT]):
409
553
  """Call the versioned function and return a wrapped Response."""
410
554
  return self._call_versioned.wrapped(ctx, *args, **kwargs)
411
555
 
556
+ @overload
557
+ def wrapped_stream(
558
+ self: VersionedContextCall[P, DepsT, None],
559
+ ctx: Context[DepsT],
560
+ *args: P.args,
561
+ **kwargs: P.kwargs,
562
+ ) -> VersionedResult[ContextStreamResponse[DepsT, None]]: ...
563
+
564
+ @overload
565
+ def wrapped_stream(
566
+ self: VersionedContextCall[P, DepsT, FormattableT],
567
+ ctx: Context[DepsT],
568
+ *args: P.args,
569
+ **kwargs: P.kwargs,
570
+ ) -> VersionedResult[ContextStreamResponse[DepsT, FormattableT]]: ...
571
+
412
572
  def wrapped_stream(
413
573
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
414
574
  ) -> VersionedResult[
@@ -473,18 +633,66 @@ class VersionedAsyncContextCall(_BaseVersionedCall, Generic[P, DepsT, Formattabl
473
633
  )
474
634
  self._compute_closure(self._call, self._call_versioned, self._stream_versioned)
475
635
 
636
+ @overload
637
+ async def call(
638
+ self: VersionedAsyncContextCall[P, DepsT, None],
639
+ ctx: Context[DepsT],
640
+ *args: P.args,
641
+ **kwargs: P.kwargs,
642
+ ) -> AsyncContextResponse[DepsT, None]: ...
643
+
644
+ @overload
645
+ async def call(
646
+ self: VersionedAsyncContextCall[P, DepsT, FormattableT],
647
+ ctx: Context[DepsT],
648
+ *args: P.args,
649
+ **kwargs: P.kwargs,
650
+ ) -> AsyncContextResponse[DepsT, FormattableT]: ...
651
+
476
652
  async def call(
477
653
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
478
654
  ) -> AsyncContextResponse[DepsT, None] | AsyncContextResponse[DepsT, FormattableT]:
479
655
  """Call the versioned function and return AsyncContextResponse directly."""
480
656
  return await self._call_versioned(ctx, *args, **kwargs)
481
657
 
658
+ @overload
659
+ async def __call__(
660
+ self: VersionedAsyncContextCall[P, DepsT, None],
661
+ ctx: Context[DepsT],
662
+ *args: P.args,
663
+ **kwargs: P.kwargs,
664
+ ) -> AsyncContextResponse[DepsT, None]: ...
665
+
666
+ @overload
667
+ async def __call__(
668
+ self: VersionedAsyncContextCall[P, DepsT, FormattableT],
669
+ ctx: Context[DepsT],
670
+ *args: P.args,
671
+ **kwargs: P.kwargs,
672
+ ) -> AsyncContextResponse[DepsT, FormattableT]: ...
673
+
482
674
  async def __call__(
483
675
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
484
676
  ) -> AsyncContextResponse[DepsT, None] | AsyncContextResponse[DepsT, FormattableT]:
485
677
  """Call the versioned function and return AsyncContextResponse directly."""
486
678
  return await self.call(ctx, *args, **kwargs)
487
679
 
680
+ @overload
681
+ async def stream(
682
+ self: VersionedAsyncContextCall[P, DepsT, None],
683
+ ctx: Context[DepsT],
684
+ *args: P.args,
685
+ **kwargs: P.kwargs,
686
+ ) -> AsyncContextStreamResponse[DepsT, None]: ...
687
+
688
+ @overload
689
+ async def stream(
690
+ self: VersionedAsyncContextCall[P, DepsT, FormattableT],
691
+ ctx: Context[DepsT],
692
+ *args: P.args,
693
+ **kwargs: P.kwargs,
694
+ ) -> AsyncContextStreamResponse[DepsT, FormattableT]: ...
695
+
488
696
  async def stream(
489
697
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
490
698
  ) -> (
@@ -494,6 +702,22 @@ class VersionedAsyncContextCall(_BaseVersionedCall, Generic[P, DepsT, Formattabl
494
702
  """Stream the versioned function and return an AsyncContextStreamResponse."""
495
703
  return await self._stream_versioned(ctx, *args, **kwargs)
496
704
 
705
+ @overload
706
+ async def wrapped(
707
+ self: VersionedAsyncContextCall[P, DepsT, None],
708
+ ctx: Context[DepsT],
709
+ *args: P.args,
710
+ **kwargs: P.kwargs,
711
+ ) -> AsyncVersionedResult[AsyncContextResponse[DepsT, None]]: ...
712
+
713
+ @overload
714
+ async def wrapped(
715
+ self: VersionedAsyncContextCall[P, DepsT, FormattableT],
716
+ ctx: Context[DepsT],
717
+ *args: P.args,
718
+ **kwargs: P.kwargs,
719
+ ) -> AsyncVersionedResult[AsyncContextResponse[DepsT, FormattableT]]: ...
720
+
497
721
  async def wrapped(
498
722
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
499
723
  ) -> AsyncVersionedResult[
@@ -502,6 +726,22 @@ class VersionedAsyncContextCall(_BaseVersionedCall, Generic[P, DepsT, Formattabl
502
726
  """Call the versioned function and return a wrapped Response."""
503
727
  return await self._call_versioned.wrapped(ctx, *args, **kwargs)
504
728
 
729
+ @overload
730
+ async def wrapped_stream(
731
+ self: VersionedAsyncContextCall[P, DepsT, None],
732
+ ctx: Context[DepsT],
733
+ *args: P.args,
734
+ **kwargs: P.kwargs,
735
+ ) -> AsyncVersionedResult[AsyncContextStreamResponse[DepsT, None]]: ...
736
+
737
+ @overload
738
+ async def wrapped_stream(
739
+ self: VersionedAsyncContextCall[P, DepsT, FormattableT],
740
+ ctx: Context[DepsT],
741
+ *args: P.args,
742
+ **kwargs: P.kwargs,
743
+ ) -> AsyncVersionedResult[AsyncContextStreamResponse[DepsT, FormattableT]]: ...
744
+
505
745
  async def wrapped_stream(
506
746
  self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
507
747
  ) -> AsyncVersionedResult[
@@ -270,6 +270,7 @@ class VersionedFunction(_BaseVersionedFunction[P, R], BaseSyncTracedFunction[P,
270
270
  signature=self.closure.signature,
271
271
  signature_hash=self.closure.signature_hash,
272
272
  name=self.name or self.closure.name,
273
+ language="python",
273
274
  description=self.closure.docstring,
274
275
  tags=list(self.tags) if self.tags else None,
275
276
  metadata=cast(dict[str, str | None], self.metadata)
@@ -344,6 +345,7 @@ class AsyncVersionedFunction(
344
345
  signature=self.closure.signature,
345
346
  signature_hash=self.closure.signature_hash,
346
347
  name=self.name or self.closure.name,
348
+ language="python",
347
349
  description=self.closure.docstring,
348
350
  tags=list(self.tags) if self.tags else None,
349
351
  metadata=cast(dict[str, str | None], self.metadata)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mirascope
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Every frontier LLM. One unified interface.
5
5
  Project-URL: Homepage, https://mirascope.com
6
6
  Project-URL: Documentation, https://mirascope.com/docs/mirascope/v2