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,9 +3,10 @@
|
|
|
3
3
|
from collections.abc import Sequence
|
|
4
4
|
from typing_extensions import Unpack
|
|
5
5
|
|
|
6
|
-
from openai import AsyncOpenAI, OpenAI
|
|
6
|
+
from openai import AsyncOpenAI, BadRequestError as OpenAIBadRequestError, OpenAI
|
|
7
7
|
|
|
8
8
|
from ....context import Context, DepsT
|
|
9
|
+
from ....exceptions import BadRequestError, NotFoundError
|
|
9
10
|
from ....formatting import Format, FormattableT
|
|
10
11
|
from ....messages import Message
|
|
11
12
|
from ....responses import (
|
|
@@ -29,6 +30,7 @@ from ....tools import (
|
|
|
29
30
|
Toolkit,
|
|
30
31
|
)
|
|
31
32
|
from ...base import BaseProvider, Params
|
|
33
|
+
from .. import _utils as _shared_utils
|
|
32
34
|
from ..model_id import OpenAIModelId, model_name
|
|
33
35
|
from . import _utils
|
|
34
36
|
|
|
@@ -38,6 +40,12 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
38
40
|
|
|
39
41
|
id = "openai:responses"
|
|
40
42
|
default_scope = "openai/"
|
|
43
|
+
error_map = {
|
|
44
|
+
**_shared_utils.OPENAI_ERROR_MAP,
|
|
45
|
+
OpenAIBadRequestError: lambda e: NotFoundError
|
|
46
|
+
if hasattr(e, "code") and e.code == "model_not_found" # pyright: ignore[reportAttributeAccessIssue,reportUnknownMemberType]
|
|
47
|
+
else BadRequestError,
|
|
48
|
+
}
|
|
41
49
|
|
|
42
50
|
def __init__(
|
|
43
51
|
self,
|
|
@@ -49,6 +57,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
49
57
|
self.client = OpenAI(api_key=api_key, base_url=base_url)
|
|
50
58
|
self.async_client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
|
51
59
|
|
|
60
|
+
def get_error_status(self, e: Exception) -> int | None:
|
|
61
|
+
"""Extract HTTP status code from OpenAI exception."""
|
|
62
|
+
return getattr(e, "status_code", None)
|
|
63
|
+
|
|
52
64
|
def _call(
|
|
53
65
|
self,
|
|
54
66
|
*,
|
|
@@ -77,7 +89,6 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
77
89
|
format=format,
|
|
78
90
|
params=params,
|
|
79
91
|
)
|
|
80
|
-
|
|
81
92
|
openai_response = self.client.responses.create(**kwargs)
|
|
82
93
|
|
|
83
94
|
assistant_message, finish_reason, usage = _utils.decode_response(
|
|
@@ -127,7 +138,6 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
127
138
|
format=format,
|
|
128
139
|
params=params,
|
|
129
140
|
)
|
|
130
|
-
|
|
131
141
|
openai_response = await self.async_client.responses.create(**kwargs)
|
|
132
142
|
|
|
133
143
|
assistant_message, finish_reason, usage = _utils.decode_response(
|
|
@@ -177,7 +187,6 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
177
187
|
format=format,
|
|
178
188
|
params=params,
|
|
179
189
|
)
|
|
180
|
-
|
|
181
190
|
openai_stream = self.client.responses.create(
|
|
182
191
|
**kwargs,
|
|
183
192
|
stream=True,
|
|
@@ -227,7 +236,6 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
227
236
|
format=format,
|
|
228
237
|
params=params,
|
|
229
238
|
)
|
|
230
|
-
|
|
231
239
|
openai_stream = await self.async_client.responses.create(
|
|
232
240
|
**kwargs,
|
|
233
241
|
stream=True,
|
|
@@ -281,7 +289,6 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
281
289
|
format=format,
|
|
282
290
|
params=params,
|
|
283
291
|
)
|
|
284
|
-
|
|
285
292
|
openai_response = self.client.responses.create(**kwargs)
|
|
286
293
|
|
|
287
294
|
assistant_message, finish_reason, usage = _utils.decode_response(
|
|
@@ -335,7 +342,6 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
|
|
|
335
342
|
format=format,
|
|
336
343
|
params=params,
|
|
337
344
|
)
|
|
338
|
-
|
|
339
345
|
openai_response = await self.async_client.responses.create(**kwargs)
|
|
340
346
|
|
|
341
347
|
assistant_message, finish_reason, usage = _utils.decode_response(
|
|
@@ -6,6 +6,7 @@ KnownProviderId: TypeAlias = Literal[
|
|
|
6
6
|
"anthropic", # Anthropic provider via AnthropicProvider
|
|
7
7
|
"anthropic-beta", # Anthropic beta provider via AnthropicBetaProvider
|
|
8
8
|
"google", # Google provider via GoogleProvider
|
|
9
|
+
"mirascope", # Mirascope Router provider via MirascopeProvider
|
|
9
10
|
"mlx", # Local inference powered by `mlx-lm`, via MLXProvider
|
|
10
11
|
"ollama", # Ollama provider via OllamaProvider
|
|
11
12
|
"openai", # OpenAI provider via OpenAIProvider (prefers Responses routing when available)
|
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
"""Provider registry for managing provider instances and scopes."""
|
|
2
2
|
|
|
3
|
+
from functools import lru_cache
|
|
3
4
|
from typing import overload
|
|
4
5
|
|
|
5
6
|
from ..exceptions import NoRegisteredProviderError
|
|
7
|
+
from .anthropic import AnthropicProvider
|
|
6
8
|
from .base import Provider
|
|
7
|
-
from .
|
|
9
|
+
from .google import GoogleProvider
|
|
10
|
+
from .mirascope import MirascopeProvider
|
|
11
|
+
from .mlx import MLXProvider
|
|
12
|
+
from .ollama import OllamaProvider
|
|
13
|
+
from .openai import OpenAIProvider
|
|
14
|
+
from .openai.completions.provider import OpenAICompletionsProvider
|
|
15
|
+
from .openai.responses.provider import OpenAIResponsesProvider
|
|
8
16
|
from .provider_id import ProviderId
|
|
17
|
+
from .together import TogetherProvider
|
|
9
18
|
|
|
10
19
|
# Global registry mapping scopes to providers
|
|
11
20
|
# Scopes are matched by prefix (longest match wins)
|
|
12
21
|
PROVIDER_REGISTRY: dict[str, Provider] = {}
|
|
13
22
|
|
|
23
|
+
|
|
24
|
+
def reset_provider_registry() -> None:
|
|
25
|
+
"""Resets the provider registry, clearing all registered providers."""
|
|
26
|
+
PROVIDER_REGISTRY.clear()
|
|
27
|
+
provider_singleton.cache_clear()
|
|
28
|
+
|
|
29
|
+
|
|
14
30
|
# Default auto-registration mapping for built-in providers
|
|
15
31
|
# These providers will be automatically registered on first use
|
|
16
32
|
DEFAULT_AUTO_REGISTER_SCOPES: dict[str, ProviderId] = {
|
|
@@ -23,6 +39,46 @@ DEFAULT_AUTO_REGISTER_SCOPES: dict[str, ProviderId] = {
|
|
|
23
39
|
}
|
|
24
40
|
|
|
25
41
|
|
|
42
|
+
@lru_cache(maxsize=256)
|
|
43
|
+
def provider_singleton(
|
|
44
|
+
provider_id: ProviderId, *, api_key: str | None = None, base_url: str | None = None
|
|
45
|
+
) -> Provider:
|
|
46
|
+
"""Create a cached provider instance for the specified provider id.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
provider_id: The provider name ("openai", "anthropic", or "google").
|
|
50
|
+
api_key: API key for authentication. If None, uses provider-specific env var.
|
|
51
|
+
base_url: Base URL for the API. If None, uses provider-specific env var.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
A cached provider instance for the specified provider with the given parameters.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
ValueError: If the provider_id is not supported.
|
|
58
|
+
"""
|
|
59
|
+
match provider_id:
|
|
60
|
+
case "anthropic":
|
|
61
|
+
return AnthropicProvider(api_key=api_key, base_url=base_url)
|
|
62
|
+
case "google":
|
|
63
|
+
return GoogleProvider(api_key=api_key, base_url=base_url)
|
|
64
|
+
case "mirascope":
|
|
65
|
+
return MirascopeProvider(api_key=api_key, base_url=base_url)
|
|
66
|
+
case "mlx": # pragma: no cover (MLX is only available on macOS)
|
|
67
|
+
return MLXProvider()
|
|
68
|
+
case "ollama":
|
|
69
|
+
return OllamaProvider(api_key=api_key, base_url=base_url)
|
|
70
|
+
case "openai":
|
|
71
|
+
return OpenAIProvider(api_key=api_key, base_url=base_url)
|
|
72
|
+
case "openai:completions":
|
|
73
|
+
return OpenAICompletionsProvider(api_key=api_key, base_url=base_url)
|
|
74
|
+
case "openai:responses":
|
|
75
|
+
return OpenAIResponsesProvider(api_key=api_key, base_url=base_url)
|
|
76
|
+
case "together":
|
|
77
|
+
return TogetherProvider(api_key=api_key, base_url=base_url)
|
|
78
|
+
case _: # pragma: no cover
|
|
79
|
+
raise ValueError(f"Unknown provider: '{provider_id}'")
|
|
80
|
+
|
|
81
|
+
|
|
26
82
|
@overload
|
|
27
83
|
def register_provider(
|
|
28
84
|
provider: Provider,
|
|
@@ -100,7 +156,7 @@ def register_provider(
|
|
|
100
156
|
"""
|
|
101
157
|
|
|
102
158
|
if isinstance(provider, str):
|
|
103
|
-
provider =
|
|
159
|
+
provider = provider_singleton(provider, api_key=api_key, base_url=base_url)
|
|
104
160
|
|
|
105
161
|
if scope is None:
|
|
106
162
|
scope = provider.default_scope
|
|
@@ -160,7 +216,7 @@ def get_provider_for_model(model_id: str) -> Provider:
|
|
|
160
216
|
if matching_defaults:
|
|
161
217
|
best_scope = max(matching_defaults, key=len)
|
|
162
218
|
provider_id = DEFAULT_AUTO_REGISTER_SCOPES[best_scope]
|
|
163
|
-
provider =
|
|
219
|
+
provider = provider_singleton(provider_id)
|
|
164
220
|
# Auto-register for future calls
|
|
165
221
|
PROVIDER_REGISTRY[best_scope] = provider
|
|
166
222
|
return provider
|
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
"""Together AI provider implementation."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
if TYPE_CHECKING:
|
|
6
|
-
from .provider import TogetherProvider
|
|
7
|
-
else:
|
|
8
|
-
try:
|
|
9
|
-
from .provider import TogetherProvider
|
|
10
|
-
except ImportError: # pragma: no cover
|
|
11
|
-
from .._missing_import_stubs import (
|
|
12
|
-
create_provider_stub,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
TogetherProvider = create_provider_stub("openai", "TogetherProvider")
|
|
3
|
+
from .provider import TogetherProvider
|
|
16
4
|
|
|
17
5
|
__all__ = [
|
|
18
6
|
"TogetherProvider",
|
|
@@ -227,7 +227,8 @@ class BaseStreamResponse(
|
|
|
227
227
|
self.messages = list(input_messages) + [self._assistant_message]
|
|
228
228
|
|
|
229
229
|
self._chunk_iterator = chunk_iterator
|
|
230
|
-
self._current_content: Text | Thought |
|
|
230
|
+
self._current_content: Text | Thought | None = None
|
|
231
|
+
self._current_tool_calls: dict[str, ToolCall] = {}
|
|
231
232
|
|
|
232
233
|
self._processing_format_tool: bool = False
|
|
233
234
|
|
|
@@ -269,7 +270,7 @@ class BaseStreamResponse(
|
|
|
269
270
|
self, chunk: TextStartChunk | TextChunk | TextEndChunk
|
|
270
271
|
) -> None:
|
|
271
272
|
if chunk.type == "text_start_chunk":
|
|
272
|
-
if self._current_content:
|
|
273
|
+
if self._current_content or self._current_tool_calls:
|
|
273
274
|
raise RuntimeError(
|
|
274
275
|
"Received text_start_chunk while processing another chunk"
|
|
275
276
|
)
|
|
@@ -292,7 +293,7 @@ class BaseStreamResponse(
|
|
|
292
293
|
self, chunk: ThoughtStartChunk | ThoughtChunk | ThoughtEndChunk
|
|
293
294
|
) -> None:
|
|
294
295
|
if chunk.type == "thought_start_chunk":
|
|
295
|
-
if self._current_content:
|
|
296
|
+
if self._current_content or self._current_tool_calls:
|
|
296
297
|
raise RuntimeError(
|
|
297
298
|
"Received thought_start_chunk while processing another chunk"
|
|
298
299
|
)
|
|
@@ -323,35 +324,38 @@ class BaseStreamResponse(
|
|
|
323
324
|
raise RuntimeError(
|
|
324
325
|
"Received tool_call_start_chunk while processing another chunk"
|
|
325
326
|
)
|
|
326
|
-
|
|
327
|
+
if chunk.id in self._current_tool_calls:
|
|
328
|
+
raise RuntimeError("Got tool_call_start_chunk with conflicting id")
|
|
329
|
+
# Create a new tool call and track it by ID
|
|
330
|
+
# Multiple tool calls can be in progress simultaneously (interleaved)
|
|
331
|
+
tool_call = ToolCall(
|
|
327
332
|
id=chunk.id,
|
|
328
333
|
name=chunk.name,
|
|
329
334
|
args="",
|
|
330
335
|
)
|
|
336
|
+
self._current_tool_calls[chunk.id] = tool_call
|
|
331
337
|
|
|
332
338
|
elif chunk.type == "tool_call_chunk":
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
):
|
|
339
|
+
# Look up the tool call by ID
|
|
340
|
+
tool_call = self._current_tool_calls.get(chunk.id)
|
|
341
|
+
if tool_call is None:
|
|
337
342
|
raise RuntimeError(
|
|
338
|
-
"Received tool_call_chunk
|
|
343
|
+
f"Received tool_call_chunk for unknown tool call ID: {chunk.id}"
|
|
339
344
|
)
|
|
340
|
-
|
|
345
|
+
tool_call.args += chunk.delta
|
|
341
346
|
|
|
342
347
|
elif chunk.type == "tool_call_end_chunk":
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
):
|
|
348
|
+
# Finalize the tool call
|
|
349
|
+
tool_call = self._current_tool_calls.get(chunk.id)
|
|
350
|
+
if tool_call is None:
|
|
347
351
|
raise RuntimeError(
|
|
348
|
-
"Received tool_call_end_chunk
|
|
352
|
+
f"Received tool_call_end_chunk for unknown tool call ID: {chunk.id}"
|
|
349
353
|
)
|
|
350
|
-
if not
|
|
351
|
-
|
|
352
|
-
self._content.append(
|
|
353
|
-
self._tool_calls.append(
|
|
354
|
-
self.
|
|
354
|
+
if not tool_call.args:
|
|
355
|
+
tool_call.args = "{}"
|
|
356
|
+
self._content.append(tool_call)
|
|
357
|
+
self._tool_calls.append(tool_call)
|
|
358
|
+
del self._current_tool_calls[chunk.id]
|
|
355
359
|
|
|
356
360
|
def _pretty_chunk(self, chunk: AssistantContentChunk, spacer: str) -> str:
|
|
357
361
|
match chunk.type:
|
mirascope/llm/tools/decorator.py
CHANGED
|
@@ -62,15 +62,19 @@ class ToolDecorator:
|
|
|
62
62
|
is_async = _tool_utils.is_async_tool_fn(fn)
|
|
63
63
|
|
|
64
64
|
if is_context and is_async:
|
|
65
|
-
return AsyncContextTool[DepsT, JsonableCovariantT, P](
|
|
65
|
+
return AsyncContextTool[DepsT, JsonableCovariantT, P].from_function(
|
|
66
66
|
fn, strict=self.strict
|
|
67
67
|
)
|
|
68
68
|
elif is_context:
|
|
69
|
-
return ContextTool[DepsT, JsonableCovariantT, P](
|
|
69
|
+
return ContextTool[DepsT, JsonableCovariantT, P].from_function(
|
|
70
|
+
fn, strict=self.strict
|
|
71
|
+
)
|
|
70
72
|
elif is_async:
|
|
71
|
-
return AsyncTool[P, JsonableCovariantT](
|
|
73
|
+
return AsyncTool[P, JsonableCovariantT].from_function(
|
|
74
|
+
fn, strict=self.strict
|
|
75
|
+
)
|
|
72
76
|
else:
|
|
73
|
-
return Tool[P, JsonableCovariantT](fn, strict=self.strict)
|
|
77
|
+
return Tool[P, JsonableCovariantT].from_function(fn, strict=self.strict)
|
|
74
78
|
|
|
75
79
|
|
|
76
80
|
@overload
|
|
@@ -175,10 +175,35 @@ class ToolSchema(Generic[ToolFnT]):
|
|
|
175
175
|
def __init__(
|
|
176
176
|
self,
|
|
177
177
|
fn: ToolFnT,
|
|
178
|
+
name: str,
|
|
179
|
+
description: str,
|
|
180
|
+
parameters: ToolParameterSchema,
|
|
178
181
|
*,
|
|
179
182
|
strict: bool = False,
|
|
180
|
-
is_context_tool: bool = False,
|
|
181
183
|
) -> None:
|
|
184
|
+
"""Create a `ToolSchema` with the provided values.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
fn: The function that implements the tool's functionality
|
|
188
|
+
name: The name of the tool
|
|
189
|
+
description: Description of what the tool does
|
|
190
|
+
parameters: JSON Schema describing the parameters accepted by the tool
|
|
191
|
+
strict: Whether the tool should use strict mode when supported
|
|
192
|
+
"""
|
|
193
|
+
self.fn = fn
|
|
194
|
+
self.name = name
|
|
195
|
+
self.description = description
|
|
196
|
+
self.parameters = parameters
|
|
197
|
+
self.strict = strict
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def from_function(
|
|
201
|
+
cls,
|
|
202
|
+
fn: AnyToolFn,
|
|
203
|
+
*,
|
|
204
|
+
strict: bool = False,
|
|
205
|
+
is_context_tool: bool = False,
|
|
206
|
+
) -> ToolSchema[AnyToolFn]:
|
|
182
207
|
"""Create a `ToolSchema` by inspecting a function and its docstring.
|
|
183
208
|
|
|
184
209
|
Uses Pydantic's create_model to dynamically build a model from the function
|
|
@@ -264,11 +289,13 @@ class ToolSchema(Generic[ToolFnT]):
|
|
|
264
289
|
if "$defs" in schema:
|
|
265
290
|
parameters.defs = schema["$defs"]
|
|
266
291
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
292
|
+
return cls(
|
|
293
|
+
fn=cast(ToolFnT, fn),
|
|
294
|
+
name=name,
|
|
295
|
+
description=description,
|
|
296
|
+
parameters=parameters,
|
|
297
|
+
strict=strict,
|
|
298
|
+
)
|
|
272
299
|
|
|
273
300
|
def can_execute(self, tool_call: ToolCall) -> bool:
|
|
274
301
|
"""Check if a `ToolCall` can be executed by tools with this `ToolSchema`.
|
mirascope/llm/tools/tools.py
CHANGED
|
@@ -40,10 +40,27 @@ class Tool(
|
|
|
40
40
|
This class is not instantiated directly but created by the `@tool()` decorator.
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_function( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
45
|
+
cls, fn: ToolFn[AnyP, JsonableCovariantT], *, strict: bool = False
|
|
46
|
+
) -> Tool[AnyP, JsonableCovariantT]:
|
|
47
|
+
"""Create a `Tool` by inspecting a function and its docstring.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
fn: The function to extract schema from
|
|
51
|
+
strict: Whether the tool should use strict mode when supported
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
a `Tool` representing the function
|
|
55
|
+
"""
|
|
56
|
+
schema = ToolSchema.from_function(fn, strict=strict, is_context_tool=False)
|
|
57
|
+
return cls(
|
|
58
|
+
fn=cast(ToolFn[AnyP, JsonableCovariantT], schema.fn),
|
|
59
|
+
name=schema.name,
|
|
60
|
+
description=schema.description,
|
|
61
|
+
parameters=schema.parameters,
|
|
62
|
+
strict=schema.strict,
|
|
63
|
+
)
|
|
47
64
|
|
|
48
65
|
def __call__(self, *args: AnyP.args, **kwargs: AnyP.kwargs) -> JsonableCovariantT:
|
|
49
66
|
"""Call the underlying function directly."""
|
|
@@ -69,10 +86,27 @@ class AsyncTool(
|
|
|
69
86
|
This class is not instantiated directly but created by the `@tool()` decorator.
|
|
70
87
|
"""
|
|
71
88
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
89
|
+
@classmethod
|
|
90
|
+
def from_function( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
91
|
+
cls, fn: AsyncToolFn[AnyP, JsonableCovariantT], *, strict: bool = False
|
|
92
|
+
) -> AsyncTool[AnyP, JsonableCovariantT]:
|
|
93
|
+
"""Create an `AsyncTool` by inspecting a function and its docstring.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
fn: The function to extract schema from
|
|
97
|
+
strict: Whether the tool should use strict mode when supported
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
an `AsyncTool` representing the function
|
|
101
|
+
"""
|
|
102
|
+
schema = ToolSchema.from_function(fn, strict=strict, is_context_tool=False)
|
|
103
|
+
return cls(
|
|
104
|
+
fn=cast(AsyncToolFn[AnyP, JsonableCovariantT], schema.fn),
|
|
105
|
+
name=schema.name,
|
|
106
|
+
description=schema.description,
|
|
107
|
+
parameters=schema.parameters,
|
|
108
|
+
strict=schema.strict,
|
|
109
|
+
)
|
|
76
110
|
|
|
77
111
|
def __call__(
|
|
78
112
|
self, *args: AnyP.args, **kwargs: AnyP.kwargs
|
|
@@ -100,13 +134,30 @@ class ContextTool(
|
|
|
100
134
|
This class is not instantiated directly but created by the `@tool()` decorator.
|
|
101
135
|
"""
|
|
102
136
|
|
|
103
|
-
|
|
104
|
-
|
|
137
|
+
@classmethod
|
|
138
|
+
def from_function( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
139
|
+
cls,
|
|
105
140
|
fn: ContextToolFn[DepsT, AnyP, JsonableCovariantT],
|
|
106
141
|
*,
|
|
107
142
|
strict: bool = False,
|
|
108
|
-
) ->
|
|
109
|
-
|
|
143
|
+
) -> ContextTool[DepsT, JsonableCovariantT, AnyP]:
|
|
144
|
+
"""Create a `ContextTool` by inspecting a function and its docstring.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
fn: The function to extract schema from
|
|
148
|
+
strict: Whether the tool should use strict mode when supported
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
a `ContextTool` representing the function
|
|
152
|
+
"""
|
|
153
|
+
schema = ToolSchema.from_function(fn, strict=strict, is_context_tool=True)
|
|
154
|
+
return cls(
|
|
155
|
+
fn=cast(ContextToolFn[DepsT, AnyP, JsonableCovariantT], schema.fn),
|
|
156
|
+
name=schema.name,
|
|
157
|
+
description=schema.description,
|
|
158
|
+
parameters=schema.parameters,
|
|
159
|
+
strict=schema.strict,
|
|
160
|
+
)
|
|
110
161
|
|
|
111
162
|
def __call__(
|
|
112
163
|
self,
|
|
@@ -141,13 +192,30 @@ class AsyncContextTool(
|
|
|
141
192
|
This class is not instantiated directly but created by the `@tool()` decorator.
|
|
142
193
|
"""
|
|
143
194
|
|
|
144
|
-
|
|
145
|
-
|
|
195
|
+
@classmethod
|
|
196
|
+
def from_function( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
197
|
+
cls,
|
|
146
198
|
fn: AsyncContextToolFn[DepsT, AnyP, JsonableCovariantT],
|
|
147
199
|
*,
|
|
148
200
|
strict: bool = False,
|
|
149
|
-
) ->
|
|
150
|
-
|
|
201
|
+
) -> AsyncContextTool[DepsT, JsonableCovariantT, AnyP]:
|
|
202
|
+
"""Create an `AsyncContextTool` by inspecting a function and its docstring.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
fn: The function to extract schema from
|
|
206
|
+
strict: Whether the tool should use strict mode when supported
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
an `AsyncContextTool` representing the function
|
|
210
|
+
"""
|
|
211
|
+
schema = ToolSchema.from_function(fn, strict=strict, is_context_tool=True)
|
|
212
|
+
return cls(
|
|
213
|
+
fn=cast(AsyncContextToolFn[DepsT, AnyP, JsonableCovariantT], schema.fn),
|
|
214
|
+
name=schema.name,
|
|
215
|
+
description=schema.description,
|
|
216
|
+
parameters=schema.parameters,
|
|
217
|
+
strict=schema.strict,
|
|
218
|
+
)
|
|
151
219
|
|
|
152
220
|
def __call__(
|
|
153
221
|
self,
|