mirascope 2.0.0a5__py3-none-any.whl → 2.0.1__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 (349) hide show
  1. mirascope/__init__.py +10 -1
  2. mirascope/_stubs.py +363 -0
  3. mirascope/api/__init__.py +8 -0
  4. mirascope/api/_generated/__init__.py +285 -2
  5. mirascope/api/_generated/annotations/__init__.py +33 -0
  6. mirascope/api/_generated/annotations/client.py +506 -0
  7. mirascope/api/_generated/annotations/raw_client.py +1414 -0
  8. mirascope/api/_generated/annotations/types/__init__.py +31 -0
  9. mirascope/api/_generated/annotations/types/annotations_create_request_label.py +5 -0
  10. mirascope/api/_generated/annotations/types/annotations_create_response.py +48 -0
  11. mirascope/api/_generated/annotations/types/annotations_create_response_label.py +5 -0
  12. mirascope/api/_generated/annotations/types/annotations_get_response.py +48 -0
  13. mirascope/api/_generated/annotations/types/annotations_get_response_label.py +5 -0
  14. mirascope/api/_generated/annotations/types/annotations_list_request_label.py +5 -0
  15. mirascope/api/_generated/annotations/types/annotations_list_response.py +21 -0
  16. mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item.py +50 -0
  17. mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item_label.py +5 -0
  18. mirascope/api/_generated/annotations/types/annotations_update_request_label.py +5 -0
  19. mirascope/api/_generated/annotations/types/annotations_update_response.py +48 -0
  20. mirascope/api/_generated/annotations/types/annotations_update_response_label.py +5 -0
  21. mirascope/api/_generated/api_keys/__init__.py +12 -2
  22. mirascope/api/_generated/api_keys/client.py +77 -0
  23. mirascope/api/_generated/api_keys/raw_client.py +422 -39
  24. mirascope/api/_generated/api_keys/types/__init__.py +7 -1
  25. mirascope/api/_generated/api_keys/types/api_keys_create_response.py +4 -12
  26. mirascope/api/_generated/api_keys/types/api_keys_get_response.py +4 -12
  27. mirascope/api/_generated/api_keys/types/api_keys_list_all_for_org_response_item.py +40 -0
  28. mirascope/api/_generated/api_keys/types/api_keys_list_response_item.py +4 -12
  29. mirascope/api/_generated/client.py +42 -0
  30. mirascope/api/_generated/core/client_wrapper.py +2 -14
  31. mirascope/api/_generated/core/datetime_utils.py +1 -3
  32. mirascope/api/_generated/core/file.py +2 -5
  33. mirascope/api/_generated/core/http_client.py +36 -112
  34. mirascope/api/_generated/core/jsonable_encoder.py +1 -3
  35. mirascope/api/_generated/core/pydantic_utilities.py +19 -74
  36. mirascope/api/_generated/core/query_encoder.py +1 -3
  37. mirascope/api/_generated/core/serialization.py +4 -10
  38. mirascope/api/_generated/docs/client.py +2 -6
  39. mirascope/api/_generated/docs/raw_client.py +51 -5
  40. mirascope/api/_generated/environment.py +3 -3
  41. mirascope/api/_generated/environments/__init__.py +6 -0
  42. mirascope/api/_generated/environments/client.py +117 -0
  43. mirascope/api/_generated/environments/raw_client.py +530 -51
  44. mirascope/api/_generated/environments/types/__init__.py +10 -0
  45. mirascope/api/_generated/environments/types/environments_create_response.py +1 -3
  46. mirascope/api/_generated/environments/types/environments_get_analytics_response.py +60 -0
  47. mirascope/api/_generated/environments/types/environments_get_analytics_response_top_functions_item.py +24 -0
  48. mirascope/api/_generated/environments/types/environments_get_analytics_response_top_models_item.py +22 -0
  49. mirascope/api/_generated/environments/types/environments_get_response.py +1 -3
  50. mirascope/api/_generated/environments/types/environments_list_response_item.py +1 -3
  51. mirascope/api/_generated/environments/types/environments_update_response.py +1 -3
  52. mirascope/api/_generated/errors/__init__.py +8 -0
  53. mirascope/api/_generated/errors/bad_request_error.py +1 -2
  54. mirascope/api/_generated/errors/conflict_error.py +1 -2
  55. mirascope/api/_generated/errors/forbidden_error.py +1 -5
  56. mirascope/api/_generated/errors/internal_server_error.py +1 -6
  57. mirascope/api/_generated/errors/not_found_error.py +1 -5
  58. mirascope/api/_generated/errors/payment_required_error.py +15 -0
  59. mirascope/api/_generated/errors/service_unavailable_error.py +14 -0
  60. mirascope/api/_generated/errors/too_many_requests_error.py +15 -0
  61. mirascope/api/_generated/errors/unauthorized_error.py +11 -0
  62. mirascope/api/_generated/functions/__init__.py +39 -0
  63. mirascope/api/_generated/functions/client.py +647 -0
  64. mirascope/api/_generated/functions/raw_client.py +1890 -0
  65. mirascope/api/_generated/functions/types/__init__.py +53 -0
  66. mirascope/api/_generated/functions/types/functions_create_request_dependencies_value.py +20 -0
  67. mirascope/api/_generated/functions/types/functions_create_response.py +37 -0
  68. mirascope/api/_generated/functions/types/functions_create_response_dependencies_value.py +20 -0
  69. mirascope/api/_generated/functions/types/functions_find_by_hash_response.py +39 -0
  70. mirascope/api/_generated/functions/types/functions_find_by_hash_response_dependencies_value.py +20 -0
  71. mirascope/api/_generated/functions/types/functions_get_by_env_response.py +53 -0
  72. mirascope/api/_generated/functions/types/functions_get_by_env_response_dependencies_value.py +22 -0
  73. mirascope/api/_generated/functions/types/functions_get_response.py +37 -0
  74. mirascope/api/_generated/functions/types/functions_get_response_dependencies_value.py +20 -0
  75. mirascope/api/_generated/functions/types/functions_list_by_env_response.py +25 -0
  76. mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +56 -0
  77. mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item_dependencies_value.py +22 -0
  78. mirascope/api/_generated/functions/types/functions_list_response.py +21 -0
  79. mirascope/api/_generated/functions/types/functions_list_response_functions_item.py +41 -0
  80. mirascope/api/_generated/functions/types/functions_list_response_functions_item_dependencies_value.py +20 -0
  81. mirascope/api/_generated/health/client.py +2 -6
  82. mirascope/api/_generated/health/raw_client.py +51 -5
  83. mirascope/api/_generated/health/types/health_check_response.py +1 -3
  84. mirascope/api/_generated/organization_invitations/__init__.py +33 -0
  85. mirascope/api/_generated/organization_invitations/client.py +546 -0
  86. mirascope/api/_generated/organization_invitations/raw_client.py +1519 -0
  87. mirascope/api/_generated/organization_invitations/types/__init__.py +53 -0
  88. mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response.py +34 -0
  89. mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response_role.py +7 -0
  90. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_request_role.py +7 -0
  91. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response.py +48 -0
  92. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_role.py +7 -0
  93. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_status.py +7 -0
  94. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response.py +48 -0
  95. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_role.py +7 -0
  96. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_status.py +7 -0
  97. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item.py +48 -0
  98. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_role.py +7 -0
  99. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_status.py +7 -0
  100. mirascope/api/_generated/organization_memberships/__init__.py +19 -0
  101. mirascope/api/_generated/organization_memberships/client.py +302 -0
  102. mirascope/api/_generated/organization_memberships/raw_client.py +736 -0
  103. mirascope/api/_generated/organization_memberships/types/__init__.py +27 -0
  104. mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item.py +33 -0
  105. mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item_role.py +7 -0
  106. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_request_role.py +7 -0
  107. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response.py +31 -0
  108. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response_role.py +7 -0
  109. mirascope/api/_generated/organizations/__init__.py +26 -0
  110. mirascope/api/_generated/organizations/client.py +465 -0
  111. mirascope/api/_generated/organizations/raw_client.py +1799 -108
  112. mirascope/api/_generated/organizations/types/__init__.py +48 -0
  113. mirascope/api/_generated/organizations/types/organizations_create_payment_intent_response.py +24 -0
  114. mirascope/api/_generated/organizations/types/organizations_create_response.py +4 -3
  115. mirascope/api/_generated/organizations/types/organizations_create_response_role.py +1 -3
  116. mirascope/api/_generated/organizations/types/organizations_get_response.py +4 -3
  117. mirascope/api/_generated/organizations/types/organizations_get_response_role.py +1 -3
  118. mirascope/api/_generated/organizations/types/organizations_list_response_item.py +4 -3
  119. mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +1 -3
  120. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_request_target_plan.py +7 -0
  121. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response.py +47 -0
  122. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item.py +33 -0
  123. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item_resource.py +7 -0
  124. mirascope/api/_generated/organizations/types/organizations_router_balance_response.py +24 -0
  125. mirascope/api/_generated/organizations/types/organizations_subscription_response.py +53 -0
  126. mirascope/api/_generated/organizations/types/organizations_subscription_response_current_plan.py +7 -0
  127. mirascope/api/_generated/organizations/types/organizations_subscription_response_payment_method.py +26 -0
  128. mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change.py +34 -0
  129. mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change_target_plan.py +7 -0
  130. mirascope/api/_generated/organizations/types/organizations_update_response.py +4 -3
  131. mirascope/api/_generated/organizations/types/organizations_update_response_role.py +1 -3
  132. mirascope/api/_generated/organizations/types/organizations_update_subscription_request_target_plan.py +7 -0
  133. mirascope/api/_generated/organizations/types/organizations_update_subscription_response.py +35 -0
  134. mirascope/api/_generated/project_memberships/__init__.py +25 -0
  135. mirascope/api/_generated/project_memberships/client.py +437 -0
  136. mirascope/api/_generated/project_memberships/raw_client.py +1039 -0
  137. mirascope/api/_generated/project_memberships/types/__init__.py +29 -0
  138. mirascope/api/_generated/project_memberships/types/project_memberships_create_request_role.py +7 -0
  139. mirascope/api/_generated/project_memberships/types/project_memberships_create_response.py +35 -0
  140. mirascope/api/_generated/project_memberships/types/project_memberships_create_response_role.py +7 -0
  141. mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item.py +33 -0
  142. mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item_role.py +7 -0
  143. mirascope/api/_generated/project_memberships/types/project_memberships_update_request_role.py +7 -0
  144. mirascope/api/_generated/project_memberships/types/project_memberships_update_response.py +35 -0
  145. mirascope/api/_generated/project_memberships/types/project_memberships_update_response_role.py +7 -0
  146. mirascope/api/_generated/projects/__init__.py +2 -12
  147. mirascope/api/_generated/projects/client.py +17 -71
  148. mirascope/api/_generated/projects/raw_client.py +295 -51
  149. mirascope/api/_generated/projects/types/__init__.py +1 -6
  150. mirascope/api/_generated/projects/types/projects_create_response.py +3 -9
  151. mirascope/api/_generated/projects/types/projects_get_response.py +3 -9
  152. mirascope/api/_generated/projects/types/projects_list_response_item.py +3 -9
  153. mirascope/api/_generated/projects/types/projects_update_response.py +3 -9
  154. mirascope/api/_generated/reference.md +3619 -182
  155. mirascope/api/_generated/tags/__init__.py +19 -0
  156. mirascope/api/_generated/tags/client.py +504 -0
  157. mirascope/api/_generated/tags/raw_client.py +1288 -0
  158. mirascope/api/_generated/tags/types/__init__.py +17 -0
  159. mirascope/api/_generated/tags/types/tags_create_response.py +41 -0
  160. mirascope/api/_generated/tags/types/tags_get_response.py +41 -0
  161. mirascope/api/_generated/tags/types/tags_list_response.py +23 -0
  162. mirascope/api/_generated/tags/types/tags_list_response_tags_item.py +41 -0
  163. mirascope/api/_generated/tags/types/tags_update_response.py +41 -0
  164. mirascope/api/_generated/token_cost/__init__.py +7 -0
  165. mirascope/api/_generated/token_cost/client.py +160 -0
  166. mirascope/api/_generated/token_cost/raw_client.py +264 -0
  167. mirascope/api/_generated/token_cost/types/__init__.py +8 -0
  168. mirascope/api/_generated/token_cost/types/token_cost_calculate_request_usage.py +54 -0
  169. mirascope/api/_generated/token_cost/types/token_cost_calculate_response.py +52 -0
  170. mirascope/api/_generated/traces/__init__.py +42 -0
  171. mirascope/api/_generated/traces/client.py +941 -0
  172. mirascope/api/_generated/traces/raw_client.py +2177 -23
  173. mirascope/api/_generated/traces/types/__init__.py +60 -0
  174. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +4 -11
  175. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +2 -6
  176. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +1 -3
  177. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +8 -24
  178. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +2 -6
  179. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +3 -9
  180. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +2 -6
  181. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +3 -9
  182. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +4 -8
  183. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +2 -6
  184. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +8 -24
  185. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +2 -6
  186. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +3 -9
  187. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value_values_item.py +1 -3
  188. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +6 -18
  189. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +3 -9
  190. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +8 -24
  191. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +2 -6
  192. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +2 -6
  193. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value_values_item.py +1 -3
  194. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +2 -6
  195. mirascope/api/_generated/traces/types/traces_create_response.py +2 -5
  196. mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +3 -9
  197. mirascope/api/_generated/traces/types/traces_get_analytics_summary_response.py +60 -0
  198. mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_functions_item.py +24 -0
  199. mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_models_item.py +22 -0
  200. mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response.py +33 -0
  201. mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response_spans_item.py +88 -0
  202. mirascope/api/_generated/traces/types/traces_get_trace_detail_response.py +33 -0
  203. mirascope/api/_generated/traces/types/traces_get_trace_detail_response_spans_item.py +88 -0
  204. mirascope/api/_generated/traces/types/traces_list_by_function_hash_response.py +25 -0
  205. mirascope/api/_generated/traces/types/traces_list_by_function_hash_response_traces_item.py +44 -0
  206. mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item.py +26 -0
  207. mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item_operator.py +7 -0
  208. mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_by.py +7 -0
  209. mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_order.py +7 -0
  210. mirascope/api/_generated/traces/types/traces_search_by_env_response.py +26 -0
  211. mirascope/api/_generated/traces/types/traces_search_by_env_response_spans_item.py +50 -0
  212. mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item.py +26 -0
  213. mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item_operator.py +7 -0
  214. mirascope/api/_generated/traces/types/traces_search_request_sort_by.py +7 -0
  215. mirascope/api/_generated/traces/types/traces_search_request_sort_order.py +5 -0
  216. mirascope/api/_generated/traces/types/traces_search_response.py +26 -0
  217. mirascope/api/_generated/traces/types/traces_search_response_spans_item.py +50 -0
  218. mirascope/api/_generated/types/__init__.py +48 -0
  219. mirascope/api/_generated/types/already_exists_error.py +1 -3
  220. mirascope/api/_generated/types/bad_request_error_body.py +50 -0
  221. mirascope/api/_generated/types/click_house_error.py +22 -0
  222. mirascope/api/_generated/types/database_error.py +1 -3
  223. mirascope/api/_generated/types/date.py +3 -0
  224. mirascope/api/_generated/types/http_api_decode_error.py +1 -3
  225. mirascope/api/_generated/types/immutable_resource_error.py +22 -0
  226. mirascope/api/_generated/types/internal_server_error_body.py +49 -0
  227. mirascope/api/_generated/types/issue.py +1 -3
  228. mirascope/api/_generated/types/issue_tag.py +1 -8
  229. mirascope/api/_generated/types/not_found_error_body.py +1 -3
  230. mirascope/api/_generated/types/number_from_string.py +3 -0
  231. mirascope/api/_generated/types/permission_denied_error.py +1 -3
  232. mirascope/api/_generated/types/permission_denied_error_tag.py +1 -3
  233. mirascope/api/_generated/types/plan_limit_exceeded_error.py +32 -0
  234. mirascope/api/_generated/types/plan_limit_exceeded_error_tag.py +7 -0
  235. mirascope/api/_generated/types/pricing_unavailable_error.py +23 -0
  236. mirascope/api/_generated/types/property_key_key.py +1 -3
  237. mirascope/api/_generated/types/rate_limit_error.py +31 -0
  238. mirascope/api/_generated/types/rate_limit_error_tag.py +5 -0
  239. mirascope/api/_generated/types/service_unavailable_error_body.py +24 -0
  240. mirascope/api/_generated/types/service_unavailable_error_tag.py +7 -0
  241. mirascope/api/_generated/types/stripe_error.py +20 -0
  242. mirascope/api/_generated/types/subscription_past_due_error.py +31 -0
  243. mirascope/api/_generated/types/subscription_past_due_error_tag.py +7 -0
  244. mirascope/api/_generated/types/unauthorized_error_body.py +21 -0
  245. mirascope/api/_generated/types/unauthorized_error_tag.py +5 -0
  246. mirascope/api/settings.py +19 -1
  247. mirascope/llm/__init__.py +55 -8
  248. mirascope/llm/calls/__init__.py +2 -1
  249. mirascope/llm/calls/calls.py +3 -1
  250. mirascope/llm/calls/decorator.py +21 -7
  251. mirascope/llm/content/tool_call.py +6 -0
  252. mirascope/llm/content/tool_output.py +22 -5
  253. mirascope/llm/exceptions.py +284 -71
  254. mirascope/llm/formatting/__init__.py +19 -2
  255. mirascope/llm/formatting/format.py +219 -30
  256. mirascope/llm/formatting/output_parser.py +178 -0
  257. mirascope/llm/formatting/partial.py +80 -7
  258. mirascope/llm/formatting/primitives.py +192 -0
  259. mirascope/llm/formatting/types.py +21 -64
  260. mirascope/llm/mcp/__init__.py +2 -2
  261. mirascope/llm/mcp/mcp_client.py +130 -0
  262. mirascope/llm/messages/__init__.py +3 -0
  263. mirascope/llm/messages/_utils.py +34 -0
  264. mirascope/llm/models/__init__.py +5 -0
  265. mirascope/llm/models/models.py +137 -69
  266. mirascope/llm/{providers/base → models}/params.py +16 -37
  267. mirascope/llm/models/thinking_config.py +61 -0
  268. mirascope/llm/prompts/_utils.py +0 -32
  269. mirascope/llm/prompts/decorator.py +16 -5
  270. mirascope/llm/prompts/prompts.py +131 -68
  271. mirascope/llm/providers/__init__.py +18 -2
  272. mirascope/llm/providers/anthropic/__init__.py +3 -21
  273. mirascope/llm/providers/anthropic/_utils/__init__.py +2 -0
  274. mirascope/llm/providers/anthropic/_utils/beta_decode.py +22 -11
  275. mirascope/llm/providers/anthropic/_utils/beta_encode.py +75 -25
  276. mirascope/llm/providers/anthropic/_utils/decode.py +22 -11
  277. mirascope/llm/providers/anthropic/_utils/encode.py +82 -20
  278. mirascope/llm/providers/anthropic/_utils/errors.py +2 -2
  279. mirascope/llm/providers/anthropic/beta_provider.py +64 -18
  280. mirascope/llm/providers/anthropic/provider.py +91 -33
  281. mirascope/llm/providers/base/__init__.py +0 -2
  282. mirascope/llm/providers/base/_utils.py +55 -11
  283. mirascope/llm/providers/base/base_provider.py +116 -37
  284. mirascope/llm/providers/google/__init__.py +2 -17
  285. mirascope/llm/providers/google/_utils/__init__.py +2 -0
  286. mirascope/llm/providers/google/_utils/decode.py +37 -15
  287. mirascope/llm/providers/google/_utils/encode.py +127 -19
  288. mirascope/llm/providers/google/_utils/errors.py +3 -2
  289. mirascope/llm/providers/google/model_info.py +1 -0
  290. mirascope/llm/providers/google/provider.py +68 -19
  291. mirascope/llm/providers/mirascope/__init__.py +5 -0
  292. mirascope/llm/providers/mirascope/_utils.py +73 -0
  293. mirascope/llm/providers/mirascope/provider.py +349 -0
  294. mirascope/llm/providers/mlx/__init__.py +2 -17
  295. mirascope/llm/providers/mlx/_utils.py +8 -3
  296. mirascope/llm/providers/mlx/encoding/base.py +5 -2
  297. mirascope/llm/providers/mlx/encoding/transformers.py +5 -2
  298. mirascope/llm/providers/mlx/mlx.py +23 -6
  299. mirascope/llm/providers/mlx/provider.py +42 -13
  300. mirascope/llm/providers/ollama/__init__.py +1 -13
  301. mirascope/llm/providers/openai/_utils/errors.py +2 -2
  302. mirascope/llm/providers/openai/completions/__init__.py +2 -20
  303. mirascope/llm/providers/openai/completions/_utils/decode.py +14 -3
  304. mirascope/llm/providers/openai/completions/_utils/encode.py +35 -28
  305. mirascope/llm/providers/openai/completions/base_provider.py +40 -11
  306. mirascope/llm/providers/openai/provider.py +40 -10
  307. mirascope/llm/providers/openai/responses/__init__.py +1 -17
  308. mirascope/llm/providers/openai/responses/_utils/__init__.py +2 -0
  309. mirascope/llm/providers/openai/responses/_utils/decode.py +21 -8
  310. mirascope/llm/providers/openai/responses/_utils/encode.py +59 -19
  311. mirascope/llm/providers/openai/responses/provider.py +56 -18
  312. mirascope/llm/providers/provider_id.py +1 -0
  313. mirascope/llm/providers/provider_registry.py +96 -19
  314. mirascope/llm/providers/together/__init__.py +1 -13
  315. mirascope/llm/responses/__init__.py +6 -1
  316. mirascope/llm/responses/_utils.py +102 -12
  317. mirascope/llm/responses/base_response.py +5 -2
  318. mirascope/llm/responses/base_stream_response.py +139 -45
  319. mirascope/llm/responses/response.py +2 -1
  320. mirascope/llm/responses/root_response.py +89 -17
  321. mirascope/llm/responses/stream_response.py +6 -9
  322. mirascope/llm/tools/decorator.py +17 -8
  323. mirascope/llm/tools/tool_schema.py +43 -10
  324. mirascope/llm/tools/toolkit.py +35 -27
  325. mirascope/llm/tools/tools.py +123 -30
  326. mirascope/ops/__init__.py +64 -109
  327. mirascope/ops/_internal/configuration.py +82 -31
  328. mirascope/ops/_internal/exporters/exporters.py +64 -11
  329. mirascope/ops/_internal/instrumentation/llm/common.py +530 -0
  330. mirascope/ops/_internal/instrumentation/llm/cost.py +190 -0
  331. mirascope/ops/_internal/instrumentation/llm/encode.py +1 -1
  332. mirascope/ops/_internal/instrumentation/llm/llm.py +116 -1243
  333. mirascope/ops/_internal/instrumentation/llm/model.py +1798 -0
  334. mirascope/ops/_internal/instrumentation/llm/response.py +521 -0
  335. mirascope/ops/_internal/instrumentation/llm/serialize.py +300 -0
  336. mirascope/ops/_internal/protocols.py +83 -1
  337. mirascope/ops/_internal/traced_calls.py +4 -0
  338. mirascope/ops/_internal/traced_functions.py +141 -12
  339. mirascope/ops/_internal/tracing.py +78 -1
  340. mirascope/ops/_internal/utils.py +52 -4
  341. mirascope/ops/_internal/versioned_functions.py +54 -43
  342. {mirascope-2.0.0a5.dist-info → mirascope-2.0.1.dist-info}/METADATA +14 -13
  343. mirascope-2.0.1.dist-info/RECORD +423 -0
  344. {mirascope-2.0.0a5.dist-info → mirascope-2.0.1.dist-info}/licenses/LICENSE +1 -1
  345. mirascope/llm/formatting/_utils.py +0 -78
  346. mirascope/llm/mcp/client.py +0 -118
  347. mirascope/llm/providers/_missing_import_stubs.py +0 -49
  348. mirascope-2.0.0a5.dist-info/RECORD +0 -265
  349. {mirascope-2.0.0a5.dist-info → mirascope-2.0.1.dist-info}/WHEEL +0 -0
