mirascope 1.19.0__py3-none-any.whl → 1.20.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.
- mirascope/__init__.py +4 -0
- mirascope/beta/openai/realtime/realtime.py +7 -8
- mirascope/beta/openai/realtime/tool.py +2 -2
- mirascope/core/__init__.py +10 -1
- mirascope/core/anthropic/_utils/__init__.py +0 -2
- mirascope/core/anthropic/_utils/_convert_message_params.py +1 -7
- mirascope/core/anthropic/_utils/_message_param_converter.py +48 -31
- mirascope/core/anthropic/call_response.py +7 -9
- mirascope/core/anthropic/call_response_chunk.py +10 -0
- mirascope/core/anthropic/stream.py +6 -8
- mirascope/core/azure/_utils/__init__.py +0 -2
- mirascope/core/azure/call_response.py +7 -10
- mirascope/core/azure/call_response_chunk.py +6 -1
- mirascope/core/azure/stream.py +6 -8
- mirascope/core/base/__init__.py +10 -1
- mirascope/core/base/_utils/__init__.py +2 -0
- mirascope/core/base/_utils/_get_image_dimensions.py +39 -0
- mirascope/core/base/call_response.py +36 -6
- mirascope/core/base/call_response_chunk.py +15 -1
- mirascope/core/base/stream.py +25 -3
- mirascope/core/base/types.py +276 -2
- mirascope/core/bedrock/_utils/__init__.py +0 -2
- mirascope/core/bedrock/call_response.py +7 -10
- mirascope/core/bedrock/call_response_chunk.py +6 -0
- mirascope/core/bedrock/stream.py +6 -10
- mirascope/core/cohere/_utils/__init__.py +0 -2
- mirascope/core/cohere/call_response.py +7 -10
- mirascope/core/cohere/call_response_chunk.py +6 -0
- mirascope/core/cohere/stream.py +5 -8
- mirascope/core/costs/__init__.py +5 -0
- mirascope/core/{anthropic/_utils/_calculate_cost.py → costs/_anthropic_calculate_cost.py} +45 -14
- mirascope/core/{azure/_utils/_calculate_cost.py → costs/_azure_calculate_cost.py} +3 -3
- mirascope/core/{bedrock/_utils/_calculate_cost.py → costs/_bedrock_calculate_cost.py} +3 -3
- mirascope/core/{cohere/_utils/_calculate_cost.py → costs/_cohere_calculate_cost.py} +12 -8
- mirascope/core/{gemini/_utils/_calculate_cost.py → costs/_gemini_calculate_cost.py} +7 -7
- mirascope/core/costs/_google_calculate_cost.py +427 -0
- mirascope/core/costs/_groq_calculate_cost.py +156 -0
- mirascope/core/costs/_litellm_calculate_cost.py +11 -0
- mirascope/core/costs/_mistral_calculate_cost.py +64 -0
- mirascope/core/costs/_openai_calculate_cost.py +416 -0
- mirascope/core/{vertex/_utils/_calculate_cost.py → costs/_vertex_calculate_cost.py} +8 -7
- mirascope/core/{xai/_utils/_calculate_cost.py → costs/_xai_calculate_cost.py} +9 -9
- mirascope/core/costs/calculate_cost.py +86 -0
- mirascope/core/gemini/_utils/__init__.py +0 -2
- mirascope/core/gemini/call_response.py +7 -10
- mirascope/core/gemini/call_response_chunk.py +6 -1
- mirascope/core/gemini/stream.py +5 -8
- mirascope/core/google/_utils/__init__.py +0 -2
- mirascope/core/google/_utils/_setup_call.py +21 -2
- mirascope/core/google/call_response.py +9 -10
- mirascope/core/google/call_response_chunk.py +6 -1
- mirascope/core/google/stream.py +5 -8
- mirascope/core/groq/_utils/__init__.py +0 -2
- mirascope/core/groq/call_response.py +22 -10
- mirascope/core/groq/call_response_chunk.py +6 -0
- mirascope/core/groq/stream.py +5 -8
- mirascope/core/litellm/call_response.py +3 -4
- mirascope/core/litellm/stream.py +30 -22
- mirascope/core/mistral/_utils/__init__.py +0 -2
- mirascope/core/mistral/call_response.py +7 -10
- mirascope/core/mistral/call_response_chunk.py +6 -0
- mirascope/core/mistral/stream.py +5 -8
- mirascope/core/openai/_utils/__init__.py +0 -2
- mirascope/core/openai/_utils/_convert_message_params.py +4 -4
- mirascope/core/openai/call_response.py +30 -10
- mirascope/core/openai/call_response_chunk.py +6 -0
- mirascope/core/openai/stream.py +5 -8
- mirascope/core/vertex/_utils/__init__.py +0 -2
- mirascope/core/vertex/call_response.py +5 -10
- mirascope/core/vertex/call_response_chunk.py +6 -0
- mirascope/core/vertex/stream.py +5 -8
- mirascope/core/xai/_utils/__init__.py +1 -2
- mirascope/core/xai/call_response.py +0 -11
- mirascope/llm/__init__.py +10 -2
- mirascope/llm/_protocols.py +8 -28
- mirascope/llm/call_response.py +6 -6
- mirascope/llm/call_response_chunk.py +12 -3
- mirascope/llm/llm_call.py +21 -23
- mirascope/llm/llm_override.py +56 -27
- mirascope/llm/stream.py +7 -7
- mirascope/llm/tool.py +1 -1
- mirascope/retries/fallback.py +1 -1
- {mirascope-1.19.0.dist-info → mirascope-1.20.1.dist-info}/METADATA +1 -1
- {mirascope-1.19.0.dist-info → mirascope-1.20.1.dist-info}/RECORD +86 -82
- mirascope/core/google/_utils/_calculate_cost.py +0 -215
- mirascope/core/groq/_utils/_calculate_cost.py +0 -69
- mirascope/core/mistral/_utils/_calculate_cost.py +0 -48
- mirascope/core/openai/_utils/_calculate_cost.py +0 -246
- {mirascope-1.19.0.dist-info → mirascope-1.20.1.dist-info}/WHEEL +0 -0
- {mirascope-1.19.0.dist-info → mirascope-1.20.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"""Calculate the cost of a completion using the Vertex AI Gemini API, considering context window size."""
|
|
2
2
|
|
|
3
|
+
from ..base.types import CostMetadata
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
def calculate_cost(
|
|
5
|
-
|
|
6
|
-
cached_chars: int | float | None,
|
|
7
|
-
output_chars: int | float | None,
|
|
7
|
+
metadata: CostMetadata,
|
|
8
8
|
model: str = "gemini-1.5-pro",
|
|
9
|
-
context_length: int = 0,
|
|
10
9
|
) -> float | None:
|
|
11
10
|
"""Calculate the cost of a completion using the Vertex AI Gemini API.
|
|
12
11
|
|
|
@@ -19,6 +18,8 @@ def calculate_cost(
|
|
|
19
18
|
|
|
20
19
|
Note: Prices are per 1k characters. Gemini 1.0 Pro only supports up to 32K context window.
|
|
21
20
|
"""
|
|
21
|
+
|
|
22
|
+
context_length = metadata.context_length or 0
|
|
22
23
|
pricing = {
|
|
23
24
|
"gemini-1.5-flash": {
|
|
24
25
|
"prompt_short": 0.000_018_75,
|
|
@@ -40,7 +41,7 @@ def calculate_cost(
|
|
|
40
41
|
},
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
if
|
|
44
|
+
if metadata.input_tokens is None or metadata.output_tokens is None:
|
|
44
45
|
return None
|
|
45
46
|
|
|
46
47
|
try:
|
|
@@ -59,8 +60,8 @@ def calculate_cost(
|
|
|
59
60
|
"completion_long" if use_long_context else "completion_short"
|
|
60
61
|
]
|
|
61
62
|
|
|
62
|
-
prompt_cost = (
|
|
63
|
-
completion_cost = (
|
|
63
|
+
prompt_cost = (metadata.input_tokens / 1000) * prompt_price
|
|
64
|
+
completion_cost = (metadata.output_tokens / 1000) * completion_price
|
|
64
65
|
total_cost = prompt_cost + completion_cost
|
|
65
66
|
|
|
66
67
|
return total_cost
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Calculate the cost of a Grok API call."""
|
|
2
2
|
|
|
3
|
+
from ..base.types import CostMetadata
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
def calculate_cost(
|
|
5
|
-
|
|
6
|
-
cached_tokens: int | float | None,
|
|
7
|
-
output_tokens: int | float | None,
|
|
7
|
+
metadata: CostMetadata,
|
|
8
8
|
model: str,
|
|
9
9
|
) -> float | None:
|
|
10
10
|
"""Calculate the cost of an xAI Grok API call.
|
|
@@ -81,11 +81,11 @@ def calculate_cost(
|
|
|
81
81
|
},
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
if input_tokens is None or output_tokens is None:
|
|
84
|
+
if metadata.input_tokens is None or metadata.output_tokens is None:
|
|
85
85
|
return None
|
|
86
86
|
|
|
87
|
-
if cached_tokens is None:
|
|
88
|
-
cached_tokens = 0
|
|
87
|
+
if metadata.cached_tokens is None:
|
|
88
|
+
metadata.cached_tokens = 0
|
|
89
89
|
|
|
90
90
|
try:
|
|
91
91
|
model_pricing = pricing[model]
|
|
@@ -96,9 +96,9 @@ def calculate_cost(
|
|
|
96
96
|
cached_price = model_pricing["cached"]
|
|
97
97
|
completion_price = model_pricing["completion"]
|
|
98
98
|
|
|
99
|
-
prompt_cost = input_tokens * prompt_price
|
|
100
|
-
cached_cost = cached_tokens * cached_price
|
|
101
|
-
completion_cost = output_tokens * completion_price
|
|
99
|
+
prompt_cost = metadata.input_tokens * prompt_price
|
|
100
|
+
cached_cost = metadata.cached_tokens * cached_price
|
|
101
|
+
completion_cost = metadata.output_tokens * completion_price
|
|
102
102
|
total_cost = prompt_cost + cached_cost + completion_cost
|
|
103
103
|
|
|
104
104
|
return total_cost
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Cost calculation utilities for LLM API calls."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ..base.types import CostMetadata, Provider
|
|
6
|
+
from ._anthropic_calculate_cost import (
|
|
7
|
+
calculate_cost as anthropic_calculate_cost,
|
|
8
|
+
)
|
|
9
|
+
from ._azure_calculate_cost import calculate_cost as azure_calculate_cost
|
|
10
|
+
from ._bedrock_calculate_cost import calculate_cost as bedrock_calculate_cost
|
|
11
|
+
from ._cohere_calculate_cost import calculate_cost as cohere_calculate_cost
|
|
12
|
+
from ._gemini_calculate_cost import calculate_cost as gemini_calculate_cost
|
|
13
|
+
from ._google_calculate_cost import calculate_cost as google_calculate_cost
|
|
14
|
+
from ._groq_calculate_cost import calculate_cost as groq_calculate_cost
|
|
15
|
+
from ._litellm_calculate_cost import calculate_cost as litellm_calculate_cost
|
|
16
|
+
from ._mistral_calculate_cost import calculate_cost as mistral_calculate_cost
|
|
17
|
+
from ._openai_calculate_cost import calculate_cost as openai_calculate_cost
|
|
18
|
+
from ._vertex_calculate_cost import calculate_cost as vertex_calculate_cost
|
|
19
|
+
from ._xai_calculate_cost import calculate_cost as xai_calculate_cost
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def calculate_cost(
|
|
23
|
+
provider: Provider,
|
|
24
|
+
model: str,
|
|
25
|
+
metadata: CostMetadata | None = None,
|
|
26
|
+
) -> float | None:
|
|
27
|
+
"""Calculate the cost for an LLM API call.
|
|
28
|
+
|
|
29
|
+
This function routes to the appropriate provider-specific cost calculation function,
|
|
30
|
+
preserving existing behavior while providing a unified interface.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
provider: The LLM provider (e.g., "openai", "anthropic")
|
|
34
|
+
model: The model name (e.g., "gpt-4", "claude-3-opus")
|
|
35
|
+
metadata: Additional metadata required for cost calculation
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
The calculated cost in USD or None if unable to calculate
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Initialize empty metadata if none provided
|
|
42
|
+
if metadata is None:
|
|
43
|
+
metadata = CostMetadata()
|
|
44
|
+
|
|
45
|
+
# Set default values
|
|
46
|
+
if metadata.cached_tokens is None:
|
|
47
|
+
metadata.cached_tokens = 0
|
|
48
|
+
|
|
49
|
+
# Route to provider-specific implementations
|
|
50
|
+
if provider == "openai":
|
|
51
|
+
return openai_calculate_cost(metadata, model)
|
|
52
|
+
|
|
53
|
+
elif provider == "anthropic":
|
|
54
|
+
return anthropic_calculate_cost(metadata, model)
|
|
55
|
+
|
|
56
|
+
elif provider == "azure":
|
|
57
|
+
return azure_calculate_cost(metadata, model)
|
|
58
|
+
|
|
59
|
+
elif provider == "bedrock":
|
|
60
|
+
return bedrock_calculate_cost(metadata, model)
|
|
61
|
+
|
|
62
|
+
elif provider == "cohere":
|
|
63
|
+
return cohere_calculate_cost(metadata, model)
|
|
64
|
+
|
|
65
|
+
elif provider == "gemini":
|
|
66
|
+
return gemini_calculate_cost(metadata, model)
|
|
67
|
+
|
|
68
|
+
elif provider == "google":
|
|
69
|
+
return google_calculate_cost(metadata, model)
|
|
70
|
+
|
|
71
|
+
elif provider == "groq":
|
|
72
|
+
return groq_calculate_cost(metadata, model)
|
|
73
|
+
|
|
74
|
+
elif provider == "mistral":
|
|
75
|
+
return mistral_calculate_cost(metadata, model)
|
|
76
|
+
|
|
77
|
+
elif provider == "vertex":
|
|
78
|
+
return vertex_calculate_cost(metadata, model)
|
|
79
|
+
|
|
80
|
+
elif provider == "xai":
|
|
81
|
+
return xai_calculate_cost(metadata, model)
|
|
82
|
+
|
|
83
|
+
elif provider == "litellm":
|
|
84
|
+
return litellm_calculate_cost(metadata, model)
|
|
85
|
+
else:
|
|
86
|
+
raise ValueError(f"Unsupported provider: {provider}")
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"""Gemini utilities for decorator factories."""
|
|
2
2
|
|
|
3
|
-
from ._calculate_cost import calculate_cost
|
|
4
3
|
from ._convert_message_params import convert_message_params
|
|
5
4
|
from ._get_json_output import get_json_output
|
|
6
5
|
from ._handle_stream import handle_stream, handle_stream_async
|
|
7
6
|
from ._setup_call import setup_call
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"calculate_cost",
|
|
11
9
|
"convert_message_params",
|
|
12
10
|
"get_json_output",
|
|
13
11
|
"handle_stream",
|
|
@@ -17,8 +17,7 @@ from pydantic import computed_field
|
|
|
17
17
|
|
|
18
18
|
from .. import BaseMessageParam
|
|
19
19
|
from ..base import BaseCallResponse, transform_tool_outputs
|
|
20
|
-
from ..base.types import FinishReason
|
|
21
|
-
from ._utils import calculate_cost
|
|
20
|
+
from ..base.types import CostMetadata, FinishReason
|
|
22
21
|
from ._utils._convert_finish_reason_to_common_finish_reasons import (
|
|
23
22
|
_convert_finish_reasons_to_common_finish_reasons,
|
|
24
23
|
)
|
|
@@ -134,14 +133,6 @@ class GeminiCallResponse(
|
|
|
134
133
|
"""Returns the number of output tokens."""
|
|
135
134
|
return None
|
|
136
135
|
|
|
137
|
-
@computed_field
|
|
138
|
-
@property
|
|
139
|
-
def cost(self) -> float | None:
|
|
140
|
-
"""Returns the cost of the call."""
|
|
141
|
-
return calculate_cost(
|
|
142
|
-
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
143
|
-
)
|
|
144
|
-
|
|
145
136
|
@computed_field
|
|
146
137
|
@cached_property
|
|
147
138
|
def message_param(self) -> ContentDict:
|
|
@@ -213,3 +204,9 @@ class GeminiCallResponse(
|
|
|
213
204
|
if not self.user_message_param:
|
|
214
205
|
return None
|
|
215
206
|
return GeminiMessageParamConverter.from_provider([self.user_message_param])[0]
|
|
207
|
+
|
|
208
|
+
@computed_field
|
|
209
|
+
@property
|
|
210
|
+
def cost_metadata(self) -> CostMetadata:
|
|
211
|
+
"""Get metadata required for cost calculation."""
|
|
212
|
+
return super().cost_metadata
|
|
@@ -7,7 +7,7 @@ from google.ai.generativelanguage import Candidate
|
|
|
7
7
|
from google.generativeai.types import GenerateContentResponse
|
|
8
8
|
|
|
9
9
|
from ..base import BaseCallResponseChunk
|
|
10
|
-
from ..base.types import FinishReason
|
|
10
|
+
from ..base.types import CostMetadata, FinishReason
|
|
11
11
|
from ._utils._convert_finish_reason_to_common_finish_reasons import (
|
|
12
12
|
_convert_finish_reasons_to_common_finish_reasons,
|
|
13
13
|
)
|
|
@@ -88,6 +88,11 @@ class GeminiCallResponseChunk(
|
|
|
88
88
|
"""Returns the number of output tokens."""
|
|
89
89
|
return None
|
|
90
90
|
|
|
91
|
+
@property
|
|
92
|
+
def cost_metadata(self) -> CostMetadata:
|
|
93
|
+
"""Returns the cost metadata."""
|
|
94
|
+
return super().cost_metadata
|
|
95
|
+
|
|
91
96
|
@property
|
|
92
97
|
def common_finish_reasons(self) -> list[FinishReason] | None:
|
|
93
98
|
return _convert_finish_reasons_to_common_finish_reasons(
|
mirascope/core/gemini/stream.py
CHANGED
|
@@ -22,7 +22,7 @@ from google.generativeai.types import (
|
|
|
22
22
|
from google.generativeai.types.content_types import PartType
|
|
23
23
|
|
|
24
24
|
from ..base.stream import BaseStream
|
|
25
|
-
from .
|
|
25
|
+
from ..base.types import CostMetadata
|
|
26
26
|
from .call_params import GeminiCallParams
|
|
27
27
|
from .call_response import GeminiCallResponse
|
|
28
28
|
from .call_response_chunk import GeminiCallResponseChunk
|
|
@@ -66,13 +66,6 @@ class GeminiStream(
|
|
|
66
66
|
|
|
67
67
|
_provider = "gemini"
|
|
68
68
|
|
|
69
|
-
@property
|
|
70
|
-
def cost(self) -> float | None:
|
|
71
|
-
"""Returns the cost of the call."""
|
|
72
|
-
return calculate_cost(
|
|
73
|
-
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
74
|
-
)
|
|
75
|
-
|
|
76
69
|
def _construct_message_param(
|
|
77
70
|
self, tool_calls: list[FunctionCall] | None = None, content: str | None = None
|
|
78
71
|
) -> ContentDict:
|
|
@@ -121,3 +114,7 @@ class GeminiStream(
|
|
|
121
114
|
start_time=self.start_time,
|
|
122
115
|
end_time=self.end_time,
|
|
123
116
|
)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def cost_metadata(self) -> CostMetadata:
|
|
120
|
+
return super().cost_metadata
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"""Google utilities for decorator factories."""
|
|
2
2
|
|
|
3
|
-
from ._calculate_cost import calculate_cost
|
|
4
3
|
from ._convert_message_params import convert_message_params
|
|
5
4
|
from ._get_json_output import get_json_output
|
|
6
5
|
from ._handle_stream import handle_stream, handle_stream_async
|
|
7
6
|
from ._setup_call import setup_call
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"calculate_cost",
|
|
11
9
|
"convert_message_params",
|
|
12
10
|
"get_json_output",
|
|
13
11
|
"handle_stream",
|
|
@@ -176,5 +176,24 @@ def setup_call(
|
|
|
176
176
|
client.models.generate_content, client.models.generate_content_stream
|
|
177
177
|
)
|
|
178
178
|
)
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
if client.vertexai:
|
|
180
|
+
if isinstance(dynamic_config, dict):
|
|
181
|
+
metadata = dynamic_config.get("metadata", {})
|
|
182
|
+
tags = metadata.get("tags", set())
|
|
183
|
+
tags.add("use_vertex_ai")
|
|
184
|
+
metadata["tags"] = tags
|
|
185
|
+
dynamic_config["metadata"] = metadata
|
|
186
|
+
else:
|
|
187
|
+
metadata = getattr(fn, "_metadata", {})
|
|
188
|
+
tags = metadata.get("tags", set())
|
|
189
|
+
tags.add("use_vertex_ai")
|
|
190
|
+
metadata["tags"] = tags
|
|
191
|
+
fn._metadata = metadata
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
create,
|
|
195
|
+
prompt_template,
|
|
196
|
+
messages,
|
|
197
|
+
tool_types,
|
|
198
|
+
call_kwargs,
|
|
199
|
+
)
|
|
@@ -21,8 +21,7 @@ from pydantic import computed_field
|
|
|
21
21
|
|
|
22
22
|
from .. import BaseMessageParam
|
|
23
23
|
from ..base import BaseCallResponse, transform_tool_outputs
|
|
24
|
-
from ..base.types import FinishReason
|
|
25
|
-
from ._utils import calculate_cost
|
|
24
|
+
from ..base.types import CostMetadata, FinishReason, GoogleMetadata
|
|
26
25
|
from ._utils._convert_finish_reason_to_common_finish_reasons import (
|
|
27
26
|
_convert_finish_reasons_to_common_finish_reasons,
|
|
28
27
|
)
|
|
@@ -145,14 +144,6 @@ class GoogleCallResponse(
|
|
|
145
144
|
else None
|
|
146
145
|
)
|
|
147
146
|
|
|
148
|
-
@computed_field
|
|
149
|
-
@property
|
|
150
|
-
def cost(self) -> float | None:
|
|
151
|
-
"""Returns the cost of the call."""
|
|
152
|
-
return calculate_cost(
|
|
153
|
-
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
154
|
-
)
|
|
155
|
-
|
|
156
147
|
@computed_field
|
|
157
148
|
@cached_property
|
|
158
149
|
def message_param(self) -> ContentDict:
|
|
@@ -228,3 +219,11 @@ class GoogleCallResponse(
|
|
|
228
219
|
if not self.user_message_param:
|
|
229
220
|
return None
|
|
230
221
|
return GoogleMessageParamConverter.from_provider([self.user_message_param])[0]
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def cost_metadata(self) -> CostMetadata:
|
|
225
|
+
cost_metadata = super().cost_metadata
|
|
226
|
+
cost_metadata.google = GoogleMetadata(
|
|
227
|
+
use_vertex_ai="use_vertex_ai" in self.metadata.get("tags", [])
|
|
228
|
+
)
|
|
229
|
+
return cost_metadata
|
|
@@ -11,7 +11,7 @@ from google.genai.types import (
|
|
|
11
11
|
GenerateContentResponseUsageMetadata,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from mirascope.core.base.types import FinishReason
|
|
14
|
+
from mirascope.core.base.types import CostMetadata, FinishReason
|
|
15
15
|
|
|
16
16
|
from ..base import BaseCallResponseChunk
|
|
17
17
|
from ._utils._convert_finish_reason_to_common_finish_reasons import (
|
|
@@ -98,6 +98,11 @@ class GoogleCallResponseChunk(
|
|
|
98
98
|
"""Returns the number of output tokens."""
|
|
99
99
|
return self.usage.candidates_token_count if self.usage else None
|
|
100
100
|
|
|
101
|
+
@property
|
|
102
|
+
def cost_metadata(self) -> CostMetadata:
|
|
103
|
+
"""Returns the cost metadata."""
|
|
104
|
+
return super().cost_metadata
|
|
105
|
+
|
|
101
106
|
@property
|
|
102
107
|
def common_finish_reasons(self) -> list[FinishReason] | None:
|
|
103
108
|
return _convert_finish_reasons_to_common_finish_reasons(
|
mirascope/core/google/stream.py
CHANGED
|
@@ -20,7 +20,7 @@ from google.genai.types import (
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
from ..base.stream import BaseStream
|
|
23
|
-
from .
|
|
23
|
+
from ..base.types import CostMetadata
|
|
24
24
|
from .call_params import GoogleCallParams
|
|
25
25
|
from .call_response import GoogleCallResponse
|
|
26
26
|
from .call_response_chunk import GoogleCallResponseChunk
|
|
@@ -64,13 +64,6 @@ class GoogleStream(
|
|
|
64
64
|
|
|
65
65
|
_provider = "google"
|
|
66
66
|
|
|
67
|
-
@property
|
|
68
|
-
def cost(self) -> float | None:
|
|
69
|
-
"""Returns the cost of the call."""
|
|
70
|
-
return calculate_cost(
|
|
71
|
-
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
72
|
-
)
|
|
73
|
-
|
|
74
67
|
def _construct_message_param(
|
|
75
68
|
self, tool_calls: list[FunctionCall] | None = None, content: str | None = None
|
|
76
69
|
) -> ContentDict:
|
|
@@ -144,3 +137,7 @@ class GoogleStream(
|
|
|
144
137
|
start_time=self.start_time,
|
|
145
138
|
end_time=self.end_time,
|
|
146
139
|
)
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def cost_metadata(self) -> CostMetadata:
|
|
143
|
+
return super().cost_metadata
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"""Groq utilities for decorator factories."""
|
|
2
2
|
|
|
3
|
-
from ._calculate_cost import calculate_cost
|
|
4
3
|
from ._convert_message_params import convert_message_params
|
|
5
4
|
from ._get_json_output import get_json_output
|
|
6
5
|
from ._handle_stream import handle_stream, handle_stream_async
|
|
7
6
|
from ._setup_call import setup_call
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"calculate_cost",
|
|
11
9
|
"convert_message_params",
|
|
12
10
|
"get_json_output",
|
|
13
11
|
"handle_stream",
|
|
@@ -19,8 +19,7 @@ from pydantic import SerializeAsAny, computed_field
|
|
|
19
19
|
|
|
20
20
|
from .. import BaseMessageParam
|
|
21
21
|
from ..base import BaseCallResponse, transform_tool_outputs
|
|
22
|
-
from ..base.types import FinishReason
|
|
23
|
-
from ._utils import calculate_cost
|
|
22
|
+
from ..base.types import CostMetadata, FinishReason, ImageMetadata
|
|
24
23
|
from ._utils._message_param_converter import GroqMessageParamConverter
|
|
25
24
|
from .call_params import GroqCallParams
|
|
26
25
|
from .dynamic_config import AsyncGroqDynamicConfig, GroqDynamicConfig
|
|
@@ -110,14 +109,6 @@ class GroqCallResponse(
|
|
|
110
109
|
"""Returns the number of output tokens."""
|
|
111
110
|
return self.usage.completion_tokens if self.usage else None
|
|
112
111
|
|
|
113
|
-
@computed_field
|
|
114
|
-
@property
|
|
115
|
-
def cost(self) -> float | None:
|
|
116
|
-
"""Returns the cost of the call."""
|
|
117
|
-
return calculate_cost(
|
|
118
|
-
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
119
|
-
)
|
|
120
|
-
|
|
121
112
|
@computed_field
|
|
122
113
|
@cached_property
|
|
123
114
|
def message_param(self) -> SerializeAsAny[ChatCompletionAssistantMessageParam]:
|
|
@@ -195,3 +186,24 @@ class GroqCallResponse(
|
|
|
195
186
|
if not self.user_message_param:
|
|
196
187
|
return None
|
|
197
188
|
return GroqMessageParamConverter.from_provider([self.user_message_param])[0]
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def cost_metadata(self) -> CostMetadata:
|
|
192
|
+
cost_metadata = super().cost_metadata
|
|
193
|
+
image_metadata = []
|
|
194
|
+
|
|
195
|
+
for message in self.messages:
|
|
196
|
+
if "content" not in message:
|
|
197
|
+
continue
|
|
198
|
+
content = message["content"]
|
|
199
|
+
|
|
200
|
+
if not isinstance(content, list):
|
|
201
|
+
continue
|
|
202
|
+
for part in content:
|
|
203
|
+
# Check if this part is an image_url
|
|
204
|
+
if isinstance(part, dict) and part.get("type") == "image_url":
|
|
205
|
+
# Only count the image if it has a URL
|
|
206
|
+
image_metadata.append(ImageMetadata(width=0, height=0))
|
|
207
|
+
|
|
208
|
+
cost_metadata.images = image_metadata
|
|
209
|
+
return cost_metadata
|
|
@@ -10,6 +10,7 @@ from groq.types.chat.chat_completion import Choice
|
|
|
10
10
|
from groq.types.completion_usage import CompletionUsage
|
|
11
11
|
|
|
12
12
|
from ..base import BaseCallResponseChunk
|
|
13
|
+
from ..base.types import CostMetadata
|
|
13
14
|
|
|
14
15
|
FinishReason = Choice.__annotations__["finish_reason"]
|
|
15
16
|
|
|
@@ -93,6 +94,11 @@ class GroqCallResponseChunk(BaseCallResponseChunk[ChatCompletionChunk, FinishRea
|
|
|
93
94
|
return self.usage.completion_tokens
|
|
94
95
|
return None
|
|
95
96
|
|
|
97
|
+
@property
|
|
98
|
+
def cost_metadata(self) -> CostMetadata:
|
|
99
|
+
"""Returns the cost metadata."""
|
|
100
|
+
return super().cost_metadata
|
|
101
|
+
|
|
96
102
|
@property
|
|
97
103
|
def common_finish_reasons(self) -> list[FinishReason] | None:
|
|
98
104
|
return cast(list[FinishReason], self.finish_reasons)
|
mirascope/core/groq/stream.py
CHANGED
|
@@ -17,7 +17,7 @@ from groq.types.chat.chat_completion_message import ChatCompletionMessage
|
|
|
17
17
|
from groq.types.completion_usage import CompletionUsage
|
|
18
18
|
|
|
19
19
|
from ..base.stream import BaseStream
|
|
20
|
-
from .
|
|
20
|
+
from ..base.types import CostMetadata
|
|
21
21
|
from .call_params import GroqCallParams
|
|
22
22
|
from .call_response import GroqCallResponse
|
|
23
23
|
from .call_response_chunk import GroqCallResponseChunk
|
|
@@ -63,13 +63,6 @@ class GroqStream(
|
|
|
63
63
|
|
|
64
64
|
_provider = "groq"
|
|
65
65
|
|
|
66
|
-
@property
|
|
67
|
-
def cost(self) -> float | None:
|
|
68
|
-
"""Returns the cost of the call."""
|
|
69
|
-
return calculate_cost(
|
|
70
|
-
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
71
|
-
)
|
|
72
|
-
|
|
73
66
|
def _construct_message_param(
|
|
74
67
|
self,
|
|
75
68
|
tool_calls: list[ChatCompletionMessageToolCallParam] | None = None,
|
|
@@ -136,3 +129,7 @@ class GroqStream(
|
|
|
136
129
|
start_time=self.start_time,
|
|
137
130
|
end_time=self.end_time,
|
|
138
131
|
)
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def cost_metadata(self) -> CostMetadata:
|
|
135
|
+
return super().cost_metadata
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
usage docs: learn/calls.md#handling-responses
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from litellm.cost_calculator import completion_cost
|
|
7
6
|
from pydantic import computed_field
|
|
8
7
|
|
|
8
|
+
from ..base.types import CostMetadata
|
|
9
9
|
from ..openai import OpenAICallResponse
|
|
10
10
|
|
|
11
11
|
|
|
@@ -20,6 +20,5 @@ class LiteLLMCallResponse(OpenAICallResponse):
|
|
|
20
20
|
|
|
21
21
|
@computed_field
|
|
22
22
|
@property
|
|
23
|
-
def
|
|
24
|
-
""
|
|
25
|
-
return completion_cost(self.response)
|
|
23
|
+
def cost_metadata(self) -> CostMetadata:
|
|
24
|
+
return CostMetadata(cost=self.response._hidden_params["response_cost"]) # pyright: ignore [reportAttributeAccessIssue]
|
mirascope/core/litellm/stream.py
CHANGED
|
@@ -6,8 +6,10 @@ usage docs: learn/streams.md
|
|
|
6
6
|
from collections.abc import AsyncGenerator, Generator
|
|
7
7
|
|
|
8
8
|
from litellm import Choices, Message
|
|
9
|
+
from litellm.cost_calculator import completion_cost
|
|
9
10
|
from litellm.types.utils import ModelResponse
|
|
10
11
|
|
|
12
|
+
from ..base.types import CostMetadata
|
|
11
13
|
from ..openai import OpenAIStream, OpenAITool
|
|
12
14
|
from .call_response import LiteLLMCallResponse
|
|
13
15
|
from .call_response_chunk import LiteLLMCallResponseChunk
|
|
@@ -34,35 +36,41 @@ class LiteLLMStream(OpenAIStream):
|
|
|
34
36
|
return super().__aiter__() # pyright: ignore [reportReturnType] # pragma: no cover
|
|
35
37
|
|
|
36
38
|
@property
|
|
37
|
-
def
|
|
38
|
-
"""Returns
|
|
39
|
+
def cost_metadata(self) -> CostMetadata:
|
|
40
|
+
"""Returns metadata needed for cost calculation."""
|
|
39
41
|
response = self.construct_call_response()
|
|
40
|
-
return
|
|
42
|
+
return CostMetadata(
|
|
43
|
+
cost=response.cost,
|
|
44
|
+
)
|
|
41
45
|
|
|
42
46
|
def construct_call_response(self) -> LiteLLMCallResponse:
|
|
43
47
|
openai_call_response = super().construct_call_response()
|
|
44
48
|
openai_response = openai_call_response.response
|
|
49
|
+
model_response = ModelResponse(
|
|
50
|
+
id=openai_response.id,
|
|
51
|
+
choices=[
|
|
52
|
+
Choices(
|
|
53
|
+
finish_reason=choice.finish_reason,
|
|
54
|
+
index=choice.index,
|
|
55
|
+
message=Message(**choice.message.model_dump()),
|
|
56
|
+
logprobs=choice.logprobs,
|
|
57
|
+
)
|
|
58
|
+
for choice in openai_response.choices
|
|
59
|
+
],
|
|
60
|
+
created=openai_response.created,
|
|
61
|
+
model=openai_response.model,
|
|
62
|
+
object=openai_response.object,
|
|
63
|
+
system_fingerprint=openai_response.system_fingerprint,
|
|
64
|
+
usage=openai_response.usage.model_dump() if openai_response.usage else None,
|
|
65
|
+
)
|
|
66
|
+
model_response._hidden_params["response_cost"] = completion_cost(
|
|
67
|
+
model=self.model,
|
|
68
|
+
messages=openai_call_response.messages,
|
|
69
|
+
completion=openai_call_response.content,
|
|
70
|
+
)
|
|
45
71
|
response = LiteLLMCallResponse(
|
|
46
72
|
metadata=openai_call_response.metadata,
|
|
47
|
-
response=
|
|
48
|
-
id=openai_response.id,
|
|
49
|
-
choices=[
|
|
50
|
-
Choices(
|
|
51
|
-
finish_reason=choice.finish_reason,
|
|
52
|
-
index=choice.index,
|
|
53
|
-
message=Message(**choice.message.model_dump()),
|
|
54
|
-
logprobs=choice.logprobs,
|
|
55
|
-
)
|
|
56
|
-
for choice in openai_response.choices
|
|
57
|
-
],
|
|
58
|
-
created=openai_response.created,
|
|
59
|
-
model=openai_response.model,
|
|
60
|
-
object=openai_response.object,
|
|
61
|
-
system_fingerprint=openai_response.system_fingerprint,
|
|
62
|
-
usage=openai_response.usage.model_dump()
|
|
63
|
-
if openai_response.usage
|
|
64
|
-
else None,
|
|
65
|
-
), # pyright: ignore [reportArgumentType]
|
|
73
|
+
response=model_response, # pyright: ignore [reportArgumentType]
|
|
66
74
|
tool_types=openai_call_response.tool_types,
|
|
67
75
|
prompt_template=openai_call_response.prompt_template,
|
|
68
76
|
fn_args=openai_call_response.fn_args,
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"""Mistral utilities for decorator factories."""
|
|
2
2
|
|
|
3
|
-
from ._calculate_cost import calculate_cost
|
|
4
3
|
from ._convert_message_params import convert_message_params
|
|
5
4
|
from ._get_json_output import get_json_output
|
|
6
5
|
from ._handle_stream import handle_stream, handle_stream_async
|
|
7
6
|
from ._setup_call import setup_call
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"calculate_cost",
|
|
11
9
|
"convert_message_params",
|
|
12
10
|
"get_json_output",
|
|
13
11
|
"handle_stream",
|
|
@@ -19,8 +19,7 @@ from pydantic import computed_field
|
|
|
19
19
|
|
|
20
20
|
from .. import BaseMessageParam
|
|
21
21
|
from ..base import BaseCallResponse, transform_tool_outputs
|
|
22
|
-
from ..base.types import FinishReason
|
|
23
|
-
from ._utils import calculate_cost
|
|
22
|
+
from ..base.types import CostMetadata, FinishReason
|
|
24
23
|
from ._utils._convert_finish_reason_to_common_finish_reasons import (
|
|
25
24
|
_convert_finish_reasons_to_common_finish_reasons,
|
|
26
25
|
)
|
|
@@ -119,14 +118,6 @@ class MistralCallResponse(
|
|
|
119
118
|
"""Returns the number of output tokens."""
|
|
120
119
|
return self.usage.completion_tokens
|
|
121
120
|
|
|
122
|
-
@computed_field
|
|
123
|
-
@property
|
|
124
|
-
def cost(self) -> float | None:
|
|
125
|
-
"""Returns the cost of the call."""
|
|
126
|
-
return calculate_cost(
|
|
127
|
-
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
128
|
-
)
|
|
129
|
-
|
|
130
121
|
@computed_field
|
|
131
122
|
@cached_property
|
|
132
123
|
def message_param(self) -> AssistantMessage:
|
|
@@ -200,3 +191,9 @@ class MistralCallResponse(
|
|
|
200
191
|
if not self.user_message_param:
|
|
201
192
|
return None
|
|
202
193
|
return MistralMessageParamConverter.from_provider([self.user_message_param])[0]
|
|
194
|
+
|
|
195
|
+
@computed_field
|
|
196
|
+
@property
|
|
197
|
+
def cost_metadata(self) -> CostMetadata:
|
|
198
|
+
"""Get metadata required for cost calculation."""
|
|
199
|
+
return super().cost_metadata
|