mirascope 1.0.5__py3-none-any.whl → 2.1.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 (632) hide show
  1. mirascope/__init__.py +6 -6
  2. mirascope/_stubs.py +384 -0
  3. mirascope/_utils.py +34 -0
  4. mirascope/api/__init__.py +14 -0
  5. mirascope/api/_generated/README.md +207 -0
  6. mirascope/api/_generated/__init__.py +444 -0
  7. mirascope/api/_generated/annotations/__init__.py +33 -0
  8. mirascope/api/_generated/annotations/client.py +506 -0
  9. mirascope/api/_generated/annotations/raw_client.py +1414 -0
  10. mirascope/api/_generated/annotations/types/__init__.py +31 -0
  11. mirascope/api/_generated/annotations/types/annotations_create_request_label.py +5 -0
  12. mirascope/api/_generated/annotations/types/annotations_create_response.py +48 -0
  13. mirascope/api/_generated/annotations/types/annotations_create_response_label.py +5 -0
  14. mirascope/api/_generated/annotations/types/annotations_get_response.py +48 -0
  15. mirascope/api/_generated/annotations/types/annotations_get_response_label.py +5 -0
  16. mirascope/api/_generated/annotations/types/annotations_list_request_label.py +5 -0
  17. mirascope/api/_generated/annotations/types/annotations_list_response.py +21 -0
  18. mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item.py +50 -0
  19. mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item_label.py +5 -0
  20. mirascope/api/_generated/annotations/types/annotations_update_request_label.py +5 -0
  21. mirascope/api/_generated/annotations/types/annotations_update_response.py +48 -0
  22. mirascope/api/_generated/annotations/types/annotations_update_response_label.py +5 -0
  23. mirascope/api/_generated/api_keys/__init__.py +17 -0
  24. mirascope/api/_generated/api_keys/client.py +530 -0
  25. mirascope/api/_generated/api_keys/raw_client.py +1236 -0
  26. mirascope/api/_generated/api_keys/types/__init__.py +15 -0
  27. mirascope/api/_generated/api_keys/types/api_keys_create_response.py +28 -0
  28. mirascope/api/_generated/api_keys/types/api_keys_get_response.py +27 -0
  29. mirascope/api/_generated/api_keys/types/api_keys_list_all_for_org_response_item.py +40 -0
  30. mirascope/api/_generated/api_keys/types/api_keys_list_response_item.py +27 -0
  31. mirascope/api/_generated/client.py +211 -0
  32. mirascope/api/_generated/core/__init__.py +52 -0
  33. mirascope/api/_generated/core/api_error.py +23 -0
  34. mirascope/api/_generated/core/client_wrapper.py +46 -0
  35. mirascope/api/_generated/core/datetime_utils.py +28 -0
  36. mirascope/api/_generated/core/file.py +67 -0
  37. mirascope/api/_generated/core/force_multipart.py +16 -0
  38. mirascope/api/_generated/core/http_client.py +543 -0
  39. mirascope/api/_generated/core/http_response.py +55 -0
  40. mirascope/api/_generated/core/jsonable_encoder.py +100 -0
  41. mirascope/api/_generated/core/pydantic_utilities.py +255 -0
  42. mirascope/api/_generated/core/query_encoder.py +58 -0
  43. mirascope/api/_generated/core/remove_none_from_dict.py +11 -0
  44. mirascope/api/_generated/core/request_options.py +35 -0
  45. mirascope/api/_generated/core/serialization.py +276 -0
  46. mirascope/api/_generated/docs/__init__.py +4 -0
  47. mirascope/api/_generated/docs/client.py +91 -0
  48. mirascope/api/_generated/docs/raw_client.py +178 -0
  49. mirascope/api/_generated/environment.py +9 -0
  50. mirascope/api/_generated/environments/__init__.py +23 -0
  51. mirascope/api/_generated/environments/client.py +649 -0
  52. mirascope/api/_generated/environments/raw_client.py +1567 -0
  53. mirascope/api/_generated/environments/types/__init__.py +25 -0
  54. mirascope/api/_generated/environments/types/environments_create_response.py +24 -0
  55. mirascope/api/_generated/environments/types/environments_get_analytics_response.py +60 -0
  56. mirascope/api/_generated/environments/types/environments_get_analytics_response_top_functions_item.py +24 -0
  57. mirascope/api/_generated/environments/types/environments_get_analytics_response_top_models_item.py +22 -0
  58. mirascope/api/_generated/environments/types/environments_get_response.py +24 -0
  59. mirascope/api/_generated/environments/types/environments_list_response_item.py +24 -0
  60. mirascope/api/_generated/environments/types/environments_update_response.py +24 -0
  61. mirascope/api/_generated/errors/__init__.py +25 -0
  62. mirascope/api/_generated/errors/bad_request_error.py +14 -0
  63. mirascope/api/_generated/errors/conflict_error.py +14 -0
  64. mirascope/api/_generated/errors/forbidden_error.py +11 -0
  65. mirascope/api/_generated/errors/internal_server_error.py +10 -0
  66. mirascope/api/_generated/errors/not_found_error.py +11 -0
  67. mirascope/api/_generated/errors/payment_required_error.py +15 -0
  68. mirascope/api/_generated/errors/service_unavailable_error.py +14 -0
  69. mirascope/api/_generated/errors/too_many_requests_error.py +15 -0
  70. mirascope/api/_generated/errors/unauthorized_error.py +11 -0
  71. mirascope/api/_generated/functions/__init__.py +39 -0
  72. mirascope/api/_generated/functions/client.py +647 -0
  73. mirascope/api/_generated/functions/raw_client.py +1890 -0
  74. mirascope/api/_generated/functions/types/__init__.py +53 -0
  75. mirascope/api/_generated/functions/types/functions_create_request_dependencies_value.py +20 -0
  76. mirascope/api/_generated/functions/types/functions_create_response.py +37 -0
  77. mirascope/api/_generated/functions/types/functions_create_response_dependencies_value.py +20 -0
  78. mirascope/api/_generated/functions/types/functions_find_by_hash_response.py +39 -0
  79. mirascope/api/_generated/functions/types/functions_find_by_hash_response_dependencies_value.py +20 -0
  80. mirascope/api/_generated/functions/types/functions_get_by_env_response.py +53 -0
  81. mirascope/api/_generated/functions/types/functions_get_by_env_response_dependencies_value.py +22 -0
  82. mirascope/api/_generated/functions/types/functions_get_response.py +37 -0
  83. mirascope/api/_generated/functions/types/functions_get_response_dependencies_value.py +20 -0
  84. mirascope/api/_generated/functions/types/functions_list_by_env_response.py +25 -0
  85. mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +56 -0
  86. mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item_dependencies_value.py +22 -0
  87. mirascope/api/_generated/functions/types/functions_list_response.py +21 -0
  88. mirascope/api/_generated/functions/types/functions_list_response_functions_item.py +41 -0
  89. mirascope/api/_generated/functions/types/functions_list_response_functions_item_dependencies_value.py +20 -0
  90. mirascope/api/_generated/health/__init__.py +7 -0
  91. mirascope/api/_generated/health/client.py +92 -0
  92. mirascope/api/_generated/health/raw_client.py +175 -0
  93. mirascope/api/_generated/health/types/__init__.py +8 -0
  94. mirascope/api/_generated/health/types/health_check_response.py +22 -0
  95. mirascope/api/_generated/health/types/health_check_response_status.py +5 -0
  96. mirascope/api/_generated/organization_invitations/__init__.py +33 -0
  97. mirascope/api/_generated/organization_invitations/client.py +546 -0
  98. mirascope/api/_generated/organization_invitations/raw_client.py +1519 -0
  99. mirascope/api/_generated/organization_invitations/types/__init__.py +53 -0
  100. mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response.py +34 -0
  101. mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response_role.py +7 -0
  102. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_request_role.py +7 -0
  103. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response.py +48 -0
  104. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_role.py +7 -0
  105. mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_status.py +7 -0
  106. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response.py +48 -0
  107. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_role.py +7 -0
  108. mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_status.py +7 -0
  109. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item.py +48 -0
  110. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_role.py +7 -0
  111. mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_status.py +7 -0
  112. mirascope/api/_generated/organization_memberships/__init__.py +19 -0
  113. mirascope/api/_generated/organization_memberships/client.py +302 -0
  114. mirascope/api/_generated/organization_memberships/raw_client.py +736 -0
  115. mirascope/api/_generated/organization_memberships/types/__init__.py +27 -0
  116. mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item.py +33 -0
  117. mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item_role.py +7 -0
  118. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_request_role.py +7 -0
  119. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response.py +31 -0
  120. mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response_role.py +7 -0
  121. mirascope/api/_generated/organizations/__init__.py +51 -0
  122. mirascope/api/_generated/organizations/client.py +869 -0
  123. mirascope/api/_generated/organizations/raw_client.py +2593 -0
  124. mirascope/api/_generated/organizations/types/__init__.py +71 -0
  125. mirascope/api/_generated/organizations/types/organizations_create_payment_intent_response.py +24 -0
  126. mirascope/api/_generated/organizations/types/organizations_create_response.py +26 -0
  127. mirascope/api/_generated/organizations/types/organizations_create_response_role.py +5 -0
  128. mirascope/api/_generated/organizations/types/organizations_get_response.py +26 -0
  129. mirascope/api/_generated/organizations/types/organizations_get_response_role.py +5 -0
  130. mirascope/api/_generated/organizations/types/organizations_list_response_item.py +26 -0
  131. mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +5 -0
  132. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_request_target_plan.py +7 -0
  133. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response.py +47 -0
  134. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item.py +33 -0
  135. mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item_resource.py +7 -0
  136. mirascope/api/_generated/organizations/types/organizations_router_balance_response.py +24 -0
  137. mirascope/api/_generated/organizations/types/organizations_subscription_response.py +53 -0
  138. mirascope/api/_generated/organizations/types/organizations_subscription_response_current_plan.py +7 -0
  139. mirascope/api/_generated/organizations/types/organizations_subscription_response_payment_method.py +26 -0
  140. mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change.py +34 -0
  141. mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change_target_plan.py +7 -0
  142. mirascope/api/_generated/organizations/types/organizations_update_response.py +26 -0
  143. mirascope/api/_generated/organizations/types/organizations_update_response_role.py +5 -0
  144. mirascope/api/_generated/organizations/types/organizations_update_subscription_request_target_plan.py +7 -0
  145. mirascope/api/_generated/organizations/types/organizations_update_subscription_response.py +35 -0
  146. mirascope/api/_generated/project_memberships/__init__.py +29 -0
  147. mirascope/api/_generated/project_memberships/client.py +528 -0
  148. mirascope/api/_generated/project_memberships/raw_client.py +1278 -0
  149. mirascope/api/_generated/project_memberships/types/__init__.py +33 -0
  150. mirascope/api/_generated/project_memberships/types/project_memberships_create_request_role.py +7 -0
  151. mirascope/api/_generated/project_memberships/types/project_memberships_create_response.py +35 -0
  152. mirascope/api/_generated/project_memberships/types/project_memberships_create_response_role.py +7 -0
  153. mirascope/api/_generated/project_memberships/types/project_memberships_get_response.py +33 -0
  154. mirascope/api/_generated/project_memberships/types/project_memberships_get_response_role.py +7 -0
  155. mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item.py +33 -0
  156. mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item_role.py +7 -0
  157. mirascope/api/_generated/project_memberships/types/project_memberships_update_request_role.py +7 -0
  158. mirascope/api/_generated/project_memberships/types/project_memberships_update_response.py +35 -0
  159. mirascope/api/_generated/project_memberships/types/project_memberships_update_response_role.py +7 -0
  160. mirascope/api/_generated/projects/__init__.py +7 -0
  161. mirascope/api/_generated/projects/client.py +428 -0
  162. mirascope/api/_generated/projects/raw_client.py +1302 -0
  163. mirascope/api/_generated/projects/types/__init__.py +10 -0
  164. mirascope/api/_generated/projects/types/projects_create_response.py +25 -0
  165. mirascope/api/_generated/projects/types/projects_get_response.py +25 -0
  166. mirascope/api/_generated/projects/types/projects_list_response_item.py +25 -0
  167. mirascope/api/_generated/projects/types/projects_update_response.py +25 -0
  168. mirascope/api/_generated/reference.md +4987 -0
  169. mirascope/api/_generated/tags/__init__.py +19 -0
  170. mirascope/api/_generated/tags/client.py +504 -0
  171. mirascope/api/_generated/tags/raw_client.py +1288 -0
  172. mirascope/api/_generated/tags/types/__init__.py +17 -0
  173. mirascope/api/_generated/tags/types/tags_create_response.py +41 -0
  174. mirascope/api/_generated/tags/types/tags_get_response.py +41 -0
  175. mirascope/api/_generated/tags/types/tags_list_response.py +23 -0
  176. mirascope/api/_generated/tags/types/tags_list_response_tags_item.py +41 -0
  177. mirascope/api/_generated/tags/types/tags_update_response.py +41 -0
  178. mirascope/api/_generated/token_cost/__init__.py +7 -0
  179. mirascope/api/_generated/token_cost/client.py +160 -0
  180. mirascope/api/_generated/token_cost/raw_client.py +264 -0
  181. mirascope/api/_generated/token_cost/types/__init__.py +8 -0
  182. mirascope/api/_generated/token_cost/types/token_cost_calculate_request_usage.py +54 -0
  183. mirascope/api/_generated/token_cost/types/token_cost_calculate_response.py +52 -0
  184. mirascope/api/_generated/traces/__init__.py +97 -0
  185. mirascope/api/_generated/traces/client.py +1103 -0
  186. mirascope/api/_generated/traces/raw_client.py +2322 -0
  187. mirascope/api/_generated/traces/types/__init__.py +155 -0
  188. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +29 -0
  189. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +27 -0
  190. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +23 -0
  191. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +38 -0
  192. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +19 -0
  193. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +22 -0
  194. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +20 -0
  195. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +29 -0
  196. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +31 -0
  197. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +23 -0
  198. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +38 -0
  199. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +19 -0
  200. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +22 -0
  201. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value_values_item.py +22 -0
  202. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +48 -0
  203. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +23 -0
  204. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +38 -0
  205. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +19 -0
  206. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +24 -0
  207. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value_values_item.py +22 -0
  208. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +20 -0
  209. mirascope/api/_generated/traces/types/traces_create_response.py +24 -0
  210. mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +22 -0
  211. mirascope/api/_generated/traces/types/traces_get_analytics_summary_response.py +60 -0
  212. mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_functions_item.py +24 -0
  213. mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_models_item.py +22 -0
  214. mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response.py +33 -0
  215. mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response_spans_item.py +88 -0
  216. mirascope/api/_generated/traces/types/traces_get_trace_detail_response.py +33 -0
  217. mirascope/api/_generated/traces/types/traces_get_trace_detail_response_spans_item.py +88 -0
  218. mirascope/api/_generated/traces/types/traces_list_by_function_hash_response.py +25 -0
  219. mirascope/api/_generated/traces/types/traces_list_by_function_hash_response_traces_item.py +44 -0
  220. mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item.py +26 -0
  221. mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item_operator.py +7 -0
  222. mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_by.py +7 -0
  223. mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_order.py +7 -0
  224. mirascope/api/_generated/traces/types/traces_search_by_env_response.py +26 -0
  225. mirascope/api/_generated/traces/types/traces_search_by_env_response_spans_item.py +50 -0
  226. mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item.py +26 -0
  227. mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item_operator.py +7 -0
  228. mirascope/api/_generated/traces/types/traces_search_request_sort_by.py +7 -0
  229. mirascope/api/_generated/traces/types/traces_search_request_sort_order.py +5 -0
  230. mirascope/api/_generated/traces/types/traces_search_response.py +26 -0
  231. mirascope/api/_generated/traces/types/traces_search_response_spans_item.py +50 -0
  232. mirascope/api/_generated/types/__init__.py +85 -0
  233. mirascope/api/_generated/types/already_exists_error.py +22 -0
  234. mirascope/api/_generated/types/already_exists_error_tag.py +5 -0
  235. mirascope/api/_generated/types/bad_request_error_body.py +50 -0
  236. mirascope/api/_generated/types/click_house_error.py +22 -0
  237. mirascope/api/_generated/types/database_error.py +22 -0
  238. mirascope/api/_generated/types/database_error_tag.py +5 -0
  239. mirascope/api/_generated/types/date.py +3 -0
  240. mirascope/api/_generated/types/http_api_decode_error.py +27 -0
  241. mirascope/api/_generated/types/http_api_decode_error_tag.py +5 -0
  242. mirascope/api/_generated/types/immutable_resource_error.py +22 -0
  243. mirascope/api/_generated/types/internal_server_error_body.py +49 -0
  244. mirascope/api/_generated/types/issue.py +38 -0
  245. mirascope/api/_generated/types/issue_tag.py +10 -0
  246. mirascope/api/_generated/types/not_found_error_body.py +22 -0
  247. mirascope/api/_generated/types/not_found_error_tag.py +5 -0
  248. mirascope/api/_generated/types/number_from_string.py +3 -0
  249. mirascope/api/_generated/types/permission_denied_error.py +22 -0
  250. mirascope/api/_generated/types/permission_denied_error_tag.py +5 -0
  251. mirascope/api/_generated/types/plan_limit_exceeded_error.py +32 -0
  252. mirascope/api/_generated/types/plan_limit_exceeded_error_tag.py +7 -0
  253. mirascope/api/_generated/types/pricing_unavailable_error.py +23 -0
  254. mirascope/api/_generated/types/property_key.py +7 -0
  255. mirascope/api/_generated/types/property_key_key.py +25 -0
  256. mirascope/api/_generated/types/property_key_key_tag.py +5 -0
  257. mirascope/api/_generated/types/rate_limit_error.py +31 -0
  258. mirascope/api/_generated/types/rate_limit_error_tag.py +5 -0
  259. mirascope/api/_generated/types/service_unavailable_error_body.py +24 -0
  260. mirascope/api/_generated/types/service_unavailable_error_tag.py +7 -0
  261. mirascope/api/_generated/types/stripe_error.py +20 -0
  262. mirascope/api/_generated/types/subscription_past_due_error.py +31 -0
  263. mirascope/api/_generated/types/subscription_past_due_error_tag.py +7 -0
  264. mirascope/api/_generated/types/unauthorized_error_body.py +21 -0
  265. mirascope/api/_generated/types/unauthorized_error_tag.py +5 -0
  266. mirascope/api/client.py +255 -0
  267. mirascope/api/settings.py +99 -0
  268. mirascope/llm/__init__.py +316 -0
  269. mirascope/llm/calls/__init__.py +17 -0
  270. mirascope/llm/calls/calls.py +348 -0
  271. mirascope/llm/calls/decorator.py +268 -0
  272. mirascope/llm/content/__init__.py +71 -0
  273. mirascope/llm/content/audio.py +173 -0
  274. mirascope/llm/content/document.py +94 -0
  275. mirascope/llm/content/image.py +206 -0
  276. mirascope/llm/content/text.py +47 -0
  277. mirascope/llm/content/thought.py +58 -0
  278. mirascope/llm/content/tool_call.py +69 -0
  279. mirascope/llm/content/tool_output.py +43 -0
  280. mirascope/llm/context/__init__.py +6 -0
  281. mirascope/llm/context/_utils.py +41 -0
  282. mirascope/llm/context/context.py +24 -0
  283. mirascope/llm/exceptions.py +360 -0
  284. mirascope/llm/formatting/__init__.py +39 -0
  285. mirascope/llm/formatting/format.py +291 -0
  286. mirascope/llm/formatting/from_call_args.py +30 -0
  287. mirascope/llm/formatting/output_parser.py +178 -0
  288. mirascope/llm/formatting/partial.py +131 -0
  289. mirascope/llm/formatting/primitives.py +192 -0
  290. mirascope/llm/formatting/types.py +83 -0
  291. mirascope/llm/mcp/__init__.py +5 -0
  292. mirascope/llm/mcp/mcp_client.py +130 -0
  293. mirascope/llm/messages/__init__.py +35 -0
  294. mirascope/llm/messages/_utils.py +34 -0
  295. mirascope/llm/messages/message.py +190 -0
  296. mirascope/llm/models/__init__.py +21 -0
  297. mirascope/llm/models/models.py +1339 -0
  298. mirascope/llm/models/params.py +72 -0
  299. mirascope/llm/models/thinking_config.py +61 -0
  300. mirascope/llm/prompts/__init__.py +34 -0
  301. mirascope/llm/prompts/_utils.py +31 -0
  302. mirascope/llm/prompts/decorator.py +215 -0
  303. mirascope/llm/prompts/prompts.py +484 -0
  304. mirascope/llm/prompts/protocols.py +65 -0
  305. mirascope/llm/providers/__init__.py +65 -0
  306. mirascope/llm/providers/anthropic/__init__.py +11 -0
  307. mirascope/llm/providers/anthropic/_utils/__init__.py +27 -0
  308. mirascope/llm/providers/anthropic/_utils/beta_decode.py +297 -0
  309. mirascope/llm/providers/anthropic/_utils/beta_encode.py +272 -0
  310. mirascope/llm/providers/anthropic/_utils/decode.py +326 -0
  311. mirascope/llm/providers/anthropic/_utils/encode.py +431 -0
  312. mirascope/llm/providers/anthropic/_utils/errors.py +46 -0
  313. mirascope/llm/providers/anthropic/beta_provider.py +338 -0
  314. mirascope/llm/providers/anthropic/model_id.py +23 -0
  315. mirascope/llm/providers/anthropic/model_info.py +87 -0
  316. mirascope/llm/providers/anthropic/provider.py +440 -0
  317. mirascope/llm/providers/base/__init__.py +14 -0
  318. mirascope/llm/providers/base/_utils.py +248 -0
  319. mirascope/llm/providers/base/base_provider.py +1463 -0
  320. mirascope/llm/providers/base/kwargs.py +12 -0
  321. mirascope/llm/providers/google/__init__.py +6 -0
  322. mirascope/llm/providers/google/_utils/__init__.py +17 -0
  323. mirascope/llm/providers/google/_utils/decode.py +357 -0
  324. mirascope/llm/providers/google/_utils/encode.py +418 -0
  325. mirascope/llm/providers/google/_utils/errors.py +50 -0
  326. mirascope/llm/providers/google/message.py +7 -0
  327. mirascope/llm/providers/google/model_id.py +22 -0
  328. mirascope/llm/providers/google/model_info.py +63 -0
  329. mirascope/llm/providers/google/provider.py +456 -0
  330. mirascope/llm/providers/mirascope/__init__.py +5 -0
  331. mirascope/llm/providers/mirascope/_utils.py +73 -0
  332. mirascope/llm/providers/mirascope/provider.py +313 -0
  333. mirascope/llm/providers/mlx/__init__.py +9 -0
  334. mirascope/llm/providers/mlx/_utils.py +141 -0
  335. mirascope/llm/providers/mlx/encoding/__init__.py +8 -0
  336. mirascope/llm/providers/mlx/encoding/base.py +69 -0
  337. mirascope/llm/providers/mlx/encoding/transformers.py +146 -0
  338. mirascope/llm/providers/mlx/mlx.py +242 -0
  339. mirascope/llm/providers/mlx/model_id.py +17 -0
  340. mirascope/llm/providers/mlx/provider.py +416 -0
  341. mirascope/llm/providers/model_id.py +16 -0
  342. mirascope/llm/providers/ollama/__init__.py +7 -0
  343. mirascope/llm/providers/ollama/provider.py +71 -0
  344. mirascope/llm/providers/openai/__init__.py +15 -0
  345. mirascope/llm/providers/openai/_utils/__init__.py +5 -0
  346. mirascope/llm/providers/openai/_utils/errors.py +46 -0
  347. mirascope/llm/providers/openai/completions/__init__.py +7 -0
  348. mirascope/llm/providers/openai/completions/_utils/__init__.py +18 -0
  349. mirascope/llm/providers/openai/completions/_utils/decode.py +252 -0
  350. mirascope/llm/providers/openai/completions/_utils/encode.py +390 -0
  351. mirascope/llm/providers/openai/completions/_utils/feature_info.py +50 -0
  352. mirascope/llm/providers/openai/completions/base_provider.py +522 -0
  353. mirascope/llm/providers/openai/completions/provider.py +28 -0
  354. mirascope/llm/providers/openai/model_id.py +31 -0
  355. mirascope/llm/providers/openai/model_info.py +303 -0
  356. mirascope/llm/providers/openai/provider.py +405 -0
  357. mirascope/llm/providers/openai/responses/__init__.py +5 -0
  358. mirascope/llm/providers/openai/responses/_utils/__init__.py +15 -0
  359. mirascope/llm/providers/openai/responses/_utils/decode.py +289 -0
  360. mirascope/llm/providers/openai/responses/_utils/encode.py +399 -0
  361. mirascope/llm/providers/openai/responses/provider.py +472 -0
  362. mirascope/llm/providers/openrouter/__init__.py +5 -0
  363. mirascope/llm/providers/openrouter/provider.py +67 -0
  364. mirascope/llm/providers/provider_id.py +26 -0
  365. mirascope/llm/providers/provider_registry.py +305 -0
  366. mirascope/llm/providers/together/__init__.py +7 -0
  367. mirascope/llm/providers/together/provider.py +40 -0
  368. mirascope/llm/responses/__init__.py +66 -0
  369. mirascope/llm/responses/_utils.py +146 -0
  370. mirascope/llm/responses/base_response.py +103 -0
  371. mirascope/llm/responses/base_stream_response.py +824 -0
  372. mirascope/llm/responses/finish_reason.py +28 -0
  373. mirascope/llm/responses/response.py +362 -0
  374. mirascope/llm/responses/root_response.py +248 -0
  375. mirascope/llm/responses/stream_response.py +577 -0
  376. mirascope/llm/responses/streams.py +363 -0
  377. mirascope/llm/responses/usage.py +139 -0
  378. mirascope/llm/tools/__init__.py +71 -0
  379. mirascope/llm/tools/_utils.py +34 -0
  380. mirascope/llm/tools/decorator.py +184 -0
  381. mirascope/llm/tools/protocols.py +96 -0
  382. mirascope/llm/tools/provider_tools.py +18 -0
  383. mirascope/llm/tools/tool_schema.py +321 -0
  384. mirascope/llm/tools/toolkit.py +178 -0
  385. mirascope/llm/tools/tools.py +263 -0
  386. mirascope/llm/tools/types.py +112 -0
  387. mirascope/llm/tools/web_search_tool.py +32 -0
  388. mirascope/llm/types/__init__.py +22 -0
  389. mirascope/llm/types/dataclass.py +9 -0
  390. mirascope/llm/types/jsonable.py +44 -0
  391. mirascope/llm/types/type_vars.py +19 -0
  392. mirascope/ops/__init__.py +129 -0
  393. mirascope/ops/_internal/__init__.py +5 -0
  394. mirascope/ops/_internal/closure.py +1172 -0
  395. mirascope/ops/_internal/configuration.py +177 -0
  396. mirascope/ops/_internal/context.py +76 -0
  397. mirascope/ops/_internal/exporters/__init__.py +26 -0
  398. mirascope/ops/_internal/exporters/exporters.py +362 -0
  399. mirascope/ops/_internal/exporters/processors.py +104 -0
  400. mirascope/ops/_internal/exporters/types.py +165 -0
  401. mirascope/ops/_internal/exporters/utils.py +66 -0
  402. mirascope/ops/_internal/instrumentation/__init__.py +28 -0
  403. mirascope/ops/_internal/instrumentation/llm/__init__.py +8 -0
  404. mirascope/ops/_internal/instrumentation/llm/common.py +500 -0
  405. mirascope/ops/_internal/instrumentation/llm/cost.py +190 -0
  406. mirascope/ops/_internal/instrumentation/llm/encode.py +238 -0
  407. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/__init__.py +38 -0
  408. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_input_messages.py +31 -0
  409. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_output_messages.py +38 -0
  410. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_system_instructions.py +18 -0
  411. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/shared.py +100 -0
  412. mirascope/ops/_internal/instrumentation/llm/llm.py +161 -0
  413. mirascope/ops/_internal/instrumentation/llm/model.py +1777 -0
  414. mirascope/ops/_internal/instrumentation/llm/response.py +521 -0
  415. mirascope/ops/_internal/instrumentation/llm/serialize.py +324 -0
  416. mirascope/ops/_internal/instrumentation/providers/__init__.py +29 -0
  417. mirascope/ops/_internal/instrumentation/providers/anthropic.py +78 -0
  418. mirascope/ops/_internal/instrumentation/providers/base.py +179 -0
  419. mirascope/ops/_internal/instrumentation/providers/google_genai.py +85 -0
  420. mirascope/ops/_internal/instrumentation/providers/openai.py +82 -0
  421. mirascope/ops/_internal/propagation.py +198 -0
  422. mirascope/ops/_internal/protocols.py +133 -0
  423. mirascope/ops/_internal/session.py +139 -0
  424. mirascope/ops/_internal/spans.py +232 -0
  425. mirascope/ops/_internal/traced_calls.py +389 -0
  426. mirascope/ops/_internal/traced_functions.py +528 -0
  427. mirascope/ops/_internal/tracing.py +353 -0
  428. mirascope/ops/_internal/types.py +13 -0
  429. mirascope/ops/_internal/utils.py +131 -0
  430. mirascope/ops/_internal/versioned_calls.py +512 -0
  431. mirascope/ops/_internal/versioned_functions.py +357 -0
  432. mirascope/ops/_internal/versioning.py +303 -0
  433. mirascope/ops/exceptions.py +21 -0
  434. mirascope-2.1.1.dist-info/METADATA +231 -0
  435. mirascope-2.1.1.dist-info/RECORD +437 -0
  436. {mirascope-1.0.5.dist-info → mirascope-2.1.1.dist-info}/WHEEL +1 -1
  437. {mirascope-1.0.5.dist-info → mirascope-2.1.1.dist-info}/licenses/LICENSE +1 -1
  438. mirascope/beta/__init__.py +0 -0
  439. mirascope/beta/openai/__init__.py +0 -5
  440. mirascope/beta/openai/parse.py +0 -129
  441. mirascope/beta/rag/__init__.py +0 -24
  442. mirascope/beta/rag/base/__init__.py +0 -22
  443. mirascope/beta/rag/base/chunkers/__init__.py +0 -2
  444. mirascope/beta/rag/base/chunkers/base_chunker.py +0 -37
  445. mirascope/beta/rag/base/chunkers/text_chunker.py +0 -33
  446. mirascope/beta/rag/base/config.py +0 -8
  447. mirascope/beta/rag/base/document.py +0 -11
  448. mirascope/beta/rag/base/embedders.py +0 -35
  449. mirascope/beta/rag/base/embedding_params.py +0 -18
  450. mirascope/beta/rag/base/embedding_response.py +0 -30
  451. mirascope/beta/rag/base/query_results.py +0 -7
  452. mirascope/beta/rag/base/vectorstore_params.py +0 -18
  453. mirascope/beta/rag/base/vectorstores.py +0 -37
  454. mirascope/beta/rag/chroma/__init__.py +0 -11
  455. mirascope/beta/rag/chroma/types.py +0 -57
  456. mirascope/beta/rag/chroma/vectorstores.py +0 -97
  457. mirascope/beta/rag/cohere/__init__.py +0 -11
  458. mirascope/beta/rag/cohere/embedders.py +0 -87
  459. mirascope/beta/rag/cohere/embedding_params.py +0 -29
  460. mirascope/beta/rag/cohere/embedding_response.py +0 -29
  461. mirascope/beta/rag/cohere/py.typed +0 -0
  462. mirascope/beta/rag/openai/__init__.py +0 -11
  463. mirascope/beta/rag/openai/embedders.py +0 -144
  464. mirascope/beta/rag/openai/embedding_params.py +0 -18
  465. mirascope/beta/rag/openai/embedding_response.py +0 -14
  466. mirascope/beta/rag/openai/py.typed +0 -0
  467. mirascope/beta/rag/pinecone/__init__.py +0 -19
  468. mirascope/beta/rag/pinecone/types.py +0 -143
  469. mirascope/beta/rag/pinecone/vectorstores.py +0 -148
  470. mirascope/beta/rag/weaviate/__init__.py +0 -6
  471. mirascope/beta/rag/weaviate/types.py +0 -92
  472. mirascope/beta/rag/weaviate/vectorstores.py +0 -103
  473. mirascope/core/__init__.py +0 -55
  474. mirascope/core/anthropic/__init__.py +0 -21
  475. mirascope/core/anthropic/_call.py +0 -71
  476. mirascope/core/anthropic/_utils/__init__.py +0 -16
  477. mirascope/core/anthropic/_utils/_calculate_cost.py +0 -63
  478. mirascope/core/anthropic/_utils/_convert_message_params.py +0 -54
  479. mirascope/core/anthropic/_utils/_get_json_output.py +0 -34
  480. mirascope/core/anthropic/_utils/_handle_stream.py +0 -89
  481. mirascope/core/anthropic/_utils/_setup_call.py +0 -76
  482. mirascope/core/anthropic/call_params.py +0 -36
  483. mirascope/core/anthropic/call_response.py +0 -158
  484. mirascope/core/anthropic/call_response_chunk.py +0 -104
  485. mirascope/core/anthropic/dynamic_config.py +0 -26
  486. mirascope/core/anthropic/py.typed +0 -0
  487. mirascope/core/anthropic/stream.py +0 -140
  488. mirascope/core/anthropic/tool.py +0 -77
  489. mirascope/core/base/__init__.py +0 -40
  490. mirascope/core/base/_call_factory.py +0 -323
  491. mirascope/core/base/_create.py +0 -167
  492. mirascope/core/base/_extract.py +0 -139
  493. mirascope/core/base/_partial.py +0 -63
  494. mirascope/core/base/_utils/__init__.py +0 -64
  495. mirascope/core/base/_utils/_base_type.py +0 -17
  496. mirascope/core/base/_utils/_convert_base_model_to_base_tool.py +0 -45
  497. mirascope/core/base/_utils/_convert_base_type_to_base_tool.py +0 -24
  498. mirascope/core/base/_utils/_convert_function_to_base_tool.py +0 -126
  499. mirascope/core/base/_utils/_default_tool_docstring.py +0 -6
  500. mirascope/core/base/_utils/_extract_tool_return.py +0 -36
  501. mirascope/core/base/_utils/_format_template.py +0 -29
  502. mirascope/core/base/_utils/_get_audio_type.py +0 -18
  503. mirascope/core/base/_utils/_get_fn_args.py +0 -14
  504. mirascope/core/base/_utils/_get_image_type.py +0 -26
  505. mirascope/core/base/_utils/_get_metadata.py +0 -17
  506. mirascope/core/base/_utils/_get_possible_user_message_param.py +0 -21
  507. mirascope/core/base/_utils/_get_prompt_template.py +0 -25
  508. mirascope/core/base/_utils/_get_template_values.py +0 -52
  509. mirascope/core/base/_utils/_get_template_variables.py +0 -38
  510. mirascope/core/base/_utils/_json_mode_content.py +0 -15
  511. mirascope/core/base/_utils/_parse_content_template.py +0 -157
  512. mirascope/core/base/_utils/_parse_prompt_messages.py +0 -51
  513. mirascope/core/base/_utils/_protocols.py +0 -215
  514. mirascope/core/base/_utils/_setup_call.py +0 -64
  515. mirascope/core/base/_utils/_setup_extract_tool.py +0 -24
  516. mirascope/core/base/call_params.py +0 -6
  517. mirascope/core/base/call_response.py +0 -189
  518. mirascope/core/base/call_response_chunk.py +0 -91
  519. mirascope/core/base/dynamic_config.py +0 -55
  520. mirascope/core/base/message_param.py +0 -61
  521. mirascope/core/base/metadata.py +0 -13
  522. mirascope/core/base/prompt.py +0 -415
  523. mirascope/core/base/stream.py +0 -365
  524. mirascope/core/base/structured_stream.py +0 -251
  525. mirascope/core/base/tool.py +0 -126
  526. mirascope/core/base/toolkit.py +0 -146
  527. mirascope/core/cohere/__init__.py +0 -21
  528. mirascope/core/cohere/_call.py +0 -71
  529. mirascope/core/cohere/_utils/__init__.py +0 -16
  530. mirascope/core/cohere/_utils/_calculate_cost.py +0 -39
  531. mirascope/core/cohere/_utils/_convert_message_params.py +0 -31
  532. mirascope/core/cohere/_utils/_get_json_output.py +0 -31
  533. mirascope/core/cohere/_utils/_handle_stream.py +0 -33
  534. mirascope/core/cohere/_utils/_setup_call.py +0 -89
  535. mirascope/core/cohere/call_params.py +0 -57
  536. mirascope/core/cohere/call_response.py +0 -167
  537. mirascope/core/cohere/call_response_chunk.py +0 -101
  538. mirascope/core/cohere/dynamic_config.py +0 -24
  539. mirascope/core/cohere/py.typed +0 -0
  540. mirascope/core/cohere/stream.py +0 -113
  541. mirascope/core/cohere/tool.py +0 -92
  542. mirascope/core/gemini/__init__.py +0 -21
  543. mirascope/core/gemini/_call.py +0 -71
  544. mirascope/core/gemini/_utils/__init__.py +0 -16
  545. mirascope/core/gemini/_utils/_calculate_cost.py +0 -8
  546. mirascope/core/gemini/_utils/_convert_message_params.py +0 -74
  547. mirascope/core/gemini/_utils/_get_json_output.py +0 -33
  548. mirascope/core/gemini/_utils/_handle_stream.py +0 -33
  549. mirascope/core/gemini/_utils/_setup_call.py +0 -68
  550. mirascope/core/gemini/call_params.py +0 -28
  551. mirascope/core/gemini/call_response.py +0 -173
  552. mirascope/core/gemini/call_response_chunk.py +0 -85
  553. mirascope/core/gemini/dynamic_config.py +0 -26
  554. mirascope/core/gemini/stream.py +0 -121
  555. mirascope/core/gemini/tool.py +0 -104
  556. mirascope/core/groq/__init__.py +0 -21
  557. mirascope/core/groq/_call.py +0 -71
  558. mirascope/core/groq/_utils/__init__.py +0 -16
  559. mirascope/core/groq/_utils/_calculate_cost.py +0 -68
  560. mirascope/core/groq/_utils/_convert_message_params.py +0 -23
  561. mirascope/core/groq/_utils/_get_json_output.py +0 -27
  562. mirascope/core/groq/_utils/_handle_stream.py +0 -121
  563. mirascope/core/groq/_utils/_setup_call.py +0 -67
  564. mirascope/core/groq/call_params.py +0 -51
  565. mirascope/core/groq/call_response.py +0 -160
  566. mirascope/core/groq/call_response_chunk.py +0 -89
  567. mirascope/core/groq/dynamic_config.py +0 -26
  568. mirascope/core/groq/py.typed +0 -0
  569. mirascope/core/groq/stream.py +0 -136
  570. mirascope/core/groq/tool.py +0 -79
  571. mirascope/core/litellm/__init__.py +0 -6
  572. mirascope/core/litellm/_call.py +0 -73
  573. mirascope/core/litellm/_utils/__init__.py +0 -5
  574. mirascope/core/litellm/_utils/_setup_call.py +0 -46
  575. mirascope/core/litellm/py.typed +0 -0
  576. mirascope/core/mistral/__init__.py +0 -21
  577. mirascope/core/mistral/_call.py +0 -69
  578. mirascope/core/mistral/_utils/__init__.py +0 -16
  579. mirascope/core/mistral/_utils/_calculate_cost.py +0 -47
  580. mirascope/core/mistral/_utils/_convert_message_params.py +0 -23
  581. mirascope/core/mistral/_utils/_get_json_output.py +0 -28
  582. mirascope/core/mistral/_utils/_handle_stream.py +0 -121
  583. mirascope/core/mistral/_utils/_setup_call.py +0 -86
  584. mirascope/core/mistral/call_params.py +0 -36
  585. mirascope/core/mistral/call_response.py +0 -156
  586. mirascope/core/mistral/call_response_chunk.py +0 -84
  587. mirascope/core/mistral/dynamic_config.py +0 -24
  588. mirascope/core/mistral/py.typed +0 -0
  589. mirascope/core/mistral/stream.py +0 -117
  590. mirascope/core/mistral/tool.py +0 -77
  591. mirascope/core/openai/__init__.py +0 -21
  592. mirascope/core/openai/_call.py +0 -71
  593. mirascope/core/openai/_utils/__init__.py +0 -16
  594. mirascope/core/openai/_utils/_calculate_cost.py +0 -110
  595. mirascope/core/openai/_utils/_convert_message_params.py +0 -53
  596. mirascope/core/openai/_utils/_get_json_output.py +0 -27
  597. mirascope/core/openai/_utils/_handle_stream.py +0 -125
  598. mirascope/core/openai/_utils/_setup_call.py +0 -62
  599. mirascope/core/openai/call_params.py +0 -54
  600. mirascope/core/openai/call_response.py +0 -162
  601. mirascope/core/openai/call_response_chunk.py +0 -90
  602. mirascope/core/openai/dynamic_config.py +0 -26
  603. mirascope/core/openai/py.typed +0 -0
  604. mirascope/core/openai/stream.py +0 -148
  605. mirascope/core/openai/tool.py +0 -79
  606. mirascope/core/py.typed +0 -0
  607. mirascope/integrations/__init__.py +0 -20
  608. mirascope/integrations/_middleware_factory.py +0 -277
  609. mirascope/integrations/langfuse/__init__.py +0 -3
  610. mirascope/integrations/langfuse/_utils.py +0 -114
  611. mirascope/integrations/langfuse/_with_langfuse.py +0 -71
  612. mirascope/integrations/logfire/__init__.py +0 -3
  613. mirascope/integrations/logfire/_utils.py +0 -188
  614. mirascope/integrations/logfire/_with_logfire.py +0 -60
  615. mirascope/integrations/otel/__init__.py +0 -5
  616. mirascope/integrations/otel/_utils.py +0 -268
  617. mirascope/integrations/otel/_with_hyperdx.py +0 -61
  618. mirascope/integrations/otel/_with_otel.py +0 -60
  619. mirascope/integrations/tenacity.py +0 -50
  620. mirascope/py.typed +0 -0
  621. mirascope/v0/__init__.py +0 -43
  622. mirascope/v0/anthropic.py +0 -54
  623. mirascope/v0/base/__init__.py +0 -12
  624. mirascope/v0/base/calls.py +0 -118
  625. mirascope/v0/base/extractors.py +0 -122
  626. mirascope/v0/base/ops_utils.py +0 -207
  627. mirascope/v0/base/prompts.py +0 -48
  628. mirascope/v0/base/types.py +0 -14
  629. mirascope/v0/base/utils.py +0 -21
  630. mirascope/v0/openai.py +0 -54
  631. mirascope-1.0.5.dist-info/METADATA +0 -519
  632. mirascope-1.0.5.dist-info/RECORD +0 -198
@@ -0,0 +1,824 @@
1
+ """Base class for StreamResponse and AsyncStreamResponse."""
2
+
3
+ from collections.abc import AsyncIterator, Iterator, Sequence
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Any, Generic, Literal, TypeAlias, TypeVar
6
+
7
+ from ..content import (
8
+ AssistantContentChunk,
9
+ AssistantContentPart,
10
+ Text,
11
+ TextChunk,
12
+ TextEndChunk,
13
+ TextStartChunk,
14
+ Thought,
15
+ ThoughtChunk,
16
+ ThoughtEndChunk,
17
+ ThoughtStartChunk,
18
+ ToolCall,
19
+ ToolCallChunk,
20
+ ToolCallEndChunk,
21
+ ToolCallStartChunk,
22
+ )
23
+ from ..formatting import (
24
+ Format,
25
+ FormattableT,
26
+ Partial,
27
+ is_output_parser,
28
+ )
29
+ from ..messages import AssistantMessage, Message
30
+ from ..tools import FORMAT_TOOL_NAME, ToolkitT
31
+ from ..types import Jsonable
32
+ from .finish_reason import FinishReasonChunk
33
+ from .root_response import RootResponse
34
+ from .streams import (
35
+ AsyncStream,
36
+ AsyncTextStream,
37
+ AsyncThoughtStream,
38
+ AsyncToolCallStream,
39
+ Stream,
40
+ TextStream,
41
+ ThoughtStream,
42
+ ToolCallStream,
43
+ )
44
+ from .usage import Usage, UsageDeltaChunk
45
+
46
+ if TYPE_CHECKING:
47
+ from ..models import Params
48
+ from ..providers import ModelId, ProviderId
49
+
50
+
51
+ StreamResponseT = TypeVar("StreamResponseT", bound="BaseStreamResponse[Any, Any, Any]")
52
+
53
+
54
+ @dataclass(kw_only=True)
55
+ class RawStreamEventChunk:
56
+ """A chunk containing a raw stream event from the underlying provider.
57
+
58
+ Will be accumulated on `StreamResponse.raw` for debugging purposes.
59
+ """
60
+
61
+ type: Literal["raw_stream_event_chunk"] = "raw_stream_event_chunk"
62
+
63
+ raw_stream_event: Any
64
+ """The raw stream event from the underlying provider."""
65
+
66
+
67
+ @dataclass(kw_only=True)
68
+ class RawMessageChunk:
69
+ """A chunk containing provider-specific raw message content that will be added to the `AssistantMessage`.
70
+
71
+ This chunk contains a provider-specific representation of a piece of content that
72
+ will be added to the `AssistantMessage` reconstructed by the containing stream.
73
+ This content should be a Jsonable Python object for serialization purposes.
74
+
75
+ The intention is that this content may be passed as-is back to the provider when the
76
+ generated `AssistantMessage` is being reused in conversation.
77
+ """
78
+
79
+ type: Literal["raw_message_chunk"] = "raw_message_chunk"
80
+
81
+ raw_message: Jsonable
82
+ """The provider-specific raw content.
83
+
84
+ Should be a Jsonable object.
85
+ """
86
+
87
+
88
+ StreamResponseChunk: TypeAlias = (
89
+ AssistantContentChunk
90
+ | FinishReasonChunk
91
+ | RawStreamEventChunk
92
+ | RawMessageChunk
93
+ | UsageDeltaChunk
94
+ )
95
+
96
+ ChunkIterator: TypeAlias = Iterator[StreamResponseChunk]
97
+ """Synchronous iterator yielding chunks with raw data."""
98
+
99
+ AsyncChunkIterator: TypeAlias = AsyncIterator[StreamResponseChunk]
100
+ """Asynchronous iterator yielding chunks with raw data."""
101
+
102
+ ChunkIteratorT = TypeVar("ChunkIteratorT", bound=ChunkIterator | AsyncChunkIterator)
103
+
104
+
105
+ class BaseStreamResponse(
106
+ RootResponse[ToolkitT, FormattableT],
107
+ Generic[ChunkIteratorT, ToolkitT, FormattableT],
108
+ ):
109
+ """Base class underpinning StreamResponse and AsyncStreamResponse.
110
+
111
+ Manages chunk handling logic for both.
112
+ """
113
+
114
+ raw_stream_events: Sequence[Any]
115
+ """The raw stream event chunks from the LLM. Provider-specific."""
116
+
117
+ chunks: Sequence[AssistantContentChunk]
118
+ """All of the Mirascope chunks consumed from the stream."""
119
+
120
+ content: Sequence[AssistantContentPart]
121
+ """The content generated by the LLM.
122
+
123
+ Content is updated in this array as it is consumed by the stream. Text content will
124
+ update with each text chunk (this will mutate the Text object that is returned
125
+ rather than creating a new one). Other content will be added once each part
126
+ is fully streamed.
127
+ """
128
+
129
+ messages: list[Message]
130
+ """The message history, including the most recent assistant message.
131
+
132
+ The most recent assistant message will have all of the completed content that has
133
+ already been consumed from the stream. Text content will be included as each chunk
134
+ is processed; other content will be included only when its corresponding part is
135
+ completed (to avoid partial tool calls and the like). If no content has been
136
+ streamed, then the final assistant message will be present (to maintain turn order
137
+ expectations), but will be empty.
138
+ """
139
+
140
+ texts: Sequence[Text]
141
+ """The text content in the generated response, if any.
142
+
143
+ Text content updates with each text chunk as it streams. The `Text` objects are
144
+ mutated in place rather than creating new ones for each chunk.
145
+ """
146
+
147
+ tool_calls: Sequence[ToolCall]
148
+ """The tools the LLM wants called on its behalf, if any.
149
+
150
+ Tool calls are only added to this sequence once they have been fully streamed
151
+ to avoid partial tool calls in the response.
152
+ """
153
+
154
+ thoughts: Sequence[Thought]
155
+ """The readable thoughts from the model's thinking process, if any.
156
+
157
+ The thoughts may be direct output from the model thinking process, or may be a
158
+ generated summary. (This depends on the provider; newer models tend to summarize.)
159
+
160
+ Thoughts are added to the sequence as they are streamed. The `Thought` objects are
161
+ mutated in place rather than creating new ones for each chunk.
162
+ """
163
+
164
+ consumed: bool = False
165
+ """Whether the stream has been fully consumed.
166
+
167
+ This is True after all chunks have been processed from the underlying iterator.
168
+ When False, more content may be available by calling the stream methods.
169
+ """
170
+
171
+ def __init__(
172
+ self,
173
+ *,
174
+ provider_id: "ProviderId",
175
+ model_id: "ModelId",
176
+ provider_model_name: str,
177
+ params: "Params",
178
+ toolkit: ToolkitT,
179
+ format: Format[FormattableT] | None = None,
180
+ input_messages: Sequence[Message],
181
+ chunk_iterator: ChunkIteratorT,
182
+ usage: Usage | None = None,
183
+ ) -> None:
184
+ """Initialize the BaseStreamResponse.
185
+
186
+ Args:
187
+ provider: The provider name (e.g. "anthropic", "openai").
188
+ model_id: The model identifier that generated the response.
189
+ provider_model_name: Optional provider-specific model name. May include
190
+ provider-specific additional info (like api mode in "gpt-5:responses").
191
+ params: The params used to generate the response (or None).
192
+ toolkit: Toolkit containing all the tools used to generate the response.
193
+ format: The `Format` for the expected structured output format (or None).
194
+ input_messages: The input messages that were sent to the LLM
195
+ usage: Token usage statistics for the response.
196
+
197
+ The BaseStreamResponse will process the tuples to build the chunks and raw lists
198
+ as the stream is consumed.
199
+ """
200
+
201
+ self.provider_id = provider_id
202
+ self.model_id = model_id
203
+ self.provider_model_name = provider_model_name
204
+ self.params = params
205
+ self.toolkit = toolkit
206
+ self.usage = usage
207
+ self.format = format
208
+
209
+ # Internal-only lists which we mutate (append) during chunk processing
210
+ self._chunks: list[AssistantContentChunk] = []
211
+ self._content: list[AssistantContentPart] = []
212
+ self._texts: list[Text] = []
213
+ self._thoughts: list[Thought] = []
214
+ self._tool_calls: list[ToolCall] = []
215
+ self._raw_stream_events: list[Any] = []
216
+ self._last_raw_stream_event_chunk: Any | None = None
217
+
218
+ # Externally-facing references typed as immutable Sequences
219
+ self.chunks = self._chunks
220
+ self.content = self._content
221
+ self.texts = self._texts
222
+ self.thoughts = self._thoughts
223
+ self.tool_calls = self._tool_calls
224
+ self.raw_stream_events = self._raw_stream_events
225
+
226
+ self.finish_reason = None
227
+
228
+ self._assistant_message = AssistantMessage(
229
+ content=self._content,
230
+ provider_id=provider_id,
231
+ model_id=model_id,
232
+ provider_model_name=provider_model_name,
233
+ raw_message=None,
234
+ )
235
+
236
+ self.messages = list(input_messages) + [self._assistant_message]
237
+
238
+ self._chunk_iterator = chunk_iterator
239
+ self._current_content: Text | Thought | None = None
240
+ self._current_tool_calls: dict[str, ToolCall] = {}
241
+
242
+ self._processing_format_tool: bool = False
243
+
244
+ def _transform_format_tool_chunks(
245
+ self, chunk: AssistantContentChunk
246
+ ) -> AssistantContentChunk:
247
+ if chunk.type == "tool_call_start_chunk" and chunk.name.startswith(
248
+ FORMAT_TOOL_NAME
249
+ ):
250
+ self._processing_format_tool = True
251
+ return TextStartChunk()
252
+ if self._processing_format_tool and chunk.type == "tool_call_chunk":
253
+ return TextChunk(delta=chunk.delta)
254
+ if self._processing_format_tool and chunk.type == "tool_call_end_chunk":
255
+ self._processing_format_tool = False
256
+ return TextEndChunk()
257
+ return chunk
258
+
259
+ def _handle_chunk(self, chunk: AssistantContentChunk) -> AssistantContentChunk:
260
+ if self.finish_reason:
261
+ raise RuntimeError(
262
+ f"Stream already finished with reason: {self.finish_reason}"
263
+ )
264
+ chunk = self._transform_format_tool_chunks(chunk)
265
+
266
+ if chunk.content_type == "text":
267
+ self._handle_text_chunk(chunk)
268
+ elif chunk.content_type == "tool_call":
269
+ self._handle_tool_call_chunk(chunk)
270
+ elif chunk.content_type == "thought":
271
+ self._handle_thought_chunk(chunk)
272
+ else:
273
+ raise NotImplementedError
274
+
275
+ self._chunks.append(chunk)
276
+ return chunk
277
+
278
+ def _handle_text_chunk(
279
+ self, chunk: TextStartChunk | TextChunk | TextEndChunk
280
+ ) -> None:
281
+ if chunk.type == "text_start_chunk":
282
+ if self._current_content or self._current_tool_calls:
283
+ raise RuntimeError(
284
+ "Received text_start_chunk while processing another chunk"
285
+ )
286
+ self._current_content = Text(text="")
287
+ # Text gets included in content even when unfinished.
288
+ self._content.append(self._current_content)
289
+ self._texts.append(self._current_content)
290
+
291
+ elif chunk.type == "text_chunk":
292
+ if self._current_content is None or self._current_content.type != "text":
293
+ raise RuntimeError("Received text_chunk while not processing text.")
294
+ self._current_content.text += chunk.delta
295
+
296
+ elif chunk.type == "text_end_chunk":
297
+ if self._current_content is None or self._current_content.type != "text":
298
+ raise RuntimeError("Received text_end_chunk while not processing text.")
299
+ self._current_content = None
300
+
301
+ def _handle_thought_chunk(
302
+ self, chunk: ThoughtStartChunk | ThoughtChunk | ThoughtEndChunk
303
+ ) -> None:
304
+ if chunk.type == "thought_start_chunk":
305
+ if self._current_content or self._current_tool_calls:
306
+ raise RuntimeError(
307
+ "Received thought_start_chunk while processing another chunk"
308
+ )
309
+ self._current_content = Thought(thought="")
310
+ # Thoughts get included even when unfinished.
311
+ self._content.append(self._current_content)
312
+ self._thoughts.append(self._current_content)
313
+
314
+ elif chunk.type == "thought_chunk":
315
+ if self._current_content is None or self._current_content.type != "thought":
316
+ raise RuntimeError(
317
+ "Received thought_chunk while not processing thought."
318
+ )
319
+ self._current_content.thought += chunk.delta
320
+
321
+ elif chunk.type == "thought_end_chunk":
322
+ if self._current_content is None or self._current_content.type != "thought":
323
+ raise RuntimeError(
324
+ "Received thought_end_chunk while not processing thought."
325
+ )
326
+ self._current_content = None
327
+
328
+ def _handle_tool_call_chunk(
329
+ self, chunk: ToolCallStartChunk | ToolCallChunk | ToolCallEndChunk
330
+ ) -> None:
331
+ if chunk.type == "tool_call_start_chunk":
332
+ if self._current_content:
333
+ raise RuntimeError(
334
+ "Received tool_call_start_chunk while processing another chunk"
335
+ )
336
+ if chunk.id in self._current_tool_calls:
337
+ raise RuntimeError("Got tool_call_start_chunk with conflicting id")
338
+ # Create a new tool call and track it by ID
339
+ # Multiple tool calls can be in progress simultaneously (interleaved)
340
+ tool_call = ToolCall(
341
+ id=chunk.id,
342
+ name=chunk.name,
343
+ args="",
344
+ )
345
+ self._current_tool_calls[chunk.id] = tool_call
346
+
347
+ elif chunk.type == "tool_call_chunk":
348
+ # Look up the tool call by ID
349
+ tool_call = self._current_tool_calls.get(chunk.id)
350
+ if tool_call is None:
351
+ raise RuntimeError(
352
+ f"Received tool_call_chunk for unknown tool call ID: {chunk.id}"
353
+ )
354
+ tool_call.args += chunk.delta
355
+
356
+ elif chunk.type == "tool_call_end_chunk":
357
+ # Finalize the tool call
358
+ tool_call = self._current_tool_calls.get(chunk.id)
359
+ if tool_call is None:
360
+ raise RuntimeError(
361
+ f"Received tool_call_end_chunk for unknown tool call ID: {chunk.id}"
362
+ )
363
+ if not tool_call.args:
364
+ tool_call.args = "{}"
365
+ self._content.append(tool_call)
366
+ self._tool_calls.append(tool_call)
367
+ del self._current_tool_calls[chunk.id]
368
+
369
+ def _pretty_chunk(self, chunk: AssistantContentChunk, spacer: str) -> str:
370
+ match chunk.type:
371
+ case "text_start_chunk":
372
+ return spacer
373
+ case "text_chunk":
374
+ return chunk.delta
375
+ case "tool_call_start_chunk":
376
+ return spacer + f"**ToolCall ({chunk.name}):** "
377
+ case "tool_call_chunk":
378
+ return chunk.delta
379
+ case "thought_start_chunk":
380
+ return spacer + "**Thinking:**\n "
381
+ case "thought_chunk":
382
+ return chunk.delta.replace("\n", "\n ") # Indent every line
383
+ case _:
384
+ return ""
385
+
386
+
387
+ class BaseSyncStreamResponse(BaseStreamResponse[ChunkIterator, ToolkitT, FormattableT]):
388
+ """A base class for synchronous Stream Responses."""
389
+
390
+ def streams(self) -> Iterator[Stream]:
391
+ """Returns an iterator that yields streams for each content part in the response.
392
+
393
+ Returns:
394
+ Iterator[Stream]: Synchronous iterator yielding Stream objects
395
+
396
+ Each content part in the response will correspond to one stream, which will yield
397
+ chunks of content as they come in from the underlying LLM.
398
+
399
+ Fully iterating through this iterator will fully consume the underlying stream,
400
+ updating the Response with all collected content.
401
+
402
+ As content is consumed, it is cached on the StreamResponse. If a new iterator
403
+ is constructed via calling `streams()`, it will start by replaying the cached
404
+ content from the response, and (if there is still more content to consume from
405
+ the LLM), it will proceed to consume it once it has iterated through all the
406
+ cached chunks.
407
+ """
408
+ chunk_iter = self.chunk_stream()
409
+
410
+ for start_chunk in chunk_iter:
411
+ # At the start of this loop, we always expect to find a start chunk. Then,
412
+ # before proceeding, we will collect from the stream we create (in case the
413
+ # user did not exhaust it), which ensures we will be expecting a start chunk
414
+ # again on the next iteration
415
+ match start_chunk.type:
416
+ case "text_start_chunk":
417
+
418
+ def text_stream_iterator() -> Iterator[TextChunk]:
419
+ for chunk in chunk_iter:
420
+ if chunk.type == "text_chunk":
421
+ yield chunk
422
+ else:
423
+ return # Stream finished
424
+
425
+ stream = TextStream(chunk_iterator=text_stream_iterator())
426
+ yield stream
427
+
428
+ case "thought_start_chunk":
429
+
430
+ def thought_stream_iterator() -> Iterator[ThoughtChunk]:
431
+ for chunk in chunk_iter:
432
+ if chunk.type == "thought_chunk":
433
+ yield chunk
434
+ else:
435
+ return # Stream finished
436
+
437
+ stream = ThoughtStream(chunk_iterator=thought_stream_iterator())
438
+ yield stream
439
+
440
+ case "tool_call_start_chunk":
441
+ tool_id = start_chunk.id
442
+ tool_name = start_chunk.name
443
+
444
+ def tool_call_stream_iterator() -> Iterator[ToolCallChunk]:
445
+ for chunk in chunk_iter:
446
+ if chunk.type == "tool_call_chunk":
447
+ yield chunk
448
+ else:
449
+ return # Stream finished
450
+
451
+ stream = ToolCallStream(
452
+ tool_id=tool_id,
453
+ tool_name=tool_name,
454
+ chunk_iterator=tool_call_stream_iterator(),
455
+ )
456
+ yield stream
457
+
458
+ case _: # pragma: no cover
459
+ raise RuntimeError(f"Expected start chunk, got: {start_chunk.type}")
460
+
461
+ # Before continuing to the next stream, make sure the last stream is consumed
462
+ # (If the user did not do so when we yielded it)
463
+ stream.collect()
464
+
465
+ def chunk_stream(
466
+ self,
467
+ ) -> Iterator[AssistantContentChunk]:
468
+ """Returns an iterator that yields content chunks as they are received.
469
+
470
+ Returns:
471
+ Iterator[AssistantContentChunk]: Synchronous iterator yielding chunks
472
+
473
+ This provides access to the Mirascope chunk data including start, delta, and end chunks
474
+ for each content type (text, thought, tool_call). Unlike the streams() method
475
+ that groups chunks by content part, this yields individual chunks as they arrive.
476
+
477
+ Fully iterating through this iterator will fully consume the underlying stream,
478
+ updating the Response with all collected content.
479
+
480
+ As chunks are consumed, they are cached on the StreamResponse. If a new iterator
481
+ is constructed via calling `chunk_stream()`, it will start by replaying the cached
482
+ chunks from the response, and (if there is still more content to consume from
483
+ the LLM), it will proceed to consume it once it has iterated through all the
484
+ cached chunks.
485
+ """
486
+ for chunk in self.chunks:
487
+ yield chunk
488
+
489
+ if self.consumed:
490
+ return
491
+
492
+ for chunk in self._chunk_iterator:
493
+ if chunk.type == "raw_stream_event_chunk":
494
+ self._raw_stream_events.append(chunk.raw_stream_event)
495
+ elif chunk.type == "raw_message_chunk":
496
+ self._assistant_message.raw_message = chunk.raw_message
497
+ elif chunk.type == "finish_reason_chunk":
498
+ self.finish_reason = chunk.finish_reason
499
+ elif chunk.type == "usage_delta_chunk":
500
+ if self.usage is None:
501
+ self.usage = Usage()
502
+ self.usage.input_tokens += chunk.input_tokens
503
+ self.usage.output_tokens += chunk.output_tokens
504
+ self.usage.cache_read_tokens += chunk.cache_read_tokens
505
+ self.usage.cache_write_tokens += chunk.cache_write_tokens
506
+ self.usage.reasoning_tokens += chunk.reasoning_tokens
507
+ if chunk.provider_tool_usage:
508
+ self.usage.provider_tool_usage = chunk.provider_tool_usage
509
+ else:
510
+ yield self._handle_chunk(chunk)
511
+
512
+ self.consumed = True
513
+
514
+ def finish(self) -> None:
515
+ """Finish streaming all of this response's content."""
516
+ for _ in self.chunk_stream():
517
+ pass
518
+
519
+ def text_stream(self, sep: str = "\n") -> Iterator[str]:
520
+ """Stream only the text content from the response.
521
+
522
+ Args:
523
+ sep: Separator to yield between text parts. Defaults to newline.
524
+
525
+ Returns:
526
+ Iterator[str]: Iterator yielding text delta strings
527
+
528
+ Yields text deltas as they arrive, ignoring thoughts, tool calls, and other
529
+ content types. Ideal for displaying text to users in real-time.
530
+
531
+ If you concatenate the strings from `.text_stream()`, it will be equivalent to
532
+ calling `.text(sep=sep)` on the fully consumed response.
533
+ """
534
+ for stream in self.streams():
535
+ if stream.content_type == "text":
536
+ yield from stream
537
+ yield sep
538
+
539
+ def pretty_stream(self) -> Iterator[str]:
540
+ """Stream a readable representation of the stream_response as text.
541
+
542
+ Returns:
543
+ Iterator[str]: Iterator yielding string chunks depicting the content
544
+
545
+ Iterating through the pretty stream will populate the stream response by consuming
546
+ the underlying iterator (if it hasn't been consumed already). Calling `.pretty_stream()`
547
+ will always return a fresh iterator that begins from the start of the stream.
548
+
549
+ If you concatenate the text from `.pretty_stream()`, it will be equivalent to the
550
+ text generated by calling `.pretty()` (assuming the response was fully consumed
551
+ at the time when you call `.pretty()`).
552
+ """
553
+ printed = False
554
+
555
+ for chunk in self.chunk_stream():
556
+ pretty = self._pretty_chunk(chunk, "\n\n" if printed else "")
557
+ if pretty != "":
558
+ printed = True
559
+ yield pretty
560
+
561
+ def structured_stream(
562
+ self,
563
+ ) -> Iterator[Partial[FormattableT]]:
564
+ """Drive the stream forward, yielding partial formatted outputs as they arrive.
565
+
566
+ This method consumes the underlying stream chunk by chunk, yielding parsed
567
+ partial outputs each time new content arrives. Each yielded value is a
568
+ Partial[FormattableT] with optional fields that may be None until fully received.
569
+
570
+ Example:
571
+ >>> response = recommend_book.stream("fantasy")
572
+ >>> for partial in response.structured_stream():
573
+ >>> print(f"Title so far: {partial.title}")
574
+ >>> book = response.parse() # Get final complete result
575
+
576
+ Fully iterating through this iterator will fully consume the underlying stream,
577
+ updating the Response with all collected content.
578
+
579
+ Yields:
580
+ Partial[FormattableT]: Partial objects with fields populated as they arrive.
581
+ Fields not yet received will be None.
582
+
583
+ Raises:
584
+ ValueError: If format parameter not set.
585
+ NotImplementedError: If format uses OutputParser (not supported).
586
+ """
587
+ if self.format is None:
588
+ raise ValueError("structured_stream() requires format parameter")
589
+
590
+ if is_output_parser(self.format.formattable):
591
+ raise NotImplementedError(
592
+ "structured_stream() not supported for OutputParser. "
593
+ "Use BaseModel or primitive types."
594
+ )
595
+
596
+ for chunk in self.chunk_stream():
597
+ if chunk.type == "text_chunk":
598
+ partial = self.parse(partial=True)
599
+ if partial:
600
+ yield partial
601
+
602
+
603
+ class BaseAsyncStreamResponse(
604
+ BaseStreamResponse[AsyncChunkIterator, ToolkitT, FormattableT]
605
+ ):
606
+ """A base class for asynchronous Stream Responses."""
607
+
608
+ async def streams(self) -> AsyncIterator[AsyncStream]:
609
+ """Returns an async iterator that yields streams for each content part in the response.
610
+
611
+ Returns:
612
+ AsyncIterator[AsyncStream]: Async iterator yielding AsyncStream objects
613
+
614
+ Each content part in the response will correspond to one stream, which will yield
615
+ chunks of content as they come in from the underlying LLM.
616
+
617
+ Fully iterating through this iterator will fully consume the underlying stream,
618
+ updating the Response with all collected content.
619
+
620
+ As content is consumed, it is cached on the AsyncStreamResponse. If a new iterator
621
+ is constructed via calling `streams()`, it will start by replaying the cached
622
+ content from the response, and (if there is still more content to consume from
623
+ the LLM), it will proceed to consume it once it has iterated through all the
624
+ cached chunks.
625
+ """
626
+ chunk_iter = self.chunk_stream()
627
+
628
+ async for start_chunk in chunk_iter:
629
+ # At the start of this loop, we always expect to find a start chunk. Then,
630
+ # before proceeding, we will collect from the stream we create (in case the
631
+ # user did not exhaust it), which ensures we will be expecting a start chunk
632
+ # again on the next iteration
633
+ match start_chunk.type:
634
+ case "text_start_chunk":
635
+
636
+ async def text_stream_iterator() -> AsyncIterator[TextChunk]:
637
+ async for chunk in chunk_iter:
638
+ if chunk.type == "text_chunk":
639
+ yield chunk
640
+ else:
641
+ return # Stream finished
642
+
643
+ stream = AsyncTextStream(chunk_iterator=text_stream_iterator())
644
+ yield stream
645
+
646
+ case "thought_start_chunk":
647
+
648
+ async def thought_stream_iterator() -> AsyncIterator[ThoughtChunk]:
649
+ async for chunk in chunk_iter:
650
+ if chunk.type == "thought_chunk":
651
+ yield chunk
652
+ else:
653
+ return # Stream finished
654
+
655
+ stream = AsyncThoughtStream(
656
+ chunk_iterator=thought_stream_iterator()
657
+ )
658
+ yield stream
659
+
660
+ case "tool_call_start_chunk":
661
+ tool_id = start_chunk.id
662
+ tool_name = start_chunk.name
663
+
664
+ async def tool_call_stream_iterator() -> AsyncIterator[
665
+ ToolCallChunk
666
+ ]:
667
+ async for chunk in chunk_iter:
668
+ if chunk.type == "tool_call_chunk":
669
+ yield chunk
670
+ else:
671
+ return # Stream finished
672
+
673
+ stream = AsyncToolCallStream(
674
+ tool_id=tool_id,
675
+ tool_name=tool_name,
676
+ chunk_iterator=tool_call_stream_iterator(),
677
+ )
678
+ yield stream
679
+
680
+ case _: # pragma: no cover
681
+ raise RuntimeError(f"Expected start chunk, got: {start_chunk.type}")
682
+
683
+ # Before continuing to the next stream, make sure the last stream is consumed
684
+ # (If the user did not do so when we yielded it)
685
+ await stream.collect()
686
+
687
+ async def chunk_stream(
688
+ self,
689
+ ) -> AsyncIterator[AssistantContentChunk]:
690
+ """Returns an async iterator that yields content chunks as they are received.
691
+
692
+ Returns:
693
+ AsyncIterator[AssistantContentChunk]: Async iterator yielding chunks
694
+
695
+ This provides access to the Mirascope chunk data including start, delta, and end chunks
696
+ for each content type (text, thinking, tool_call). Unlike the streams() method
697
+ that groups chunks by content part, this yields individual chunks as they arrive.
698
+
699
+ Fully iterating through this iterator will fully consume the underlying stream,
700
+ updating the Response with all collected content.
701
+
702
+ As chunks are consumed, they are cached on the AsyncStreamResponse. If a new iterator
703
+ is constructed via calling `chunk_stream()`, it will start by replaying the cached
704
+ chunks from the response, and (if there is still more content to consume from
705
+ the LLM), it will proceed to consume it once it has iterated through all the
706
+ cached chunks.
707
+ """
708
+
709
+ for chunk in self.chunks:
710
+ yield chunk
711
+
712
+ if self.consumed:
713
+ return
714
+
715
+ async for chunk in self._chunk_iterator:
716
+ if chunk.type == "raw_stream_event_chunk":
717
+ self._raw_stream_events.append(chunk.raw_stream_event)
718
+ elif chunk.type == "raw_message_chunk":
719
+ self._assistant_message.raw_message = chunk.raw_message
720
+ elif chunk.type == "finish_reason_chunk":
721
+ self.finish_reason = chunk.finish_reason
722
+ elif chunk.type == "usage_delta_chunk":
723
+ if self.usage is None:
724
+ self.usage = Usage()
725
+ self.usage.input_tokens += chunk.input_tokens
726
+ self.usage.output_tokens += chunk.output_tokens
727
+ self.usage.cache_read_tokens += chunk.cache_read_tokens
728
+ self.usage.cache_write_tokens += chunk.cache_write_tokens
729
+ self.usage.reasoning_tokens += chunk.reasoning_tokens
730
+ if chunk.provider_tool_usage:
731
+ self.usage.provider_tool_usage = chunk.provider_tool_usage
732
+ else:
733
+ yield self._handle_chunk(chunk)
734
+
735
+ self.consumed = True
736
+
737
+ async def finish(self) -> None:
738
+ """Finish streaming all of this response's content."""
739
+ async for _ in self.chunk_stream():
740
+ pass
741
+
742
+ async def text_stream(self, sep: str = "\n") -> AsyncIterator[str]:
743
+ """Stream only the text content from the response.
744
+
745
+ Args:
746
+ sep: Separator to yield between text parts. Defaults to newline.
747
+
748
+ Returns:
749
+ AsyncIterator[str]: Async iterator yielding text delta strings
750
+
751
+ Yields text deltas as they arrive, ignoring thoughts, tool calls, and other
752
+ content types. Ideal for displaying text to users in real-time.
753
+
754
+ If you concatenate the strings from `.text_stream()`, it will be equivalent to
755
+ calling `.text(sep=sep)` on the fully consumed response.
756
+ """
757
+ async for stream in self.streams():
758
+ if stream.content_type == "text":
759
+ async for delta in stream:
760
+ yield delta
761
+ yield sep
762
+
763
+ async def pretty_stream(self) -> AsyncIterator[str]:
764
+ """Stream a readable representation of the stream_response as text.
765
+
766
+ Returns:
767
+ AsyncIterator[str]: Async iterator yielding string chunks depicting the content
768
+
769
+ Iterating through the pretty stream will populate the stream response by consuming
770
+ the underlying iterator (if it hasn't been consumed already). Calling `.pretty_stream()`
771
+ will always return a fresh iterator that begins from the start of the stream.
772
+
773
+ If you concatenate the text from `.pretty_stream()`, it will be equivalent to the
774
+ text generated by calling `.pretty()` (assuming the response was fully consumed
775
+ at the time when you call `.pretty()`).
776
+ """
777
+ printed = False
778
+
779
+ async for chunk in self.chunk_stream():
780
+ pretty = self._pretty_chunk(chunk, "\n\n" if printed else "")
781
+ if pretty != "":
782
+ printed = True
783
+ yield pretty
784
+
785
+ async def structured_stream(
786
+ self,
787
+ ) -> AsyncIterator[Partial[FormattableT]]:
788
+ """Drive the stream forward, yielding partial formatted outputs as they arrive.
789
+
790
+ This method consumes the underlying stream chunk by chunk, yielding parsed
791
+ partial outputs each time new content arrives. Each yielded value is a
792
+ Partial[FormattableT] with optional fields that may be None until fully received.
793
+
794
+ Example:
795
+ >>> response = await recommend_book.stream("fantasy")
796
+ >>> async for partial in response.structured_stream():
797
+ >>> print(f"Title so far: {partial.title}")
798
+ >>> book = response.parse() # Get final complete result
799
+
800
+ Fully iterating through this iterator will fully consume the underlying stream,
801
+ updating the Response with all collected content.
802
+
803
+ Yields:
804
+ Partial[FormattableT]: Partial objects with fields populated as they arrive.
805
+ Fields not yet received will be None.
806
+
807
+ Raises:
808
+ ValueError: If format parameter not set.
809
+ NotImplementedError: If format uses OutputParser (not supported).
810
+ """
811
+ if self.format is None:
812
+ raise ValueError("structured_stream() requires format parameter")
813
+
814
+ if is_output_parser(self.format.formattable):
815
+ raise NotImplementedError(
816
+ "structured_stream() not supported for OutputParser. "
817
+ "Use BaseModel or primitive types."
818
+ )
819
+
820
+ async for chunk in self.chunk_stream():
821
+ if chunk.type == "text_chunk":
822
+ partial = self.parse(partial=True)
823
+ if partial:
824
+ yield partial