mirascope 2.0.0a6__py3-none-any.whl → 2.0.2__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/_utils.py +34 -0
- mirascope/api/_generated/__init__.py +186 -5
- mirascope/api/_generated/annotations/client.py +38 -6
- mirascope/api/_generated/annotations/raw_client.py +366 -47
- mirascope/api/_generated/annotations/types/annotations_create_response.py +19 -6
- mirascope/api/_generated/annotations/types/annotations_get_response.py +19 -6
- mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item.py +22 -7
- mirascope/api/_generated/annotations/types/annotations_update_response.py +19 -6
- mirascope/api/_generated/api_keys/__init__.py +12 -2
- mirascope/api/_generated/api_keys/client.py +107 -6
- mirascope/api/_generated/api_keys/raw_client.py +486 -38
- mirascope/api/_generated/api_keys/types/__init__.py +7 -1
- mirascope/api/_generated/api_keys/types/api_keys_list_all_for_org_response_item.py +40 -0
- mirascope/api/_generated/client.py +36 -0
- mirascope/api/_generated/docs/raw_client.py +71 -9
- mirascope/api/_generated/environment.py +3 -3
- mirascope/api/_generated/environments/__init__.py +6 -0
- mirascope/api/_generated/environments/client.py +158 -9
- mirascope/api/_generated/environments/raw_client.py +620 -52
- mirascope/api/_generated/environments/types/__init__.py +10 -0
- mirascope/api/_generated/environments/types/environments_get_analytics_response.py +60 -0
- mirascope/api/_generated/environments/types/environments_get_analytics_response_top_functions_item.py +24 -0
- mirascope/api/_generated/{organizations/types/organizations_credits_response.py → environments/types/environments_get_analytics_response_top_models_item.py} +6 -3
- mirascope/api/_generated/errors/__init__.py +6 -0
- mirascope/api/_generated/errors/bad_request_error.py +5 -2
- mirascope/api/_generated/errors/conflict_error.py +5 -2
- mirascope/api/_generated/errors/payment_required_error.py +15 -0
- mirascope/api/_generated/errors/service_unavailable_error.py +14 -0
- mirascope/api/_generated/errors/too_many_requests_error.py +15 -0
- mirascope/api/_generated/functions/__init__.py +10 -0
- mirascope/api/_generated/functions/client.py +222 -8
- mirascope/api/_generated/functions/raw_client.py +975 -134
- mirascope/api/_generated/functions/types/__init__.py +28 -4
- mirascope/api/_generated/functions/types/functions_get_by_env_response.py +53 -0
- mirascope/api/_generated/functions/types/functions_get_by_env_response_dependencies_value.py +22 -0
- mirascope/api/_generated/functions/types/functions_list_by_env_response.py +25 -0
- mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +56 -0
- mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item_dependencies_value.py +22 -0
- mirascope/api/_generated/health/raw_client.py +74 -10
- mirascope/api/_generated/organization_invitations/__init__.py +33 -0
- mirascope/api/_generated/organization_invitations/client.py +546 -0
- mirascope/api/_generated/organization_invitations/raw_client.py +1519 -0
- mirascope/api/_generated/organization_invitations/types/__init__.py +53 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response.py +34 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_request_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response.py +48 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_status.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response.py +48 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_status.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item.py +48 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_status.py +7 -0
- mirascope/api/_generated/organization_memberships/__init__.py +19 -0
- mirascope/api/_generated/organization_memberships/client.py +302 -0
- mirascope/api/_generated/organization_memberships/raw_client.py +736 -0
- mirascope/api/_generated/organization_memberships/types/__init__.py +27 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item.py +33 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item_role.py +7 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_request_role.py +7 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response.py +31 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response_role.py +7 -0
- mirascope/api/_generated/organizations/__init__.py +26 -2
- mirascope/api/_generated/organizations/client.py +442 -20
- mirascope/api/_generated/organizations/raw_client.py +1763 -164
- mirascope/api/_generated/organizations/types/__init__.py +48 -2
- mirascope/api/_generated/organizations/types/organizations_create_payment_intent_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_request_target_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response.py +47 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item.py +33 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item_resource.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_router_balance_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response.py +53 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_current_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_payment_method.py +26 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change.py +34 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change_target_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_update_subscription_request_target_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_update_subscription_response.py +35 -0
- mirascope/api/_generated/project_memberships/__init__.py +25 -0
- mirascope/api/_generated/project_memberships/client.py +437 -0
- mirascope/api/_generated/project_memberships/raw_client.py +1039 -0
- mirascope/api/_generated/project_memberships/types/__init__.py +29 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_create_request_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_create_response.py +35 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_create_response_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item.py +33 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_update_request_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_update_response.py +35 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_update_response_role.py +7 -0
- mirascope/api/_generated/projects/raw_client.py +415 -58
- mirascope/api/_generated/reference.md +2767 -397
- mirascope/api/_generated/tags/__init__.py +19 -0
- mirascope/api/_generated/tags/client.py +504 -0
- mirascope/api/_generated/tags/raw_client.py +1288 -0
- mirascope/api/_generated/tags/types/__init__.py +17 -0
- mirascope/api/_generated/tags/types/tags_create_response.py +41 -0
- mirascope/api/_generated/tags/types/tags_get_response.py +41 -0
- mirascope/api/_generated/tags/types/tags_list_response.py +23 -0
- mirascope/api/_generated/tags/types/tags_list_response_tags_item.py +41 -0
- mirascope/api/_generated/tags/types/tags_update_response.py +41 -0
- mirascope/api/_generated/token_cost/__init__.py +7 -0
- mirascope/api/_generated/token_cost/client.py +160 -0
- mirascope/api/_generated/token_cost/raw_client.py +264 -0
- mirascope/api/_generated/token_cost/types/__init__.py +8 -0
- mirascope/api/_generated/token_cost/types/token_cost_calculate_request_usage.py +54 -0
- mirascope/api/_generated/token_cost/types/token_cost_calculate_response.py +52 -0
- mirascope/api/_generated/traces/__init__.py +20 -0
- mirascope/api/_generated/traces/client.py +543 -0
- mirascope/api/_generated/traces/raw_client.py +1366 -96
- mirascope/api/_generated/traces/types/__init__.py +28 -0
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response.py +6 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response.py +33 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response_spans_item.py +88 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_response_spans_item.py +0 -2
- mirascope/api/_generated/traces/types/traces_list_by_function_hash_response.py +25 -0
- mirascope/api/_generated/traces/types/traces_list_by_function_hash_response_traces_item.py +44 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item_operator.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_by.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_order.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_response.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_response_spans_item.py +50 -0
- mirascope/api/_generated/traces/types/traces_search_response_spans_item.py +10 -1
- mirascope/api/_generated/types/__init__.py +32 -2
- mirascope/api/_generated/types/bad_request_error_body.py +50 -0
- mirascope/api/_generated/types/date.py +3 -0
- mirascope/api/_generated/types/immutable_resource_error.py +22 -0
- mirascope/api/_generated/types/internal_server_error_body.py +3 -3
- mirascope/api/_generated/types/plan_limit_exceeded_error.py +32 -0
- mirascope/api/_generated/types/plan_limit_exceeded_error_tag.py +7 -0
- mirascope/api/_generated/types/pricing_unavailable_error.py +23 -0
- mirascope/api/_generated/types/rate_limit_error.py +31 -0
- mirascope/api/_generated/types/rate_limit_error_tag.py +5 -0
- mirascope/api/_generated/types/service_unavailable_error_body.py +24 -0
- mirascope/api/_generated/types/service_unavailable_error_tag.py +7 -0
- mirascope/api/_generated/types/subscription_past_due_error.py +31 -0
- mirascope/api/_generated/types/subscription_past_due_error_tag.py +7 -0
- mirascope/api/settings.py +19 -1
- mirascope/llm/__init__.py +53 -10
- mirascope/llm/calls/__init__.py +2 -1
- mirascope/llm/calls/calls.py +29 -20
- mirascope/llm/calls/decorator.py +21 -7
- mirascope/llm/content/tool_output.py +22 -5
- mirascope/llm/exceptions.py +284 -71
- mirascope/llm/formatting/__init__.py +17 -0
- mirascope/llm/formatting/format.py +112 -35
- mirascope/llm/formatting/output_parser.py +178 -0
- mirascope/llm/formatting/partial.py +80 -7
- mirascope/llm/formatting/primitives.py +192 -0
- mirascope/llm/formatting/types.py +20 -8
- mirascope/llm/messages/__init__.py +3 -0
- mirascope/llm/messages/_utils.py +34 -0
- mirascope/llm/models/__init__.py +5 -0
- mirascope/llm/models/models.py +137 -69
- mirascope/llm/{providers/base → models}/params.py +7 -57
- mirascope/llm/models/thinking_config.py +61 -0
- mirascope/llm/prompts/_utils.py +0 -32
- mirascope/llm/prompts/decorator.py +16 -5
- mirascope/llm/prompts/prompts.py +160 -92
- mirascope/llm/providers/__init__.py +1 -4
- mirascope/llm/providers/anthropic/_utils/__init__.py +2 -0
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +18 -9
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +62 -13
- mirascope/llm/providers/anthropic/_utils/decode.py +18 -9
- mirascope/llm/providers/anthropic/_utils/encode.py +26 -7
- mirascope/llm/providers/anthropic/_utils/errors.py +2 -2
- mirascope/llm/providers/anthropic/beta_provider.py +64 -18
- mirascope/llm/providers/anthropic/provider.py +91 -33
- mirascope/llm/providers/base/__init__.py +0 -4
- mirascope/llm/providers/base/_utils.py +55 -6
- mirascope/llm/providers/base/base_provider.py +116 -37
- mirascope/llm/providers/google/_utils/__init__.py +2 -0
- mirascope/llm/providers/google/_utils/decode.py +20 -7
- mirascope/llm/providers/google/_utils/encode.py +26 -7
- mirascope/llm/providers/google/_utils/errors.py +3 -2
- mirascope/llm/providers/google/provider.py +64 -18
- mirascope/llm/providers/mirascope/_utils.py +13 -17
- mirascope/llm/providers/mirascope/provider.py +49 -18
- mirascope/llm/providers/mlx/_utils.py +7 -2
- mirascope/llm/providers/mlx/encoding/base.py +5 -2
- mirascope/llm/providers/mlx/encoding/transformers.py +5 -2
- mirascope/llm/providers/mlx/mlx.py +23 -6
- mirascope/llm/providers/mlx/provider.py +42 -13
- mirascope/llm/providers/openai/_utils/errors.py +2 -2
- mirascope/llm/providers/openai/completions/_utils/encode.py +20 -16
- mirascope/llm/providers/openai/completions/base_provider.py +40 -11
- mirascope/llm/providers/openai/provider.py +40 -10
- mirascope/llm/providers/openai/responses/_utils/__init__.py +2 -0
- mirascope/llm/providers/openai/responses/_utils/decode.py +19 -6
- mirascope/llm/providers/openai/responses/_utils/encode.py +22 -10
- mirascope/llm/providers/openai/responses/provider.py +56 -18
- mirascope/llm/providers/provider_registry.py +93 -19
- mirascope/llm/responses/__init__.py +6 -1
- mirascope/llm/responses/_utils.py +102 -12
- mirascope/llm/responses/base_response.py +5 -2
- mirascope/llm/responses/base_stream_response.py +115 -25
- mirascope/llm/responses/response.py +2 -1
- mirascope/llm/responses/root_response.py +89 -17
- mirascope/llm/responses/stream_response.py +6 -9
- mirascope/llm/tools/decorator.py +9 -4
- mirascope/llm/tools/tool_schema.py +17 -6
- mirascope/llm/tools/toolkit.py +35 -27
- mirascope/llm/tools/tools.py +45 -20
- mirascope/ops/__init__.py +4 -0
- mirascope/ops/_internal/closure.py +4 -1
- mirascope/ops/_internal/configuration.py +82 -31
- mirascope/ops/_internal/exporters/exporters.py +55 -35
- mirascope/ops/_internal/exporters/utils.py +37 -0
- mirascope/ops/_internal/instrumentation/llm/common.py +530 -0
- mirascope/ops/_internal/instrumentation/llm/cost.py +190 -0
- mirascope/ops/_internal/instrumentation/llm/encode.py +1 -1
- mirascope/ops/_internal/instrumentation/llm/llm.py +116 -1242
- mirascope/ops/_internal/instrumentation/llm/model.py +1798 -0
- mirascope/ops/_internal/instrumentation/llm/response.py +521 -0
- mirascope/ops/_internal/instrumentation/llm/serialize.py +300 -0
- mirascope/ops/_internal/protocols.py +83 -1
- mirascope/ops/_internal/traced_calls.py +18 -0
- mirascope/ops/_internal/traced_functions.py +125 -10
- mirascope/ops/_internal/tracing.py +78 -1
- mirascope/ops/_internal/utils.py +60 -4
- mirascope/ops/_internal/versioned_functions.py +1 -1
- {mirascope-2.0.0a6.dist-info → mirascope-2.0.2.dist-info}/METADATA +12 -11
- mirascope-2.0.2.dist-info/RECORD +424 -0
- {mirascope-2.0.0a6.dist-info → mirascope-2.0.2.dist-info}/licenses/LICENSE +1 -1
- mirascope-2.0.0a6.dist-info/RECORD +0 -316
- {mirascope-2.0.0a6.dist-info → mirascope-2.0.2.dist-info}/WHEEL +0 -0
mirascope/llm/tools/tools.py
CHANGED
|
@@ -9,6 +9,7 @@ from typing_extensions import TypeVar
|
|
|
9
9
|
|
|
10
10
|
from ..content import ToolCall, ToolOutput
|
|
11
11
|
from ..context import Context, DepsT
|
|
12
|
+
from ..exceptions import ToolError, ToolExecutionError
|
|
12
13
|
from ..types import AnyP, JsonableCovariantT
|
|
13
14
|
from .protocols import (
|
|
14
15
|
AsyncContextToolFn,
|
|
@@ -42,13 +43,14 @@ class Tool(
|
|
|
42
43
|
|
|
43
44
|
@classmethod
|
|
44
45
|
def from_function( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
45
|
-
cls, fn: ToolFn[AnyP, JsonableCovariantT], *, strict: bool =
|
|
46
|
+
cls, fn: ToolFn[AnyP, JsonableCovariantT], *, strict: bool | None = None
|
|
46
47
|
) -> Tool[AnyP, JsonableCovariantT]:
|
|
47
48
|
"""Create a `Tool` by inspecting a function and its docstring.
|
|
48
49
|
|
|
49
50
|
Args:
|
|
50
51
|
fn: The function to extract schema from
|
|
51
|
-
strict: Whether the tool should use strict mode when supported
|
|
52
|
+
strict: Whether the tool should use strict mode when supported.
|
|
53
|
+
If None, uses provider's default (usually as strict as possible).
|
|
52
54
|
|
|
53
55
|
Returns:
|
|
54
56
|
a `Tool` representing the function
|
|
@@ -68,10 +70,15 @@ class Tool(
|
|
|
68
70
|
|
|
69
71
|
def execute(self, tool_call: ToolCall) -> ToolOutput[JsonableCovariantT]:
|
|
70
72
|
"""Execute the tool using an LLM-provided `ToolCall`."""
|
|
71
|
-
kwargs_from_json = json.loads(tool_call.args)
|
|
72
73
|
kwargs_callable = cast(KwargsCallable[JsonableCovariantT], self.fn)
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
error: ToolError | None = None
|
|
75
|
+
try:
|
|
76
|
+
kwargs_from_json = json.loads(tool_call.args)
|
|
77
|
+
result = kwargs_callable(**kwargs_from_json)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
result = str(e)
|
|
80
|
+
error = ToolExecutionError(e)
|
|
81
|
+
return ToolOutput(id=tool_call.id, result=result, error=error, name=self.name)
|
|
75
82
|
|
|
76
83
|
|
|
77
84
|
class AsyncTool(
|
|
@@ -88,13 +95,14 @@ class AsyncTool(
|
|
|
88
95
|
|
|
89
96
|
@classmethod
|
|
90
97
|
def from_function( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
91
|
-
cls, fn: AsyncToolFn[AnyP, JsonableCovariantT], *, strict: bool =
|
|
98
|
+
cls, fn: AsyncToolFn[AnyP, JsonableCovariantT], *, strict: bool | None = None
|
|
92
99
|
) -> AsyncTool[AnyP, JsonableCovariantT]:
|
|
93
100
|
"""Create an `AsyncTool` by inspecting a function and its docstring.
|
|
94
101
|
|
|
95
102
|
Args:
|
|
96
103
|
fn: The function to extract schema from
|
|
97
|
-
strict: Whether the tool should use strict mode when supported
|
|
104
|
+
strict: Whether the tool should use strict mode when supported.
|
|
105
|
+
If None, uses provider's default (usually as strict as possible).
|
|
98
106
|
|
|
99
107
|
Returns:
|
|
100
108
|
an `AsyncTool` representing the function
|
|
@@ -116,10 +124,15 @@ class AsyncTool(
|
|
|
116
124
|
|
|
117
125
|
async def execute(self, tool_call: ToolCall) -> ToolOutput[JsonableCovariantT]:
|
|
118
126
|
"""Execute the async tool using an LLM-provided `ToolCall`."""
|
|
119
|
-
kwargs_from_json = json.loads(tool_call.args)
|
|
120
127
|
kwargs_callable = cast(AsyncKwargsCallable[JsonableCovariantT], self.fn)
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
error: ToolError | None = None
|
|
129
|
+
try:
|
|
130
|
+
kwargs_from_json = json.loads(tool_call.args)
|
|
131
|
+
result = await kwargs_callable(**kwargs_from_json)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
result = str(e)
|
|
134
|
+
error = ToolExecutionError(e)
|
|
135
|
+
return ToolOutput(id=tool_call.id, result=result, error=error, name=self.name)
|
|
123
136
|
|
|
124
137
|
|
|
125
138
|
class ContextTool(
|
|
@@ -139,13 +152,14 @@ class ContextTool(
|
|
|
139
152
|
cls,
|
|
140
153
|
fn: ContextToolFn[DepsT, AnyP, JsonableCovariantT],
|
|
141
154
|
*,
|
|
142
|
-
strict: bool =
|
|
155
|
+
strict: bool | None = None,
|
|
143
156
|
) -> ContextTool[DepsT, JsonableCovariantT, AnyP]:
|
|
144
157
|
"""Create a `ContextTool` by inspecting a function and its docstring.
|
|
145
158
|
|
|
146
159
|
Args:
|
|
147
160
|
fn: The function to extract schema from
|
|
148
|
-
strict: Whether the tool should use strict mode when supported
|
|
161
|
+
strict: Whether the tool should use strict mode when supported.
|
|
162
|
+
If None, uses provider's default (usually as strict as possible).
|
|
149
163
|
|
|
150
164
|
Returns:
|
|
151
165
|
a `ContextTool` representing the function
|
|
@@ -172,12 +186,17 @@ class ContextTool(
|
|
|
172
186
|
self, ctx: Context[DepsT], tool_call: ToolCall
|
|
173
187
|
) -> ToolOutput[JsonableCovariantT]:
|
|
174
188
|
"""Execute the context tool using an LLM-provided `ToolCall`."""
|
|
175
|
-
kwargs_from_json = json.loads(tool_call.args)
|
|
176
189
|
kwargs_callable = cast(
|
|
177
190
|
ContextKwargsCallable[DepsT, JsonableCovariantT], self.fn
|
|
178
191
|
)
|
|
179
|
-
|
|
180
|
-
|
|
192
|
+
error: ToolError | None = None
|
|
193
|
+
try:
|
|
194
|
+
kwargs_from_json = json.loads(tool_call.args)
|
|
195
|
+
result = kwargs_callable(ctx, **kwargs_from_json)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
result = str(e)
|
|
198
|
+
error = ToolExecutionError(e)
|
|
199
|
+
return ToolOutput(id=tool_call.id, result=result, error=error, name=self.name)
|
|
181
200
|
|
|
182
201
|
|
|
183
202
|
class AsyncContextTool(
|
|
@@ -197,13 +216,14 @@ class AsyncContextTool(
|
|
|
197
216
|
cls,
|
|
198
217
|
fn: AsyncContextToolFn[DepsT, AnyP, JsonableCovariantT],
|
|
199
218
|
*,
|
|
200
|
-
strict: bool =
|
|
219
|
+
strict: bool | None = None,
|
|
201
220
|
) -> AsyncContextTool[DepsT, JsonableCovariantT, AnyP]:
|
|
202
221
|
"""Create an `AsyncContextTool` by inspecting a function and its docstring.
|
|
203
222
|
|
|
204
223
|
Args:
|
|
205
224
|
fn: The function to extract schema from
|
|
206
|
-
strict: Whether the tool should use strict mode when supported
|
|
225
|
+
strict: Whether the tool should use strict mode when supported.
|
|
226
|
+
If None, uses provider's default (usually as strict as possible).
|
|
207
227
|
|
|
208
228
|
Returns:
|
|
209
229
|
an `AsyncContextTool` representing the function
|
|
@@ -230,9 +250,14 @@ class AsyncContextTool(
|
|
|
230
250
|
self, ctx: Context[DepsT], tool_call: ToolCall
|
|
231
251
|
) -> ToolOutput[JsonableCovariantT]:
|
|
232
252
|
"""Execute the async context tool using an LLM-provided `ToolCall`."""
|
|
233
|
-
kwargs_from_json = json.loads(tool_call.args)
|
|
234
253
|
kwargs_callable = cast(
|
|
235
254
|
AsyncJsonKwargsCallable[DepsT, JsonableCovariantT], self.fn
|
|
236
255
|
)
|
|
237
|
-
|
|
238
|
-
|
|
256
|
+
error: ToolError | None = None
|
|
257
|
+
try:
|
|
258
|
+
kwargs_from_json = json.loads(tool_call.args)
|
|
259
|
+
result = await kwargs_callable(ctx, **kwargs_from_json)
|
|
260
|
+
except Exception as e:
|
|
261
|
+
result = str(e)
|
|
262
|
+
error = ToolExecutionError(e)
|
|
263
|
+
return ToolOutput(id=tool_call.id, result=result, error=error, name=self.name)
|
mirascope/ops/__init__.py
CHANGED
|
@@ -41,8 +41,10 @@ from ._internal.traced_calls import (
|
|
|
41
41
|
from ._internal.traced_functions import (
|
|
42
42
|
AsyncTrace,
|
|
43
43
|
AsyncTracedFunction,
|
|
44
|
+
AsyncTracedSpanFunction,
|
|
44
45
|
Trace,
|
|
45
46
|
TracedFunction,
|
|
47
|
+
TracedSpanFunction,
|
|
46
48
|
)
|
|
47
49
|
from ._internal.tracing import (
|
|
48
50
|
TraceDecorator,
|
|
@@ -69,6 +71,7 @@ __all__ = [
|
|
|
69
71
|
"SESSION_HEADER_NAME",
|
|
70
72
|
"AsyncTrace",
|
|
71
73
|
"AsyncTracedFunction",
|
|
74
|
+
"AsyncTracedSpanFunction",
|
|
72
75
|
"AsyncVersionedFunction",
|
|
73
76
|
"ClosureComputationError",
|
|
74
77
|
"ContextPropagator",
|
|
@@ -82,6 +85,7 @@ __all__ = [
|
|
|
82
85
|
"TracedCall",
|
|
83
86
|
"TracedContextCall",
|
|
84
87
|
"TracedFunction",
|
|
88
|
+
"TracedSpanFunction",
|
|
85
89
|
"VersionDecorator",
|
|
86
90
|
"VersionInfo",
|
|
87
91
|
"VersionedAsyncCall",
|
|
@@ -738,12 +738,15 @@ class _DependencyCollector:
|
|
|
738
738
|
# For Python 3.13+
|
|
739
739
|
return definition.func # pyright: ignore[reportFunctionMemberAccess] # pragma: no cover
|
|
740
740
|
|
|
741
|
+
# Handle objects with .fn but no __qualname__ (e.g., old-style wrappers).
|
|
742
|
+
# With copy_function_metadata() now copying __qualname__ to ToolSchema, Prompt,
|
|
743
|
+
# Call, etc., this branch is no longer reached in normal usage.
|
|
741
744
|
if (
|
|
742
745
|
(wrapped_function := getattr(definition, "fn", None)) is not None
|
|
743
746
|
and not hasattr(definition, "__qualname__")
|
|
744
747
|
and callable(wrapped_function)
|
|
745
748
|
):
|
|
746
|
-
return wrapped_function
|
|
749
|
+
return wrapped_function # pragma: no cover
|
|
747
750
|
|
|
748
751
|
return definition
|
|
749
752
|
|
|
@@ -2,22 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from collections.abc import Iterator
|
|
6
7
|
from contextlib import contextmanager
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
8
9
|
|
|
10
|
+
from opentelemetry import trace as otel_trace
|
|
11
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
12
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
13
|
+
|
|
14
|
+
from ...api.client import Mirascope
|
|
15
|
+
from ...api.settings import update_settings
|
|
16
|
+
from .exporters import MirascopeOTLPExporter
|
|
17
|
+
|
|
9
18
|
if TYPE_CHECKING:
|
|
10
|
-
from opentelemetry.sdk.trace import TracerProvider
|
|
11
19
|
from opentelemetry.trace import Tracer
|
|
12
|
-
else:
|
|
13
|
-
Tracer = None
|
|
14
20
|
|
|
15
21
|
DEFAULT_TRACER_NAME = "mirascope.llm"
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
from opentelemetry import trace as otel_trace
|
|
19
|
-
except ImportError: # pragma: no cover
|
|
20
|
-
otel_trace = None
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
21
24
|
|
|
22
25
|
_tracer_provider: TracerProvider | None = None
|
|
23
26
|
_tracer_name: str = DEFAULT_TRACER_NAME
|
|
@@ -25,59 +28,107 @@ _tracer_version: str | None = None
|
|
|
25
28
|
_tracer: Tracer | None = None
|
|
26
29
|
|
|
27
30
|
|
|
31
|
+
def _create_mirascope_cloud_provider(
|
|
32
|
+
api_key: str | None = None, base_url: str | None = None
|
|
33
|
+
) -> TracerProvider:
|
|
34
|
+
"""Create a TracerProvider configured for Mirascope Cloud.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
api_key: Optional API key. If not provided, uses MIRASCOPE_API_KEY env var.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Configured TracerProvider with MirascopeOTLPExporter.
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
RuntimeError: If API key is not available.
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
client = Mirascope(api_key=api_key, base_url=base_url)
|
|
47
|
+
except (ValueError, RuntimeError) as e:
|
|
48
|
+
raise RuntimeError(
|
|
49
|
+
"Failed to create Mirascope Cloud client. "
|
|
50
|
+
"Set MIRASCOPE_API_KEY environment variable or pass api_key parameter."
|
|
51
|
+
) from e
|
|
52
|
+
|
|
53
|
+
exporter = MirascopeOTLPExporter(client=client)
|
|
54
|
+
provider = TracerProvider()
|
|
55
|
+
provider.add_span_processor(BatchSpanProcessor(exporter))
|
|
56
|
+
|
|
57
|
+
return provider
|
|
58
|
+
|
|
59
|
+
|
|
28
60
|
def configure(
|
|
29
61
|
*,
|
|
62
|
+
api_key: str | None = None,
|
|
63
|
+
base_url: str | None = None,
|
|
30
64
|
tracer_provider: TracerProvider | None = None,
|
|
31
65
|
tracer_name: str = DEFAULT_TRACER_NAME,
|
|
32
66
|
tracer_version: str | None = None,
|
|
33
67
|
) -> None:
|
|
34
|
-
"""Configure the ops module
|
|
68
|
+
"""Configure the ops module for tracing.
|
|
35
69
|
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
When called without arguments, automatically configures Mirascope Cloud
|
|
71
|
+
using the MIRASCOPE_API_KEY environment variable.
|
|
38
72
|
|
|
39
73
|
Args:
|
|
40
|
-
tracer_provider: Optional
|
|
41
|
-
|
|
74
|
+
tracer_provider: Optional custom TracerProvider. If provided, this takes
|
|
75
|
+
precedence over automatic Mirascope Cloud configuration.
|
|
76
|
+
api_key: Optional Mirascope Cloud API key. If not provided, uses
|
|
77
|
+
MIRASCOPE_API_KEY environment variable.
|
|
42
78
|
tracer_name: Tracer name to use when creating a tracer.
|
|
43
79
|
Defaults to "mirascope.llm".
|
|
44
80
|
tracer_version: Optional tracer version.
|
|
45
81
|
|
|
82
|
+
Raises:
|
|
83
|
+
RuntimeError: If no tracer_provider is given and Mirascope Cloud
|
|
84
|
+
cannot be configured (missing API key).
|
|
85
|
+
|
|
46
86
|
Example:
|
|
87
|
+
Simple Mirascope Cloud configuration (recommended):
|
|
88
|
+
```python
|
|
89
|
+
import os
|
|
90
|
+
os.environ["MIRASCOPE_API_KEY"] = "your-api-key"
|
|
47
91
|
|
|
48
|
-
|
|
92
|
+
from mirascope import ops
|
|
93
|
+
|
|
94
|
+
ops.configure() # Automatically uses Mirascope Cloud
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
With explicit API key:
|
|
98
|
+
```python
|
|
99
|
+
from mirascope import ops
|
|
100
|
+
|
|
101
|
+
ops.configure(api_key="your-api-key")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
With custom tracer provider:
|
|
49
105
|
```python
|
|
50
106
|
from mirascope import ops
|
|
51
107
|
from opentelemetry.sdk.trace import TracerProvider
|
|
52
108
|
|
|
53
109
|
provider = TracerProvider()
|
|
54
110
|
ops.configure(tracer_provider=provider)
|
|
55
|
-
ops.instrument_llm()
|
|
56
111
|
```
|
|
57
112
|
"""
|
|
58
|
-
# TODO: refactor alongside other import error handling improvements
|
|
59
|
-
if otel_trace is None: # pragma: no cover
|
|
60
|
-
raise ImportError(
|
|
61
|
-
"OpenTelemetry is not installed. Run `pip install mirascope[otel]` "
|
|
62
|
-
"before calling `ops.configure(tracer_provider=...)`."
|
|
63
|
-
)
|
|
64
|
-
|
|
65
113
|
global _tracer_provider, _tracer_name, _tracer_version, _tracer
|
|
66
114
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
# Update settings so get_sync_client/get_async_client can use these values
|
|
116
|
+
if api_key is not None or base_url is not None:
|
|
117
|
+
update_settings(api_key=api_key, base_url=base_url)
|
|
118
|
+
|
|
119
|
+
# If no tracer_provider given, auto-configure Mirascope Cloud
|
|
120
|
+
if tracer_provider is None:
|
|
121
|
+
tracer_provider = _create_mirascope_cloud_provider(
|
|
122
|
+
api_key=api_key, base_url=base_url
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
_tracer_provider = tracer_provider
|
|
126
|
+
otel_trace.set_tracer_provider(tracer_provider)
|
|
70
127
|
|
|
71
128
|
_tracer_name = tracer_name
|
|
72
129
|
_tracer_version = tracer_version
|
|
73
130
|
|
|
74
|
-
|
|
75
|
-
provider = (
|
|
76
|
-
otel_trace.get_tracer_provider()
|
|
77
|
-
if _tracer_provider is None
|
|
78
|
-
else _tracer_provider
|
|
79
|
-
)
|
|
80
|
-
_tracer = provider.get_tracer(_tracer_name, _tracer_version)
|
|
131
|
+
_tracer = tracer_provider.get_tracer(_tracer_name, _tracer_version)
|
|
81
132
|
|
|
82
133
|
|
|
83
134
|
def set_tracer(tracer: Tracer | None) -> None:
|
|
@@ -20,14 +20,17 @@ from ....api._generated.traces.types import (
|
|
|
20
20
|
TracesCreateRequestResourceSpansItemResource,
|
|
21
21
|
TracesCreateRequestResourceSpansItemResourceAttributesItem,
|
|
22
22
|
TracesCreateRequestResourceSpansItemResourceAttributesItemValue,
|
|
23
|
+
TracesCreateRequestResourceSpansItemResourceAttributesItemValueArrayValue,
|
|
23
24
|
TracesCreateRequestResourceSpansItemScopeSpansItem,
|
|
24
25
|
TracesCreateRequestResourceSpansItemScopeSpansItemScope,
|
|
25
26
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItem,
|
|
26
27
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItem,
|
|
27
28
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue,
|
|
29
|
+
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValueArrayValue,
|
|
28
30
|
TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemStatus,
|
|
29
31
|
)
|
|
30
32
|
from ....api.client import Mirascope
|
|
33
|
+
from .utils import to_otlp_any_value
|
|
31
34
|
|
|
32
35
|
logger = logging.getLogger(__name__)
|
|
33
36
|
|
|
@@ -233,39 +236,56 @@ class MirascopeOTLPExporter(SpanExporter):
|
|
|
233
236
|
message=span.status.description or "",
|
|
234
237
|
)
|
|
235
238
|
|
|
239
|
+
# Convert events
|
|
240
|
+
events = None
|
|
241
|
+
if span.events:
|
|
242
|
+
events = []
|
|
243
|
+
for event in span.events:
|
|
244
|
+
event_attrs: list[dict[str, object]] = []
|
|
245
|
+
if event.attributes:
|
|
246
|
+
for key, value in event.attributes.items():
|
|
247
|
+
# Convert to OTLP attribute format with typed values
|
|
248
|
+
attr_value = self._convert_event_attribute_value(value)
|
|
249
|
+
event_attrs.append({"key": key, "value": attr_value})
|
|
250
|
+
events.append(
|
|
251
|
+
{
|
|
252
|
+
"name": event.name,
|
|
253
|
+
"timeUnixNano": str(event.timestamp)
|
|
254
|
+
if event.timestamp
|
|
255
|
+
else None,
|
|
256
|
+
"attributes": event_attrs if event_attrs else None,
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
|
|
236
260
|
trace_id = format(context.trace_id, "032x")
|
|
237
261
|
span_id = format(context.span_id, "016x")
|
|
238
262
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
263
|
+
# Build kwargs, only including events if present (to avoid sending null)
|
|
264
|
+
kwargs: dict[str, object] = {
|
|
265
|
+
"trace_id": trace_id,
|
|
266
|
+
"span_id": span_id,
|
|
267
|
+
"parent_span_id": (
|
|
243
268
|
format(span.parent.span_id, "016x")
|
|
244
269
|
if span.parent and span.parent.span_id
|
|
245
270
|
else None
|
|
246
271
|
),
|
|
247
|
-
name
|
|
248
|
-
kind
|
|
249
|
-
start_time_unix_nano
|
|
250
|
-
end_time_unix_nano
|
|
251
|
-
attributes
|
|
252
|
-
status
|
|
253
|
-
|
|
272
|
+
"name": span.name,
|
|
273
|
+
"kind": span.kind.value if span.kind else 0,
|
|
274
|
+
"start_time_unix_nano": str(span.start_time) if span.start_time else "0",
|
|
275
|
+
"end_time_unix_nano": str(span.end_time) if span.end_time else "0",
|
|
276
|
+
"attributes": attributes or None,
|
|
277
|
+
"status": status,
|
|
278
|
+
}
|
|
279
|
+
# Only include events if present (omit entirely to avoid sending null)
|
|
280
|
+
if events:
|
|
281
|
+
kwargs["events"] = events
|
|
282
|
+
|
|
283
|
+
return TracesCreateRequestResourceSpansItemScopeSpansItemSpansItem(**kwargs) # type: ignore[arg-type]
|
|
254
284
|
|
|
255
285
|
def _convert_attribute_value(
|
|
256
286
|
self, value: AttributeValue
|
|
257
287
|
) -> TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue:
|
|
258
|
-
"""Convert OpenTelemetry AttributeValue to Mirascope API's KeyValueValue.
|
|
259
|
-
|
|
260
|
-
This conversion is necessary because the Fern-generated API client
|
|
261
|
-
expects KeyValueValue objects, not OpenTelemetry's AttributeValue types.
|
|
262
|
-
|
|
263
|
-
Args:
|
|
264
|
-
value: An OpenTelemetry AttributeValue (bool, int, float, str, or Sequence)
|
|
265
|
-
|
|
266
|
-
Returns:
|
|
267
|
-
A KeyValueValue object for the Mirascope API
|
|
268
|
-
"""
|
|
288
|
+
"""Convert OpenTelemetry AttributeValue to Mirascope API's KeyValueValue."""
|
|
269
289
|
match value:
|
|
270
290
|
case str():
|
|
271
291
|
return TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue(
|
|
@@ -285,23 +305,21 @@ class MirascopeOTLPExporter(SpanExporter):
|
|
|
285
305
|
)
|
|
286
306
|
case _:
|
|
287
307
|
return TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValue(
|
|
288
|
-
|
|
308
|
+
array_value=TracesCreateRequestResourceSpansItemScopeSpansItemSpansItemAttributesItemValueArrayValue(
|
|
309
|
+
values=[to_otlp_any_value(v) for v in value]
|
|
310
|
+
)
|
|
289
311
|
)
|
|
290
312
|
|
|
313
|
+
def _convert_event_attribute_value(
|
|
314
|
+
self, value: AttributeValue
|
|
315
|
+
) -> dict[str, object]:
|
|
316
|
+
"""Convert OpenTelemetry AttributeValue to OTLP event attribute value format."""
|
|
317
|
+
return to_otlp_any_value(value)
|
|
318
|
+
|
|
291
319
|
def _convert_resource_attribute_value(
|
|
292
320
|
self, value: AttributeValue
|
|
293
321
|
) -> TracesCreateRequestResourceSpansItemResourceAttributesItemValue:
|
|
294
|
-
"""Convert OpenTelemetry AttributeValue to Mirascope API's resource KeyValueValue.
|
|
295
|
-
|
|
296
|
-
This conversion is necessary because the Fern-generated API client
|
|
297
|
-
expects KeyValueValue objects, not OpenTelemetry's AttributeValue types.
|
|
298
|
-
|
|
299
|
-
Args:
|
|
300
|
-
value: An OpenTelemetry AttributeValue (bool, int, float, str, or Sequence)
|
|
301
|
-
|
|
302
|
-
Returns:
|
|
303
|
-
A KeyValueValue object for the Mirascope API resource attributes
|
|
304
|
-
"""
|
|
322
|
+
"""Convert OpenTelemetry AttributeValue to Mirascope API's resource KeyValueValue."""
|
|
305
323
|
match value:
|
|
306
324
|
case str():
|
|
307
325
|
return TracesCreateRequestResourceSpansItemResourceAttributesItemValue(
|
|
@@ -321,7 +339,9 @@ class MirascopeOTLPExporter(SpanExporter):
|
|
|
321
339
|
)
|
|
322
340
|
case _:
|
|
323
341
|
return TracesCreateRequestResourceSpansItemResourceAttributesItemValue(
|
|
324
|
-
|
|
342
|
+
array_value=TracesCreateRequestResourceSpansItemResourceAttributesItemValueArrayValue(
|
|
343
|
+
values=[to_otlp_any_value(v) for v in value]
|
|
344
|
+
)
|
|
325
345
|
)
|
|
326
346
|
|
|
327
347
|
def shutdown(self) -> None:
|
|
@@ -4,6 +4,43 @@ This module provides helper functions for formatting and converting
|
|
|
4
4
|
OpenTelemetry data types for export.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Mapping, Sequence
|
|
10
|
+
|
|
11
|
+
from opentelemetry.util.types import AttributeValue
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def to_otlp_any_value(value: AttributeValue) -> dict[str, object]:
|
|
15
|
+
"""Convert AttributeValue to OTLP AnyValue (dict form).
|
|
16
|
+
|
|
17
|
+
- string/bool/int/float are converted to stringValue/boolValue/intValue/doubleValue
|
|
18
|
+
- Sequence (excluding str/bytes/Mapping) is converted to arrayValue.values
|
|
19
|
+
- Unsupported types fallback to stringValue=str(value)
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
value: An OpenTelemetry AttributeValue (bool, int, float, str, or Sequence)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A dict representing OTLP AnyValue (e.g., {"stringValue": "..."})
|
|
26
|
+
"""
|
|
27
|
+
match value:
|
|
28
|
+
case str():
|
|
29
|
+
return {"stringValue": value}
|
|
30
|
+
case bool():
|
|
31
|
+
return {"boolValue": value}
|
|
32
|
+
case int():
|
|
33
|
+
return {"intValue": str(value)}
|
|
34
|
+
case float():
|
|
35
|
+
return {"doubleValue": value}
|
|
36
|
+
case _ if isinstance(value, bytes | bytearray | memoryview | Mapping):
|
|
37
|
+
return {"stringValue": str(value)}
|
|
38
|
+
case _ if isinstance(value, Sequence):
|
|
39
|
+
values = [to_otlp_any_value(v) for v in value]
|
|
40
|
+
return {"arrayValue": {"values": values}}
|
|
41
|
+
case _:
|
|
42
|
+
return {"stringValue": str(value)}
|
|
43
|
+
|
|
7
44
|
|
|
8
45
|
def format_trace_id(trace_id: int) -> str:
|
|
9
46
|
"""Format a trace ID as a 32-character hex string.
|