mirascope 2.0.0a2__py3-none-any.whl → 2.0.0a4__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 +2 -2
- mirascope/api/__init__.py +6 -0
- mirascope/api/_generated/README.md +207 -0
- mirascope/api/_generated/__init__.py +141 -0
- mirascope/api/_generated/client.py +163 -0
- mirascope/api/_generated/core/__init__.py +52 -0
- mirascope/api/_generated/core/api_error.py +23 -0
- mirascope/api/_generated/core/client_wrapper.py +58 -0
- mirascope/api/_generated/core/datetime_utils.py +30 -0
- mirascope/api/_generated/core/file.py +70 -0
- mirascope/api/_generated/core/force_multipart.py +16 -0
- mirascope/api/_generated/core/http_client.py +619 -0
- mirascope/api/_generated/core/http_response.py +55 -0
- mirascope/api/_generated/core/jsonable_encoder.py +102 -0
- mirascope/api/_generated/core/pydantic_utilities.py +310 -0
- mirascope/api/_generated/core/query_encoder.py +60 -0
- mirascope/api/_generated/core/remove_none_from_dict.py +11 -0
- mirascope/api/_generated/core/request_options.py +35 -0
- mirascope/api/_generated/core/serialization.py +282 -0
- mirascope/api/_generated/docs/__init__.py +4 -0
- mirascope/api/_generated/docs/client.py +95 -0
- mirascope/api/_generated/docs/raw_client.py +132 -0
- mirascope/api/_generated/environment.py +9 -0
- mirascope/api/_generated/errors/__init__.py +17 -0
- mirascope/api/_generated/errors/bad_request_error.py +15 -0
- mirascope/api/_generated/errors/conflict_error.py +15 -0
- mirascope/api/_generated/errors/forbidden_error.py +15 -0
- mirascope/api/_generated/errors/internal_server_error.py +15 -0
- mirascope/api/_generated/errors/not_found_error.py +15 -0
- mirascope/api/_generated/health/__init__.py +7 -0
- mirascope/api/_generated/health/client.py +96 -0
- mirascope/api/_generated/health/raw_client.py +129 -0
- mirascope/api/_generated/health/types/__init__.py +8 -0
- mirascope/api/_generated/health/types/health_check_response.py +24 -0
- mirascope/api/_generated/health/types/health_check_response_status.py +5 -0
- mirascope/api/_generated/organizations/__init__.py +25 -0
- mirascope/api/_generated/organizations/client.py +380 -0
- mirascope/api/_generated/organizations/raw_client.py +876 -0
- mirascope/api/_generated/organizations/types/__init__.py +23 -0
- mirascope/api/_generated/organizations/types/organizations_create_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_create_response_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_get_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_get_response_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_update_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_update_response_role.py +7 -0
- mirascope/api/_generated/projects/__init__.py +17 -0
- mirascope/api/_generated/projects/client.py +458 -0
- mirascope/api/_generated/projects/raw_client.py +1016 -0
- mirascope/api/_generated/projects/types/__init__.py +15 -0
- mirascope/api/_generated/projects/types/projects_create_response.py +30 -0
- mirascope/api/_generated/projects/types/projects_get_response.py +30 -0
- mirascope/api/_generated/projects/types/projects_list_response_item.py +30 -0
- mirascope/api/_generated/projects/types/projects_update_response.py +30 -0
- mirascope/api/_generated/reference.md +753 -0
- mirascope/api/_generated/traces/__init__.py +55 -0
- mirascope/api/_generated/traces/client.py +162 -0
- mirascope/api/_generated/traces/raw_client.py +168 -0
- mirascope/api/_generated/traces/types/__init__.py +95 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +36 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +31 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +25 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +54 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +28 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +35 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +35 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +27 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +54 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +28 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value_values_item.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +60 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +29 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +54 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +28 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value_values_item.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_response.py +27 -0
- mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +28 -0
- mirascope/api/_generated/types/__init__.py +37 -0
- mirascope/api/_generated/types/already_exists_error.py +24 -0
- mirascope/api/_generated/types/already_exists_error_tag.py +5 -0
- mirascope/api/_generated/types/database_error.py +24 -0
- mirascope/api/_generated/types/database_error_tag.py +5 -0
- mirascope/api/_generated/types/http_api_decode_error.py +29 -0
- mirascope/api/_generated/types/http_api_decode_error_tag.py +5 -0
- mirascope/api/_generated/types/issue.py +40 -0
- mirascope/api/_generated/types/issue_tag.py +17 -0
- mirascope/api/_generated/types/not_found_error_body.py +24 -0
- mirascope/api/_generated/types/not_found_error_tag.py +5 -0
- mirascope/api/_generated/types/permission_denied_error.py +24 -0
- mirascope/api/_generated/types/permission_denied_error_tag.py +7 -0
- mirascope/api/_generated/types/property_key.py +7 -0
- mirascope/api/_generated/types/property_key_key.py +27 -0
- mirascope/api/_generated/types/property_key_key_tag.py +5 -0
- mirascope/api/client.py +255 -0
- mirascope/api/settings.py +81 -0
- mirascope/llm/__init__.py +45 -11
- mirascope/llm/calls/calls.py +81 -57
- mirascope/llm/calls/decorator.py +121 -115
- mirascope/llm/content/__init__.py +3 -2
- mirascope/llm/context/_utils.py +19 -6
- mirascope/llm/exceptions.py +30 -16
- mirascope/llm/formatting/_utils.py +9 -5
- mirascope/llm/formatting/format.py +2 -2
- mirascope/llm/formatting/from_call_args.py +2 -2
- mirascope/llm/messages/message.py +13 -5
- mirascope/llm/models/__init__.py +2 -2
- mirascope/llm/models/models.py +189 -81
- mirascope/llm/prompts/__init__.py +13 -12
- mirascope/llm/prompts/_utils.py +27 -24
- mirascope/llm/prompts/decorator.py +133 -204
- mirascope/llm/prompts/prompts.py +424 -0
- mirascope/llm/prompts/protocols.py +25 -59
- mirascope/llm/providers/__init__.py +44 -0
- mirascope/llm/{clients → providers}/_missing_import_stubs.py +8 -6
- mirascope/llm/providers/anthropic/__init__.py +29 -0
- mirascope/llm/providers/anthropic/_utils/__init__.py +23 -0
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +271 -0
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +216 -0
- mirascope/llm/{clients → providers}/anthropic/_utils/decode.py +44 -11
- mirascope/llm/providers/anthropic/_utils/encode.py +356 -0
- mirascope/llm/providers/anthropic/beta_provider.py +322 -0
- mirascope/llm/providers/anthropic/model_id.py +23 -0
- mirascope/llm/providers/anthropic/model_info.py +87 -0
- mirascope/llm/providers/anthropic/provider.py +416 -0
- mirascope/llm/{clients → providers}/base/__init__.py +3 -3
- mirascope/llm/{clients → providers}/base/_utils.py +25 -8
- mirascope/llm/{clients/base/client.py → providers/base/base_provider.py} +255 -126
- mirascope/llm/providers/google/__init__.py +21 -0
- mirascope/llm/{clients → providers}/google/_utils/decode.py +61 -7
- mirascope/llm/{clients → providers}/google/_utils/encode.py +44 -30
- mirascope/llm/providers/google/model_id.py +22 -0
- mirascope/llm/providers/google/model_info.py +62 -0
- mirascope/llm/providers/google/provider.py +442 -0
- mirascope/llm/providers/load_provider.py +54 -0
- mirascope/llm/providers/mlx/__init__.py +24 -0
- mirascope/llm/providers/mlx/_utils.py +129 -0
- mirascope/llm/providers/mlx/encoding/__init__.py +8 -0
- mirascope/llm/providers/mlx/encoding/base.py +69 -0
- mirascope/llm/providers/mlx/encoding/transformers.py +147 -0
- mirascope/llm/providers/mlx/mlx.py +237 -0
- mirascope/llm/providers/mlx/model_id.py +17 -0
- mirascope/llm/providers/mlx/provider.py +415 -0
- mirascope/llm/providers/model_id.py +16 -0
- mirascope/llm/providers/ollama/__init__.py +19 -0
- mirascope/llm/providers/ollama/provider.py +71 -0
- mirascope/llm/providers/openai/__init__.py +6 -0
- mirascope/llm/providers/openai/completions/__init__.py +25 -0
- mirascope/llm/{clients → providers}/openai/completions/_utils/__init__.py +2 -0
- mirascope/llm/{clients → providers}/openai/completions/_utils/decode.py +60 -6
- mirascope/llm/{clients → providers}/openai/completions/_utils/encode.py +37 -26
- mirascope/llm/providers/openai/completions/base_provider.py +513 -0
- mirascope/llm/providers/openai/completions/provider.py +22 -0
- mirascope/llm/providers/openai/model_id.py +31 -0
- mirascope/llm/providers/openai/model_info.py +303 -0
- mirascope/llm/providers/openai/provider.py +398 -0
- mirascope/llm/providers/openai/responses/__init__.py +21 -0
- mirascope/llm/{clients → providers}/openai/responses/_utils/decode.py +59 -6
- mirascope/llm/{clients → providers}/openai/responses/_utils/encode.py +34 -23
- mirascope/llm/providers/openai/responses/provider.py +469 -0
- mirascope/llm/providers/provider_id.py +23 -0
- mirascope/llm/providers/provider_registry.py +169 -0
- mirascope/llm/providers/together/__init__.py +19 -0
- mirascope/llm/providers/together/provider.py +40 -0
- mirascope/llm/responses/__init__.py +3 -0
- mirascope/llm/responses/base_response.py +14 -5
- mirascope/llm/responses/base_stream_response.py +35 -6
- mirascope/llm/responses/finish_reason.py +1 -0
- mirascope/llm/responses/response.py +33 -13
- mirascope/llm/responses/root_response.py +12 -13
- mirascope/llm/responses/stream_response.py +35 -23
- mirascope/llm/responses/usage.py +95 -0
- mirascope/llm/tools/__init__.py +9 -2
- mirascope/llm/tools/_utils.py +12 -3
- mirascope/llm/tools/protocols.py +4 -4
- mirascope/llm/tools/tool_schema.py +44 -9
- mirascope/llm/tools/tools.py +10 -9
- mirascope/ops/__init__.py +156 -0
- mirascope/ops/_internal/__init__.py +5 -0
- mirascope/ops/_internal/closure.py +1118 -0
- mirascope/ops/_internal/configuration.py +126 -0
- mirascope/ops/_internal/context.py +76 -0
- mirascope/ops/_internal/exporters/__init__.py +26 -0
- mirascope/ops/_internal/exporters/exporters.py +342 -0
- mirascope/ops/_internal/exporters/processors.py +104 -0
- mirascope/ops/_internal/exporters/types.py +165 -0
- mirascope/ops/_internal/exporters/utils.py +29 -0
- mirascope/ops/_internal/instrumentation/__init__.py +8 -0
- mirascope/ops/_internal/instrumentation/llm/__init__.py +8 -0
- mirascope/ops/_internal/instrumentation/llm/encode.py +238 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/__init__.py +38 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_input_messages.py +31 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_output_messages.py +38 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_system_instructions.py +18 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/shared.py +100 -0
- mirascope/ops/_internal/instrumentation/llm/llm.py +1288 -0
- mirascope/ops/_internal/propagation.py +198 -0
- mirascope/ops/_internal/protocols.py +51 -0
- mirascope/ops/_internal/session.py +139 -0
- mirascope/ops/_internal/spans.py +232 -0
- mirascope/ops/_internal/traced_calls.py +371 -0
- mirascope/ops/_internal/traced_functions.py +394 -0
- mirascope/ops/_internal/tracing.py +276 -0
- mirascope/ops/_internal/types.py +13 -0
- mirascope/ops/_internal/utils.py +75 -0
- mirascope/ops/_internal/versioned_calls.py +512 -0
- mirascope/ops/_internal/versioned_functions.py +346 -0
- mirascope/ops/_internal/versioning.py +303 -0
- mirascope/ops/exceptions.py +21 -0
- {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/METADATA +78 -3
- mirascope-2.0.0a4.dist-info/RECORD +247 -0
- {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/WHEEL +1 -1
- mirascope/graphs/__init__.py +0 -22
- mirascope/graphs/finite_state_machine.py +0 -625
- mirascope/llm/agents/__init__.py +0 -15
- mirascope/llm/agents/agent.py +0 -97
- mirascope/llm/agents/agent_template.py +0 -45
- mirascope/llm/agents/decorator.py +0 -176
- mirascope/llm/calls/base_call.py +0 -33
- mirascope/llm/clients/__init__.py +0 -34
- mirascope/llm/clients/anthropic/__init__.py +0 -25
- mirascope/llm/clients/anthropic/_utils/encode.py +0 -243
- mirascope/llm/clients/anthropic/clients.py +0 -819
- mirascope/llm/clients/anthropic/model_ids.py +0 -8
- mirascope/llm/clients/google/__init__.py +0 -20
- mirascope/llm/clients/google/clients.py +0 -853
- mirascope/llm/clients/google/model_ids.py +0 -15
- mirascope/llm/clients/openai/__init__.py +0 -25
- mirascope/llm/clients/openai/completions/__init__.py +0 -28
- mirascope/llm/clients/openai/completions/_utils/model_features.py +0 -81
- mirascope/llm/clients/openai/completions/clients.py +0 -833
- mirascope/llm/clients/openai/completions/model_ids.py +0 -8
- mirascope/llm/clients/openai/responses/__init__.py +0 -26
- mirascope/llm/clients/openai/responses/_utils/__init__.py +0 -13
- mirascope/llm/clients/openai/responses/_utils/model_features.py +0 -87
- mirascope/llm/clients/openai/responses/clients.py +0 -832
- mirascope/llm/clients/openai/responses/model_ids.py +0 -8
- mirascope/llm/clients/openai/shared/__init__.py +0 -7
- mirascope/llm/clients/openai/shared/_utils.py +0 -55
- mirascope/llm/clients/providers.py +0 -175
- mirascope-2.0.0a2.dist-info/RECORD +0 -102
- /mirascope/llm/{clients → providers}/base/kwargs.py +0 -0
- /mirascope/llm/{clients → providers}/base/params.py +0 -0
- /mirascope/llm/{clients/anthropic → providers/google}/_utils/__init__.py +0 -0
- /mirascope/llm/{clients → providers}/google/message.py +0 -0
- /mirascope/llm/{clients/google → providers/openai/responses}/_utils/__init__.py +0 -0
- {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"""Tracing decorators for `mirascope.ops`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from collections.abc import Generator
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import (
|
|
10
|
+
Any,
|
|
11
|
+
Generic,
|
|
12
|
+
Literal,
|
|
13
|
+
TypeVar,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from opentelemetry.util.types import AttributeValue
|
|
17
|
+
|
|
18
|
+
from ...llm.context import Context, DepsT
|
|
19
|
+
from ...llm.responses.root_response import RootResponse
|
|
20
|
+
from .protocols import (
|
|
21
|
+
AsyncContextFunction,
|
|
22
|
+
AsyncFunction,
|
|
23
|
+
SyncContextFunction,
|
|
24
|
+
SyncFunction,
|
|
25
|
+
)
|
|
26
|
+
from .spans import Span
|
|
27
|
+
from .types import Jsonable, P, R
|
|
28
|
+
from .utils import PrimitiveType, extract_arguments, get_qualified_name, json_dumps
|
|
29
|
+
|
|
30
|
+
FunctionT = TypeVar(
|
|
31
|
+
"FunctionT",
|
|
32
|
+
bound="SyncFunction[..., Any] | AsyncFunction[..., Any]",
|
|
33
|
+
covariant=True,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def record_result_to_span(span: Span, result: object) -> None:
|
|
38
|
+
"""Records the function result in the given span.
|
|
39
|
+
|
|
40
|
+
This is a shared helper function used by all traced function classes
|
|
41
|
+
to record results consistently.
|
|
42
|
+
"""
|
|
43
|
+
if result is None:
|
|
44
|
+
return # NOTE: we treat `None` valued results as such through omission.
|
|
45
|
+
elif isinstance(result, PrimitiveType):
|
|
46
|
+
output: str | int | float | bool = result
|
|
47
|
+
elif isinstance(result, RootResponse):
|
|
48
|
+
output = result.pretty()
|
|
49
|
+
else:
|
|
50
|
+
try:
|
|
51
|
+
output = json_dumps(result)
|
|
52
|
+
except (TypeError, ValueError):
|
|
53
|
+
output = repr(result)
|
|
54
|
+
span.set(**{"mirascope.trace.output": output})
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(kw_only=True, frozen=True)
|
|
58
|
+
class _BaseTrace(Generic[R]):
|
|
59
|
+
"""Base class for trace results with shared functionality."""
|
|
60
|
+
|
|
61
|
+
result: R
|
|
62
|
+
"""Return value produced by the traced call."""
|
|
63
|
+
|
|
64
|
+
span: Span
|
|
65
|
+
"""Span associated with the traced call."""
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def span_id(self) -> str | None:
|
|
69
|
+
"""Returns the ID of the span for the trace."""
|
|
70
|
+
return self.span.span_id
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def trace_id(self) -> str | None:
|
|
74
|
+
"""Returns the ID of the trace."""
|
|
75
|
+
return self.span.trace_id
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(kw_only=True, frozen=True)
|
|
79
|
+
class Trace(_BaseTrace[R]):
|
|
80
|
+
"""Result container returned by traced function calls.
|
|
81
|
+
|
|
82
|
+
Provides access to the function result and trace span metadata,
|
|
83
|
+
as well as per-call operations for annotation, tagging, and assignment.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def annotate(
|
|
87
|
+
self,
|
|
88
|
+
*,
|
|
89
|
+
label: Literal["pass", "fail"],
|
|
90
|
+
reasoning: str | None = None,
|
|
91
|
+
metadata: dict[str, Jsonable] | None = None,
|
|
92
|
+
) -> None:
|
|
93
|
+
"""Annotates the current trace span."""
|
|
94
|
+
raise NotImplementedError("Trace.annotate not yet implemented")
|
|
95
|
+
|
|
96
|
+
def tag(self, *tags: str) -> None:
|
|
97
|
+
"""Adds given tags to the current trace span."""
|
|
98
|
+
raise NotImplementedError("Trace.tag not yet implemented")
|
|
99
|
+
|
|
100
|
+
def assign(self, *emails: str) -> None:
|
|
101
|
+
"""Assigns the trace to users with the given emails."""
|
|
102
|
+
raise NotImplementedError("Trace.assign not yet implemented")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(kw_only=True, frozen=True)
|
|
106
|
+
class AsyncTrace(_BaseTrace[R]):
|
|
107
|
+
"""Result container returned by async traced function calls.
|
|
108
|
+
|
|
109
|
+
Provides access to the function result and trace span metadata,
|
|
110
|
+
as well as per-call operations for annotation, tagging, and assignment.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
async def annotate(
|
|
114
|
+
self,
|
|
115
|
+
*,
|
|
116
|
+
label: str | None = None,
|
|
117
|
+
data: dict[str, Jsonable] | None = None,
|
|
118
|
+
reasoning: str | None = None,
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Annotates the current trace span."""
|
|
121
|
+
raise NotImplementedError("AsyncTrace.annotate not yet implemented")
|
|
122
|
+
|
|
123
|
+
async def tag(self, *tags: str) -> None:
|
|
124
|
+
"""Adds given tags to the current trace span."""
|
|
125
|
+
raise NotImplementedError("AsyncTrace.tag not yet implemented")
|
|
126
|
+
|
|
127
|
+
async def assign(self, *emails: str) -> None:
|
|
128
|
+
"""Assigns the trace to users with the given emails."""
|
|
129
|
+
raise NotImplementedError("AsyncTrace.assign not yet implemented")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass(kw_only=True)
|
|
133
|
+
class _BaseFunction(Generic[P, R, FunctionT], ABC):
|
|
134
|
+
"""Abstract base class for base functions to be traced."""
|
|
135
|
+
|
|
136
|
+
fn: FunctionT
|
|
137
|
+
"""The function being traced."""
|
|
138
|
+
|
|
139
|
+
tags: tuple[str, ...]
|
|
140
|
+
"""Tags to be associated with the trace span."""
|
|
141
|
+
|
|
142
|
+
metadata: dict[str, str] = field(default_factory=dict)
|
|
143
|
+
"""Arbitrary key-value pairs for additional metadata."""
|
|
144
|
+
|
|
145
|
+
_qualified_name: str = field(init=False)
|
|
146
|
+
"""Fully qualified name of the wrapped function."""
|
|
147
|
+
|
|
148
|
+
_module_name: str = field(init=False)
|
|
149
|
+
"""Module name of the wrapped function."""
|
|
150
|
+
|
|
151
|
+
_is_async: bool = field(init=False)
|
|
152
|
+
"""Whether the wrapped function is asynchronous."""
|
|
153
|
+
|
|
154
|
+
def __post_init__(self) -> None:
|
|
155
|
+
"""Initialize additional attributes after dataclass init."""
|
|
156
|
+
self._qualified_name = get_qualified_name(self.fn)
|
|
157
|
+
self._module_name = getattr(self.fn, "__module__", "")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@dataclass(kw_only=True)
|
|
161
|
+
class _BaseTracedFunction(_BaseFunction[P, R, FunctionT]):
|
|
162
|
+
"""Abstract base class for traced function wrappers."""
|
|
163
|
+
|
|
164
|
+
@contextmanager
|
|
165
|
+
def _span(self, *args: P.args, **kwargs: P.kwargs) -> Generator[Span, None, None]:
|
|
166
|
+
"""Returns a span context manager populated with call attributes."""
|
|
167
|
+
arg_types, arg_values = extract_arguments(self.fn, *args, **kwargs)
|
|
168
|
+
with Span(self._qualified_name) as span:
|
|
169
|
+
attributes: dict[str, AttributeValue] = {
|
|
170
|
+
"mirascope.type": "trace",
|
|
171
|
+
"mirascope.fn.qualname": self._qualified_name,
|
|
172
|
+
"mirascope.fn.module": self._module_name,
|
|
173
|
+
"mirascope.fn.is_async": self._is_async,
|
|
174
|
+
"mirascope.trace.arg_types": json_dumps(arg_types),
|
|
175
|
+
"mirascope.trace.arg_values": json_dumps(arg_values),
|
|
176
|
+
}
|
|
177
|
+
if self.tags:
|
|
178
|
+
attributes["mirascope.trace.tags"] = self.tags
|
|
179
|
+
if self.metadata:
|
|
180
|
+
attributes["mirascope.trace.metadata"] = json_dumps(self.metadata)
|
|
181
|
+
span.set(**attributes)
|
|
182
|
+
yield span
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dataclass(kw_only=True)
|
|
186
|
+
class BaseSyncTracedFunction(_BaseTracedFunction[P, R, SyncFunction[P, R]]):
|
|
187
|
+
"""Abstract base class for synchronous traced function wrappers."""
|
|
188
|
+
|
|
189
|
+
_is_async: bool = field(default=False, init=False)
|
|
190
|
+
"""Whether the wrapped function is asynchronous."""
|
|
191
|
+
|
|
192
|
+
@abstractmethod
|
|
193
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
194
|
+
"""Returns the result of the traced function directly."""
|
|
195
|
+
...
|
|
196
|
+
|
|
197
|
+
@abstractmethod
|
|
198
|
+
def wrapped(self, *args: P.args, **kwargs: P.kwargs) -> Trace[R]:
|
|
199
|
+
"""Returns the trace containing the function result and span info."""
|
|
200
|
+
...
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@dataclass(kw_only=True)
|
|
204
|
+
class TracedFunction(BaseSyncTracedFunction[P, R]):
|
|
205
|
+
"""Wrapper for synchronous functions with tracing capabilities.
|
|
206
|
+
|
|
207
|
+
Provides traced execution of synchronous functions, returning Trace
|
|
208
|
+
with access to span information.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
212
|
+
"""Returns the result of the traced function directly."""
|
|
213
|
+
with self._span(*args, **kwargs) as span:
|
|
214
|
+
result = self.fn(*args, **kwargs)
|
|
215
|
+
record_result_to_span(span, result)
|
|
216
|
+
return result
|
|
217
|
+
|
|
218
|
+
def wrapped(self, *args: P.args, **kwargs: P.kwargs) -> Trace[R]:
|
|
219
|
+
"""Returns the trace containing the function result and span info."""
|
|
220
|
+
with self._span(*args, **kwargs) as span:
|
|
221
|
+
result = self.fn(*args, **kwargs)
|
|
222
|
+
record_result_to_span(span, result)
|
|
223
|
+
return Trace(result=result, span=span)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dataclass(kw_only=True)
|
|
227
|
+
class BaseAsyncTracedFunction(_BaseTracedFunction[P, R, AsyncFunction[P, R]]):
|
|
228
|
+
"""Abstract base class for asynchronous traced function wrappers."""
|
|
229
|
+
|
|
230
|
+
_is_async: bool = field(default=True, init=False)
|
|
231
|
+
"""Whether the wrapped function is asynchronous."""
|
|
232
|
+
|
|
233
|
+
@abstractmethod
|
|
234
|
+
async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
235
|
+
"""Returns the result of the traced function directly."""
|
|
236
|
+
...
|
|
237
|
+
|
|
238
|
+
@abstractmethod
|
|
239
|
+
async def wrapped(self, *args: P.args, **kwargs: P.kwargs) -> AsyncTrace[R]:
|
|
240
|
+
"""Returns the trace containing the function result and span info."""
|
|
241
|
+
...
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@dataclass(kw_only=True)
|
|
245
|
+
class AsyncTracedFunction(BaseAsyncTracedFunction[P, R]):
|
|
246
|
+
"""Wrapper for asynchronous functions with tracing capabilities.
|
|
247
|
+
|
|
248
|
+
Provides traced execution of asynchronous functions, returning AsyncTrace
|
|
249
|
+
with access to span information.
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
253
|
+
"""Returns the result of the traced function directly."""
|
|
254
|
+
with self._span(*args, **kwargs) as span:
|
|
255
|
+
result = await self.fn(*args, **kwargs)
|
|
256
|
+
record_result_to_span(span, result)
|
|
257
|
+
return result
|
|
258
|
+
|
|
259
|
+
async def wrapped(self, *args: P.args, **kwargs: P.kwargs) -> AsyncTrace[R]:
|
|
260
|
+
"""Returns the trace containing the function result and span info."""
|
|
261
|
+
with self._span(*args, **kwargs) as span:
|
|
262
|
+
result = await self.fn(*args, **kwargs)
|
|
263
|
+
record_result_to_span(span, result)
|
|
264
|
+
return AsyncTrace(result=result, span=span)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@dataclass(kw_only=True)
|
|
268
|
+
class _BaseTracedContextFunction(
|
|
269
|
+
_BaseFunction[P, R, FunctionT], Generic[P, DepsT, R, FunctionT]
|
|
270
|
+
):
|
|
271
|
+
"""Abstract base class for traced function wrappers."""
|
|
272
|
+
|
|
273
|
+
_is_async: bool = field(default=False, init=False)
|
|
274
|
+
"""Whether the wrapped function is asynchronous."""
|
|
275
|
+
|
|
276
|
+
@contextmanager
|
|
277
|
+
def _span(
|
|
278
|
+
self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
|
|
279
|
+
) -> Generator[Span, None, None]:
|
|
280
|
+
"""Returns a span context manager populated with call attributes."""
|
|
281
|
+
arg_types, arg_values = extract_arguments(self.fn, ctx, *args, **kwargs)
|
|
282
|
+
with Span(name=self._qualified_name) as span:
|
|
283
|
+
attributes: dict[str, AttributeValue] = {
|
|
284
|
+
"mirascope.type": "trace",
|
|
285
|
+
"mirascope.fn.qualname": self._qualified_name,
|
|
286
|
+
"mirascope.fn.module": self.fn.__module__,
|
|
287
|
+
"mirascope.fn.is_async": self._is_async,
|
|
288
|
+
"mirascope.trace.arg_types": json_dumps(arg_types),
|
|
289
|
+
"mirascope.trace.arg_values": json_dumps(arg_values),
|
|
290
|
+
}
|
|
291
|
+
if self.tags:
|
|
292
|
+
attributes["mirascope.trace.tags"] = self.tags
|
|
293
|
+
if self.metadata:
|
|
294
|
+
attributes["mirascope.trace.metadata"] = json_dumps(self.metadata)
|
|
295
|
+
span.set(**attributes)
|
|
296
|
+
yield span
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@dataclass(kw_only=True)
|
|
300
|
+
class BaseTracedSyncContextFunction(
|
|
301
|
+
_BaseTracedContextFunction[P, DepsT, R, SyncContextFunction[P, DepsT, R]]
|
|
302
|
+
):
|
|
303
|
+
"""Abstract base class for synchronous traced function wrappers."""
|
|
304
|
+
|
|
305
|
+
_is_async: bool = field(default=False, init=False)
|
|
306
|
+
"""Whether the wrapped function is asynchronous."""
|
|
307
|
+
|
|
308
|
+
@abstractmethod
|
|
309
|
+
def __call__(self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs) -> R:
|
|
310
|
+
"""Returns the result of the traced function directly."""
|
|
311
|
+
...
|
|
312
|
+
|
|
313
|
+
@abstractmethod
|
|
314
|
+
def wrapped(
|
|
315
|
+
self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
|
|
316
|
+
) -> Trace[R]:
|
|
317
|
+
"""Returns the trace containing the function result and span info."""
|
|
318
|
+
...
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@dataclass(kw_only=True)
|
|
322
|
+
class TracedContextFunction(BaseTracedSyncContextFunction[P, DepsT, R]):
|
|
323
|
+
"""Wrapper for synchronous context functions with tracing capabilities.
|
|
324
|
+
|
|
325
|
+
Provides traced execution of synchronous functions that take a Context parameter,
|
|
326
|
+
returning Trace with access to span information.
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
def __call__(self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs) -> R:
|
|
330
|
+
"""Returns the result of the traced function directly."""
|
|
331
|
+
with self._span(ctx, *args, **kwargs) as span:
|
|
332
|
+
result = self.fn(ctx, *args, **kwargs)
|
|
333
|
+
record_result_to_span(span, result)
|
|
334
|
+
return result
|
|
335
|
+
|
|
336
|
+
def wrapped(
|
|
337
|
+
self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
|
|
338
|
+
) -> Trace[R]:
|
|
339
|
+
"""Returns the trace containing the function result and span info."""
|
|
340
|
+
with self._span(ctx, *args, **kwargs) as span:
|
|
341
|
+
result = self.fn(ctx, *args, **kwargs)
|
|
342
|
+
record_result_to_span(span, result)
|
|
343
|
+
return Trace(result=result, span=span)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@dataclass(kw_only=True)
|
|
347
|
+
class BaseTracedAsyncContextFunction(
|
|
348
|
+
_BaseTracedContextFunction[P, DepsT, R, AsyncContextFunction[P, DepsT, R]]
|
|
349
|
+
):
|
|
350
|
+
"""Abstract base class for synchronous traced function wrappers."""
|
|
351
|
+
|
|
352
|
+
_is_async: bool = field(default=True, init=False)
|
|
353
|
+
"""Whether the wrapped function is asynchronous."""
|
|
354
|
+
|
|
355
|
+
@abstractmethod
|
|
356
|
+
async def __call__(
|
|
357
|
+
self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
|
|
358
|
+
) -> R:
|
|
359
|
+
"""Returns the result of the traced function directly."""
|
|
360
|
+
...
|
|
361
|
+
|
|
362
|
+
@abstractmethod
|
|
363
|
+
async def wrapped(
|
|
364
|
+
self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
|
|
365
|
+
) -> AsyncTrace[R]:
|
|
366
|
+
"""Returns the trace containing the function result and span info."""
|
|
367
|
+
...
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@dataclass(kw_only=True)
|
|
371
|
+
class AsyncTracedContextFunction(BaseTracedAsyncContextFunction[P, DepsT, R]):
|
|
372
|
+
"""Wrapper for asynchronous context functions with tracing capabilities.
|
|
373
|
+
|
|
374
|
+
Provides traced execution of asynchronous functions that take a Context parameter,
|
|
375
|
+
returning AsyncTrace with access to span information.
|
|
376
|
+
"""
|
|
377
|
+
|
|
378
|
+
async def __call__(
|
|
379
|
+
self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
|
|
380
|
+
) -> R:
|
|
381
|
+
"""Returns the result of the traced function directly."""
|
|
382
|
+
with self._span(ctx, *args, **kwargs) as span:
|
|
383
|
+
result = await self.fn(ctx, *args, **kwargs)
|
|
384
|
+
record_result_to_span(span, result)
|
|
385
|
+
return result
|
|
386
|
+
|
|
387
|
+
async def wrapped(
|
|
388
|
+
self, ctx: Context[DepsT], *args: P.args, **kwargs: P.kwargs
|
|
389
|
+
) -> AsyncTrace[R]:
|
|
390
|
+
"""Returns the trace containing the function result and span info."""
|
|
391
|
+
with self._span(ctx, *args, **kwargs) as span:
|
|
392
|
+
result = await self.fn(ctx, *args, **kwargs)
|
|
393
|
+
record_result_to_span(span, result)
|
|
394
|
+
return AsyncTrace(result=result, span=span)
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import (
|
|
6
|
+
TYPE_CHECKING,
|
|
7
|
+
overload,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from ...llm.calls import AsyncCall, AsyncContextCall, Call, ContextCall
|
|
11
|
+
from ...llm.context import DepsT
|
|
12
|
+
from .protocols import (
|
|
13
|
+
AsyncFunction,
|
|
14
|
+
SyncFunction,
|
|
15
|
+
fn_is_async,
|
|
16
|
+
)
|
|
17
|
+
from .traced_calls import (
|
|
18
|
+
TracedAsyncCall,
|
|
19
|
+
TracedAsyncContextCall,
|
|
20
|
+
TracedCall,
|
|
21
|
+
TracedContextCall,
|
|
22
|
+
is_call_type,
|
|
23
|
+
wrap_call,
|
|
24
|
+
)
|
|
25
|
+
from .traced_functions import (
|
|
26
|
+
AsyncTracedFunction,
|
|
27
|
+
TracedFunction,
|
|
28
|
+
)
|
|
29
|
+
from .types import P, R
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from ...llm.formatting import FormattableT
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(kw_only=True)
|
|
36
|
+
class TraceDecorator:
|
|
37
|
+
"""Decorator implementation for adding tracing capabilities to functions."""
|
|
38
|
+
|
|
39
|
+
tags: tuple[str, ...] = ()
|
|
40
|
+
"""Tags to be associated with traced function calls."""
|
|
41
|
+
|
|
42
|
+
metadata: dict[str, str] = field(default_factory=dict)
|
|
43
|
+
"""Arbitrary key-value pairs for additional metadata."""
|
|
44
|
+
|
|
45
|
+
# IMPORTANT: The order of these overloads matters for type inference.
|
|
46
|
+
# Call type overloads come first, then function overloads.
|
|
47
|
+
@overload
|
|
48
|
+
def __call__( # pyright: ignore[reportOverlappingOverload]
|
|
49
|
+
self,
|
|
50
|
+
fn: AsyncContextCall[P, DepsT, FormattableT],
|
|
51
|
+
) -> TracedAsyncContextCall[P, DepsT, FormattableT]:
|
|
52
|
+
"""Overload for applying decorator to an AsyncContextCall."""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
@overload
|
|
56
|
+
def __call__(
|
|
57
|
+
self,
|
|
58
|
+
fn: ContextCall[P, DepsT, FormattableT],
|
|
59
|
+
) -> TracedContextCall[P, DepsT, FormattableT]:
|
|
60
|
+
"""Overload for applying decorator to a ContextCall."""
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
@overload
|
|
64
|
+
def __call__(
|
|
65
|
+
self,
|
|
66
|
+
fn: AsyncCall[P, FormattableT],
|
|
67
|
+
) -> TracedAsyncCall[P, FormattableT]:
|
|
68
|
+
"""Overload for applying decorator to an AsyncCall."""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
@overload
|
|
72
|
+
def __call__(
|
|
73
|
+
self,
|
|
74
|
+
fn: Call[P, FormattableT],
|
|
75
|
+
) -> TracedCall[P, FormattableT]:
|
|
76
|
+
"""Overload for applying decorator to a Call."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
@overload
|
|
80
|
+
def __call__(
|
|
81
|
+
self,
|
|
82
|
+
fn: AsyncFunction[P, R],
|
|
83
|
+
) -> AsyncTracedFunction[P, R]:
|
|
84
|
+
"""Overload for applying decorator to an async function."""
|
|
85
|
+
...
|
|
86
|
+
|
|
87
|
+
@overload
|
|
88
|
+
def __call__(
|
|
89
|
+
self,
|
|
90
|
+
fn: SyncFunction[P, R],
|
|
91
|
+
) -> TracedFunction[P, R]:
|
|
92
|
+
"""Overload for applying decorator to a sync function."""
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
def __call__( # pyright: ignore[reportGeneralTypeIssues]
|
|
96
|
+
self,
|
|
97
|
+
fn: (
|
|
98
|
+
AsyncContextCall[P, DepsT, FormattableT]
|
|
99
|
+
| ContextCall[P, DepsT, FormattableT]
|
|
100
|
+
| AsyncCall[P, FormattableT]
|
|
101
|
+
| Call[P, FormattableT]
|
|
102
|
+
| AsyncFunction[P, R]
|
|
103
|
+
| SyncFunction[P, R]
|
|
104
|
+
),
|
|
105
|
+
) -> (
|
|
106
|
+
TracedAsyncContextCall[P, DepsT, FormattableT]
|
|
107
|
+
| TracedContextCall[P, DepsT, FormattableT]
|
|
108
|
+
| TracedAsyncCall[P, FormattableT]
|
|
109
|
+
| TracedCall[P, FormattableT]
|
|
110
|
+
| AsyncTracedFunction[P, R]
|
|
111
|
+
| TracedFunction[P, R]
|
|
112
|
+
):
|
|
113
|
+
"""Applies the decorator to the given function or Call object."""
|
|
114
|
+
if is_call_type(fn):
|
|
115
|
+
return wrap_call(fn=fn, tags=self.tags, metadata=self.metadata)
|
|
116
|
+
elif fn_is_async(fn):
|
|
117
|
+
return AsyncTracedFunction(fn=fn, tags=self.tags, metadata=self.metadata)
|
|
118
|
+
else:
|
|
119
|
+
return TracedFunction(fn=fn, tags=self.tags, metadata=self.metadata)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@overload
|
|
123
|
+
def trace(
|
|
124
|
+
__fn: None = None,
|
|
125
|
+
*,
|
|
126
|
+
tags: list[str] | None = None,
|
|
127
|
+
metadata: dict[str, str] | None = None,
|
|
128
|
+
) -> TraceDecorator:
|
|
129
|
+
"""Overload for providing kwargs before decorating (e.g. tags)."""
|
|
130
|
+
...
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@overload
|
|
134
|
+
def trace( # pyright: ignore[reportOverlappingOverload]
|
|
135
|
+
__fn: AsyncContextCall[P, DepsT, FormattableT],
|
|
136
|
+
*,
|
|
137
|
+
tags: None = None,
|
|
138
|
+
metadata: None = None,
|
|
139
|
+
) -> TracedAsyncContextCall[P, DepsT, FormattableT]:
|
|
140
|
+
"""Overload for directly decorating an AsyncContextCall."""
|
|
141
|
+
...
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@overload
|
|
145
|
+
def trace(
|
|
146
|
+
__fn: ContextCall[P, DepsT, FormattableT],
|
|
147
|
+
*,
|
|
148
|
+
tags: None = None,
|
|
149
|
+
metadata: None = None,
|
|
150
|
+
) -> TracedContextCall[P, DepsT, FormattableT]:
|
|
151
|
+
"""Overload for directly decorating a ContextCall."""
|
|
152
|
+
...
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@overload
|
|
156
|
+
def trace(
|
|
157
|
+
__fn: AsyncCall[P, FormattableT],
|
|
158
|
+
*,
|
|
159
|
+
tags: None = None,
|
|
160
|
+
metadata: None = None,
|
|
161
|
+
) -> TracedAsyncCall[P, FormattableT]:
|
|
162
|
+
"""Overload for directly decorating an AsyncCall."""
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@overload
|
|
167
|
+
def trace(
|
|
168
|
+
__fn: Call[P, FormattableT],
|
|
169
|
+
*,
|
|
170
|
+
tags: None = None,
|
|
171
|
+
metadata: None = None,
|
|
172
|
+
) -> TracedCall[P, FormattableT]:
|
|
173
|
+
"""Overload for directly decorating a Call."""
|
|
174
|
+
...
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@overload
|
|
178
|
+
def trace(
|
|
179
|
+
__fn: AsyncFunction[P, R],
|
|
180
|
+
*,
|
|
181
|
+
tags: None = None,
|
|
182
|
+
metadata: None = None,
|
|
183
|
+
) -> AsyncTracedFunction[P, R]:
|
|
184
|
+
"""Overload for directly (no argument) decorating an asynchronous function"""
|
|
185
|
+
...
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@overload
|
|
189
|
+
def trace(
|
|
190
|
+
__fn: SyncFunction[P, R],
|
|
191
|
+
*,
|
|
192
|
+
tags: None = None,
|
|
193
|
+
metadata: None = None,
|
|
194
|
+
) -> TracedFunction[P, R]:
|
|
195
|
+
"""Overload for directly (no argument) decorating a synchronous function"""
|
|
196
|
+
...
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def trace( # pyright: ignore[reportGeneralTypeIssues]
|
|
200
|
+
__fn: (
|
|
201
|
+
AsyncContextCall[P, DepsT, FormattableT]
|
|
202
|
+
| ContextCall[P, DepsT, FormattableT]
|
|
203
|
+
| AsyncCall[P, FormattableT]
|
|
204
|
+
| Call[P, FormattableT]
|
|
205
|
+
| AsyncFunction[P, R]
|
|
206
|
+
| SyncFunction[P, R]
|
|
207
|
+
| None
|
|
208
|
+
) = None,
|
|
209
|
+
*,
|
|
210
|
+
tags: Sequence[str] | None = None,
|
|
211
|
+
metadata: dict[str, str] | None = None,
|
|
212
|
+
) -> (
|
|
213
|
+
TracedAsyncContextCall[P, DepsT, FormattableT]
|
|
214
|
+
| TracedContextCall[P, DepsT, FormattableT]
|
|
215
|
+
| TracedAsyncCall[P, FormattableT]
|
|
216
|
+
| TracedCall[P, FormattableT]
|
|
217
|
+
| AsyncTracedFunction[P, R]
|
|
218
|
+
| TracedFunction[P, R]
|
|
219
|
+
| TraceDecorator
|
|
220
|
+
):
|
|
221
|
+
"""Decorator for adding tracing capabilities to functions and LLM calls.
|
|
222
|
+
|
|
223
|
+
Creates a wrapper that enables distributed tracing, performance monitoring,
|
|
224
|
+
and execution tracking for decorated functions. When called, the decorated
|
|
225
|
+
function returns a Trace containing both the result and span info.
|
|
226
|
+
|
|
227
|
+
When decorating an @llm.call function, returns a TracedCall that wraps both
|
|
228
|
+
the call and stream methods with tracing capabilities.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
__fn: The function or Call object to decorate.
|
|
232
|
+
tags: Optional list of string tags to associate with traced executions.
|
|
233
|
+
metadata: Arbitrary key-value pairs for additional metadata.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
A decorator that wraps functions with tracing capabilities.
|
|
237
|
+
|
|
238
|
+
Example:
|
|
239
|
+
```python
|
|
240
|
+
@ops.trace
|
|
241
|
+
def process_data(data: dict) -> dict:
|
|
242
|
+
return {"processed": data}
|
|
243
|
+
|
|
244
|
+
traced_result = process_data({"key": "value"})
|
|
245
|
+
print(traced_result.result) # {"processed": {"key": "value"}}
|
|
246
|
+
print(traced_result.span_id) # Access span ID
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Example:
|
|
250
|
+
```python
|
|
251
|
+
@ops.trace
|
|
252
|
+
@llm.call("gpt-4o-mini")
|
|
253
|
+
def recommend_book(genre: str):
|
|
254
|
+
return f"Recommend a {genre} book"
|
|
255
|
+
|
|
256
|
+
# Returns Response directly (execution is still traced)
|
|
257
|
+
response = recommend_book("fantasy")
|
|
258
|
+
print(response.content)
|
|
259
|
+
|
|
260
|
+
# Use .wrapped() to get Trace[Response] with span info
|
|
261
|
+
trace = recommend_book.wrapped("fantasy")
|
|
262
|
+
print(trace.result.content)
|
|
263
|
+
print(trace.span_id)
|
|
264
|
+
```
|
|
265
|
+
"""
|
|
266
|
+
tags = tuple(sorted(set(tags or [])))
|
|
267
|
+
metadata = metadata or {}
|
|
268
|
+
if __fn is None:
|
|
269
|
+
return TraceDecorator(tags=tags, metadata=metadata)
|
|
270
|
+
|
|
271
|
+
if is_call_type(__fn):
|
|
272
|
+
return wrap_call(fn=__fn, tags=tags, metadata=metadata)
|
|
273
|
+
elif fn_is_async(__fn):
|
|
274
|
+
return AsyncTracedFunction(fn=__fn, tags=tags, metadata=metadata)
|
|
275
|
+
else:
|
|
276
|
+
return TracedFunction(fn=__fn, tags=tags, metadata=metadata)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Type definitions for Mirascope tracing and capabilities."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, Sequence
|
|
4
|
+
from typing import ParamSpec, TypeAlias
|
|
5
|
+
from typing_extensions import TypeVar
|
|
6
|
+
|
|
7
|
+
P = ParamSpec("P")
|
|
8
|
+
R = TypeVar("R", infer_variance=True)
|
|
9
|
+
|
|
10
|
+
Jsonable: TypeAlias = (
|
|
11
|
+
None | str | int | float | bool | Sequence["Jsonable"] | Mapping[str, "Jsonable"]
|
|
12
|
+
)
|
|
13
|
+
"""Simple type alias for JSON-serializable types."""
|