@@ -1,7 +1,9 @@
1
1
  """OpenAI Responses message encoding and request preparation."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from collections.abc import Sequence
4
- from typing import TypedDict, cast
6
+ from typing import TYPE_CHECKING, TypedDict, cast
5
7
 
6
8
  from openai import Omit
7
9
  from openai.types.responses import (
@@ -23,7 +25,7 @@ from openai.types.responses.response_input_param import (
23
25
  FunctionCallOutput,
24
26
  Message as ResponseInputMessageParam,
25
27
  )
26
- from openai.types.shared_params import Reasoning
28
+ from openai.types.shared_params import Reasoning, ReasoningEffort
27
29
  from openai.types.shared_params.response_format_json_object import (
28
30
  ResponseFormatJSONObject,
29
31
  )
@@ -33,12 +35,12 @@ from .....exceptions import FeatureNotSupportedError
33
35
  from .....formatting import (
34
36
  Format,
35
37
  FormattableT,
36
- _utils as _formatting_utils,
38
+ OutputParser,
37
39
  resolve_format,
38
40
  )
39
41
  from .....messages import AssistantMessage, Message, UserMessage
40
42
  from .....tools import FORMAT_TOOL_NAME, AnyToolSchema, BaseToolkit
41
- from ....base import Params, _utils as _base_utils
43
+ from ....base import _utils as _base_utils
42
44
  from ...model_id import OpenAIModelId, model_name
43
45
  from ...model_info import (
44
46
  MODELS_WITHOUT_JSON_OBJECT_SUPPORT,
@@ -46,6 +48,20 @@ from ...model_info import (
46
48
  NON_REASONING_MODELS,
47
49
  )
48
50
 
51
+ if TYPE_CHECKING:
52
+ from .....models import Params, ThinkingLevel
53
+
54
+ # Thinking level to a float multiplier % of max tokens
55
+ THINKING_LEVEL_TO_EFFORT: dict[ThinkingLevel, ReasoningEffort] = {
56
+ "default": "medium",
57
+ "none": "none",
58
+ "minimal": "minimal",
59
+ "low": "low",
60
+ "medium": "medium",
61
+ "high": "high",
62
+ "max": "xhigh",
63
+ }
64
+
49
65
 
50
66
  class ResponseCreateKwargs(TypedDict, total=False):
51
67
  """Kwargs to the OpenAI `client.responses.create` method."""
@@ -103,7 +119,7 @@ def _encode_user_message(
103
119
  result.append(
104
120
  FunctionCallOutput(
105
121
  call_id=part.id,
106
- output=str(part.value),
122
+ output=str(part.result),
107
123
  type="function_call_output",
108
124
  )
109
125
  )
@@ -201,13 +217,16 @@ def _convert_tool_to_function_tool_param(tool: AnyToolSchema) -> FunctionToolPar
201
217
  schema_dict = tool.parameters.model_dump(by_alias=True, exclude_none=True)
202
218
  schema_dict["type"] = "object"
203
219
  _base_utils.ensure_additional_properties_false(schema_dict)
220
+ strict = True if tool.strict is None else tool.strict
221
+ if strict:
222
+ _base_utils.ensure_all_properties_required(schema_dict)
204
223
 
205
224
  return FunctionToolParam(
206
225
  type="function",
207
226
  name=tool.name,
208
227
  description=tool.description,
209
228
  parameters=schema_dict,
210
- strict=tool.strict,
229
+ strict=strict,
211
230
  )
212
231
 
213
232
 
@@ -237,12 +256,25 @@ def _create_strict_response_format(
237
256
  return response_format
238
257
 
239
258
 
240
- def _compute_reasoning(thinking: bool) -> Reasoning:
241
- """Compute the OpenAI `Reasoning` config based on thinking settings."""
242
- if thinking:
243
- return {"effort": "medium", "summary": "auto"}
244
- else:
245
- return {"effort": "minimal"}
259
+ def _compute_reasoning(
260
+ level: ThinkingLevel,
261
+ include_thoughts: bool,
262
+ ) -> Reasoning:
263
+ """Compute the OpenAI `Reasoning` config based on ThinkingConfig.
264
+
265
+ Args:
266
+ level: The thinking level
267
+ include_thoughts: Whether to include summary (True/False for auto)
268
+
269
+ Returns:
270
+ OpenAI Reasoning configuration
271
+ """
272
+ reasoning: Reasoning = {"effort": THINKING_LEVEL_TO_EFFORT.get(level) or "medium"}
273
+
274
+ if include_thoughts:
275
+ reasoning["summary"] = "auto"
276
+
277
+ return reasoning
246
278
 
247
279
 
248
280
  def encode_request(
@@ -250,7 +282,10 @@ def encode_request(
250
282
  model_id: OpenAIModelId,
251
283
  messages: Sequence[Message],
252
284
  tools: Sequence[AnyToolSchema] | BaseToolkit[AnyToolSchema] | None,
253
- format: type[FormattableT] | Format[FormattableT] | None,
285
+ format: type[FormattableT]
286
+ | Format[FormattableT]
287
+ | OutputParser[FormattableT]
288
+ | None,
254
289
  params: Params,
255
290
  ) -> tuple[Sequence[Message], Format[FormattableT] | None, ResponseCreateKwargs]:
256
291
  """Prepares a request for the `OpenAI.responses.create` method."""
@@ -283,16 +318,21 @@ def encode_request(
283
318
  if param_accessor.top_p is not None:
284
319
  kwargs["top_p"] = param_accessor.top_p
285
320
  if param_accessor.thinking is not None:
321
+ thinking_config = param_accessor.thinking
286
322
  if base_model_name in NON_REASONING_MODELS:
287
323
  param_accessor.emit_warning_for_unused_param(
288
- "thinking", param_accessor.thinking, "openai", model_id
324
+ "thinking", thinking_config, "openai", model_id
289
325
  )
290
326
  else:
291
327
  # Assume model supports reasoning unless explicitly listed as non-reasoning
292
328
  # This ensures new reasoning models work immediately without code updates
293
- kwargs["reasoning"] = _compute_reasoning(param_accessor.thinking)
294
- if param_accessor.encode_thoughts_as_text:
295
- encode_thoughts = True
329
+ level = thinking_config.get("level")
330
+ include_thoughts = thinking_config.get("include_thoughts", False)
331
+ kwargs["reasoning"] = _compute_reasoning(level, include_thoughts)
332
+
333
+ # Handle encode_thoughts_as_text from ThinkingConfig
334
+ if thinking_config.get("encode_thoughts_as_text"):
335
+ encode_thoughts = True
296
336
 
297
337
  tools = tools.tools if isinstance(tools, BaseToolkit) else tools or []
298
338
  openai_tools = [_convert_tool_to_function_tool_param(tool) for tool in tools]
@@ -305,9 +345,9 @@ def encode_request(
305
345
  if format.mode == "strict":
306
346
  kwargs["text"] = {"format": _create_strict_response_format(format)}
307
347
  elif format.mode == "tool":
308
- format_tool_shared_utils = _formatting_utils.create_tool_schema(format)
348
+ format_tool_schema = format.create_tool_schema()
309
349
  openai_tools.append(
310
- _convert_tool_to_function_tool_param(format_tool_shared_utils)
350
+ _convert_tool_to_function_tool_param(format_tool_schema)
311
351
  )
312
352
  if tools:
313
353
  kwargs["tool_choice"] = ToolChoiceAllowedParam(
@@ -1,13 +1,16 @@
1
1
  """OpenAI Responses API client implementation."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from collections.abc import Sequence
