mirascope 2.0.0a2__py3-none-any.whl → 2.0.0a4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. mirascope/__init__.py +2 -2
  2. mirascope/api/__init__.py +6 -0
  3. mirascope/api/_generated/README.md +207 -0
  4. mirascope/api/_generated/__init__.py +141 -0
  5. mirascope/api/_generated/client.py +163 -0
  6. mirascope/api/_generated/core/__init__.py +52 -0
  7. mirascope/api/_generated/core/api_error.py +23 -0
  8. mirascope/api/_generated/core/client_wrapper.py +58 -0
  9. mirascope/api/_generated/core/datetime_utils.py +30 -0
  10. mirascope/api/_generated/core/file.py +70 -0
  11. mirascope/api/_generated/core/force_multipart.py +16 -0
  12. mirascope/api/_generated/core/http_client.py +619 -0
  13. mirascope/api/_generated/core/http_response.py +55 -0
  14. mirascope/api/_generated/core/jsonable_encoder.py +102 -0
  15. mirascope/api/_generated/core/pydantic_utilities.py +310 -0
  16. mirascope/api/_generated/core/query_encoder.py +60 -0
  17. mirascope/api/_generated/core/remove_none_from_dict.py +11 -0
  18. mirascope/api/_generated/core/request_options.py +35 -0
  19. mirascope/api/_generated/core/serialization.py +282 -0
  20. mirascope/api/_generated/docs/__init__.py +4 -0
  21. mirascope/api/_generated/docs/client.py +95 -0
  22. mirascope/api/_generated/docs/raw_client.py +132 -0
  23. mirascope/api/_generated/environment.py +9 -0
  24. mirascope/api/_generated/errors/__init__.py +17 -0
  25. mirascope/api/_generated/errors/bad_request_error.py +15 -0
  26. mirascope/api/_generated/errors/conflict_error.py +15 -0
  27. mirascope/api/_generated/errors/forbidden_error.py +15 -0
  28. mirascope/api/_generated/errors/internal_server_error.py +15 -0
  29. mirascope/api/_generated/errors/not_found_error.py +15 -0
  30. mirascope/api/_generated/health/__init__.py +7 -0
  31. mirascope/api/_generated/health/client.py +96 -0
  32. mirascope/api/_generated/health/raw_client.py +129 -0
  33. mirascope/api/_generated/health/types/__init__.py +8 -0
  34. mirascope/api/_generated/health/types/health_check_response.py +24 -0
  35. mirascope/api/_generated/health/types/health_check_response_status.py +5 -0
  36. mirascope/api/_generated/organizations/__init__.py +25 -0
  37. mirascope/api/_generated/organizations/client.py +380 -0
  38. mirascope/api/_generated/organizations/raw_client.py +876 -0
  39. mirascope/api/_generated/organizations/types/__init__.py +23 -0
  40. mirascope/api/_generated/organizations/types/organizations_create_response.py +24 -0
  41. mirascope/api/_generated/organizations/types/organizations_create_response_role.py +7 -0
  42. mirascope/api/_generated/organizations/types/organizations_get_response.py +24 -0
  43. mirascope/api/_generated/organizations/types/organizations_get_response_role.py +7 -0
  44. mirascope/api/_generated/organizations/types/organizations_list_response_item.py +24 -0
  45. mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +7 -0
  46. mirascope/api/_generated/organizations/types/organizations_update_response.py +24 -0
  47. mirascope/api/_generated/organizations/types/organizations_update_response_role.py +7 -0
  48. mirascope/api/_generated/projects/__init__.py +17 -0
  49. mirascope/api/_generated/projects/client.py +458 -0
  50. mirascope/api/_generated/projects/raw_client.py +1016 -0
  51. mirascope/api/_generated/projects/types/__init__.py +15 -0
  52. mirascope/api/_generated/projects/types/projects_create_response.py +30 -0
  53. mirascope/api/_generated/projects/types/projects_get_response.py +30 -0
  54. mirascope/api/_generated/projects/types/projects_list_response_item.py +30 -0
  55. mirascope/api/_generated/projects/types/projects_update_response.py +30 -0
  56. mirascope/api/_generated/reference.md +753 -0
  57. mirascope/api/_generated/traces/__init__.py +55 -0
  58. mirascope/api/_generated/traces/client.py +162 -0
  59. mirascope/api/_generated/traces/raw_client.py +168 -0
  60. mirascope/api/_generated/traces/types/__init__.py +95 -0
  61. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +36 -0
  62. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +31 -0
  63. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +25 -0
  64. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +54 -0
  65. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +23 -0
  66. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +28 -0
  67. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +24 -0
  68. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +35 -0
  69. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +35 -0
  70. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +27 -0
  71. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +54 -0
  72. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +23 -0
  73. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +28 -0
  74. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value_values_item.py +24 -0
  75. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +60 -0
  76. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +29 -0
  77. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +54 -0
  78. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +23 -0
  79. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +28 -0
  80. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value_values_item.py +24 -0
  81. mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +24 -0
  82. mirascope/api/_generated/traces/types/traces_create_response.py +27 -0
  83. mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +28 -0
  84. mirascope/api/_generated/types/__init__.py +37 -0
  85. mirascope/api/_generated/types/already_exists_error.py +24 -0
  86. mirascope/api/_generated/types/already_exists_error_tag.py +5 -0
  87. mirascope/api/_generated/types/database_error.py +24 -0
  88. mirascope/api/_generated/types/database_error_tag.py +5 -0
  89. mirascope/api/_generated/types/http_api_decode_error.py +29 -0
  90. mirascope/api/_generated/types/http_api_decode_error_tag.py +5 -0
  91. mirascope/api/_generated/types/issue.py +40 -0
  92. mirascope/api/_generated/types/issue_tag.py +17 -0
  93. mirascope/api/_generated/types/not_found_error_body.py +24 -0
  94. mirascope/api/_generated/types/not_found_error_tag.py +5 -0
  95. mirascope/api/_generated/types/permission_denied_error.py +24 -0
  96. mirascope/api/_generated/types/permission_denied_error_tag.py +7 -0
  97. mirascope/api/_generated/types/property_key.py +7 -0
  98. mirascope/api/_generated/types/property_key_key.py +27 -0
  99. mirascope/api/_generated/types/property_key_key_tag.py +5 -0
  100. mirascope/api/client.py +255 -0
  101. mirascope/api/settings.py +81 -0
  102. mirascope/llm/__init__.py +45 -11
  103. mirascope/llm/calls/calls.py +81 -57
  104. mirascope/llm/calls/decorator.py +121 -115
  105. mirascope/llm/content/__init__.py +3 -2
  106. mirascope/llm/context/_utils.py +19 -6
  107. mirascope/llm/exceptions.py +30 -16
  108. mirascope/llm/formatting/_utils.py +9 -5
  109. mirascope/llm/formatting/format.py +2 -2
  110. mirascope/llm/formatting/from_call_args.py +2 -2
  111. mirascope/llm/messages/message.py +13 -5
  112. mirascope/llm/models/__init__.py +2 -2
  113. mirascope/llm/models/models.py +189 -81
  114. mirascope/llm/prompts/__init__.py +13 -12
  115. mirascope/llm/prompts/_utils.py +27 -24
  116. mirascope/llm/prompts/decorator.py +133 -204
  117. mirascope/llm/prompts/prompts.py +424 -0
  118. mirascope/llm/prompts/protocols.py +25 -59
  119. mirascope/llm/providers/__init__.py +44 -0
  120. mirascope/llm/{clients → providers}/_missing_import_stubs.py +8 -6
  121. mirascope/llm/providers/anthropic/__init__.py +29 -0
  122. mirascope/llm/providers/anthropic/_utils/__init__.py +23 -0
  123. mirascope/llm/providers/anthropic/_utils/beta_decode.py +271 -0
  124. mirascope/llm/providers/anthropic/_utils/beta_encode.py +216 -0
  125. mirascope/llm/{clients → providers}/anthropic/_utils/decode.py +44 -11
  126. mirascope/llm/providers/anthropic/_utils/encode.py +356 -0
  127. mirascope/llm/providers/anthropic/beta_provider.py +322 -0
  128. mirascope/llm/providers/anthropic/model_id.py +23 -0
  129. mirascope/llm/providers/anthropic/model_info.py +87 -0
  130. mirascope/llm/providers/anthropic/provider.py +416 -0
  131. mirascope/llm/{clients → providers}/base/__init__.py +3 -3
  132. mirascope/llm/{clients → providers}/base/_utils.py +25 -8
  133. mirascope/llm/{clients/base/client.py → providers/base/base_provider.py} +255 -126
  134. mirascope/llm/providers/google/__init__.py +21 -0
  135. mirascope/llm/{clients → providers}/google/_utils/decode.py +61 -7
  136. mirascope/llm/{clients → providers}/google/_utils/encode.py +44 -30
  137. mirascope/llm/providers/google/model_id.py +22 -0
  138. mirascope/llm/providers/google/model_info.py +62 -0
  139. mirascope/llm/providers/google/provider.py +442 -0
  140. mirascope/llm/providers/load_provider.py +54 -0
  141. mirascope/llm/providers/mlx/__init__.py +24 -0
  142. mirascope/llm/providers/mlx/_utils.py +129 -0
  143. mirascope/llm/providers/mlx/encoding/__init__.py +8 -0
  144. mirascope/llm/providers/mlx/encoding/base.py +69 -0
  145. mirascope/llm/providers/mlx/encoding/transformers.py +147 -0
  146. mirascope/llm/providers/mlx/mlx.py +237 -0
  147. mirascope/llm/providers/mlx/model_id.py +17 -0
  148. mirascope/llm/providers/mlx/provider.py +415 -0
  149. mirascope/llm/providers/model_id.py +16 -0
  150. mirascope/llm/providers/ollama/__init__.py +19 -0
  151. mirascope/llm/providers/ollama/provider.py +71 -0
  152. mirascope/llm/providers/openai/__init__.py +6 -0
  153. mirascope/llm/providers/openai/completions/__init__.py +25 -0
  154. mirascope/llm/{clients → providers}/openai/completions/_utils/__init__.py +2 -0
  155. mirascope/llm/{clients → providers}/openai/completions/_utils/decode.py +60 -6
  156. mirascope/llm/{clients → providers}/openai/completions/_utils/encode.py +37 -26
  157. mirascope/llm/providers/openai/completions/base_provider.py +513 -0
  158. mirascope/llm/providers/openai/completions/provider.py +22 -0
  159. mirascope/llm/providers/openai/model_id.py +31 -0
  160. mirascope/llm/providers/openai/model_info.py +303 -0
  161. mirascope/llm/providers/openai/provider.py +398 -0
  162. mirascope/llm/providers/openai/responses/__init__.py +21 -0
  163. mirascope/llm/{clients → providers}/openai/responses/_utils/decode.py +59 -6
  164. mirascope/llm/{clients → providers}/openai/responses/_utils/encode.py +34 -23
  165. mirascope/llm/providers/openai/responses/provider.py +469 -0
  166. mirascope/llm/providers/provider_id.py +23 -0
  167. mirascope/llm/providers/provider_registry.py +169 -0
  168. mirascope/llm/providers/together/__init__.py +19 -0
  169. mirascope/llm/providers/together/provider.py +40 -0
  170. mirascope/llm/responses/__init__.py +3 -0
  171. mirascope/llm/responses/base_response.py +14 -5
  172. mirascope/llm/responses/base_stream_response.py +35 -6
  173. mirascope/llm/responses/finish_reason.py +1 -0
  174. mirascope/llm/responses/response.py +33 -13
  175. mirascope/llm/responses/root_response.py +12 -13
  176. mirascope/llm/responses/stream_response.py +35 -23
  177. mirascope/llm/responses/usage.py +95 -0
  178. mirascope/llm/tools/__init__.py +9 -2
  179. mirascope/llm/tools/_utils.py +12 -3
  180. mirascope/llm/tools/protocols.py +4 -4
  181. mirascope/llm/tools/tool_schema.py +44 -9
  182. mirascope/llm/tools/tools.py +10 -9
  183. mirascope/ops/__init__.py +156 -0
  184. mirascope/ops/_internal/__init__.py +5 -0
  185. mirascope/ops/_internal/closure.py +1118 -0
  186. mirascope/ops/_internal/configuration.py +126 -0
  187. mirascope/ops/_internal/context.py +76 -0
  188. mirascope/ops/_internal/exporters/__init__.py +26 -0
  189. mirascope/ops/_internal/exporters/exporters.py +342 -0
  190. mirascope/ops/_internal/exporters/processors.py +104 -0
  191. mirascope/ops/_internal/exporters/types.py +165 -0
  192. mirascope/ops/_internal/exporters/utils.py +29 -0
  193. mirascope/ops/_internal/instrumentation/__init__.py +8 -0
  194. mirascope/ops/_internal/instrumentation/llm/__init__.py +8 -0
  195. mirascope/ops/_internal/instrumentation/llm/encode.py +238 -0
  196. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/__init__.py +38 -0
  197. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_input_messages.py +31 -0
  198. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_output_messages.py +38 -0
  199. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_system_instructions.py +18 -0
  200. mirascope/ops/_internal/instrumentation/llm/gen_ai_types/shared.py +100 -0
  201. mirascope/ops/_internal/instrumentation/llm/llm.py +1288 -0
  202. mirascope/ops/_internal/propagation.py +198 -0
  203. mirascope/ops/_internal/protocols.py +51 -0
  204. mirascope/ops/_internal/session.py +139 -0
  205. mirascope/ops/_internal/spans.py +232 -0
  206. mirascope/ops/_internal/traced_calls.py +371 -0
  207. mirascope/ops/_internal/traced_functions.py +394 -0
  208. mirascope/ops/_internal/tracing.py +276 -0
  209. mirascope/ops/_internal/types.py +13 -0
  210. mirascope/ops/_internal/utils.py +75 -0
  211. mirascope/ops/_internal/versioned_calls.py +512 -0
  212. mirascope/ops/_internal/versioned_functions.py +346 -0
  213. mirascope/ops/_internal/versioning.py +303 -0
  214. mirascope/ops/exceptions.py +21 -0
  215. {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/METADATA +78 -3
  216. mirascope-2.0.0a4.dist-info/RECORD +247 -0
  217. {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/WHEEL +1 -1
  218. mirascope/graphs/__init__.py +0 -22
  219. mirascope/graphs/finite_state_machine.py +0 -625
  220. mirascope/llm/agents/__init__.py +0 -15
  221. mirascope/llm/agents/agent.py +0 -97
  222. mirascope/llm/agents/agent_template.py +0 -45
  223. mirascope/llm/agents/decorator.py +0 -176
  224. mirascope/llm/calls/base_call.py +0 -33
  225. mirascope/llm/clients/__init__.py +0 -34
  226. mirascope/llm/clients/anthropic/__init__.py +0 -25
  227. mirascope/llm/clients/anthropic/_utils/encode.py +0 -243
  228. mirascope/llm/clients/anthropic/clients.py +0 -819
  229. mirascope/llm/clients/anthropic/model_ids.py +0 -8
  230. mirascope/llm/clients/google/__init__.py +0 -20
  231. mirascope/llm/clients/google/clients.py +0 -853
  232. mirascope/llm/clients/google/model_ids.py +0 -15
  233. mirascope/llm/clients/openai/__init__.py +0 -25
  234. mirascope/llm/clients/openai/completions/__init__.py +0 -28
  235. mirascope/llm/clients/openai/completions/_utils/model_features.py +0 -81
  236. mirascope/llm/clients/openai/completions/clients.py +0 -833
  237. mirascope/llm/clients/openai/completions/model_ids.py +0 -8
  238. mirascope/llm/clients/openai/responses/__init__.py +0 -26
  239. mirascope/llm/clients/openai/responses/_utils/__init__.py +0 -13
  240. mirascope/llm/clients/openai/responses/_utils/model_features.py +0 -87
  241. mirascope/llm/clients/openai/responses/clients.py +0 -832
  242. mirascope/llm/clients/openai/responses/model_ids.py +0 -8
  243. mirascope/llm/clients/openai/shared/__init__.py +0 -7
  244. mirascope/llm/clients/openai/shared/_utils.py +0 -55
  245. mirascope/llm/clients/providers.py +0 -175
  246. mirascope-2.0.0a2.dist-info/RECORD +0 -102
  247. /mirascope/llm/{clients → providers}/base/kwargs.py +0 -0
  248. /mirascope/llm/{clients → providers}/base/params.py +0 -0
  249. /mirascope/llm/{clients/anthropic → providers/google}/_utils/__init__.py +0 -0
  250. /mirascope/llm/{clients → providers}/google/message.py +0 -0
  251. /mirascope/llm/{clients/google → providers/openai/responses}/_utils/__init__.py +0 -0
  252. {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,40 @@
1
+ """Together AI provider implementation."""
2
+
3
+ from typing import ClassVar
4
+
5
+ from ..openai.completions.base_provider import BaseOpenAICompletionsProvider
6
+
7
+
8
+ class TogetherProvider(BaseOpenAICompletionsProvider):
9
+ """Provider for Together AI's OpenAI-compatible API.
10
+
11
+ Inherits from BaseOpenAICompletionsProvider with Together-specific configuration:
12
+ - Uses Together AI's API endpoint
13
+ - Requires TOGETHER_API_KEY
14
+
15
+ Usage:
16
+ Register the provider with model ID prefixes you want to use:
17
+
18
+ ```python
19
+ import llm
20
+
21
+ # Register for meta-llama models
22
+ llm.register_provider("together", "meta-llama/")
23
+
24
+ # Now you can use meta-llama models directly
25
+ @llm.call("meta-llama/Llama-3.3-70B-Instruct-Turbo")
26
+ def my_prompt():
27
+ return [llm.messages.user("Hello!")]
28
+ ```
29
+ """
30
+
31
+ id: ClassVar[str] = "together"
32
+ default_scope: ClassVar[str | list[str]] = []
33
+ default_base_url: ClassVar[str | None] = "https://api.together.xyz/v1"
34
+ api_key_env_var: ClassVar[str] = "TOGETHER_API_KEY"
35
+ api_key_required: ClassVar[bool] = True
36
+ provider_name: ClassVar[str | None] = "Together"
37
+
38
+ def _model_name(self, model_id: str) -> str:
39
+ """Return the model ID as-is for Together API."""
40
+ return model_id
@@ -27,6 +27,7 @@ from .streams import (
27
27
  ThoughtStream,
28
28
  ToolCallStream,
29
29
  )
30
+ from .usage import Usage, UsageDeltaChunk
30
31
 
31
32
  __all__ = [
32
33
  "AsyncChunkIterator",
@@ -53,5 +54,7 @@ __all__ = [
53
54
  "TextStream",
54
55
  "ThoughtStream",
55
56
  "ToolCallStream",
57
+ "Usage",
58
+ "UsageDeltaChunk",
56
59
  "_utils",
57
60
  ]
@@ -9,9 +9,10 @@ from ..messages import AssistantMessage, Message
9
9
  from ..tools import FORMAT_TOOL_NAME, ToolkitT
10
10
  from .finish_reason import FinishReason
11
11
  from .root_response import RootResponse
12
+ from .usage import Usage
12
13
 
13
14
  if TYPE_CHECKING:
14
- from ..clients import ModelId, Params, Provider
15
+ from ..providers import ModelId, Params, ProviderId
15
16
 
16
17
 
17
18
  class BaseResponse(RootResponse[ToolkitT, FormattableT]):
@@ -21,34 +22,41 @@ class BaseResponse(RootResponse[ToolkitT, FormattableT]):
21
22
  self,
22
23
  *,
23
24
  raw: Any, # noqa: ANN401
24
- provider: "Provider",
25
+ provider_id: "ProviderId",
25
26
  model_id: "ModelId",
27
+ provider_model_name: str,
26
28
  params: "Params",
27
29
  toolkit: ToolkitT,
28
30
  format: Format[FormattableT] | None = None,
29
31
  input_messages: Sequence[Message],
30
32
  assistant_message: AssistantMessage,
31
33
  finish_reason: FinishReason | None,
34
+ usage: Usage | None,
32
35
  ) -> None:
33
36
  """Initialize a Response.
34
37
 
35
38
  Args:
36
39
  raw: The raw response from the LLM.
37
- provider: The provider name (e.g. "anthropic", "openai:completions").
40
+ provider: The provider name (e.g. "anthropic", "openai").
38
41
  model_id: The model identifier that generated the response.
42
+ provider_model_name: Optional provider-specific model name. May include
43
+ provider-specific additional info (like api mode in "gpt-5:responses").
39
44
  params: The params used to generate the response (or None).
40
45
  toolkit: Toolkit containing all the tools used to generate the response.
41
46
  format: The `Format` for the expected structured output format (or None).
42
47
  input_messages: The message history before the final assistant message.
43
48
  assistant_message: The final assistant message containing the response content.
44
49
  finish_reason: The reason why the LLM finished generating a response.
50
+ usage: Token usage statistics for the response.
45
51
  """
46
52
  self.raw = raw
47
- self.provider = provider
53
+ self.provider_id = provider_id
48
54
  self.model_id = model_id
55
+ self.provider_model_name = provider_model_name
49
56
  self.params = params
50
57
  self.toolkit = toolkit
51
58
  self.finish_reason = finish_reason
59
+ self.usage = usage
52
60
  self.format = format
53
61
 
54
62
  # Process content in the assistant message, organizing it by type and
@@ -84,8 +92,9 @@ class BaseResponse(RootResponse[ToolkitT, FormattableT]):
84
92
  assistant_message = AssistantMessage(
85
93
  content=self.content,
86
94
  name=assistant_message.name,
87
- provider=assistant_message.provider,
95
+ provider_id=assistant_message.provider_id,
88
96
  model_id=assistant_message.model_id,
97
+ provider_model_name=assistant_message.provider_model_name,
89
98
  raw_message=assistant_message.raw_message,
90
99
  )
91
100
  self.messages = list(input_messages) + [assistant_message]
@@ -36,9 +36,10 @@ from .streams import (
36
36
  ThoughtStream,
37
37
  ToolCallStream,
38
38
  )
39
+ from .usage import Usage, UsageDeltaChunk
39
40
 
40
41
  if TYPE_CHECKING:
41
- from ..clients import ModelId, Params, Provider
42
+ from ..providers import ModelId, Params, ProviderId
42
43
 
43
44
 
44
45
  @dataclass(kw_only=True)
@@ -76,7 +77,11 @@ class RawMessageChunk:
76
77
 
77
78
 
78
79
  StreamResponseChunk: TypeAlias = (
79
- AssistantContentChunk | FinishReasonChunk | RawStreamEventChunk | RawMessageChunk
80
+ AssistantContentChunk
81
+ | FinishReasonChunk
82
+ | RawStreamEventChunk
83
+ | RawMessageChunk
84
+ | UsageDeltaChunk
80
85
  )
81
86
 
82
87
  ChunkIterator: TypeAlias = Iterator[StreamResponseChunk]
@@ -157,32 +162,39 @@ class BaseStreamResponse(
157
162
  def __init__(
158
163
  self,
159
164
  *,
160
- provider: "Provider",
165
+ provider_id: "ProviderId",
161
166
  model_id: "ModelId",
167
+ provider_model_name: str,
162
168
  params: "Params",
163
169
  toolkit: ToolkitT,
164
170
  format: Format[FormattableT] | None = None,
165
171
  input_messages: Sequence[Message],
166
172
  chunk_iterator: ChunkIteratorT,
173
+ usage: Usage | None = None,
167
174
  ) -> None:
168
175
  """Initialize the BaseStreamResponse.
169
176
 
170
177
  Args:
171
- provider: The provider name (e.g. "anthropic", "openai:completions").
178
+ provider: The provider name (e.g. "anthropic", "openai").
172
179
  model_id: The model identifier that generated the response.
180
+ provider_model_name: Optional provider-specific model name. May include
181
+ provider-specific additional info (like api mode in "gpt-5:responses").
173
182
  params: The params used to generate the response (or None).
174
183
  toolkit: Toolkit containing all the tools used to generate the response.
175
184
  format: The `Format` for the expected structured output format (or None).
176
185
  input_messages: The input messages that were sent to the LLM
186
+ usage: Token usage statistics for the response.
177
187
 
178
188
  The BaseStreamResponse will process the tuples to build the chunks and raw lists
179
189
  as the stream is consumed.
180
190
  """
181
191
 
182
- self.provider = provider
192
+ self.provider_id = provider_id
183
193
  self.model_id = model_id
194
+ self.provider_model_name = provider_model_name
184
195
  self.params = params
185
196
  self.toolkit = toolkit
197
+ self.usage = usage
186
198
  self.format = format
187
199
 
188
200
  # Internal-only lists which we mutate (append) during chunk processing
@@ -206,8 +218,9 @@ class BaseStreamResponse(
206
218
 
207
219
  self._assistant_message = AssistantMessage(
208
220
  content=self._content,
209
- provider=provider,
221
+ provider_id=provider_id,
210
222
  model_id=model_id,
223
+ provider_model_name=provider_model_name,
211
224
  raw_message=None,
212
225
  )
213
226
 
@@ -470,6 +483,14 @@ class BaseSyncStreamResponse(BaseStreamResponse[ChunkIterator, ToolkitT, Formatt
470
483
  self._assistant_message.raw_message = chunk.raw_message
471
484
  elif chunk.type == "finish_reason_chunk":
472
485
  self.finish_reason = chunk.finish_reason
486
+ elif chunk.type == "usage_delta_chunk":
487
+ if self.usage is None:
488
+ self.usage = Usage()
489
+ self.usage.input_tokens += chunk.input_tokens
490
+ self.usage.output_tokens += chunk.output_tokens
491
+ self.usage.cache_read_tokens += chunk.cache_read_tokens
492
+ self.usage.cache_write_tokens += chunk.cache_write_tokens
493
+ self.usage.reasoning_tokens += chunk.reasoning_tokens
473
494
  else:
474
495
  yield self._handle_chunk(chunk)
475
496
 
@@ -643,6 +664,14 @@ class BaseAsyncStreamResponse(
643
664
  self._assistant_message.raw_message = chunk.raw_message
644
665
  elif chunk.type == "finish_reason_chunk":
645
666
  self.finish_reason = chunk.finish_reason
667
+ elif chunk.type == "usage_delta_chunk":
668
+ if self.usage is None:
669
+ self.usage = Usage()
670
+ self.usage.input_tokens += chunk.input_tokens
671
+ self.usage.output_tokens += chunk.output_tokens
672
+ self.usage.cache_read_tokens += chunk.cache_read_tokens
673
+ self.usage.cache_write_tokens += chunk.cache_write_tokens
674
+ self.usage.reasoning_tokens += chunk.reasoning_tokens
646
675
  else:
647
676
  yield self._handle_chunk(chunk)
648
677
 
@@ -15,6 +15,7 @@ class FinishReason(str, Enum):
15
15
 
16
16
  MAX_TOKENS = "max_tokens"
17
17
  REFUSAL = "refusal"
18
+ CONTEXT_LENGTH_EXCEEDED = "context_length_exceeded"
18
19
 
19
20
 
20
21
  @dataclass(kw_only=True)
@@ -18,11 +18,13 @@ from ..tools import (
18
18
  Tool,
19
19
  Toolkit,
20
20
  )
21
+ from ..types import Jsonable
21
22
  from .base_response import BaseResponse
22
23
  from .finish_reason import FinishReason
24
+ from .usage import Usage
23
25
 
24
26
  if TYPE_CHECKING:
25
- from ..clients import ModelId, Params, Provider
27
+ from ..providers import ModelId, Params, ProviderId
26
28
 
27
29
 
28
30
  class Response(BaseResponse[Toolkit, FormattableT]):
@@ -32,30 +34,34 @@ class Response(BaseResponse[Toolkit, FormattableT]):
32
34
  self,
33
35
  *,
34
36
  raw: Any, # noqa: ANN401
35
- provider: "Provider",
37
+ provider_id: "ProviderId",
36
38
  model_id: "ModelId",
39
+ provider_model_name: str,
37
40
  params: "Params",
38
41
  tools: Sequence[Tool] | Toolkit | None = None,
39
42
  format: Format[FormattableT] | None = None,
40
43
  input_messages: Sequence[Message],
41
44
  assistant_message: AssistantMessage,
42
45
  finish_reason: FinishReason | None,
46
+ usage: Usage | None,
43
47
  ) -> None:
44
48
  """Initialize a `Response`."""
45
49
  toolkit = tools if isinstance(tools, Toolkit) else Toolkit(tools=tools)
46
50
  super().__init__(
47
51
  raw=raw,
48
- provider=provider,
52
+ provider_id=provider_id,
49
53
  model_id=model_id,
54
+ provider_model_name=provider_model_name,
50
55
  params=params,
51
56
  toolkit=toolkit,
52
57
  format=format,
53
58
  input_messages=input_messages,
54
59
  assistant_message=assistant_message,
55
60
  finish_reason=finish_reason,
61
+ usage=usage,
56
62
  )
57
63
 
58
- def execute_tools(self) -> Sequence[ToolOutput]:
64
+ def execute_tools(self) -> Sequence[ToolOutput[Jsonable]]:
59
65
  """Execute and return all of the tool calls in the response.
60
66
 
61
67
  Returns:
@@ -101,14 +107,16 @@ class AsyncResponse(BaseResponse[AsyncToolkit, FormattableT]):
101
107
  self,
102
108
  *,
103
109
  raw: Any, # noqa: ANN401
104
- provider: "Provider",
110
+ provider_id: "ProviderId",
105
111
  model_id: "ModelId",
112
+ provider_model_name: str,
106
113
  params: "Params",
107
114
  tools: Sequence[AsyncTool] | AsyncToolkit | None = None,
108
115
  format: Format[FormattableT] | None = None,
109
116
  input_messages: Sequence[Message],
110
117
  assistant_message: AssistantMessage,
111
118
  finish_reason: FinishReason | None,
119
+ usage: Usage | None,
112
120
  ) -> None:
113
121
  """Initialize an `AsyncResponse`."""
114
122
  toolkit = (
@@ -116,17 +124,19 @@ class AsyncResponse(BaseResponse[AsyncToolkit, FormattableT]):
116
124
  )
117
125
  super().__init__(
118
126
  raw=raw,
119
- provider=provider,
127
+ provider_id=provider_id,
120
128
  model_id=model_id,
129
+ provider_model_name=provider_model_name,
121
130
  params=params,
122
131
  toolkit=toolkit,
123
132
  format=format,
124
133
  input_messages=input_messages,
125
134
  assistant_message=assistant_message,
126
135
  finish_reason=finish_reason,
136
+ usage=usage,
127
137
  )
128
138
 
129
- async def execute_tools(self) -> Sequence[ToolOutput]:
139
+ async def execute_tools(self) -> Sequence[ToolOutput[Jsonable]]:
130
140
  """Execute and return all of the tool calls in the response.
131
141
 
132
142
  Returns:
@@ -179,8 +189,9 @@ class ContextResponse(
179
189
  self,
180
190
  *,
181
191
  raw: Any, # noqa: ANN401
182
- provider: "Provider",
192
+ provider_id: "ProviderId",
183
193
  model_id: "ModelId",
194
+ provider_model_name: str,
184
195
  params: "Params",
185
196
  tools: Sequence[Tool | ContextTool[DepsT]]
186
197
  | ContextToolkit[DepsT]
@@ -189,6 +200,7 @@ class ContextResponse(
189
200
  input_messages: Sequence[Message],
190
201
  assistant_message: AssistantMessage,
191
202
  finish_reason: FinishReason | None,
203
+ usage: Usage | None,
192
204
  ) -> None:
193
205
  """Initialize a `ContextResponse`."""
194
206
  toolkit = (
@@ -196,17 +208,19 @@ class ContextResponse(
196
208
  )
197
209
  super().__init__(
198
210
  raw=raw,
199
- provider=provider,
211
+ provider_id=provider_id,
200
212
  model_id=model_id,
213
+ provider_model_name=provider_model_name,
201
214
  params=params,
202
215
  toolkit=toolkit,
203
216
  format=format,
204
217
  input_messages=input_messages,
205
218
  assistant_message=assistant_message,
206
219
  finish_reason=finish_reason,
220
+ usage=usage,
207
221
  )
208
222
 
209
- def execute_tools(self, ctx: Context[DepsT]) -> Sequence[ToolOutput]:
223
+ def execute_tools(self, ctx: Context[DepsT]) -> Sequence[ToolOutput[Jsonable]]:
210
224
  """Execute and return all of the tool calls in the response.
211
225
 
212
226
  Args:
@@ -265,8 +279,9 @@ class AsyncContextResponse(
265
279
  self,
266
280
  *,
267
281
  raw: Any, # noqa: ANN401
268
- provider: "Provider",
282
+ provider_id: "ProviderId",
269
283
  model_id: "ModelId",
284
+ provider_model_name: str,
270
285
  params: "Params",
271
286
  tools: Sequence[AsyncTool | AsyncContextTool[DepsT]]
272
287
  | AsyncContextToolkit[DepsT]
@@ -275,6 +290,7 @@ class AsyncContextResponse(
275
290
  input_messages: Sequence[Message],
276
291
  assistant_message: AssistantMessage,
277
292
  finish_reason: FinishReason | None,
293
+ usage: Usage | None,
278
294
  ) -> None:
279
295
  """Initialize an `AsyncContextResponse`."""
280
296
  toolkit = (
@@ -284,17 +300,21 @@ class AsyncContextResponse(
284
300
  )
285
301
  super().__init__(
286
302
  raw=raw,
287
- provider=provider,
303
+ provider_id=provider_id,
288
304
  model_id=model_id,
305
+ provider_model_name=provider_model_name,
289
306
  params=params,
290
307
  toolkit=toolkit,
291
308
  format=format,
292
309
  input_messages=input_messages,
293
310
  assistant_message=assistant_message,
294
311
  finish_reason=finish_reason,
312
+ usage=usage,
295
313
  )
296
314
 
297
- async def execute_tools(self, ctx: Context[DepsT]) -> Sequence[ToolOutput]:
315
+ async def execute_tools(
316
+ self, ctx: Context[DepsT]
317
+ ) -> Sequence[ToolOutput[Jsonable]]:
298
318
  """Execute and return all of the tool calls in the response.
299
319
 
300
320
  Args:
@@ -11,10 +11,11 @@ from ..messages import Message
11
11
  from ..tools import ToolkitT
12
12
  from . import _utils
13
13
  from .finish_reason import FinishReason
14
+ from .usage import Usage
14
15
 
15
16
  if TYPE_CHECKING:
16
- from ..clients import ModelId, Params, Provider
17
17
  from ..models import Model
18
+ from ..providers import ModelId, Params, ProviderId
18
19
 
19
20
 
20
21
  class RootResponse(Generic[ToolkitT, FormattableT], ABC):
@@ -23,7 +24,7 @@ class RootResponse(Generic[ToolkitT, FormattableT], ABC):
23
24
  raw: Any
24
25
  """The raw response from the LLM."""
25
26
 
26
- provider: "Provider"
27
+ provider_id: "ProviderId"
27
28
  """The provider that generated this response."""
28
29
 
29
30
  model_id: "ModelId"
@@ -55,12 +56,15 @@ class RootResponse(Generic[ToolkitT, FormattableT], ABC):
55
56
  """
56
57
  finish_reason: FinishReason | None
57
58
  """The reason why the LLM finished generating a response, if set.
58
-
59
+
59
60
  `finish_reason` is only set if the response did not finish generating normally,
60
61
  e.g. `FinishReason.MAX_TOKENS` if the model ran out of tokens before completing.
61
62
  When the response generates normally, `response.finish_reason` will be `None`.
62
63
  """
63
64
 
65
+ usage: Usage | None
66
+ """Token usage statistics for this response, if available."""
67
+
64
68
  format: Format[FormattableT] | None
65
69
  """The `Format` describing the structured response format, if available."""
66
70
 
@@ -116,7 +120,9 @@ class RootResponse(Generic[ToolkitT, FormattableT], ABC):
116
120
  return None
117
121
 
118
122
  formattable = self.format.formattable
119
- if formattable is None or formattable is NoneType:
123
+ if formattable is None or formattable is NoneType: # pyright: ignore[reportUnnecessaryComparison]
124
+ # note: pyright claims the None comparison is unnecessary, but removing it
125
+ # introduces type errors.
120
126
  return None # pragma: no cover
121
127
 
122
128
  if partial:
@@ -165,13 +171,6 @@ class RootResponse(Generic[ToolkitT, FormattableT], ABC):
165
171
  @property
166
172
  def model(self) -> "Model":
167
173
  """A `Model` with parameters matching this response."""
168
- from ..models import Model, get_model_from_context
169
-
170
- if context_model := get_model_from_context():
171
- return context_model
174
+ from ..models import use_model # Dynamic import to avoid circular dependency
172
175
 
173
- return Model(
174
- provider=self.provider,
175
- model_id=self.model_id,
176
- **self.params,
177
- )
176
+ return use_model(self.model_id, **self.params)
@@ -18,6 +18,7 @@ from ..tools import (
18
18
  Tool,
19
19
  Toolkit,
20
20
  )
21
+ from ..types import Jsonable
21
22
  from .base_stream_response import (
22
23
  AsyncChunkIterator,
23
24
  BaseAsyncStreamResponse,
@@ -26,7 +27,7 @@ from .base_stream_response import (
26
27
  )
27
28
 
28
29
  if TYPE_CHECKING:
29
- from ..clients import ModelId, Params, Provider
30
+ from ..providers import ModelId, Params, ProviderId
30
31
 
31
32
 
32
33
  class StreamResponse(BaseSyncStreamResponse[Toolkit, FormattableT]):
@@ -76,8 +77,8 @@ class StreamResponse(BaseSyncStreamResponse[Toolkit, FormattableT]):
76
77
  from mirascope import llm
77
78
 
78
79
  @llm.call(
79
- provider="openai:completions",
80
- model_id="gpt-4o-mini",
80
+ provider_id="openai",
81
+ model_id="openai/gpt-5-mini",
81
82
  )
82
83
  def answer_question(question: str) -> str:
83
84
  return f"Answer this question: {question}"
@@ -93,8 +94,9 @@ class StreamResponse(BaseSyncStreamResponse[Toolkit, FormattableT]):
93
94
  def __init__(
94
95
  self,
95
96
  *,
96
- provider: "Provider",
97
+ provider_id: "ProviderId",
97
98
  model_id: "ModelId",
99
+ provider_model_name: str,
98
100
  params: "Params",
99
101
  tools: Sequence[Tool] | Toolkit | None = None,
100
102
  format: Format[FormattableT] | None = None,
@@ -104,8 +106,9 @@ class StreamResponse(BaseSyncStreamResponse[Toolkit, FormattableT]):
104
106
  """Initialize a `StreamResponse`."""
105
107
  toolkit = tools if isinstance(tools, Toolkit) else Toolkit(tools=tools)
106
108
  super().__init__(
107
- provider=provider,
109
+ provider_id=provider_id,
108
110
  model_id=model_id,
111
+ provider_model_name=provider_model_name,
109
112
  params=params,
110
113
  toolkit=toolkit,
111
114
  format=format,
@@ -113,7 +116,7 @@ class StreamResponse(BaseSyncStreamResponse[Toolkit, FormattableT]):
113
116
  chunk_iterator=chunk_iterator,
114
117
  )
115
118
 
116
- def execute_tools(self) -> Sequence[ToolOutput]:
119
+ def execute_tools(self) -> Sequence[ToolOutput[Jsonable]]:
117
120
  """Execute and return all of the tool calls in the response.
118
121
 
119
122
  Returns:
@@ -201,8 +204,8 @@ class AsyncStreamResponse(BaseAsyncStreamResponse[AsyncToolkit, FormattableT]):
201
204
  from mirascope import llm
202
205
 
203
206
  @llm.call(
204
- provider="openai:completions",
205
- model_id="gpt-4o-mini",
207
+ provider_id="openai",
208
+ model_id="openai/gpt-5-mini",
206
209
  )
207
210
  async def answer_question(question: str) -> str:
208
211
  return f"Answer this question: {question}"
@@ -218,8 +221,9 @@ class AsyncStreamResponse(BaseAsyncStreamResponse[AsyncToolkit, FormattableT]):
218
221
  def __init__(
219
222
  self,
220
223
  *,
221
- provider: "Provider",
224
+ provider_id: "ProviderId",
222
225
  model_id: "ModelId",
226
+ provider_model_name: str,
223
227
  params: "Params",
224
228
  tools: Sequence[AsyncTool] | AsyncToolkit | None = None,
225
229
  format: Format[FormattableT] | None = None,
@@ -231,8 +235,9 @@ class AsyncStreamResponse(BaseAsyncStreamResponse[AsyncToolkit, FormattableT]):
231
235
  tools if isinstance(tools, AsyncToolkit) else AsyncToolkit(tools=tools)
232
236
  )
233
237
  super().__init__(
234
- provider=provider,
238
+ provider_id=provider_id,
235
239
  model_id=model_id,
240
+ provider_model_name=provider_model_name,
236
241
  params=params,
237
242
  toolkit=toolkit,
238
243
  format=format,
@@ -240,7 +245,7 @@ class AsyncStreamResponse(BaseAsyncStreamResponse[AsyncToolkit, FormattableT]):
240
245
  chunk_iterator=chunk_iterator,
241
246
  )
242
247
 
243
- async def execute_tools(self) -> Sequence[ToolOutput]:
248
+ async def execute_tools(self) -> Sequence[ToolOutput[Jsonable]]:
244
249
  """Execute and return all of the tool calls in the response.
245
250
 
246
251
  Returns:
@@ -285,7 +290,8 @@ class AsyncStreamResponse(BaseAsyncStreamResponse[AsyncToolkit, FormattableT]):
285
290
 
286
291
 
287
292
  class ContextStreamResponse(
288
- BaseSyncStreamResponse[ContextToolkit, FormattableT], Generic[DepsT, FormattableT]
293
+ BaseSyncStreamResponse[ContextToolkit[DepsT], FormattableT],
294
+ Generic[DepsT, FormattableT],
289
295
  ):
290
296
  """A `ContextStreamResponse` wraps response content from the LLM with a streaming interface.
291
297
 
@@ -333,8 +339,8 @@ class ContextStreamResponse(
333
339
  from mirascope import llm
334
340
 
335
341
  @llm.call(
336
- provider="openai:completions",
337
- model_id="gpt-4o-mini",
342
+ provider_id="openai",
343
+ model_id="openai/gpt-5-mini",
338
344
  )
339
345
  def answer_question(ctx: llm.Context, question: str) -> str:
340
346
  return f"Answer this question: {question}"
@@ -351,8 +357,9 @@ class ContextStreamResponse(
351
357
  def __init__(
352
358
  self,
353
359
  *,
354
- provider: "Provider",
360
+ provider_id: "ProviderId",
355
361
  model_id: "ModelId",
362
+ provider_model_name: str,
356
363
  params: "Params",
357
364
  tools: Sequence[Tool | ContextTool[DepsT]]
358
365
  | ContextToolkit[DepsT]
@@ -366,8 +373,9 @@ class ContextStreamResponse(
366
373
  tools if isinstance(tools, ContextToolkit) else ContextToolkit(tools=tools)
367
374
  )
368
375
  super().__init__(
369
- provider=provider,
376
+ provider_id=provider_id,
370
377
  model_id=model_id,
378
+ provider_model_name=provider_model_name,
371
379
  params=params,
372
380
  toolkit=toolkit,
373
381
  format=format,
@@ -375,7 +383,7 @@ class ContextStreamResponse(
375
383
  chunk_iterator=chunk_iterator,
376
384
  )
377
385
 
378
- def execute_tools(self, ctx: Context[DepsT]) -> Sequence[ToolOutput]:
386
+ def execute_tools(self, ctx: Context[DepsT]) -> Sequence[ToolOutput[Jsonable]]:
379
387
  """Execute and return all of the tool calls in the response.
380
388
 
381
389
  Args:
@@ -426,7 +434,7 @@ class ContextStreamResponse(
426
434
 
427
435
 
428
436
  class AsyncContextStreamResponse(
429
- BaseAsyncStreamResponse[AsyncContextToolkit, FormattableT],
437
+ BaseAsyncStreamResponse[AsyncContextToolkit[DepsT], FormattableT],
430
438
  Generic[DepsT, FormattableT],
431
439
  ):
432
440
  """An `AsyncContextStreamResponse` wraps response content from the LLM with a streaming interface.
@@ -475,8 +483,8 @@ class AsyncContextStreamResponse(
475
483
  from mirascope import llm
476
484
 
477
485
  @llm.call(
478
- provider="openai:completions",
479
- model_id="gpt-4o-mini",
486
+ provider_id="openai",
487
+ model_id="openai/gpt-5-mini",
480
488
  )
481
489
  async def answer_question(ctx: llm.Context, question: str) -> str:
482
490
  return f"Answer this question: {question}"
@@ -493,8 +501,9 @@ class AsyncContextStreamResponse(
493
501
  def __init__(
494
502
  self,
495
503
  *,
496
- provider: "Provider",
504
+ provider_id: "ProviderId",
497
505
  model_id: "ModelId",
506
+ provider_model_name: str,
498
507
  params: "Params",
499
508
  tools: Sequence[AsyncTool | AsyncContextTool[DepsT]]
500
509
  | AsyncContextToolkit[DepsT]
@@ -510,8 +519,9 @@ class AsyncContextStreamResponse(
510
519
  else AsyncContextToolkit(tools=tools)
511
520
  )
512
521
  super().__init__(
513
- provider=provider,
522
+ provider_id=provider_id,
514
523
  model_id=model_id,
524
+ provider_model_name=provider_model_name,
515
525
  params=params,
516
526
  toolkit=toolkit,
517
527
  format=format,
@@ -519,7 +529,9 @@ class AsyncContextStreamResponse(
519
529
  chunk_iterator=chunk_iterator,
520
530
  )
521
531
 
522
- async def execute_tools(self, ctx: Context[DepsT]) -> Sequence[ToolOutput]:
532
+ async def execute_tools(
533
+ self, ctx: Context[DepsT]
534
+ ) -> Sequence[ToolOutput[Jsonable]]:
523
535
  """Execute and return all of the tool calls in the response.
524
536
 
525
537
  Args: