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.
Files changed (230) hide show
  1. mirascope/_utils.py +34 -0
  2. mirascope/api/_generated/__init__.py +186 -5
  3. mirascope/api/_generated/annotations/client.py +38 -6
  4. mirascope/api/_generated/annotations/raw_client.py +366 -47
  5. mirascope/api/_generated/annotations/types/annotations_create_response.py +19 -6
  6. mirascope/api/_generated/annotations/types/annotations_get_response.py +19 -6
  7. mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item.py +22 -7
  8. mirascope/api/_generated/annotations/types/annotations_update_response.py +19 -6
  9. mirascope/api/_generated/api_keys/__init__.py +12 -2
  10. mirascope/api/_generated/api_keys/client.py +107 -6
  11. mirascope/api/_generated/api_keys/raw_client.py +486 -38
  12. mirascope/api/_generated/api_keys/types/__init__.py +7 -1
  13. mirascope/api/_generated/api_keys/types/api_keys_list_all_for_org_response_item.py +40 -0
  14. mirascope/api/_generated/client.py +36 -0
  15. mirascope/api/_generated/docs/raw_client.py +71 -9
  16. mirascope/api/_generated/environment.py +3 -3
  17. mirascope/api/_generated/environments/__init__.py +6 -0
  18. mirascope/api/_generated/environments/client.py +158 -9
  19. mirascope/api/_generated/environments/raw_client.py +620 -52
  20. mirascope/api/_generated/environments/types/__init__.py +10 -0
  21. mirascope/api/_generated/environments/types/environments_get_analytics_response.py +60 -0
  22. mirascope/api/_generated/environments/types/environments_get_analytics_response_top_functions_item.py +24 -0
  23. mirascope/api/_generated/{organizations/types/organizations_credits_response.py → environments/types/environments_get_analytics_response_top_models_item.py} +6 -3
  24. mirascope/api/_generated/errors/__init__.py +6 -0
  25. mirascope/api/_generated/errors/bad_request_error.py +5 -2
  26. mirascope/api/_generated/errors/conflict_error.py +5 -2
  27. mirascope/api/_generated/errors/payment_required_error.py +15 -0
  28. mirascope/api/_generated/errors/service_unavailable_error.py +14 -0
  29. mirascope/api/_generated/errors/too_many_requests_error.py +15 -0
  30. mirascope/api/_generated/functions/__init__.py +10 -0
  31. mirascope/api/_generated/functions/client.py +222 -8
  32. mirascope/api/_generated/functions/raw_client.py +975 -134
  33. mirascope/api/_generated/functions/types/__init__.py +28 -4
  34. mirascope/api/_generated/functions/types/functions_get_by_env_response.py +53 -0
  35. mirascope/api/_generated/functions/types/functions_get_by_env_response_dependencies_value.py +22 -0
  36. mirascope/api/_generated/functions/types/functions_list_by_env_response.py +25 -0
  37. mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +56 -0
  38. mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item_dependencies_value.py +22 -0
  39. mirascope/api/_generated/health/raw_client.py +74 -10
  40. mirascope/api/_generated/organization_invitations/__init__.py +33 -0
  41. mirascope/api/_generated/organization_invitations/client.py +546 -0
  42. mirascope/api/_generated/organization_invitations/raw_client.py +1519 -0
  43. mirascope/api/_generated/organization_invitations/types/__init__.py +53 -0
  44. mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response.py +34 -0
  45. mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response_role.py +7 -0
  46. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_request_role.py +7 -0
  47. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response.py +48 -0
  48. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_role.py +7 -0
  49. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_status.py +7 -0
  50. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response.py +48 -0
  51. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_role.py +7 -0
  52. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_status.py +7 -0
  53. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item.py +48 -0
  54. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_role.py +7 -0
  55. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_status.py +7 -0
  56. mirascope/api/_generated/organization_memberships/__init__.py +19 -0
  57. mirascope/api/_generated/organization_memberships/client.py +302 -0
  58. mirascope/api/_generated/organization_memberships/raw_client.py +736 -0
  59. mirascope/api/_generated/organization_memberships/types/__init__.py +27 -0
  60. mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item.py +33 -0
  61. mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item_role.py +7 -0
  62. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_request_role.py +7 -0
  63. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response.py +31 -0
  64. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response_role.py +7 -0
  65. mirascope/api/_generated/organizations/__init__.py +26 -2
  66. mirascope/api/_generated/organizations/client.py +442 -20
  67. mirascope/api/_generated/organizations/raw_client.py +1763 -164
  68. mirascope/api/_generated/organizations/types/__init__.py +48 -2
  69. mirascope/api/_generated/organizations/types/organizations_create_payment_intent_response.py +24 -0
  70. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_request_target_plan.py +7 -0
  71. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response.py +47 -0
  72. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item.py +33 -0
  73. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item_resource.py +7 -0
  74. mirascope/api/_generated/organizations/types/organizations_router_balance_response.py +24 -0
  75. mirascope/api/_generated/organizations/types/organizations_subscription_response.py +53 -0
  76. mirascope/api/_generated/organizations/types/organizations_subscription_response_current_plan.py +7 -0
  77. mirascope/api/_generated/organizations/types/organizations_subscription_response_payment_method.py +26 -0
  78. mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change.py +34 -0
  79. mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change_target_plan.py +7 -0
  80. mirascope/api/_generated/organizations/types/organizations_update_subscription_request_target_plan.py +7 -0
  81. mirascope/api/_generated/organizations/types/organizations_update_subscription_response.py +35 -0
  82. mirascope/api/_generated/project_memberships/__init__.py +25 -0
  83. mirascope/api/_generated/project_memberships/client.py +437 -0
  84. mirascope/api/_generated/project_memberships/raw_client.py +1039 -0
  85. mirascope/api/_generated/project_memberships/types/__init__.py +29 -0
  86. mirascope/api/_generated/project_memberships/types/project_memberships_create_request_role.py +7 -0
  87. mirascope/api/_generated/project_memberships/types/project_memberships_create_response.py +35 -0
  88. mirascope/api/_generated/project_memberships/types/project_memberships_create_response_role.py +7 -0
  89. mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item.py +33 -0
  90. mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item_role.py +7 -0
  91. mirascope/api/_generated/project_memberships/types/project_memberships_update_request_role.py +7 -0
  92. mirascope/api/_generated/project_memberships/types/project_memberships_update_response.py +35 -0
  93. mirascope/api/_generated/project_memberships/types/project_memberships_update_response_role.py +7 -0
  94. mirascope/api/_generated/projects/raw_client.py +415 -58
  95. mirascope/api/_generated/reference.md +2767 -397
  96. mirascope/api/_generated/tags/__init__.py +19 -0
  97. mirascope/api/_generated/tags/client.py +504 -0
  98. mirascope/api/_generated/tags/raw_client.py +1288 -0
  99. mirascope/api/_generated/tags/types/__init__.py +17 -0
  100. mirascope/api/_generated/tags/types/tags_create_response.py +41 -0
  101. mirascope/api/_generated/tags/types/tags_get_response.py +41 -0
  102. mirascope/api/_generated/tags/types/tags_list_response.py +23 -0
  103. mirascope/api/_generated/tags/types/tags_list_response_tags_item.py +41 -0
  104. mirascope/api/_generated/tags/types/tags_update_response.py +41 -0
  105. mirascope/api/_generated/token_cost/__init__.py +7 -0
  106. mirascope/api/_generated/token_cost/client.py +160 -0
  107. mirascope/api/_generated/token_cost/raw_client.py +264 -0
  108. mirascope/api/_generated/token_cost/types/__init__.py +8 -0
  109. mirascope/api/_generated/token_cost/types/token_cost_calculate_request_usage.py +54 -0
  110. mirascope/api/_generated/token_cost/types/token_cost_calculate_response.py +52 -0
  111. mirascope/api/_generated/traces/__init__.py +20 -0
  112. mirascope/api/_generated/traces/client.py +543 -0
  113. mirascope/api/_generated/traces/raw_client.py +1366 -96
  114. mirascope/api/_generated/traces/types/__init__.py +28 -0
  115. mirascope/api/_generated/traces/types/traces_get_analytics_summary_response.py +6 -0
  116. mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response.py +33 -0
  117. mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response_spans_item.py +88 -0
  118. mirascope/api/_generated/traces/types/traces_get_trace_detail_response_spans_item.py +0 -2
  119. mirascope/api/_generated/traces/types/traces_list_by_function_hash_response.py +25 -0
  120. mirascope/api/_generated/traces/types/traces_list_by_function_hash_response_traces_item.py +44 -0
  121. mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item.py +26 -0
  122. mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item_operator.py +7 -0
  123. mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_by.py +7 -0
  124. mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_order.py +7 -0
  125. mirascope/api/_generated/traces/types/traces_search_by_env_response.py +26 -0
  126. mirascope/api/_generated/traces/types/traces_search_by_env_response_spans_item.py +50 -0
  127. mirascope/api/_generated/traces/types/traces_search_response_spans_item.py +10 -1
  128. mirascope/api/_generated/types/__init__.py +32 -2
  129. mirascope/api/_generated/types/bad_request_error_body.py +50 -0
  130. mirascope/api/_generated/types/date.py +3 -0
  131. mirascope/api/_generated/types/immutable_resource_error.py +22 -0
  132. mirascope/api/_generated/types/internal_server_error_body.py +3 -3
  133. mirascope/api/_generated/types/plan_limit_exceeded_error.py +32 -0
  134. mirascope/api/_generated/types/plan_limit_exceeded_error_tag.py +7 -0
  135. mirascope/api/_generated/types/pricing_unavailable_error.py +23 -0
  136. mirascope/api/_generated/types/rate_limit_error.py +31 -0
  137. mirascope/api/_generated/types/rate_limit_error_tag.py +5 -0
  138. mirascope/api/_generated/types/service_unavailable_error_body.py +24 -0
  139. mirascope/api/_generated/types/service_unavailable_error_tag.py +7 -0
  140. mirascope/api/_generated/types/subscription_past_due_error.py +31 -0
  141. mirascope/api/_generated/types/subscription_past_due_error_tag.py +7 -0
  142. mirascope/api/settings.py +19 -1
  143. mirascope/llm/__init__.py +53 -10
  144. mirascope/llm/calls/__init__.py +2 -1
  145. mirascope/llm/calls/calls.py +29 -20
  146. mirascope/llm/calls/decorator.py +21 -7
  147. mirascope/llm/content/tool_output.py +22 -5
  148. mirascope/llm/exceptions.py +284 -71
  149. mirascope/llm/formatting/__init__.py +17 -0
  150. mirascope/llm/formatting/format.py +112 -35
  151. mirascope/llm/formatting/output_parser.py +178 -0
  152. mirascope/llm/formatting/partial.py +80 -7
  153. mirascope/llm/formatting/primitives.py +192 -0
  154. mirascope/llm/formatting/types.py +20 -8
  155. mirascope/llm/messages/__init__.py +3 -0
  156. mirascope/llm/messages/_utils.py +34 -0
  157. mirascope/llm/models/__init__.py +5 -0
  158. mirascope/llm/models/models.py +137 -69
  159. mirascope/llm/{providers/base → models}/params.py +7 -57
  160. mirascope/llm/models/thinking_config.py +61 -0
  161. mirascope/llm/prompts/_utils.py +0 -32
  162. mirascope/llm/prompts/decorator.py +16 -5
  163. mirascope/llm/prompts/prompts.py +160 -92
  164. mirascope/llm/providers/__init__.py +1 -4
  165. mirascope/llm/providers/anthropic/_utils/__init__.py +2 -0
  166. mirascope/llm/providers/anthropic/_utils/beta_decode.py +18 -9
  167. mirascope/llm/providers/anthropic/_utils/beta_encode.py +62 -13
  168. mirascope/llm/providers/anthropic/_utils/decode.py +18 -9
  169. mirascope/llm/providers/anthropic/_utils/encode.py +26 -7
  170. mirascope/llm/providers/anthropic/_utils/errors.py +2 -2
  171. mirascope/llm/providers/anthropic/beta_provider.py +64 -18
  172. mirascope/llm/providers/anthropic/provider.py +91 -33
  173. mirascope/llm/providers/base/__init__.py +0 -4
  174. mirascope/llm/providers/base/_utils.py +55 -6
  175. mirascope/llm/providers/base/base_provider.py +116 -37
  176. mirascope/llm/providers/google/_utils/__init__.py +2 -0
  177. mirascope/llm/providers/google/_utils/decode.py +20 -7
  178. mirascope/llm/providers/google/_utils/encode.py +26 -7
  179. mirascope/llm/providers/google/_utils/errors.py +3 -2
  180. mirascope/llm/providers/google/provider.py +64 -18
  181. mirascope/llm/providers/mirascope/_utils.py +13 -17
  182. mirascope/llm/providers/mirascope/provider.py +49 -18
  183. mirascope/llm/providers/mlx/_utils.py +7 -2
  184. mirascope/llm/providers/mlx/encoding/base.py +5 -2
  185. mirascope/llm/providers/mlx/encoding/transformers.py +5 -2
  186. mirascope/llm/providers/mlx/mlx.py +23 -6
  187. mirascope/llm/providers/mlx/provider.py +42 -13
  188. mirascope/llm/providers/openai/_utils/errors.py +2 -2
  189. mirascope/llm/providers/openai/completions/_utils/encode.py +20 -16
  190. mirascope/llm/providers/openai/completions/base_provider.py +40 -11
  191. mirascope/llm/providers/openai/provider.py +40 -10
  192. mirascope/llm/providers/openai/responses/_utils/__init__.py +2 -0
  193. mirascope/llm/providers/openai/responses/_utils/decode.py +19 -6
  194. mirascope/llm/providers/openai/responses/_utils/encode.py +22 -10
  195. mirascope/llm/providers/openai/responses/provider.py +56 -18
  196. mirascope/llm/providers/provider_registry.py +93 -19
  197. mirascope/llm/responses/__init__.py +6 -1
  198. mirascope/llm/responses/_utils.py +102 -12
  199. mirascope/llm/responses/base_response.py +5 -2
  200. mirascope/llm/responses/base_stream_response.py +115 -25
  201. mirascope/llm/responses/response.py +2 -1
  202. mirascope/llm/responses/root_response.py +89 -17
  203. mirascope/llm/responses/stream_response.py +6 -9
  204. mirascope/llm/tools/decorator.py +9 -4
  205. mirascope/llm/tools/tool_schema.py +17 -6
  206. mirascope/llm/tools/toolkit.py +35 -27
  207. mirascope/llm/tools/tools.py +45 -20
  208. mirascope/ops/__init__.py +4 -0
  209. mirascope/ops/_internal/closure.py +4 -1
  210. mirascope/ops/_internal/configuration.py +82 -31
  211. mirascope/ops/_internal/exporters/exporters.py +55 -35
  212. mirascope/ops/_internal/exporters/utils.py +37 -0
  213. mirascope/ops/_internal/instrumentation/llm/common.py +530 -0
  214. mirascope/ops/_internal/instrumentation/llm/cost.py +190 -0
  215. mirascope/ops/_internal/instrumentation/llm/encode.py +1 -1
  216. mirascope/ops/_internal/instrumentation/llm/llm.py +116 -1242
  217. mirascope/ops/_internal/instrumentation/llm/model.py +1798 -0
  218. mirascope/ops/_internal/instrumentation/llm/response.py +521 -0
  219. mirascope/ops/_internal/instrumentation/llm/serialize.py +300 -0
  220. mirascope/ops/_internal/protocols.py +83 -1
  221. mirascope/ops/_internal/traced_calls.py +18 -0
  222. mirascope/ops/_internal/traced_functions.py +125 -10
  223. mirascope/ops/_internal/tracing.py +78 -1
  224. mirascope/ops/_internal/utils.py +60 -4
  225. mirascope/ops/_internal/versioned_functions.py +1 -1
  226. {mirascope-2.0.0a6.dist-info → mirascope-2.0.2.dist-info}/METADATA +12 -11
  227. mirascope-2.0.2.dist-info/RECORD +424 -0
  228. {mirascope-2.0.0a6.dist-info → mirascope-2.0.2.dist-info}/licenses/LICENSE +1 -1
  229. mirascope-2.0.0a6.dist-info/RECORD +0 -316
  230. {mirascope-2.0.0a6.dist-info → mirascope-2.0.2.dist-info}/WHEEL +0 -0
@@ -11,8 +11,11 @@ from ...llm.calls import AsyncCall, AsyncContextCall, Call, ContextCall
11
11
  from ...llm.context import DepsT
12
12
  from .protocols import (
13
13
  AsyncFunction,
14
+ AsyncSpanFunction,
14
15
  SyncFunction,
16
+ SyncSpanFunction,
15
17
  fn_is_async,
18
+ fn_wants_span,
16
19
  )
17
20
  from .traced_calls import (
18
21
  TracedAsyncCall,
@@ -24,7 +27,9 @@ from .traced_calls import (
24
27
  )
25
28
  from .traced_functions import (
26
29
  AsyncTracedFunction,
30
+ AsyncTracedSpanFunction,
27
31
  TracedFunction,
32
+ TracedSpanFunction,
28
33
  )
29
34
  from .types import P, R
30
35
 
@@ -43,7 +48,7 @@ class TraceDecorator:
43
48
  """Arbitrary key-value pairs for additional metadata."""
44
49
 
45
50
  # IMPORTANT: The order of these overloads matters for type inference.
46
- # Call type overloads come first, then function overloads.
51
+ # Call type overloads come first, then span function overloads, then regular functions.
47
52
  @overload
48
53
  def __call__( # pyright: ignore[reportOverlappingOverload]
49
54
  self,
@@ -76,6 +81,22 @@ class TraceDecorator:
76
81
  """Overload for applying decorator to a Call."""
77
82
  ...
78
83
 
84
+ @overload
85
+ def __call__( # pyright: ignore[reportOverlappingOverload]
86
+ self,
87
+ fn: AsyncSpanFunction[P, R],
88
+ ) -> AsyncTracedSpanFunction[P, R]:
89
+ """Overload for applying decorator to an async function with span injection."""
90
+ ...
91
+
92
+ @overload
93
+ def __call__(
94
+ self,
95
+ fn: SyncSpanFunction[P, R],
96
+ ) -> TracedSpanFunction[P, R]:
97
+ """Overload for applying decorator to a sync function with span injection."""
98
+ ...
99
+
79
100
  @overload
80
101
  def __call__(
81
102
  self,
@@ -99,6 +120,8 @@ class TraceDecorator:
99
120
  | ContextCall[P, DepsT, FormattableT]
100
121
  | AsyncCall[P, FormattableT]
101
122
  | Call[P, FormattableT]
123
+ | AsyncSpanFunction[P, R]
124
+ | SyncSpanFunction[P, R]
102
125
  | AsyncFunction[P, R]
103
126
  | SyncFunction[P, R]
104
127
  ),
@@ -107,12 +130,21 @@ class TraceDecorator:
107
130
  | TracedContextCall[P, DepsT, FormattableT]
108
131
  | TracedAsyncCall[P, FormattableT]
109
132
  | TracedCall[P, FormattableT]
133
+ | AsyncTracedSpanFunction[P, R]
134
+ | TracedSpanFunction[P, R]
110
135
  | AsyncTracedFunction[P, R]
111
136
  | TracedFunction[P, R]
112
137
  ):
113
138
  """Applies the decorator to the given function or Call object."""
114
139
  if is_call_type(fn):
115
140
  return wrap_call(fn=fn, tags=self.tags, metadata=self.metadata)
141
+ elif fn_wants_span(fn):
142
+ if fn_is_async(fn):
143
+ return AsyncTracedSpanFunction(
144
+ fn=fn, tags=self.tags, metadata=self.metadata
145
+ )
146
+ else:
147
+ return TracedSpanFunction(fn=fn, tags=self.tags, metadata=self.metadata)
116
148
  elif fn_is_async(fn):
117
149
  return AsyncTracedFunction(fn=fn, tags=self.tags, metadata=self.metadata)
118
150
  else:
@@ -174,6 +206,28 @@ def trace(
174
206
  ...
175
207
 
176
208
 
209
+ @overload
210
+ def trace( # pyright: ignore[reportOverlappingOverload]
211
+ __fn: AsyncSpanFunction[P, R],
212
+ *,
213
+ tags: None = None,
214
+ metadata: None = None,
215
+ ) -> AsyncTracedSpanFunction[P, R]:
216
+ """Overload for directly decorating an async function with span injection."""
217
+ ...
218
+
219
+
220
+ @overload
221
+ def trace(
222
+ __fn: SyncSpanFunction[P, R],
223
+ *,
224
+ tags: None = None,
225
+ metadata: None = None,
226
+ ) -> TracedSpanFunction[P, R]:
227
+ """Overload for directly decorating a sync function with span injection."""
228
+ ...
229
+
230
+
177
231
  @overload
178
232
  def trace(
179
233
  __fn: AsyncFunction[P, R],
@@ -202,6 +256,8 @@ def trace( # pyright: ignore[reportGeneralTypeIssues]
202
256
  | ContextCall[P, DepsT, FormattableT]
203
257
  | AsyncCall[P, FormattableT]
204
258
  | Call[P, FormattableT]
259
+ | AsyncSpanFunction[P, R]
260
+ | SyncSpanFunction[P, R]
205
261
  | AsyncFunction[P, R]
206
262
  | SyncFunction[P, R]
207
263
  | None
@@ -214,6 +270,8 @@ def trace( # pyright: ignore[reportGeneralTypeIssues]
214
270
  | TracedContextCall[P, DepsT, FormattableT]
215
271
  | TracedAsyncCall[P, FormattableT]
216
272
  | TracedCall[P, FormattableT]
273
+ | AsyncTracedSpanFunction[P, R]
274
+ | TracedSpanFunction[P, R]
217
275
  | AsyncTracedFunction[P, R]
218
276
  | TracedFunction[P, R]
219
277
  | TraceDecorator
@@ -227,6 +285,9 @@ def trace( # pyright: ignore[reportGeneralTypeIssues]
227
285
  When decorating an @llm.call function, returns a TracedCall that wraps both
228
286
  the call and stream methods with tracing capabilities.
229
287
 
288
+ If the decorated function has `trace_ctx: Span` as its first parameter,
289
+ the span will be injected automatically and callers should NOT pass it.
290
+
230
291
  Args:
231
292
  __fn: The function or Call object to decorate.
232
293
  tags: Optional list of string tags to associate with traced executions.
@@ -262,6 +323,17 @@ def trace( # pyright: ignore[reportGeneralTypeIssues]
262
323
  print(trace.result.content)
263
324
  print(trace.span_id)
264
325
  ```
326
+
327
+ Example:
328
+ ```python
329
+ @ops.trace
330
+ def my_fn(trace_ctx: Span, arg: str) -> str:
331
+ trace_ctx.info(f"Processing: {arg}")
332
+ return arg.upper()
333
+
334
+ # Call without trace_ctx - it's injected automatically
335
+ result = my_fn("hello") # Returns "HELLO"
336
+ ```
265
337
  """
266
338
  tags = tuple(sorted(set(tags or [])))
267
339
  metadata = metadata or {}
@@ -270,6 +342,11 @@ def trace( # pyright: ignore[reportGeneralTypeIssues]
270
342
 
271
343
  if is_call_type(__fn):
272
344
  return wrap_call(fn=__fn, tags=tags, metadata=metadata)
345
+ elif fn_wants_span(__fn):
346
+ if fn_is_async(__fn):
347
+ return AsyncTracedSpanFunction(fn=__fn, tags=tags, metadata=metadata)
348
+ else:
349
+ return TracedSpanFunction(fn=__fn, tags=tags, metadata=metadata)
273
350
  elif fn_is_async(__fn):
274
351
  return AsyncTracedFunction(fn=__fn, tags=tags, metadata=metadata)
275
352
  else:
@@ -6,7 +6,7 @@ from typing import Any, TypeAlias
6
6
 
7
7
  import orjson
8
8
 
9
- from .protocols import P
9
+ from .protocols import P, fn_wants_span
10
10
 
11
11
  ORJSON_OPTS = (
12
12
  orjson.OPT_NON_STR_KEYS
@@ -25,18 +25,56 @@ def json_dumps(obj: Any) -> str: # noqa: ANN401
25
25
  return orjson.dumps(obj, option=ORJSON_OPTS).decode("utf-8")
26
26
 
27
27
 
28
+ def _is_call_method(fn: Callable[..., Any]) -> bool:
29
+ """Check if fn is a bound method of a Call object (e.g., Call.call or Call.stream)."""
30
+ return (
31
+ hasattr(fn, "__self__") and hasattr(fn.__self__, "prompt") # pyright: ignore[reportFunctionMemberAccess]
32
+ )
33
+
34
+
35
+ def get_original_fn(fn: Callable[..., Any]) -> Callable[..., Any]:
36
+ """Get the original unwrapped function.
37
+
38
+ Follows the __wrapped__ chain set by copy_function_metadata() to find the
39
+ original function. Falls back to checking for Call methods for bound methods.
40
+ """
41
+ # Follow __wrapped__ chain if available (set by copy_function_metadata)
42
+ # In practice, get_original_fn is called with original functions or bound methods,
43
+ # so this loop is defensive code for edge cases like @functools.wraps chains.
44
+ while hasattr(fn, "__wrapped__"):
45
+ fn = fn.__wrapped__ # pyright: ignore[reportFunctionMemberAccess] # pragma: no cover
46
+
47
+ # Handle bound methods of Call objects (e.g., Call.call or Call.stream)
48
+ if _is_call_method(fn):
49
+ prompt = fn.__self__.prompt # pyright: ignore[reportFunctionMemberAccess]
50
+ if hasattr(prompt, "fn"):
51
+ return get_original_fn(prompt.fn)
52
+
53
+ return fn
54
+
55
+
28
56
  def get_qualified_name(fn: Callable[..., Any]) -> str:
29
57
  """Return the simplified qualified name of a function.
30
58
 
59
+ If the function is a bound method of a Call object (e.g., Call.call or Call.stream),
60
+ returns the qualified name of the original decorated function from prompt.fn,
61
+ suffixed with the method name (e.g., "recommend.call" or "recommend.stream").
62
+
31
63
  If the function is defined locally, return the name after '<locals>.'; otherwise,
32
64
  return the last non-empty part after splitting by '.'.
33
65
  """
66
+ # Check if this is a Call method and capture the method name
67
+ method_suffix = f".{fn.__name__}" if _is_call_method(fn) else ""
68
+
69
+ fn = get_original_fn(fn)
34
70
  qualified_name = fn.__qualname__
35
71
  if "<locals>." in qualified_name:
36
- return qualified_name.split("<locals>.")[-1]
72
+ base_name = qualified_name.split("<locals>.")[-1]
37
73
  else:
38
74
  parts = [part for part in qualified_name.split(".") if part]
39
- return parts[-1] if parts else qualified_name
75
+ base_name = parts[-1] if parts else qualified_name
76
+
77
+ return f"{base_name}{method_suffix}"
40
78
 
41
79
 
42
80
  def extract_arguments(
@@ -44,8 +82,26 @@ def extract_arguments(
44
82
  *args: P.args,
45
83
  **kwargs: P.kwargs,
46
84
  ) -> tuple[dict[str, str], dict[str, Any]]:
47
- """Returns a tuple of (arg_types, arg_values) dictionaries from function call."""
85
+ """Returns a tuple of (arg_types, arg_values) dictionaries from function call.
86
+
87
+ If the function is a Call method (e.g., Call.call), uses the original
88
+ decorated function's signature to get proper parameter names and types.
89
+
90
+ If the function has `trace_ctx: Span` as first parameter (detected via
91
+ fn_wants_span), that parameter is skipped since it's injected by the
92
+ decorator and shouldn't be recorded in span attributes.
93
+ """
94
+ # Use original function signature for Call methods
95
+ fn = get_original_fn(fn)
48
96
  signature = inspect.signature(fn)
97
+
98
+ # If function wants span injection, skip the first parameter
99
+ if fn_wants_span(fn):
100
+ params = list(signature.parameters.values())
101
+ if params:
102
+ remaining_params = params[1:]
103
+ signature = signature.replace(parameters=remaining_params)
104
+
49
105
  bound_arguments = signature.bind(*args, **kwargs)
50
106
  bound_arguments.apply_defaults()
51
107
 
@@ -196,7 +196,7 @@ class _BaseVersionedFunction(_BaseTracedFunction[P, R, Any]):
196
196
  if self.name:
197
197
  span.set(**{"mirascope.version.name": self.name})
198
198
  if self.tags:
199
- span.set(**{"mirascope.version.tags": self.tags})
199
+ span.set(**{"mirascope.version.tags": list(self.tags)})
200
200
  if self.metadata:
201
201
  for key, value in self.metadata.items():
202
202
  span.set(**{f"mirascope.version.meta.{key}": value})
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mirascope
3
- Version: 2.0.0a6
4
- Summary: LLM abstractions that aren't obstructions
3
+ Version: 2.0.2
4
+ Summary: Every frontier LLM. One unified interface.
5
5
  Project-URL: Homepage, https://mirascope.com
6
6
  Project-URL: Documentation, https://mirascope.com/docs/mirascope/v2
7
7
  Project-URL: Repository, https://github.com/Mirascope/mirascope/tree/v2
@@ -11,7 +11,7 @@ Author-email: William Bakst <william@mirascope.com>, Dandelion Mané <dandelion@
11
11
  Maintainer-email: William Bakst <william@mirascope.com>, Dandelion Mané <dandelion@mirascope.com>
12
12
  License: MIT License
13
13
 
14
- Copyright (c) 2023 Mirascope, Inc.
14
+ Copyright (c) 2026 Mirascope, Inc.
15
15
 
16
16
  Permission is hereby granted, free of charge, to any person obtaining a copy
17
17
  of this software and associated documentation files (the "Software"), to deal
@@ -31,8 +31,8 @@ License: MIT License
31
31
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
32
  SOFTWARE.
33
33
  License-File: LICENSE
34
- Keywords: agents,artificial intelligence,developer tools,llm,llm tools,prompt engineering
35
- Classifier: Development Status :: 3 - Alpha
34
+ Keywords: agents,anthropic,artificial intelligence,context engineering,developer tools,gemini,llm,llm tools,openai,prompt engineering
35
+ Classifier: Development Status :: 5 - Production/Stable
36
36
  Classifier: Intended Audience :: Developers
37
37
  Classifier: License :: OSI Approved :: MIT License
38
38
  Classifier: Programming Language :: Python :: 3
@@ -48,15 +48,16 @@ Classifier: Topic :: Software Development :: Libraries
48
48
  Requires-Python: >=3.10
49
49
  Requires-Dist: docstring-parser>=0.17.0
50
50
  Requires-Dist: httpx>=0.27.0
51
+ Requires-Dist: jiter>=0.7.0
51
52
  Requires-Dist: pydantic>=2.0.0
52
53
  Requires-Dist: typing-extensions>=4.10.0
53
54
  Provides-Extra: all
54
- Requires-Dist: anthropic<1.0,>=0.75.0; extra == 'all'
55
- Requires-Dist: google-genai<2,>=1.57.0; extra == 'all'
55
+ Requires-Dist: anthropic<1.0,>=0.76.0; extra == 'all'
56
+ Requires-Dist: google-genai<2,>=1.58.0; extra == 'all'
56
57
  Requires-Dist: libcst>=1.8.6; extra == 'all'
57
58
  Requires-Dist: mcp<2,>=1.25.0; extra == 'all'
58
59
  Requires-Dist: mlx-lm<1,>=0.28.4; extra == 'all'
59
- Requires-Dist: openai<3,>=2.14.0; extra == 'all'
60
+ Requires-Dist: openai<3,>=2.15.0; extra == 'all'
60
61
  Requires-Dist: opentelemetry-api<2,>=1.38.0; extra == 'all'
61
62
  Requires-Dist: opentelemetry-exporter-otlp<2,>=1.38.0; extra == 'all'
62
63
  Requires-Dist: opentelemetry-instrumentation<1,>=0.59b0; extra == 'all'
@@ -70,11 +71,11 @@ Requires-Dist: pillow<11,>=10.4.0; extra == 'all'
70
71
  Requires-Dist: proto-plus>=1.24.0; extra == 'all'
71
72
  Requires-Dist: pydantic-settings>=2.12.0; extra == 'all'
72
73
  Provides-Extra: anthropic
73
- Requires-Dist: anthropic<1.0,>=0.75.0; extra == 'anthropic'
74
+ Requires-Dist: anthropic<1.0,>=0.76.0; extra == 'anthropic'
74
75
  Provides-Extra: api
75
76
  Requires-Dist: pydantic-settings>=2.12.0; extra == 'api'
76
77
  Provides-Extra: google
77
- Requires-Dist: google-genai<2,>=1.57.0; extra == 'google'
78
+ Requires-Dist: google-genai<2,>=1.58.0; extra == 'google'
78
79
  Requires-Dist: pillow<11,>=10.4.0; extra == 'google'
79
80
  Requires-Dist: proto-plus>=1.24.0; extra == 'google'
80
81
  Provides-Extra: mcp
@@ -82,7 +83,7 @@ Requires-Dist: mcp<2,>=1.25.0; extra == 'mcp'
82
83
  Provides-Extra: mlx
83
84
  Requires-Dist: mlx-lm<1,>=0.28.4; extra == 'mlx'
84
85
  Provides-Extra: openai
85
- Requires-Dist: openai<3,>=2.14.0; extra == 'openai'
86
+ Requires-Dist: openai<3,>=2.15.0; extra == 'openai'
86
87
  Provides-Extra: ops
87
88
  Requires-Dist: libcst>=1.8.6; extra == 'ops'
88
89
  Requires-Dist: opentelemetry-api<2,>=1.38.0; extra == 'ops'