6
+ from typing import TYPE_CHECKING
4
7
  from typing_extensions import Unpack
5
8
 
6
9
  from openai import AsyncOpenAI, BadRequestError as OpenAIBadRequestError, OpenAI
7
10
 
8
11
  from ....context import Context, DepsT
9
12
  from ....exceptions import BadRequestError, NotFoundError
10
- from ....formatting import Format, FormattableT
13
+ from ....formatting import Format, FormattableT, OutputParser
11
14
  from ....messages import Message
12
15
  from ....responses import (
13
16
  AsyncContextResponse,
@@ -29,11 +32,14 @@ from ....tools import (
29
32
  Tool,
30
33
  Toolkit,
31
34
  )
32
- from ...base import BaseProvider, Params
35
+ from ...base import BaseProvider
33
36
  from .. import _utils as _shared_utils
34
37
  from ..model_id import OpenAIModelId, model_name
35
38
  from . import _utils
36
39
 
40
+ if TYPE_CHECKING:
41
+ from ....models import Params
42
+
37
43
 
38
44
  class OpenAIResponsesProvider(BaseProvider[OpenAI]):
39
45
  """The client for the OpenAI Responses API."""
@@ -67,7 +73,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
67
73
  model_id: OpenAIModelId,
68
74
  messages: Sequence[Message],
69
75
  tools: Sequence[Tool] | Toolkit | None = None,
70
- format: type[FormattableT] | Format[FormattableT] | None = None,
76
+ format: type[FormattableT]
77
+ | Format[FormattableT]
78
+ | OutputParser[FormattableT]
79
+ | None = None,
71
80
  **params: Unpack[Params],
72
81
  ) -> Response | Response[FormattableT]:
73
82
  """Generate an `llm.Response` by synchronously calling the OpenAI Responses API.
@@ -91,8 +100,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
91
100
  )
92
101
  openai_response = self.client.responses.create(**kwargs)
93
102
 
103
+ include_thoughts = _utils.get_include_thoughts(params)
94
104
  assistant_message, finish_reason, usage = _utils.decode_response(
95
- openai_response, model_id, self.id
105
+ openai_response, model_id, self.id, include_thoughts=include_thoughts
96
106
  )
97
107
  provider_model_name = model_name(model_id, "responses")
98
108
 
@@ -116,7 +126,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
116
126
  model_id: OpenAIModelId,
117
127
  messages: Sequence[Message],
118
128
  tools: Sequence[AsyncTool] | AsyncToolkit | None = None,
119
- format: type[FormattableT] | Format[FormattableT] | None = None,
129
+ format: type[FormattableT]
130
+ | Format[FormattableT]
131
+ | OutputParser[FormattableT]
132
+ | None = None,
120
133
  **params: Unpack[Params],
121
134
  ) -> AsyncResponse | AsyncResponse[FormattableT]:
122
135
  """Generate an `llm.AsyncResponse` by asynchronously calling the OpenAI Responses API.
@@ -140,8 +153,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
140
153
  )
141
154
  openai_response = await self.async_client.responses.create(**kwargs)
142
155
 
156
+ include_thoughts = _utils.get_include_thoughts(params)
143
157
  assistant_message, finish_reason, usage = _utils.decode_response(
144
- openai_response, model_id, self.id
158
+ openai_response, model_id, self.id, include_thoughts=include_thoughts
145
159
  )
146
160
  provider_model_name = model_name(model_id, "responses")
147
161
 
@@ -165,7 +179,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
165
179
  model_id: OpenAIModelId,
166
180
  messages: Sequence[Message],
167
181
  tools: Sequence[Tool] | Toolkit | None = None,
168
- format: type[FormattableT] | Format[FormattableT] | None = None,
182
+ format: type[FormattableT]
183
+ | Format[FormattableT]
184
+ | OutputParser[FormattableT]
185
+ | None = None,
169
186
  **params: Unpack[Params],
170
187
  ) -> StreamResponse | StreamResponse[FormattableT]:
171
188
  """Generate a `llm.StreamResponse` by synchronously streaming from the OpenAI Responses API.
@@ -192,8 +209,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
192
209
  stream=True,
193
210
  )
194
211
 
212
+ include_thoughts = _utils.get_include_thoughts(params)
195
213
  chunk_iterator = _utils.decode_stream(
196
- openai_stream,
214
+ openai_stream, include_thoughts=include_thoughts
197
215
  )
198
216
  provider_model_name = model_name(model_id, "responses")
199
217
 
@@ -214,7 +232,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
214
232
  model_id: OpenAIModelId,
215
233
  messages: Sequence[Message],
216
234
  tools: Sequence[AsyncTool] | AsyncToolkit | None = None,
217
- format: type[FormattableT] | Format[FormattableT] | None = None,
235
+ format: type[FormattableT]
236
+ | Format[FormattableT]
237
+ | OutputParser[FormattableT]
238
+ | None = None,
218
239
  **params: Unpack[Params],
219
240
  ) -> AsyncStreamResponse | AsyncStreamResponse[FormattableT]:
220
241
  """Generate a `llm.AsyncStreamResponse` by asynchronously streaming from the OpenAI Responses API.
@@ -241,8 +262,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
241
262
  stream=True,
242
263
  )
243
264
 
265
+ include_thoughts = _utils.get_include_thoughts(params)
244
266
  chunk_iterator = _utils.decode_async_stream(
245
- openai_stream,
267
+ openai_stream, include_thoughts=include_thoughts
246
268
  )
247
269
  provider_model_name = model_name(model_id, "responses")
248
270
 
@@ -266,7 +288,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
266
288
  tools: Sequence[Tool | ContextTool[DepsT]]
267
289
  | ContextToolkit[DepsT]
268
290
  | None = None,
269
- format: type[FormattableT] | Format[FormattableT] | None = None,
291
+ format: type[FormattableT]
292
+ | Format[FormattableT]
293
+ | OutputParser[FormattableT]
294
+ | None = None,
270
295
  **params: Unpack[Params],
271
296
  ) -> ContextResponse[DepsT] | ContextResponse[DepsT, FormattableT]:
272
297
  """Generate a `llm.ContextResponse` by synchronously calling the OpenAI Responses API with context.
@@ -291,8 +316,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
291
316
  )
292
317
  openai_response = self.client.responses.create(**kwargs)
293
318
 
319
+ include_thoughts = _utils.get_include_thoughts(params)
294
320
  assistant_message, finish_reason, usage = _utils.decode_response(
295
- openai_response, model_id, self.id
321
+ openai_response, model_id, self.id, include_thoughts=include_thoughts
296
322
  )
297
323
  provider_model_name = model_name(model_id, "responses")
298
324
 
@@ -319,7 +345,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
319
345
  tools: Sequence[AsyncTool | AsyncContextTool[DepsT]]
320
346
  | AsyncContextToolkit[DepsT]
321
347
  | None = None,
322
- format: type[FormattableT] | Format[FormattableT] | None = None,
348
+ format: type[FormattableT]
349
+ | Format[FormattableT]
350
+ | OutputParser[FormattableT]
351
+ | None = None,
323
352
  **params: Unpack[Params],
324
353
  ) -> AsyncContextResponse[DepsT] | AsyncContextResponse[DepsT, FormattableT]:
325
354
  """Generate a `llm.AsyncContextResponse` by asynchronously calling the OpenAI Responses API with context.
@@ -344,8 +373,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
344
373
  )
345
374
  openai_response = await self.async_client.responses.create(**kwargs)
346
375
 
376
+ include_thoughts = _utils.get_include_thoughts(params)
347
377
  assistant_message, finish_reason, usage = _utils.decode_response(
348
- openai_response, model_id, self.id
378
+ openai_response, model_id, self.id, include_thoughts=include_thoughts
349
379
  )
350
380
  provider_model_name = model_name(model_id, "responses")
351
381
 
@@ -372,7 +402,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
372
402
  tools: Sequence[Tool | ContextTool[DepsT]]
373
403
  | ContextToolkit[DepsT]
374
404
  | None = None,
375
- format: type[FormattableT] | Format[FormattableT] | None = None,
405
+ format: type[FormattableT]
406
+ | Format[FormattableT]
407
+ | OutputParser[FormattableT]
408
+ | None = None,
376
409
  **params: Unpack[Params],
377
410
  ) -> ContextStreamResponse[DepsT] | ContextStreamResponse[DepsT, FormattableT]:
378
411
  """Generate a `llm.ContextStreamResponse` by synchronously streaming from the OpenAI Responses API with context.
@@ -401,8 +434,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
401
434
  stream=True,
402
435
  )
403
436
 
437
+ include_thoughts = _utils.get_include_thoughts(params)
404
438
  chunk_iterator = _utils.decode_stream(
405
- openai_stream,
439
+ openai_stream, include_thoughts=include_thoughts
406
440
  )
407
441
  provider_model_name = model_name(model_id, "responses")
408
442
 
@@ -426,7 +460,10 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
426
460
  tools: Sequence[AsyncTool | AsyncContextTool[DepsT]]
427
461
  | AsyncContextToolkit[DepsT]
428
462
  | None = None,
429
- format: type[FormattableT] | Format[FormattableT] | None = None,
463
+ format: type[FormattableT]
464
+ | Format[FormattableT]
465
+ | OutputParser[FormattableT]
466
+ | None = None,
430
467
  **params: Unpack[Params],
431
468
  ) -> (
432
469
  AsyncContextStreamResponse[DepsT]
@@ -458,8 +495,9 @@ class OpenAIResponsesProvider(BaseProvider[OpenAI]):
458
495
  stream=True,
459
496
  )
460
497
 
498
+ include_thoughts = _utils.get_include_thoughts(params)
461
499
  chunk_iterator = _utils.decode_async_stream(
462
- openai_stream,
500
+ openai_stream, include_thoughts=include_thoughts
463
501
  )
464
502
  provider_model_name = model_name(model_id, "responses")
465
503
 
@@ -6,6 +6,7 @@ KnownProviderId: TypeAlias = Literal[
6
6
  "anthropic", # Anthropic provider via AnthropicProvider
7
7
  "anthropic-beta", # Anthropic beta provider via AnthropicBetaProvider
8
8
  "google", # Google provider via GoogleProvider
9
+ "mirascope", # Mirascope Router provider via MirascopeProvider
9
10
  "mlx", # Local inference powered by `mlx-lm`, via MLXProvider
10
11
  "ollama", # Ollama provider via OllamaProvider
11
12
  "openai", # OpenAI provider via OpenAIProvider (prefers Responses routing when available)
@@ -1,12 +1,16 @@
1
1
  """Provider registry for managing provider instances and scopes."""
2
2
 
3
+ import os
4
+ from collections.abc import Sequence
5
+ from dataclasses import dataclass
3
6
  from functools import lru_cache
4
7
  from typing import overload
5
8
 
6
- from ..exceptions import NoRegisteredProviderError
9
+ from ..exceptions import MissingAPIKeyError, NoRegisteredProviderError
7
10
  from .anthropic import AnthropicProvider
8
11
  from .base import Provider
9
12
  from .google import GoogleProvider
13
+ from .mirascope import MirascopeProvider
10
14
  from .mlx import MLXProvider
11
15
  from .ollama import OllamaProvider
12
16
  from .openai import OpenAIProvider
@@ -26,18 +30,64 @@ def reset_provider_registry() -> None:
26
30
  provider_singleton.cache_clear()
27
31
 
28
32
 
29
- # Default auto-registration mapping for built-in providers
30
- # These providers will be automatically registered on first use
31
- DEFAULT_AUTO_REGISTER_SCOPES: dict[str, ProviderId] = {
32
- "anthropic/": "anthropic",
33
- "google/": "google",
34
- "mlx-community/": "mlx",
35
- "ollama/": "ollama",
36
- "openai/": "openai",
37
- "together/": "together",
33
+ @dataclass(frozen=True)
34
+ class ProviderDefault:
35
+ """Configuration for a provider in the auto-registration fallback chain.
36
+
37
+ When auto-registering a provider for a scope, the fallback chain is tried
38
+ in order. The first provider whose API key is available will be used.
39
+ """
40
+
41
+ provider_id: ProviderId
42
+ """The provider identifier."""
43
+
44
+ api_key_env_var: str | None
45
+ """Environment variable for the API key, or None if no key is required."""
46
+
47
+
48
+ # Fallback chain for auto-registration: try providers in order until one has
49
+ # its API key available. This enables automatic fallback to Mirascope Router
50
+ # when direct provider keys are not set.
51
+ DEFAULT_AUTO_REGISTER_SCOPES: dict[str, Sequence[ProviderDefault]] = {
52
+ "anthropic/": [
53
+ ProviderDefault("anthropic", "ANTHROPIC_API_KEY"),
54
+ ProviderDefault("mirascope", "MIRASCOPE_API_KEY"),
55
+ ],
56
+ "google/": [
57
+ ProviderDefault("google", "GOOGLE_API_KEY"),
58
+ ProviderDefault("mirascope", "MIRASCOPE_API_KEY"),
59
+ ],
60
+ "openai/": [
61
+ ProviderDefault("openai", "OPENAI_API_KEY"),
62
+ ProviderDefault("mirascope", "MIRASCOPE_API_KEY"),
63
+ ],
64
+ "together/": [
65
+ ProviderDefault("together", "TOGETHER_API_KEY"),
66
+ # No Mirascope fallback for together
67
+ ],
68
+ "ollama/": [
69
+ ProviderDefault("ollama", None), # No API key required
70
+ ],
71
+ "mlx-community/": [
72
+ ProviderDefault("mlx", None), # No API key required
73
+ ],
38
74
  }
39
75
 
40
76
 
77
+ def _has_api_key(default: ProviderDefault) -> bool:
78
+ """Check if the API key for a provider default is available.
79
+
80
+ Args:
81
+ default: The provider default configuration to check.
82
+
83
+ Returns:
84
+ True if the API key is available or not required, False otherwise.
85
+ """
86
+ if default.api_key_env_var is None:
87
+ return True # Provider doesn't require API key
88
+ return os.environ.get(default.api_key_env_var) is not None
89
+
90
+
41
91
  @lru_cache(maxsize=256)
42
92
  def provider_singleton(
43
93
  provider_id: ProviderId, *, api_key: str | None = None, base_url: str | None = None
@@ -60,6 +110,8 @@ def provider_singleton(
60
110
  return AnthropicProvider(api_key=api_key, base_url=base_url)
61
111
  case "google":
62
112
  return GoogleProvider(api_key=api_key, base_url=base_url)
113
+ case "mirascope":
114
+ return MirascopeProvider(api_key=api_key, base_url=base_url)
63
115
  case "mlx": # pragma: no cover (MLX is only available on macOS)
64
116
  return MLXProvider()
65
117
  case "ollama":
@@ -172,6 +224,10 @@ def get_provider_for_model(model_id: str) -> Provider:
172
224
  If no explicit registration is found, checks for auto-registration defaults
173
225
  and automatically registers the provider on first use.
174
226
 
227
+ When auto-registering, providers are tried in fallback order. For example,
228
+ if ANTHROPIC_API_KEY is not set but MIRASCOPE_API_KEY is, the Mirascope
229
+ Router will be used as a fallback for anthropic/ models.
230
+
175
231
  Args:
176
232
  model_id: The full model ID (e.g., "anthropic/claude-4-5-sonnet").
177
233
 
@@ -179,7 +235,8 @@ def get_provider_for_model(model_id: str) -> Provider:
179
235
  The provider instance registered for this model.
180
236
 
181
237
  Raises:
182
- ValueError: If no provider is registered or available for this model.
238
+ NoRegisteredProviderError: If no provider scope matches the model_id.
239
+ MissingAPIKeyError: If no provider in the fallback chain has its API key set.
183
240
 
184
241
  Example:
185
242
  ```python
@@ -196,6 +253,11 @@ def get_provider_for_model(model_id: str) -> Provider:
196
253
  # Auto-registration on first use:
197
254
  provider = get_provider_for_model("openai/gpt-4")
198
255
  # Automatically loads and registers OpenAIProvider() for "openai/"
256
+
257
+ # Fallback to Mirascope Router if direct key missing:
258
+ # (with MIRASCOPE_API_KEY set but not ANTHROPIC_API_KEY)
259
+ provider = get_provider_for_model("anthropic/claude-4-5-sonnet")
260
+ # Returns MirascopeProvider registered for "anthropic/" scope
199
261
  ```
200
262
  """
