mirascope 2.0.0a4__py3-none-any.whl → 2.0.0a6__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/__init__.py +10 -1
- mirascope/_stubs.py +363 -0
- mirascope/api/__init__.py +8 -0
- mirascope/api/_generated/__init__.py +119 -1
- mirascope/api/_generated/annotations/__init__.py +33 -0
- mirascope/api/_generated/annotations/client.py +474 -0
- mirascope/api/_generated/annotations/raw_client.py +1095 -0
- mirascope/api/_generated/annotations/types/__init__.py +31 -0
- mirascope/api/_generated/annotations/types/annotations_create_request_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_create_response.py +35 -0
- mirascope/api/_generated/annotations/types/annotations_create_response_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_get_response.py +35 -0
- mirascope/api/_generated/annotations/types/annotations_get_response_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_list_request_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_list_response.py +21 -0
- mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item.py +35 -0
- mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_update_request_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_update_response.py +35 -0
- mirascope/api/_generated/annotations/types/annotations_update_response_label.py +5 -0
- mirascope/api/_generated/api_keys/__init__.py +7 -0
- mirascope/api/_generated/api_keys/client.py +429 -0
- mirascope/api/_generated/api_keys/raw_client.py +788 -0
- mirascope/api/_generated/api_keys/types/__init__.py +9 -0
- mirascope/api/_generated/api_keys/types/api_keys_create_response.py +28 -0
- mirascope/api/_generated/api_keys/types/api_keys_get_response.py +27 -0
- mirascope/api/_generated/api_keys/types/api_keys_list_response_item.py +27 -0
- mirascope/api/_generated/client.py +12 -0
- mirascope/api/_generated/core/client_wrapper.py +2 -14
- mirascope/api/_generated/core/datetime_utils.py +1 -3
- mirascope/api/_generated/core/file.py +2 -5
- mirascope/api/_generated/core/http_client.py +36 -112
- mirascope/api/_generated/core/jsonable_encoder.py +1 -3
- mirascope/api/_generated/core/pydantic_utilities.py +19 -74
- mirascope/api/_generated/core/query_encoder.py +1 -3
- mirascope/api/_generated/core/serialization.py +4 -10
- mirascope/api/_generated/docs/client.py +2 -6
- mirascope/api/_generated/docs/raw_client.py +4 -20
- mirascope/api/_generated/environments/__init__.py +17 -0
- mirascope/api/_generated/environments/client.py +500 -0
- mirascope/api/_generated/environments/raw_client.py +999 -0
- mirascope/api/_generated/environments/types/__init__.py +15 -0
- mirascope/api/_generated/environments/types/environments_create_response.py +24 -0
- mirascope/api/_generated/environments/types/environments_get_response.py +24 -0
- mirascope/api/_generated/environments/types/environments_list_response_item.py +24 -0
- mirascope/api/_generated/environments/types/environments_update_response.py +24 -0
- mirascope/api/_generated/errors/__init__.py +2 -0
- mirascope/api/_generated/errors/bad_request_error.py +1 -5
- mirascope/api/_generated/errors/conflict_error.py +1 -5
- mirascope/api/_generated/errors/forbidden_error.py +1 -5
- mirascope/api/_generated/errors/internal_server_error.py +1 -6
- mirascope/api/_generated/errors/not_found_error.py +1 -5
- mirascope/api/_generated/errors/unauthorized_error.py +11 -0
- mirascope/api/_generated/functions/__init__.py +29 -0
- mirascope/api/_generated/functions/client.py +433 -0
- mirascope/api/_generated/functions/raw_client.py +1049 -0
- mirascope/api/_generated/functions/types/__init__.py +29 -0
- mirascope/api/_generated/functions/types/functions_create_request_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_create_response.py +37 -0
- mirascope/api/_generated/functions/types/functions_create_response_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_find_by_hash_response.py +39 -0
- mirascope/api/_generated/functions/types/functions_find_by_hash_response_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_get_response.py +37 -0
- mirascope/api/_generated/functions/types/functions_get_response_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_list_response.py +21 -0
- mirascope/api/_generated/functions/types/functions_list_response_functions_item.py +41 -0
- mirascope/api/_generated/functions/types/functions_list_response_functions_item_dependencies_value.py +20 -0
- mirascope/api/_generated/health/client.py +2 -6
- mirascope/api/_generated/health/raw_client.py +5 -23
- mirascope/api/_generated/health/types/health_check_response.py +1 -3
- mirascope/api/_generated/organizations/__init__.py +2 -0
- mirascope/api/_generated/organizations/client.py +94 -27
- mirascope/api/_generated/organizations/raw_client.py +246 -128
- mirascope/api/_generated/organizations/types/__init__.py +2 -0
- mirascope/api/_generated/organizations/types/organizations_create_response.py +5 -3
- mirascope/api/_generated/organizations/types/organizations_create_response_role.py +1 -3
- mirascope/api/_generated/organizations/types/organizations_credits_response.py +19 -0
- mirascope/api/_generated/organizations/types/organizations_get_response.py +5 -3
- mirascope/api/_generated/organizations/types/organizations_get_response_role.py +1 -3
- mirascope/api/_generated/organizations/types/organizations_list_response_item.py +5 -3
- mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +1 -3
- mirascope/api/_generated/organizations/types/organizations_update_response.py +5 -3
- mirascope/api/_generated/organizations/types/organizations_update_response_role.py +1 -3
- mirascope/api/_generated/projects/__init__.py +2 -12
- mirascope/api/_generated/projects/client.py +38 -68
- mirascope/api/_generated/projects/raw_client.py +92 -163
- mirascope/api/_generated/projects/types/__init__.py +1 -6
- mirascope/api/_generated/projects/types/projects_create_response.py +4 -9
- mirascope/api/_generated/projects/types/projects_get_response.py +4 -9
- mirascope/api/_generated/projects/types/projects_list_response_item.py +4 -9
- mirascope/api/_generated/projects/types/projects_update_response.py +4 -9
- mirascope/api/_generated/reference.md +1862 -70
- mirascope/api/_generated/traces/__init__.py +22 -0
- mirascope/api/_generated/traces/client.py +398 -0
- mirascope/api/_generated/traces/raw_client.py +902 -18
- mirascope/api/_generated/traces/types/__init__.py +32 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +4 -11
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +1 -3
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +8 -24
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +3 -9
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +3 -9
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +4 -8
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +8 -24
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +3 -9
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value_values_item.py +1 -3
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +6 -18
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +3 -9
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +8 -24
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value_values_item.py +1 -3
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +2 -6
- mirascope/api/_generated/traces/types/traces_create_response.py +2 -5
- mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +3 -9
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response.py +54 -0
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_functions_item.py +24 -0
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_models_item.py +22 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_response.py +33 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_response_spans_item.py +90 -0
- mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item_operator.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_request_sort_by.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_request_sort_order.py +5 -0
- mirascope/api/_generated/traces/types/traces_search_response.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_response_spans_item.py +41 -0
- mirascope/api/_generated/types/__init__.py +18 -0
- mirascope/api/_generated/types/already_exists_error.py +1 -3
- mirascope/api/_generated/types/click_house_error.py +22 -0
- mirascope/api/_generated/types/database_error.py +1 -3
- mirascope/api/_generated/types/http_api_decode_error.py +1 -3
- mirascope/api/_generated/types/internal_server_error_body.py +49 -0
- mirascope/api/_generated/types/issue.py +1 -3
- mirascope/api/_generated/types/issue_tag.py +1 -8
- mirascope/api/_generated/types/not_found_error_body.py +1 -3
- mirascope/api/_generated/types/number_from_string.py +3 -0
- mirascope/api/_generated/types/permission_denied_error.py +1 -3
- mirascope/api/_generated/types/permission_denied_error_tag.py +1 -3
- mirascope/api/_generated/types/property_key_key.py +1 -3
- mirascope/api/_generated/types/stripe_error.py +20 -0
- mirascope/api/_generated/types/unauthorized_error_body.py +21 -0
- mirascope/api/_generated/types/unauthorized_error_tag.py +5 -0
- mirascope/llm/__init__.py +6 -2
- mirascope/llm/content/tool_call.py +6 -0
- mirascope/llm/exceptions.py +28 -0
- mirascope/llm/formatting/__init__.py +2 -2
- mirascope/llm/formatting/format.py +120 -8
- mirascope/llm/formatting/types.py +1 -56
- mirascope/llm/mcp/__init__.py +2 -2
- mirascope/llm/mcp/mcp_client.py +130 -0
- mirascope/llm/providers/__init__.py +26 -5
- mirascope/llm/providers/anthropic/__init__.py +3 -21
- mirascope/llm/providers/anthropic/_utils/__init__.py +2 -0
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +4 -2
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +13 -12
- mirascope/llm/providers/anthropic/_utils/decode.py +4 -2
- mirascope/llm/providers/anthropic/_utils/encode.py +57 -14
- mirascope/llm/providers/anthropic/_utils/errors.py +46 -0
- mirascope/llm/providers/anthropic/beta_provider.py +6 -0
- mirascope/llm/providers/anthropic/provider.py +5 -0
- mirascope/llm/providers/base/__init__.py +5 -2
- mirascope/llm/providers/base/_utils.py +2 -7
- mirascope/llm/providers/base/base_provider.py +173 -58
- mirascope/llm/providers/base/params.py +63 -34
- mirascope/llm/providers/google/__init__.py +2 -17
- mirascope/llm/providers/google/_utils/__init__.py +2 -0
- mirascope/llm/providers/google/_utils/decode.py +17 -8
- mirascope/llm/providers/google/_utils/encode.py +105 -16
- mirascope/llm/providers/google/_utils/errors.py +49 -0
- mirascope/llm/providers/google/model_info.py +1 -0
- mirascope/llm/providers/google/provider.py +9 -5
- mirascope/llm/providers/mirascope/__init__.py +5 -0
- mirascope/llm/providers/mirascope/_utils.py +77 -0
- mirascope/llm/providers/mirascope/provider.py +318 -0
- mirascope/llm/providers/mlx/__init__.py +2 -17
- mirascope/llm/providers/mlx/_utils.py +9 -2
- mirascope/llm/providers/mlx/provider.py +8 -0
- mirascope/llm/providers/ollama/__init__.py +1 -13
- mirascope/llm/providers/openai/__init__.py +10 -1
- mirascope/llm/providers/openai/_utils/__init__.py +5 -0
- mirascope/llm/providers/openai/_utils/errors.py +46 -0
- mirascope/llm/providers/openai/completions/__init__.py +2 -20
- mirascope/llm/providers/openai/completions/_utils/decode.py +14 -3
- mirascope/llm/providers/openai/completions/_utils/encode.py +15 -12
- mirascope/llm/providers/openai/completions/base_provider.py +6 -6
- mirascope/llm/providers/openai/provider.py +14 -1
- mirascope/llm/providers/openai/responses/__init__.py +1 -17
- mirascope/llm/providers/openai/responses/_utils/decode.py +2 -2
- mirascope/llm/providers/openai/responses/_utils/encode.py +43 -15
- mirascope/llm/providers/openai/responses/provider.py +13 -7
- mirascope/llm/providers/provider_id.py +1 -0
- mirascope/llm/providers/provider_registry.py +59 -3
- mirascope/llm/providers/together/__init__.py +1 -13
- mirascope/llm/responses/base_stream_response.py +24 -20
- mirascope/llm/tools/decorator.py +8 -4
- mirascope/llm/tools/tool_schema.py +33 -6
- mirascope/llm/tools/tools.py +84 -16
- mirascope/ops/__init__.py +60 -109
- mirascope/ops/_internal/closure.py +62 -11
- mirascope/ops/_internal/instrumentation/llm/llm.py +1 -2
- mirascope/ops/_internal/traced_functions.py +23 -4
- mirascope/ops/_internal/versioned_functions.py +54 -43
- {mirascope-2.0.0a4.dist-info → mirascope-2.0.0a6.dist-info}/METADATA +7 -7
- mirascope-2.0.0a6.dist-info/RECORD +316 -0
- mirascope/llm/formatting/_utils.py +0 -78
- mirascope/llm/mcp/client.py +0 -118
- mirascope/llm/providers/_missing_import_stubs.py +0 -49
- mirascope/llm/providers/load_provider.py +0 -54
- mirascope-2.0.0a4.dist-info/RECORD +0 -247
- {mirascope-2.0.0a4.dist-info → mirascope-2.0.0a6.dist-info}/WHEEL +0 -0
- {mirascope-2.0.0a4.dist-info → mirascope-2.0.0a6.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
-
from collections.abc import Sequence
|
|
7
|
-
from
|
|
6
|
+
from collections.abc import Callable, Generator, Mapping, Sequence
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeAlias, cast, overload
|
|
8
9
|
from typing_extensions import TypeVar, Unpack
|
|
9
10
|
|
|
10
11
|
from ...context import Context, DepsT
|
|
12
|
+
from ...exceptions import APIError, MirascopeLLMError
|
|
11
13
|
from ...formatting import Format, FormattableT
|
|
12
14
|
from ...messages import Message, UserContent, user
|
|
13
15
|
from ...responses import (
|
|
16
|
+
AsyncChunkIterator,
|
|
14
17
|
AsyncContextResponse,
|
|
15
18
|
AsyncContextStreamResponse,
|
|
16
19
|
AsyncResponse,
|
|
17
20
|
AsyncStreamResponse,
|
|
21
|
+
ChunkIterator,
|
|
18
22
|
ContextResponse,
|
|
19
23
|
ContextStreamResponse,
|
|
20
24
|
Response,
|
|
@@ -33,6 +37,7 @@ from ...tools import (
|
|
|
33
37
|
from .params import Params
|
|
34
38
|
|
|
35
39
|
if TYPE_CHECKING:
|
|
40
|
+
from ...exceptions import MirascopeLLMError
|
|
36
41
|
from ..provider_id import ProviderId
|
|
37
42
|
|
|
38
43
|
ProviderClientT = TypeVar("ProviderClientT")
|
|
@@ -40,6 +45,18 @@ ProviderClientT = TypeVar("ProviderClientT")
|
|
|
40
45
|
Provider: TypeAlias = "BaseProvider[Any]"
|
|
41
46
|
"""Type alias for `BaseProvider` with any client type."""
|
|
42
47
|
|
|
48
|
+
ProviderErrorMap: TypeAlias = Mapping[
|
|
49
|
+
type[Exception],
|
|
50
|
+
"type[MirascopeLLMError] | Callable[[Exception], type[MirascopeLLMError]]",
|
|
51
|
+
]
|
|
52
|
+
"""Mapping from provider SDK exceptions to Mirascope error types.
|
|
53
|
+
|
|
54
|
+
Keys are provider SDK exception types (e.g., OpenAIError, AnthropicError).
|
|
55
|
+
Values can be:
|
|
56
|
+
- Error type: Simple 1:1 mapping (e.g., RateLimitError)
|
|
57
|
+
- Callable: Transform function returning error type based on exception details
|
|
58
|
+
"""
|
|
59
|
+
|
|
43
60
|
|
|
44
61
|
class BaseProvider(Generic[ProviderClientT], ABC):
|
|
45
62
|
"""Base abstract provider for LLM interactions.
|
|
@@ -59,8 +76,67 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
59
76
|
- ["anthropic/", "openai/"] - Multiple scopes (e.g., for AWS Bedrock)
|
|
60
77
|
"""
|
|
61
78
|
|
|
79
|
+
error_map: ClassVar[ProviderErrorMap]
|
|
80
|
+
"""Mapping from provider SDK exceptions to Mirascope error types.
|
|
81
|
+
|
|
82
|
+
Values can be:
|
|
83
|
+
- Error type: Simple 1:1 mapping (e.g., AnthropicRateLimitError -> RateLimitError)
|
|
84
|
+
- Callable: Transform function returning error type based on exception details
|
|
85
|
+
(e.g., lambda e: NotFoundError if e.code == "model_not_found" else BadRequestError)
|
|
86
|
+
|
|
87
|
+
The mapping is walked via the exception's MRO, allowing both specific error handling
|
|
88
|
+
and fallback to base SDK error types (e.g., AnthropicError -> APIError).
|
|
89
|
+
"""
|
|
90
|
+
|
|
62
91
|
client: ProviderClientT
|
|
63
92
|
|
|
93
|
+
@contextmanager
|
|
94
|
+
def _wrap_errors(self) -> Generator[None, None, None]:
|
|
95
|
+
"""Wrap provider API calls and convert errors to Mirascope exceptions.
|
|
96
|
+
|
|
97
|
+
Walks the exception's MRO to find the first matching error type in the
|
|
98
|
+
provider's error_map, allowing both specific error handling and fallback
|
|
99
|
+
to base SDK error types (e.g., AnthropicError -> APIError).
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
yield
|
|
103
|
+
except Exception as e:
|
|
104
|
+
# Walk MRO to find first matching error type in provider's error_map
|
|
105
|
+
for error_class in type(e).__mro__:
|
|
106
|
+
if error_class in self.error_map:
|
|
107
|
+
error_type_or_fn = self.error_map[error_class]
|
|
108
|
+
|
|
109
|
+
if isinstance(error_type_or_fn, type):
|
|
110
|
+
error_type = cast(type[MirascopeLLMError], error_type_or_fn)
|
|
111
|
+
else:
|
|
112
|
+
error_type = error_type_or_fn(e)
|
|
113
|
+
|
|
114
|
+
# Construct Mirascope error with metadata
|
|
115
|
+
error: MirascopeLLMError = error_type(str(e))
|
|
116
|
+
if isinstance(error, APIError):
|
|
117
|
+
error.status_code = self.get_error_status(e)
|
|
118
|
+
error.provider = self.id
|
|
119
|
+
error.original_exception = e
|
|
120
|
+
raise error from e
|
|
121
|
+
|
|
122
|
+
# Not in error_map - not a provider error, re-raise as-is
|
|
123
|
+
raise
|
|
124
|
+
|
|
125
|
+
def _wrap_iterator_errors(self, iterator: ChunkIterator) -> ChunkIterator:
|
|
126
|
+
"""Wrap sync chunk iterator to handle errors during iteration."""
|
|
127
|
+
# TODO: Consider moving this logic into BaseSyncStreamResponse if appropriate.
|
|
128
|
+
with self._wrap_errors():
|
|
129
|
+
yield from iterator
|
|
130
|
+
|
|
131
|
+
async def _wrap_async_iterator_errors(
|
|
132
|
+
self, iterator: AsyncChunkIterator
|
|
133
|
+
) -> AsyncChunkIterator:
|
|
134
|
+
"""Wrap async chunk iterator to handle errors during iteration."""
|
|
135
|
+
# TODO: Consider moving this logic into BaseAsyncStreamResponse if appropriate.
|
|
136
|
+
with self._wrap_errors():
|
|
137
|
+
async for chunk in iterator:
|
|
138
|
+
yield chunk
|
|
139
|
+
|
|
64
140
|
@overload
|
|
65
141
|
def call(
|
|
66
142
|
self,
|
|
@@ -121,13 +197,14 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
121
197
|
Returns:
|
|
122
198
|
An `llm.Response` object containing the LLM-generated content.
|
|
123
199
|
"""
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
200
|
+
with self._wrap_errors():
|
|
201
|
+
return self._call(
|
|
202
|
+
model_id=model_id,
|
|
203
|
+
messages=messages,
|
|
204
|
+
tools=tools,
|
|
205
|
+
format=format,
|
|
206
|
+
**params,
|
|
207
|
+
)
|
|
131
208
|
|
|
132
209
|
@abstractmethod
|
|
133
210
|
def _call(
|
|
@@ -215,14 +292,15 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
215
292
|
Returns:
|
|
216
293
|
An `llm.ContextResponse` object containing the LLM-generated content.
|
|
217
294
|
"""
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
295
|
+
with self._wrap_errors():
|
|
296
|
+
return self._context_call(
|
|
297
|
+
ctx=ctx,
|
|
298
|
+
model_id=model_id,
|
|
299
|
+
messages=messages,
|
|
300
|
+
tools=tools,
|
|
301
|
+
format=format,
|
|
302
|
+
**params,
|
|
303
|
+
)
|
|
226
304
|
|
|
227
305
|
@abstractmethod
|
|
228
306
|
def _context_call(
|
|
@@ -300,13 +378,14 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
300
378
|
Returns:
|
|
301
379
|
An `llm.AsyncResponse` object containing the LLM-generated content.
|
|
302
380
|
"""
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
381
|
+
with self._wrap_errors():
|
|
382
|
+
return await self._call_async(
|
|
383
|
+
model_id=model_id,
|
|
384
|
+
messages=messages,
|
|
385
|
+
tools=tools,
|
|
386
|
+
format=format,
|
|
387
|
+
**params,
|
|
388
|
+
)
|
|
310
389
|
|
|
311
390
|
@abstractmethod
|
|
312
391
|
async def _call_async(
|
|
@@ -394,14 +473,15 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
394
473
|
Returns:
|
|
395
474
|
An `llm.AsyncContextResponse` object containing the LLM-generated content.
|
|
396
475
|
"""
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
476
|
+
with self._wrap_errors():
|
|
477
|
+
return await self._context_call_async(
|
|
478
|
+
ctx=ctx,
|
|
479
|
+
model_id=model_id,
|
|
480
|
+
messages=messages,
|
|
481
|
+
tools=tools,
|
|
482
|
+
format=format,
|
|
483
|
+
**params,
|
|
484
|
+
)
|
|
405
485
|
|
|
406
486
|
@abstractmethod
|
|
407
487
|
async def _context_call_async(
|
|
@@ -479,13 +559,18 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
479
559
|
Returns:
|
|
480
560
|
An `llm.StreamResponse` object for iterating over the LLM-generated content.
|
|
481
561
|
"""
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
562
|
+
with self._wrap_errors():
|
|
563
|
+
stream_response = self._stream(
|
|
564
|
+
model_id=model_id,
|
|
565
|
+
messages=messages,
|
|
566
|
+
tools=tools,
|
|
567
|
+
format=format,
|
|
568
|
+
**params,
|
|
569
|
+
)
|
|
570
|
+
stream_response._chunk_iterator = self._wrap_iterator_errors( # pyright: ignore[reportPrivateUsage]
|
|
571
|
+
stream_response._chunk_iterator # pyright: ignore[reportPrivateUsage]
|
|
488
572
|
)
|
|
573
|
+
return stream_response
|
|
489
574
|
|
|
490
575
|
@abstractmethod
|
|
491
576
|
def _stream(
|
|
@@ -577,14 +662,19 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
577
662
|
Returns:
|
|
578
663
|
An `llm.ContextStreamResponse` object for iterating over the LLM-generated content.
|
|
579
664
|
"""
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
665
|
+
with self._wrap_errors():
|
|
666
|
+
stream_response = self._context_stream(
|
|
667
|
+
ctx=ctx,
|
|
668
|
+
model_id=model_id,
|
|
669
|
+
messages=messages,
|
|
670
|
+
tools=tools,
|
|
671
|
+
format=format,
|
|
672
|
+
**params,
|
|
673
|
+
)
|
|
674
|
+
stream_response._chunk_iterator = self._wrap_iterator_errors( # pyright: ignore[reportPrivateUsage]
|
|
675
|
+
stream_response._chunk_iterator # pyright: ignore[reportPrivateUsage]
|
|
587
676
|
)
|
|
677
|
+
return stream_response
|
|
588
678
|
|
|
589
679
|
@abstractmethod
|
|
590
680
|
def _context_stream(
|
|
@@ -664,13 +754,18 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
664
754
|
Returns:
|
|
665
755
|
An `llm.AsyncStreamResponse` object for asynchronously iterating over the LLM-generated content.
|
|
666
756
|
"""
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
757
|
+
with self._wrap_errors():
|
|
758
|
+
stream_response = await self._stream_async(
|
|
759
|
+
model_id=model_id,
|
|
760
|
+
messages=messages,
|
|
761
|
+
tools=tools,
|
|
762
|
+
format=format,
|
|
763
|
+
**params,
|
|
764
|
+
)
|
|
765
|
+
stream_response._chunk_iterator = self._wrap_async_iterator_errors( # pyright: ignore[reportPrivateUsage]
|
|
766
|
+
stream_response._chunk_iterator # pyright: ignore[reportPrivateUsage]
|
|
673
767
|
)
|
|
768
|
+
return stream_response
|
|
674
769
|
|
|
675
770
|
@abstractmethod
|
|
676
771
|
async def _stream_async(
|
|
@@ -764,14 +859,19 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
764
859
|
Returns:
|
|
765
860
|
An `llm.AsyncContextStreamResponse` object for asynchronously iterating over the LLM-generated content.
|
|
766
861
|
"""
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
862
|
+
with self._wrap_errors():
|
|
863
|
+
stream_response = await self._context_stream_async(
|
|
864
|
+
ctx=ctx,
|
|
865
|
+
model_id=model_id,
|
|
866
|
+
messages=messages,
|
|
867
|
+
tools=tools,
|
|
868
|
+
format=format,
|
|
869
|
+
**params,
|
|
870
|
+
)
|
|
871
|
+
stream_response._chunk_iterator = self._wrap_async_iterator_errors( # pyright: ignore[reportPrivateUsage]
|
|
872
|
+
stream_response._chunk_iterator # pyright: ignore[reportPrivateUsage]
|
|
774
873
|
)
|
|
874
|
+
return stream_response
|
|
775
875
|
|
|
776
876
|
@abstractmethod
|
|
777
877
|
async def _context_stream_async(
|
|
@@ -1383,3 +1483,18 @@ class BaseProvider(Generic[ProviderClientT], ABC):
|
|
|
1383
1483
|
format=response.format,
|
|
1384
1484
|
**params,
|
|
1385
1485
|
)
|
|
1486
|
+
|
|
1487
|
+
@abstractmethod
|
|
1488
|
+
def get_error_status(self, e: Exception) -> int | None:
|
|
1489
|
+
"""Extract HTTP status code from provider-specific exception.
|
|
1490
|
+
|
|
1491
|
+
Different SDKs store status codes differently (e.g., .status_code vs .code).
|
|
1492
|
+
Each provider implements this to handle their SDK's convention.
|
|
1493
|
+
|
|
1494
|
+
Args:
|
|
1495
|
+
e: The exception to extract status code from.
|
|
1496
|
+
|
|
1497
|
+
Returns:
|
|
1498
|
+
The HTTP status code if available, None otherwise.
|
|
1499
|
+
"""
|
|
1500
|
+
...
|
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
"""Base parameters for LLM providers."""
|
|
2
2
|
|
|
3
|
-
from typing import TypedDict
|
|
3
|
+
from typing import Literal, TypedDict
|
|
4
|
+
from typing_extensions import Required
|
|
5
|
+
|
|
6
|
+
ThinkingLevel = Literal["none", "default", "minimal", "low", "medium", "high", "max"]
|
|
7
|
+
"""Level of effort/reasoning to apply to thinking."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ThinkingConfig(TypedDict, total=False):
|
|
11
|
+
"""Configuration for extended reasoning/thinking in LLM responses.
|
|
12
|
+
|
|
13
|
+
Thinking is a process where the model spends additional tokens reasoning about
|
|
14
|
+
the prompt before generating a response. Providing any `ThinkingConfig` will enable
|
|
15
|
+
thinking (unless it is specifically disabled via level="minimal"). Depending on
|
|
16
|
+
the provider and model, thinking may always be active regardless of user settings.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
level: Required[ThinkingLevel]
|
|
20
|
+
"""Level of effort/reasoning to apply to thinking.
|
|
21
|
+
|
|
22
|
+
- none: Disable thinking entirely. Minimizes cost and latency.
|
|
23
|
+
- default: Use the provider's default
|
|
24
|
+
- minimal: Use the provider's lowest setting for reasoning
|
|
25
|
+
- medium: Use a moderate amount of reasoning tokens
|
|
26
|
+
- high: Allow extensive resources for thinking
|
|
27
|
+
- max: Uses as much thinking as allowed by the provider.
|
|
28
|
+
|
|
29
|
+
Mirascope makes a best effort to apply the chosen thinking level, but exact behavior
|
|
30
|
+
varies by provider and model. For example, some models may not support thinking,
|
|
31
|
+
while other models may not allow disabling it.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
include_summaries: bool
|
|
35
|
+
"""Whether to generate reasoning summaries (human readable Thoughts) from model output.
|
|
36
|
+
|
|
37
|
+
Generally, providers do not return raw model thinking output, but may produce
|
|
38
|
+
thought summaries. When `include_summaries` is true, these will be requested from
|
|
39
|
+
the provider (if available). Otherwise, they will not be requested.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
encode_thoughts_as_text: bool
|
|
43
|
+
"""Re-encode Thought content as text for model consumption.
|
|
44
|
+
|
|
45
|
+
If `True`, when an `AssistantMessage` contains `Thoughts` and is passed back
|
|
46
|
+
to an LLM, those `Thoughts` will be encoded as `Text`, ensuring the assistant
|
|
47
|
+
can read its prior reasoning. This contrasts with provider defaults which may
|
|
48
|
+
ignore prior thoughts, particularly if tool calls are not involved.
|
|
49
|
+
|
|
50
|
+
When `True`, Mirascope will re-encode messages rather than reusing raw provider
|
|
51
|
+
response content, which may disable provider-specific optimizations like cached
|
|
52
|
+
reasoning tokens.
|
|
53
|
+
|
|
54
|
+
Defaults to `False` if unset.
|
|
55
|
+
"""
|
|
4
56
|
|
|
5
57
|
|
|
6
58
|
class Params(TypedDict, total=False):
|
|
@@ -55,39 +107,16 @@ class Params(TypedDict, total=False):
|
|
|
55
107
|
response.
|
|
56
108
|
"""
|
|
57
109
|
|
|
58
|
-
thinking:
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
Thinking is a process where the model spends additional tokens thinking about the
|
|
62
|
-
prompt before generating a response. You may configure thinking either by passing
|
|
63
|
-
a bool to enable or disable it.
|
|
64
|
-
|
|
65
|
-
If `params.thinking` is `True`, then thinking and thought summaries will be enabled
|
|
66
|
-
(if supported by the model/provider), with a default budget for thinking tokens.
|
|
67
|
-
|
|
68
|
-
If `params.thinking` is `False`, then thinking will be wholly disabled, assuming
|
|
69
|
-
the model allows this (some models, e.g. `google:gemini-2.5-pro`, do not allow
|
|
70
|
-
disabling thinking).
|
|
71
|
-
|
|
72
|
-
If `params.thinking` is unset (or `None`), then we will use provider-specific default
|
|
73
|
-
behavior for the chosen model.
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
|
-
encode_thoughts_as_text: bool
|
|
77
|
-
"""Configures whether `Thought` content should be re-encoded as text for model consumption.
|
|
78
|
-
|
|
79
|
-
If `True`, then when an `AssistantMessage` contains `Thoughts` and is being passed back
|
|
80
|
-
to an LLM, those `Thoughts` will be encoded as `Text`, so that the assistant can read
|
|
81
|
-
those thoughts. That ensures the assistant has access to (at least the summarized output of)
|
|
82
|
-
its reasoning process, and contrasts with provider default behaviors which may ignore
|
|
83
|
-
prior thoughts, particularly if tool calls are not involved.
|
|
84
|
-
|
|
85
|
-
When `True`, we will always re-encode Mirascope messages being passed to the provider,
|
|
86
|
-
rather than reusing raw provider response content. This may disable provider-specific
|
|
87
|
-
behavior like cached reasoning tokens.
|
|
110
|
+
thinking: ThinkingConfig | None
|
|
111
|
+
"""Configuration for extended reasoning/thinking.
|
|
88
112
|
|
|
89
|
-
|
|
90
|
-
is
|
|
113
|
+
Pass a `ThinkingConfig` to configure thinking behavior. The `level` field controls
|
|
114
|
+
whether thinking is enabled and how much reasoning to use. Level may be one of
|
|
115
|
+
"minimal", "low", "medium", or "high". If level is unset, then thinking is enabled
|
|
116
|
+
with a provider-specific default level.
|
|
91
117
|
|
|
92
|
-
|
|
118
|
+
`ThinkingConfig` can also include `encode_thoughts_as_text`, which is an advanced
|
|
119
|
+
feature for providing past thoughts back to the model as text content. This is
|
|
120
|
+
primarily useful for making thoughts transferable when passing a conversation
|
|
121
|
+
to a different model or provider than the one that generated the thinking.
|
|
93
122
|
"""
|
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
"""Google client implementation."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
if TYPE_CHECKING:
|
|
6
|
-
from .model_id import GoogleModelId
|
|
7
|
-
from .provider import GoogleProvider
|
|
8
|
-
else:
|
|
9
|
-
try:
|
|
10
|
-
from .model_id import GoogleModelId
|
|
11
|
-
from .provider import GoogleProvider
|
|
12
|
-
except ImportError: # pragma: no cover
|
|
13
|
-
from .._missing_import_stubs import (
|
|
14
|
-
create_import_error_stub,
|
|
15
|
-
create_provider_stub,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
GoogleProvider = create_provider_stub("google", "GoogleProvider")
|
|
19
|
-
GoogleModelId = str
|
|
3
|
+
from .model_id import GoogleModelId
|
|
4
|
+
from .provider import GoogleProvider
|
|
20
5
|
|
|
21
6
|
__all__ = ["GoogleModelId", "GoogleProvider"]
|
|
@@ -177,15 +177,19 @@ class _GoogleChunkProcessor:
|
|
|
177
177
|
if self.current_content_type == "thought" and not part.thought:
|
|
178
178
|
yield ThoughtEndChunk()
|
|
179
179
|
self.current_content_type = None
|
|
180
|
-
elif
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
elif (
|
|
181
|
+
self.current_content_type == "text" and not part.text
|
|
182
|
+
): # pragma: no cover
|
|
183
|
+
yield TextEndChunk()
|
|
184
|
+
self.current_content_type = None
|
|
185
|
+
elif (
|
|
186
|
+
self.current_content_type == "tool_call" and not part.function_call
|
|
187
|
+
): # pragma: no cover
|
|
184
188
|
# In testing, Gemini never emits tool calls and text in the same message
|
|
185
189
|
# (even when specifically asked in system and user prompt), so
|
|
186
190
|
# the following code is uncovered but included for completeness
|
|
187
|
-
yield ToolCallEndChunk()
|
|
188
|
-
self.current_content_type = None
|
|
191
|
+
yield ToolCallEndChunk(id=UNKNOWN_TOOL_ID)
|
|
192
|
+
self.current_content_type = None
|
|
189
193
|
|
|
190
194
|
if part.thought:
|
|
191
195
|
if self.current_content_type is None:
|
|
@@ -210,17 +214,22 @@ class _GoogleChunkProcessor:
|
|
|
210
214
|
"Required name missing on Google function call"
|
|
211
215
|
) # pragma: no cover
|
|
212
216
|
|
|
217
|
+
tool_id = function_call.id or UNKNOWN_TOOL_ID
|
|
218
|
+
self.current_content_type = "tool_call"
|
|
219
|
+
|
|
213
220
|
yield ToolCallStartChunk(
|
|
214
|
-
id=
|
|
221
|
+
id=tool_id,
|
|
215
222
|
name=function_call.name,
|
|
216
223
|
)
|
|
217
224
|
|
|
218
225
|
yield ToolCallChunk(
|
|
226
|
+
id=tool_id,
|
|
219
227
|
delta=json.dumps(function_call.args)
|
|
220
228
|
if function_call.args
|
|
221
229
|
else "{}",
|
|
222
230
|
)
|
|
223
|
-
yield ToolCallEndChunk()
|
|
231
|
+
yield ToolCallEndChunk(id=tool_id)
|
|
232
|
+
self.current_content_type = None
|
|
224
233
|
|
|
225
234
|
if candidate.finish_reason:
|
|
226
235
|
if self.current_content_type == "text":
|