mirascope 2.0.0a1__py3-none-any.whl → 2.0.0a3__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 +85 -0
- mirascope/api/_generated/client.py +155 -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 +7 -0
- mirascope/api/_generated/errors/bad_request_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/reference.md +167 -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 +21 -0
- mirascope/api/_generated/types/http_api_decode_error.py +31 -0
- mirascope/api/_generated/types/http_api_decode_error_tag.py +5 -0
- mirascope/api/_generated/types/issue.py +44 -0
- mirascope/api/_generated/types/issue_tag.py +17 -0
- mirascope/api/_generated/types/property_key.py +7 -0
- mirascope/api/_generated/types/property_key_tag.py +29 -0
- mirascope/api/_generated/types/property_key_tag_tag.py +5 -0
- mirascope/api/client.py +255 -0
- mirascope/api/settings.py +81 -0
- mirascope/llm/__init__.py +41 -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 +38 -0
- mirascope/llm/{clients → providers}/_missing_import_stubs.py +8 -6
- mirascope/llm/providers/anthropic/__init__.py +24 -0
- mirascope/llm/{clients → providers}/anthropic/_utils/decode.py +5 -4
- mirascope/llm/{clients → providers}/anthropic/_utils/encode.py +31 -10
- mirascope/llm/providers/anthropic/model_id.py +40 -0
- mirascope/llm/{clients/anthropic/clients.py → providers/anthropic/provider.py} +33 -418
- mirascope/llm/{clients → providers}/base/__init__.py +3 -3
- mirascope/llm/{clients → providers}/base/_utils.py +10 -7
- 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 +6 -4
- mirascope/llm/{clients → providers}/google/_utils/encode.py +30 -24
- mirascope/llm/providers/google/model_id.py +28 -0
- mirascope/llm/providers/google/provider.py +438 -0
- mirascope/llm/providers/load_provider.py +48 -0
- mirascope/llm/providers/mlx/__init__.py +24 -0
- mirascope/llm/providers/mlx/_utils.py +107 -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 +131 -0
- mirascope/llm/providers/mlx/mlx.py +237 -0
- mirascope/llm/providers/mlx/model_id.py +17 -0
- mirascope/llm/providers/mlx/provider.py +411 -0
- mirascope/llm/providers/model_id.py +16 -0
- mirascope/llm/providers/openai/__init__.py +6 -0
- mirascope/llm/providers/openai/completions/__init__.py +20 -0
- mirascope/llm/{clients/openai/responses → providers/openai/completions}/_utils/__init__.py +2 -0
- mirascope/llm/{clients → providers}/openai/completions/_utils/decode.py +5 -3
- mirascope/llm/{clients → providers}/openai/completions/_utils/encode.py +33 -23
- mirascope/llm/providers/openai/completions/provider.py +456 -0
- mirascope/llm/providers/openai/model_id.py +31 -0
- mirascope/llm/providers/openai/model_info.py +246 -0
- mirascope/llm/providers/openai/provider.py +386 -0
- mirascope/llm/providers/openai/responses/__init__.py +21 -0
- mirascope/llm/{clients → providers}/openai/responses/_utils/decode.py +5 -3
- mirascope/llm/{clients → providers}/openai/responses/_utils/encode.py +28 -17
- mirascope/llm/providers/openai/responses/provider.py +470 -0
- mirascope/llm/{clients → providers}/openai/shared/_utils.py +7 -3
- mirascope/llm/providers/provider_id.py +13 -0
- mirascope/llm/providers/provider_registry.py +167 -0
- mirascope/llm/responses/base_response.py +10 -5
- mirascope/llm/responses/base_stream_response.py +10 -5
- mirascope/llm/responses/response.py +24 -13
- mirascope/llm/responses/root_response.py +7 -12
- mirascope/llm/responses/stream_response.py +35 -23
- mirascope/llm/tools/__init__.py +9 -2
- mirascope/llm/tools/_utils.py +12 -3
- mirascope/llm/tools/decorator.py +10 -10
- mirascope/llm/tools/protocols.py +4 -4
- mirascope/llm/tools/tool_schema.py +44 -9
- mirascope/llm/tools/tools.py +12 -11
- 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.0a1.dist-info → mirascope-2.0.0a3.dist-info}/METADATA +77 -1
- mirascope-2.0.0a3.dist-info/RECORD +206 -0
- {mirascope-2.0.0a1.dist-info → mirascope-2.0.0a3.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/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/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/providers.py +0 -175
- mirascope-2.0.0a1.dist-info/RECORD +0 -102
- /mirascope/llm/{clients → providers}/anthropic/_utils/__init__.py +0 -0
- /mirascope/llm/{clients → providers}/base/kwargs.py +0 -0
- /mirascope/llm/{clients → providers}/base/params.py +0 -0
- /mirascope/llm/{clients → providers}/google/_utils/__init__.py +0 -0
- /mirascope/llm/{clients → providers}/google/message.py +0 -0
- /mirascope/llm/{clients/openai/completions → providers/openai/responses}/_utils/__init__.py +0 -0
- /mirascope/llm/{clients → providers}/openai/shared/__init__.py +0 -0
- {mirascope-2.0.0a1.dist-info → mirascope-2.0.0a3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Type definitions for the two-phase export system.
|
|
2
|
+
|
|
3
|
+
This module defines the event types and data structures used for
|
|
4
|
+
immediate start event transmission and batched end event export.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import Literal, TypedDict
|
|
9
|
+
|
|
10
|
+
from opentelemetry.util.types import AttributeValue
|
|
11
|
+
|
|
12
|
+
# TODO: unify/DRY types in the _internal package
|
|
13
|
+
|
|
14
|
+
SpanKind = Literal["CLIENT", "SERVER", "PRODUCER", "CONSUMER", "INTERNAL"]
|
|
15
|
+
StatusCode = Literal["UNSET", "OK", "ERROR"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SpanEventType(str, Enum):
|
|
19
|
+
"""Event types for span lifecycle tracking."""
|
|
20
|
+
|
|
21
|
+
SPAN_STARTED = "span_started"
|
|
22
|
+
SPAN_UPDATED = "span_updated"
|
|
23
|
+
SPAN_COMPLETED = "span_completed"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SpanEvent(TypedDict):
|
|
27
|
+
"""Individual event within a span."""
|
|
28
|
+
|
|
29
|
+
name: str
|
|
30
|
+
"""The name of the event."""
|
|
31
|
+
|
|
32
|
+
timestamp: int
|
|
33
|
+
"""Nanoseconds since epoch (OTel standard)."""
|
|
34
|
+
|
|
35
|
+
attributes: dict[str, AttributeValue]
|
|
36
|
+
"""Event-specific attributes."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Status(TypedDict):
|
|
40
|
+
"""Status representation for serialization."""
|
|
41
|
+
|
|
42
|
+
code: StatusCode
|
|
43
|
+
"""The status code: UNSET, OK, or ERROR."""
|
|
44
|
+
|
|
45
|
+
description: str | None
|
|
46
|
+
"""Optional human-readable description of the status."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SpanContextDict(TypedDict):
|
|
50
|
+
"""Span context for links and parent references."""
|
|
51
|
+
|
|
52
|
+
trace_id: str
|
|
53
|
+
"""The trace ID as a 32-character hex string."""
|
|
54
|
+
|
|
55
|
+
span_id: str
|
|
56
|
+
"""The span ID as a 16-character hex string."""
|
|
57
|
+
|
|
58
|
+
trace_flags: int
|
|
59
|
+
"""Trace flags (e.g., for sampling decisions)."""
|
|
60
|
+
|
|
61
|
+
trace_state: str | None
|
|
62
|
+
"""Optional vendor-specific trace state."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Link(TypedDict):
|
|
66
|
+
"""Link representation for span relationships."""
|
|
67
|
+
|
|
68
|
+
context: SpanContextDict
|
|
69
|
+
"""The linked span's context."""
|
|
70
|
+
|
|
71
|
+
attributes: dict[str, AttributeValue]
|
|
72
|
+
"""Attributes describing the link."""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class SpanStartEvent(TypedDict):
|
|
76
|
+
"""Minimal span data for immediate transmission.
|
|
77
|
+
|
|
78
|
+
This event is sent immediately when a span starts to provide
|
|
79
|
+
real-time visibility into long-running operations.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
trace_id: str
|
|
83
|
+
"""The trace ID as a 32-character hex string."""
|
|
84
|
+
|
|
85
|
+
span_id: str
|
|
86
|
+
"""The span ID as a 16-character hex string."""
|
|
87
|
+
|
|
88
|
+
parent_span_id: str | None
|
|
89
|
+
"""The parent span ID if this is a child span."""
|
|
90
|
+
|
|
91
|
+
name: str
|
|
92
|
+
"""The name of the span."""
|
|
93
|
+
|
|
94
|
+
start_time: int
|
|
95
|
+
"""Nanoseconds since epoch."""
|
|
96
|
+
|
|
97
|
+
kind: SpanKind
|
|
98
|
+
"""The span kind: CLIENT, SERVER, PRODUCER, CONSUMER, or INTERNAL."""
|
|
99
|
+
|
|
100
|
+
attributes: dict[str, AttributeValue]
|
|
101
|
+
"""Minimal required attributes only."""
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class SpanUpdateEvent(TypedDict, total=False):
|
|
105
|
+
"""Incremental updates to span data.
|
|
106
|
+
|
|
107
|
+
These events are batched and sent periodically to update
|
|
108
|
+
span attributes or add events without waiting for completion.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
trace_id: str
|
|
112
|
+
"""The trace ID as a 32-character hex string."""
|
|
113
|
+
|
|
114
|
+
span_id: str
|
|
115
|
+
"""The span ID as a 16-character hex string."""
|
|
116
|
+
|
|
117
|
+
timestamp: int
|
|
118
|
+
"""Nanoseconds since epoch."""
|
|
119
|
+
|
|
120
|
+
attributes: dict[str, AttributeValue]
|
|
121
|
+
"""Additional or updated attributes."""
|
|
122
|
+
|
|
123
|
+
events: list[SpanEvent]
|
|
124
|
+
"""New events to add to the span."""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class SpanCompleteEvent(TypedDict):
|
|
128
|
+
"""Complete span data for batch export.
|
|
129
|
+
|
|
130
|
+
This event contains all span data and is sent when the span
|
|
131
|
+
completes, typically in batches for efficiency.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
trace_id: str
|
|
135
|
+
"""The trace ID as a 32-character hex string."""
|
|
136
|
+
|
|
137
|
+
span_id: str
|
|
138
|
+
"""The span ID as a 16-character hex string."""
|
|
139
|
+
|
|
140
|
+
parent_span_id: str | None
|
|
141
|
+
"""The parent span ID if this is a child span."""
|
|
142
|
+
|
|
143
|
+
name: str
|
|
144
|
+
"""The name of the span."""
|
|
145
|
+
|
|
146
|
+
kind: SpanKind
|
|
147
|
+
"""The span kind: CLIENT, SERVER, PRODUCER, CONSUMER, or INTERNAL."""
|
|
148
|
+
|
|
149
|
+
start_time: int
|
|
150
|
+
"""Nanoseconds since epoch."""
|
|
151
|
+
|
|
152
|
+
end_time: int
|
|
153
|
+
"""Nanoseconds since epoch."""
|
|
154
|
+
|
|
155
|
+
status: Status
|
|
156
|
+
"""The final status of the span."""
|
|
157
|
+
|
|
158
|
+
attributes: dict[str, AttributeValue]
|
|
159
|
+
"""All span attributes."""
|
|
160
|
+
|
|
161
|
+
events: list[SpanEvent]
|
|
162
|
+
"""All events that occurred during the span."""
|
|
163
|
+
|
|
164
|
+
links: list[Link]
|
|
165
|
+
"""Links to other spans."""
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Utility functions for OpenTelemetry exporters.
|
|
2
|
+
|
|
3
|
+
This module provides helper functions for formatting and converting
|
|
4
|
+
OpenTelemetry data types for export.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_trace_id(trace_id: int) -> str:
|
|
9
|
+
"""Format a trace ID as a 32-character hex string.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
trace_id: The trace ID as an integer.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
32-character hexadecimal string representation of the trace ID.
|
|
16
|
+
"""
|
|
17
|
+
return format(trace_id, "032x")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def format_span_id(span_id: int) -> str:
|
|
21
|
+
"""Format a span ID as a 16-character hex string.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
span_id: The span ID as an integer.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
16-character hexadecimal string representation of the span ID.
|
|
28
|
+
"""
|
|
29
|
+
return format(span_id, "016x")
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Utilities for encoding Mirascope messages into GenAI semconv payloads."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from . import gen_ai_types
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from typing import TypeAlias
|
|
14
|
+
|
|
15
|
+
MessagePart: TypeAlias = (
|
|
16
|
+
gen_ai_types.TextPart
|
|
17
|
+
| gen_ai_types.ToolCallRequestPart
|
|
18
|
+
| gen_ai_types.ToolCallResponsePart
|
|
19
|
+
| gen_ai_types.BlobPart
|
|
20
|
+
| gen_ai_types.FilePart
|
|
21
|
+
| gen_ai_types.UriPart
|
|
22
|
+
| gen_ai_types.ReasoningPart
|
|
23
|
+
| gen_ai_types.GenericPart
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
from .....llm.content import (
|
|
28
|
+
Audio,
|
|
29
|
+
Base64ImageSource,
|
|
30
|
+
Document,
|
|
31
|
+
Image,
|
|
32
|
+
Text,
|
|
33
|
+
Thought,
|
|
34
|
+
ToolCall,
|
|
35
|
+
ToolOutput,
|
|
36
|
+
)
|
|
37
|
+
from .....llm.formatting import FormattableT
|
|
38
|
+
from .....llm.messages import AssistantMessage, Message, SystemMessage
|
|
39
|
+
from .....llm.responses.finish_reason import FinishReason
|
|
40
|
+
from .....llm.responses.root_response import RootResponse
|
|
41
|
+
from .....llm.tools import ToolkitT
|
|
42
|
+
from .....llm.types import Jsonable
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class OTelMessageSnapshot:
|
|
47
|
+
"""Structured view of system, input, and output messages."""
|
|
48
|
+
|
|
49
|
+
system_instructions: gen_ai_types.SystemInstructions
|
|
50
|
+
"""Instructions to be executed prior to the conversation."""
|
|
51
|
+
|
|
52
|
+
inputs: gen_ai_types.InputMessages
|
|
53
|
+
"""Messages to be sent to the model."""
|
|
54
|
+
|
|
55
|
+
outputs: gen_ai_types.OutputMessages
|
|
56
|
+
"""Messages received from the model."""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _serialize_message_parts(
|
|
60
|
+
parts: Sequence[
|
|
61
|
+
Text | ToolCall | ToolOutput[Jsonable] | Thought | Image | Audio | Document
|
|
62
|
+
],
|
|
63
|
+
) -> list[MessagePart]:
|
|
64
|
+
"""Serialize message content parts into GenAI-compliant dictionaries.
|
|
65
|
+
|
|
66
|
+
Handles Text, ToolCall, ToolOutput, Thought, Image, Audio, Document,
|
|
67
|
+
and str types.
|
|
68
|
+
"""
|
|
69
|
+
serialized: list[MessagePart] = []
|
|
70
|
+
for part in parts:
|
|
71
|
+
match part:
|
|
72
|
+
case Text():
|
|
73
|
+
text_part: gen_ai_types.TextPart = {
|
|
74
|
+
"type": "text",
|
|
75
|
+
"content": part.text,
|
|
76
|
+
}
|
|
77
|
+
serialized.append(text_part)
|
|
78
|
+
case ToolCall():
|
|
79
|
+
try:
|
|
80
|
+
arguments = json.loads(part.args)
|
|
81
|
+
except json.JSONDecodeError: # pragma: no cover
|
|
82
|
+
arguments = ""
|
|
83
|
+
tool_call_part: gen_ai_types.ToolCallRequestPart = {
|
|
84
|
+
"type": "tool_call",
|
|
85
|
+
"id": part.id,
|
|
86
|
+
"name": part.name,
|
|
87
|
+
"arguments": arguments,
|
|
88
|
+
}
|
|
89
|
+
serialized.append(tool_call_part)
|
|
90
|
+
case ToolOutput():
|
|
91
|
+
tool_output_part: gen_ai_types.ToolCallResponsePart = {
|
|
92
|
+
"type": "tool_call_response",
|
|
93
|
+
"id": part.id,
|
|
94
|
+
"response": part.value,
|
|
95
|
+
}
|
|
96
|
+
serialized.append(tool_output_part)
|
|
97
|
+
case Thought():
|
|
98
|
+
serialized.append(
|
|
99
|
+
gen_ai_types.ReasoningPart(type="reasoning", content=part.thought)
|
|
100
|
+
)
|
|
101
|
+
case Image():
|
|
102
|
+
if isinstance(part.source, Base64ImageSource):
|
|
103
|
+
image = gen_ai_types.BlobPart(
|
|
104
|
+
type="blob",
|
|
105
|
+
modality="image",
|
|
106
|
+
mime_type=part.source.mime_type,
|
|
107
|
+
content=part.source.data,
|
|
108
|
+
)
|
|
109
|
+
else:
|
|
110
|
+
image = gen_ai_types.UriPart(
|
|
111
|
+
type="uri", modality="image", uri=part.source.url
|
|
112
|
+
)
|
|
113
|
+
serialized.append(image)
|
|
114
|
+
case Audio():
|
|
115
|
+
serialized.append(
|
|
116
|
+
gen_ai_types.BlobPart(
|
|
117
|
+
type="blob",
|
|
118
|
+
modality="audio",
|
|
119
|
+
mime_type=part.source.mime_type,
|
|
120
|
+
content=part.source.data,
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
case Document(): # pragma: no cover
|
|
124
|
+
raise NotImplementedError(
|
|
125
|
+
"Document serialization is not yet supported by any provider. "
|
|
126
|
+
"This will be implemented when provider support is added."
|
|
127
|
+
)
|
|
128
|
+
return serialized
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _serialize_message(message: Message) -> gen_ai_types.ChatMessage:
|
|
132
|
+
"""Serialize a Mirascope message into a GenAI ChatMessage."""
|
|
133
|
+
content_parts = _serialize_message_parts(
|
|
134
|
+
message.content if not isinstance(message, SystemMessage) else [message.content]
|
|
135
|
+
)
|
|
136
|
+
serialized: gen_ai_types.ChatMessage = {
|
|
137
|
+
"role": message.role,
|
|
138
|
+
"parts": content_parts,
|
|
139
|
+
}
|
|
140
|
+
name = getattr(message, "name", None)
|
|
141
|
+
if name:
|
|
142
|
+
serialized["name"] = name
|
|
143
|
+
return serialized
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def map_finish_reason(
|
|
147
|
+
reason: FinishReason | None,
|
|
148
|
+
) -> gen_ai_types.FinishReason:
|
|
149
|
+
"""Map a finish reason to a GenAI finish reason."""
|
|
150
|
+
if reason is None:
|
|
151
|
+
return "stop"
|
|
152
|
+
elif reason == FinishReason.MAX_TOKENS:
|
|
153
|
+
return "length"
|
|
154
|
+
elif reason == FinishReason.REFUSAL: # pragma: no cover
|
|
155
|
+
return "content_filter"
|
|
156
|
+
raise ValueError(f"Unknown finish reason: {reason}") # pragma: no cover
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _serialize_output_message(
|
|
160
|
+
message: AssistantMessage, finish_reason: FinishReason | None
|
|
161
|
+
) -> gen_ai_types.OutputMessage:
|
|
162
|
+
"""Serialize an assistant message into a GenAI OutputMessage with finish_reason."""
|
|
163
|
+
chat_message = _serialize_message(message)
|
|
164
|
+
output_message: gen_ai_types.OutputMessage = {
|
|
165
|
+
"role": chat_message["role"],
|
|
166
|
+
"parts": chat_message["parts"],
|
|
167
|
+
"finish_reason": map_finish_reason(finish_reason),
|
|
168
|
+
}
|
|
169
|
+
if "name" in chat_message:
|
|
170
|
+
output_message["name"] = chat_message["name"]
|
|
171
|
+
return output_message
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def split_request_messages(
|
|
175
|
+
messages: Sequence[Message],
|
|
176
|
+
) -> tuple[gen_ai_types.SystemInstructions, gen_ai_types.InputMessages]:
|
|
177
|
+
"""Return serialized system instructions and non-system input messages.
|
|
178
|
+
|
|
179
|
+
System messages are flattened into a list of parts (not wrapped in message objects),
|
|
180
|
+
while other messages are serialized as complete ChatMessage objects.
|
|
181
|
+
"""
|
|
182
|
+
system_instructions: gen_ai_types.SystemInstructions = []
|
|
183
|
+
inputs: gen_ai_types.InputMessages = []
|
|
184
|
+
for message in messages:
|
|
185
|
+
if message.role == "system":
|
|
186
|
+
# System messages contribute only their parts (flattened)
|
|
187
|
+
parts = _serialize_message_parts(
|
|
188
|
+
[message.content]
|
|
189
|
+
if isinstance(message, SystemMessage)
|
|
190
|
+
else message.content
|
|
191
|
+
)
|
|
192
|
+
system_instructions.extend(parts)
|
|
193
|
+
else:
|
|
194
|
+
# Non-system messages are serialized as full ChatMessage objects
|
|
195
|
+
serialized = _serialize_message(message)
|
|
196
|
+
inputs.append(serialized)
|
|
197
|
+
return system_instructions, inputs
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def snapshot_from_response_messages(
|
|
201
|
+
*,
|
|
202
|
+
request_messages: Sequence[Message],
|
|
203
|
+
assistant_message: AssistantMessage,
|
|
204
|
+
finish_reason: FinishReason | None,
|
|
205
|
+
) -> OTelMessageSnapshot:
|
|
206
|
+
"""Build a snapshot using the request/response boundary for a call."""
|
|
207
|
+
|
|
208
|
+
system_instructions, inputs = split_request_messages(request_messages)
|
|
209
|
+
outputs = [_serialize_output_message(assistant_message, finish_reason)]
|
|
210
|
+
return OTelMessageSnapshot(
|
|
211
|
+
system_instructions=system_instructions,
|
|
212
|
+
inputs=inputs,
|
|
213
|
+
outputs=outputs,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def snapshot_from_root_response(
|
|
218
|
+
response: RootResponse[ToolkitT, FormattableT | None],
|
|
219
|
+
*,
|
|
220
|
+
request_messages: Sequence[Message] | None = None,
|
|
221
|
+
) -> OTelMessageSnapshot:
|
|
222
|
+
"""Build a snapshot directly from a `RootResponse`.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
response: The response that includes the entire conversation history.
|
|
226
|
+
request_messages: Optional explicit request message sequence. Defaults to all
|
|
227
|
+
but the final assistant message inside `response.messages`.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
assistant_message = response.messages[-1]
|
|
231
|
+
if not isinstance(assistant_message, AssistantMessage): # pragma: no cover
|
|
232
|
+
raise TypeError("Final response message must be an AssistantMessage")
|
|
233
|
+
|
|
234
|
+
return snapshot_from_response_messages(
|
|
235
|
+
request_messages=request_messages or response.messages[:-1],
|
|
236
|
+
assistant_message=assistant_message,
|
|
237
|
+
finish_reason=response.finish_reason,
|
|
238
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""OpenTelemetry Gen AI Semantic Conventions types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .gen_ai_input_messages import ChatMessage, InputMessages
|
|
6
|
+
from .gen_ai_output_messages import FinishReason, OutputMessage, OutputMessages
|
|
7
|
+
from .gen_ai_system_instructions import SystemInstructions
|
|
8
|
+
from .shared import (
|
|
9
|
+
BlobPart,
|
|
10
|
+
FilePart,
|
|
11
|
+
GenericPart,
|
|
12
|
+
Modality,
|
|
13
|
+
ReasoningPart,
|
|
14
|
+
Role,
|
|
15
|
+
TextPart,
|
|
16
|
+
ToolCallRequestPart,
|
|
17
|
+
ToolCallResponsePart,
|
|
18
|
+
UriPart,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"BlobPart",
|
|
23
|
+
"ChatMessage",
|
|
24
|
+
"FilePart",
|
|
25
|
+
"FinishReason",
|
|
26
|
+
"GenericPart",
|
|
27
|
+
"InputMessages",
|
|
28
|
+
"Modality",
|
|
29
|
+
"OutputMessage",
|
|
30
|
+
"OutputMessages",
|
|
31
|
+
"ReasoningPart",
|
|
32
|
+
"Role",
|
|
33
|
+
"SystemInstructions",
|
|
34
|
+
"TextPart",
|
|
35
|
+
"ToolCallRequestPart",
|
|
36
|
+
"ToolCallResponsePart",
|
|
37
|
+
"UriPart",
|
|
38
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""OpenTelemetry Gen AI Semantic Conventions types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TypeAlias, TypedDict
|
|
6
|
+
from typing_extensions import NotRequired
|
|
7
|
+
|
|
8
|
+
from . import shared
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChatMessage(TypedDict):
|
|
12
|
+
role: shared.Role | str
|
|
13
|
+
"""Role of the entity that created the message."""
|
|
14
|
+
|
|
15
|
+
parts: list[
|
|
16
|
+
shared.TextPart
|
|
17
|
+
| shared.ToolCallRequestPart
|
|
18
|
+
| shared.ToolCallResponsePart
|
|
19
|
+
| shared.BlobPart
|
|
20
|
+
| shared.FilePart
|
|
21
|
+
| shared.UriPart
|
|
22
|
+
| shared.ReasoningPart
|
|
23
|
+
| shared.GenericPart
|
|
24
|
+
]
|
|
25
|
+
"""List of message parts that make up the message content."""
|
|
26
|
+
|
|
27
|
+
name: NotRequired[str | None]
|
|
28
|
+
"""The name of the participant."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
InputMessages: TypeAlias = list[ChatMessage]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""OpenTelemetry Gen AI Semantic Conventions types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal, TypeAlias, TypedDict
|
|
6
|
+
from typing_extensions import NotRequired
|
|
7
|
+
|
|
8
|
+
from . import shared
|
|
9
|
+
|
|
10
|
+
FinishReason: TypeAlias = Literal[
|
|
11
|
+
"stop", "length", "content_filter", "tool_call", "error"
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OutputMessage(TypedDict):
|
|
16
|
+
role: shared.Role | str
|
|
17
|
+
"""Role of the entity that created the message."""
|
|
18
|
+
|
|
19
|
+
parts: list[
|
|
20
|
+
shared.TextPart
|
|
21
|
+
| shared.ToolCallRequestPart
|
|
22
|
+
| shared.ToolCallResponsePart
|
|
23
|
+
| shared.BlobPart
|
|
24
|
+
| shared.FilePart
|
|
25
|
+
| shared.UriPart
|
|
26
|
+
| shared.ReasoningPart
|
|
27
|
+
| shared.GenericPart
|
|
28
|
+
]
|
|
29
|
+
"""List of message parts that make up the message content."""
|
|
30
|
+
|
|
31
|
+
name: NotRequired[str | None]
|
|
32
|
+
"""The name of the participant."""
|
|
33
|
+
|
|
34
|
+
finish_reason: FinishReason | str
|
|
35
|
+
"""Reason for finishing the generation."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
OutputMessages: TypeAlias = list[OutputMessage]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""OpenTelemetry Gen AI Semantic Conventions types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TypeAlias
|
|
6
|
+
|
|
7
|
+
from . import shared
|
|
8
|
+
|
|
9
|
+
SystemInstructions: TypeAlias = list[
|
|
10
|
+
shared.TextPart
|
|
11
|
+
| shared.ToolCallRequestPart
|
|
12
|
+
| shared.ToolCallResponsePart
|
|
13
|
+
| shared.BlobPart
|
|
14
|
+
| shared.FilePart
|
|
15
|
+
| shared.UriPart
|
|
16
|
+
| shared.ReasoningPart
|
|
17
|
+
| shared.GenericPart
|
|
18
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""OpenTelemetry Gen AI Semantic Conventions types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Literal, TypeAlias, TypedDict
|
|
6
|
+
from typing_extensions import NotRequired
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BlobPart(TypedDict):
|
|
10
|
+
type: Literal["blob"]
|
|
11
|
+
"""The type of the content captured in this part."""
|
|
12
|
+
|
|
13
|
+
mime_type: NotRequired[str | None]
|
|
14
|
+
"""The IANA MIME type of the attached data."""
|
|
15
|
+
|
|
16
|
+
modality: Modality | str
|
|
17
|
+
"""The general modality of the data if it is known. Instrumentations SHOULD also set the mimeType field if the specific type is known."""
|
|
18
|
+
|
|
19
|
+
content: str
|
|
20
|
+
"""Raw bytes of the attached data. This field SHOULD be encoded as a base64 string when transmitted as JSON."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class FilePart(TypedDict):
|
|
24
|
+
type: Literal["file"]
|
|
25
|
+
"""The type of the content captured in this part."""
|
|
26
|
+
|
|
27
|
+
mime_type: NotRequired[str | None]
|
|
28
|
+
"""The IANA MIME type of the attached data."""
|
|
29
|
+
|
|
30
|
+
modality: Modality | str
|
|
31
|
+
"""The general modality of the data if it is known. Instrumentations SHOULD also set the mimeType field if the specific type is known."""
|
|
32
|
+
|
|
33
|
+
file_id: str
|
|
34
|
+
"""An identifier referencing a file that was pre-uploaded to the provider."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class GenericPart(TypedDict):
|
|
38
|
+
type: str
|
|
39
|
+
"""The type of the content captured in this part."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
Modality: TypeAlias = Literal["image", "video", "audio"]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ReasoningPart(TypedDict):
|
|
46
|
+
type: Literal["reasoning"]
|
|
47
|
+
"""The type of the content captured in this part."""
|
|
48
|
+
|
|
49
|
+
content: str
|
|
50
|
+
"""Reasoning/thinking content received from the model."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
Role: TypeAlias = Literal["system", "user", "assistant", "tool"]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TextPart(TypedDict):
|
|
57
|
+
type: Literal["text"]
|
|
58
|
+
"""The type of the content captured in this part."""
|
|
59
|
+
|
|
60
|
+
content: str
|
|
61
|
+
"""Text content sent to or received from the model."""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ToolCallRequestPart(TypedDict):
|
|
65
|
+
type: Literal["tool_call"]
|
|
66
|
+
"""The type of the content captured in this part."""
|
|
67
|
+
|
|
68
|
+
id: NotRequired[str | None]
|
|
69
|
+
"""Unique identifier for the tool call."""
|
|
70
|
+
|
|
71
|
+
name: str
|
|
72
|
+
"""Name of the tool."""
|
|
73
|
+
|
|
74
|
+
arguments: NotRequired[Any]
|
|
75
|
+
"""Arguments for the tool call."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ToolCallResponsePart(TypedDict):
|
|
79
|
+
type: Literal["tool_call_response"]
|
|
80
|
+
"""The type of the content captured in this part."""
|
|
81
|
+
|
|
82
|
+
id: NotRequired[str | None]
|
|
83
|
+
"""Unique tool call identifier."""
|
|
84
|
+
|
|
85
|
+
response: Any
|
|
86
|
+
"""Tool call response."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class UriPart(TypedDict):
|
|
90
|
+
type: Literal["uri"]
|
|
91
|
+
"""The type of the content captured in this part."""
|
|
92
|
+
|
|
93
|
+
mime_type: NotRequired[str | None]
|
|
94
|
+
"""The IANA MIME type of the attached data."""
|
|
95
|
+
|
|
96
|
+
modality: Modality | str
|
|
97
|
+
"""The general modality of the data if it is known. Instrumentations SHOULD also set the mimeType field if the specific type is known."""
|
|
98
|
+
|
|
99
|
+
uri: str
|
|
100
|
+
"""A URI referencing attached data. It should not be a base64 data URL, which should use the `blob` part instead. The URI may use a scheme known to the provider api (e.g. `gs://bucket/object.png`), or be a publicly accessible location."""
|