201
263
  # Try explicit registry first (longest match wins)
@@ -206,17 +268,32 @@ def get_provider_for_model(model_id: str) -> Provider:
206
268
  best_scope = max(matching_scopes, key=len)
207
269
  return PROVIDER_REGISTRY[best_scope]
208
270
 
209
- # Fall back to auto-registration
271
+ # Fall back to auto-registration with fallback chain
210
272
  matching_defaults = [
211
273
  scope for scope in DEFAULT_AUTO_REGISTER_SCOPES if model_id.startswith(scope)
212
274
  ]
213
275
  if matching_defaults:
214
276
  best_scope = max(matching_defaults, key=len)
215
- provider_id = DEFAULT_AUTO_REGISTER_SCOPES[best_scope]
216
- provider = provider_singleton(provider_id)
217
- # Auto-register for future calls
218
- PROVIDER_REGISTRY[best_scope] = provider
219
- return provider
220
-
221
- # No provider found
277
+ fallback_chain = DEFAULT_AUTO_REGISTER_SCOPES[best_scope]
278
+
279
+ # Try each provider in the fallback chain
280
+ for default in fallback_chain:
281
+ if _has_api_key(default):
282
+ provider = provider_singleton(default.provider_id)
283
+ # Register for just this scope (not all provider's default scopes)
284
+ PROVIDER_REGISTRY[best_scope] = provider
285
+ return provider
286
+
287
+ # No provider in chain has API key - raise helpful error
288
+ primary = fallback_chain[0]
289
+ has_mirascope_fallback = any(
290
+ d.provider_id == "mirascope" for d in fallback_chain
291
+ )
292
+ raise MissingAPIKeyError(
293
+ provider_id=primary.provider_id,
294
+ env_var=primary.api_key_env_var or "",
295
+ has_mirascope_fallback=has_mirascope_fallback,
296
+ )
297
+
298
+ # No matching scope at all
222
299
  raise NoRegisteredProviderError(model_id)
@@ -1,18 +1,6 @@
1
1
  """Together AI provider implementation."""
2
2
 
3
- from typing import TYPE_CHECKING
4
-
5
- if TYPE_CHECKING:
6
- from .provider import TogetherProvider
7
- else:
8
- try:
9
- from .provider import TogetherProvider
10
- except ImportError: # pragma: no cover
11
- from .._missing_import_stubs import (
12
- create_provider_stub,
13
- )
14
-
15
- TogetherProvider = create_provider_stub("openai", "TogetherProvider")
3
+ from .provider import TogetherProvider
16
4
 
17
5
  __all__ = [
18
6
  "TogetherProvider",
@@ -1,16 +1,18 @@
1
1
  """The Responses module for LLM responses."""
2
2
 
3
3
  from . import _utils
4
+ from .base_response import ResponseT
4
5
  from .base_stream_response import (
5
6
  AsyncChunkIterator,
6
7
  ChunkIterator,
7
8
  RawMessageChunk,
8
9
  RawStreamEventChunk,
9
10
  StreamResponseChunk,
11
+ StreamResponseT,
10
12
  )
11
13
  from .finish_reason import FinishReason, FinishReasonChunk
12
14
  from .response import AsyncContextResponse, AsyncResponse, ContextResponse, Response
13
- from .root_response import RootResponse
15
+ from .root_response import AnyResponse, RootResponse
14
16
  from .stream_response import (
15
17
  AsyncContextStreamResponse,
16
18
  AsyncStreamResponse,
@@ -30,6 +32,7 @@ from .streams import (
30
32
  from .usage import Usage, UsageDeltaChunk
31
33
 
32
34
  __all__ = [
35
+ "AnyResponse",
33
36
  "AsyncChunkIterator",
34
37
  "AsyncContextResponse",
35
38
  "AsyncContextStreamResponse",
@@ -47,10 +50,12 @@ __all__ = [
47
50
  "RawMessageChunk",
48
51
  "RawStreamEventChunk",
49
52
  "Response",
53
+ "ResponseT",
50
54
  "RootResponse",
51
55
  "Stream",
52
56
  "StreamResponse",
53
57
  "StreamResponseChunk",
58
+ "StreamResponseT",
54
59
  "TextStream",
55
60
  "ThoughtStream",
56
61
  "ToolCallStream",