mirascope 1.18.2__py3-none-any.whl → 1.18.4__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 +20 -1
- mirascope/beta/openai/__init__.py +1 -1
- mirascope/beta/openai/realtime/__init__.py +1 -1
- mirascope/beta/openai/realtime/tool.py +1 -1
- mirascope/beta/rag/__init__.py +2 -2
- mirascope/beta/rag/base/__init__.py +2 -2
- mirascope/beta/rag/weaviate/__init__.py +1 -1
- mirascope/core/__init__.py +29 -6
- mirascope/core/anthropic/__init__.py +3 -3
- mirascope/core/anthropic/_utils/_calculate_cost.py +114 -47
- mirascope/core/anthropic/call_response.py +9 -3
- mirascope/core/anthropic/call_response_chunk.py +7 -0
- mirascope/core/anthropic/stream.py +3 -1
- mirascope/core/azure/__init__.py +2 -2
- mirascope/core/azure/_utils/_calculate_cost.py +4 -1
- mirascope/core/azure/call_response.py +9 -3
- mirascope/core/azure/call_response_chunk.py +5 -0
- mirascope/core/azure/stream.py +3 -1
- mirascope/core/base/__init__.py +11 -9
- mirascope/core/base/_utils/__init__.py +10 -10
- mirascope/core/base/_utils/_get_common_usage.py +8 -4
- mirascope/core/base/_utils/_get_create_fn_or_async_create_fn.py +2 -2
- mirascope/core/base/_utils/_protocols.py +9 -8
- mirascope/core/base/call_response.py +22 -22
- mirascope/core/base/call_response_chunk.py +12 -1
- mirascope/core/base/stream.py +24 -21
- mirascope/core/base/tool.py +7 -5
- mirascope/core/base/types.py +22 -5
- mirascope/core/bedrock/__init__.py +3 -3
- mirascope/core/bedrock/_utils/_calculate_cost.py +4 -1
- mirascope/core/bedrock/call_response.py +8 -3
- mirascope/core/bedrock/call_response_chunk.py +5 -0
- mirascope/core/bedrock/stream.py +3 -1
- mirascope/core/cohere/__init__.py +2 -2
- mirascope/core/cohere/_utils/_calculate_cost.py +4 -3
- mirascope/core/cohere/call_response.py +9 -3
- mirascope/core/cohere/call_response_chunk.py +5 -0
- mirascope/core/cohere/stream.py +3 -1
- mirascope/core/gemini/__init__.py +2 -2
- mirascope/core/gemini/_utils/_calculate_cost.py +4 -1
- mirascope/core/gemini/_utils/_convert_message_params.py +1 -1
- mirascope/core/gemini/call_response.py +9 -3
- mirascope/core/gemini/call_response_chunk.py +5 -0
- mirascope/core/gemini/stream.py +3 -1
- mirascope/core/google/__init__.py +2 -2
- mirascope/core/google/_utils/_calculate_cost.py +141 -14
- mirascope/core/google/_utils/_convert_message_params.py +120 -115
- mirascope/core/google/_utils/_message_param_converter.py +34 -33
- mirascope/core/google/_utils/_validate_media_type.py +34 -0
- mirascope/core/google/call_response.py +38 -10
- mirascope/core/google/call_response_chunk.py +17 -9
- mirascope/core/google/stream.py +20 -2
- mirascope/core/groq/__init__.py +2 -2
- mirascope/core/groq/_utils/_calculate_cost.py +12 -11
- mirascope/core/groq/call_response.py +9 -3
- mirascope/core/groq/call_response_chunk.py +5 -0
- mirascope/core/groq/stream.py +3 -1
- mirascope/core/litellm/__init__.py +1 -1
- mirascope/core/litellm/_utils/_setup_call.py +7 -3
- mirascope/core/mistral/__init__.py +2 -2
- mirascope/core/mistral/_utils/_calculate_cost.py +10 -9
- mirascope/core/mistral/call_response.py +9 -3
- mirascope/core/mistral/call_response_chunk.py +5 -0
- mirascope/core/mistral/stream.py +3 -1
- mirascope/core/openai/__init__.py +2 -2
- mirascope/core/openai/_utils/_calculate_cost.py +78 -37
- mirascope/core/openai/call_params.py +13 -0
- mirascope/core/openai/call_response.py +14 -3
- mirascope/core/openai/call_response_chunk.py +12 -0
- mirascope/core/openai/stream.py +6 -4
- mirascope/core/vertex/__init__.py +1 -1
- mirascope/core/vertex/_utils/_calculate_cost.py +1 -0
- mirascope/core/vertex/_utils/_convert_message_params.py +1 -1
- mirascope/core/vertex/call_response.py +9 -3
- mirascope/core/vertex/call_response_chunk.py +5 -0
- mirascope/core/vertex/stream.py +3 -1
- mirascope/integrations/_middleware_factory.py +6 -6
- mirascope/integrations/logfire/_utils.py +1 -1
- mirascope/llm/__init__.py +3 -1
- mirascope/llm/_protocols.py +5 -5
- mirascope/llm/call_response.py +16 -9
- mirascope/llm/llm_call.py +53 -25
- mirascope/llm/stream.py +43 -31
- mirascope/retries/__init__.py +1 -1
- mirascope/tools/__init__.py +2 -2
- {mirascope-1.18.2.dist-info → mirascope-1.18.4.dist-info}/METADATA +2 -2
- {mirascope-1.18.2.dist-info → mirascope-1.18.4.dist-info}/RECORD +89 -88
- {mirascope-1.18.2.dist-info → mirascope-1.18.4.dist-info}/WHEEL +0 -0
- {mirascope-1.18.2.dist-info → mirascope-1.18.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -13,35 +13,15 @@ from mirascope.core.base._utils._base_message_param_converter import (
|
|
|
13
13
|
BaseMessageParamConverter,
|
|
14
14
|
)
|
|
15
15
|
from mirascope.core.base.message_param import (
|
|
16
|
+
AudioPart,
|
|
16
17
|
AudioURLPart,
|
|
17
18
|
ImageURLPart,
|
|
18
19
|
ToolCallPart,
|
|
19
20
|
ToolResultPart,
|
|
20
21
|
)
|
|
21
|
-
from mirascope.core.gemini._utils._message_param_converter import _is_audio_mime
|
|
22
22
|
from mirascope.core.google._utils import convert_message_params
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
def _is_image_mime(mime_type: str) -> bool:
|
|
26
|
-
return mime_type in ["image/jpeg", "image/png", "image/gif", "image/webp"]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _to_image_part(mime_type: str, data: bytes) -> ImagePart:
|
|
30
|
-
if not _is_image_mime(mime_type):
|
|
31
|
-
raise ValueError(
|
|
32
|
-
f"Unsupported image media type: {mime_type}. "
|
|
33
|
-
"Expected one of: image/jpeg, image/png, image/gif, image/webp."
|
|
34
|
-
)
|
|
35
|
-
return ImagePart(type="image", media_type=mime_type, image=data, detail=None)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _to_document_part(mime_type: str, data: bytes) -> DocumentPart:
|
|
39
|
-
if mime_type != "application/pdf":
|
|
40
|
-
raise ValueError(
|
|
41
|
-
f"Unsupported document media type: {mime_type}. "
|
|
42
|
-
"Only application/pdf is supported."
|
|
43
|
-
)
|
|
44
|
-
return DocumentPart(type="document", media_type=mime_type, document=data)
|
|
24
|
+
from ._validate_media_type import _check_audio_media_type, _check_image_media_type
|
|
45
25
|
|
|
46
26
|
|
|
47
27
|
class GoogleMessageParamConverter(BaseMessageParamConverter):
|
|
@@ -74,21 +54,42 @@ class GoogleMessageParamConverter(BaseMessageParamConverter):
|
|
|
74
54
|
if part.text:
|
|
75
55
|
content_list.append(TextPart(type="text", text=part.text))
|
|
76
56
|
|
|
77
|
-
elif part.inline_data:
|
|
78
|
-
|
|
79
|
-
mime = blob.mime_type or ""
|
|
57
|
+
elif blob := part.inline_data:
|
|
58
|
+
mime_type = blob.mime_type or ""
|
|
80
59
|
data = blob.data or b""
|
|
81
|
-
if
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
60
|
+
if mime_type.startswith("image/"):
|
|
61
|
+
_check_image_media_type(mime_type)
|
|
62
|
+
content_list.append(
|
|
63
|
+
ImagePart(
|
|
64
|
+
type="image",
|
|
65
|
+
media_type=mime_type,
|
|
66
|
+
image=data,
|
|
67
|
+
detail=None,
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
elif mime_type.startswith("audio/"):
|
|
71
|
+
_check_audio_media_type(mime_type)
|
|
72
|
+
content_list.append(
|
|
73
|
+
AudioPart(
|
|
74
|
+
type="audio",
|
|
75
|
+
media_type=mime_type,
|
|
76
|
+
audio=data,
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
elif mime_type == "application/pdf":
|
|
80
|
+
content_list.append(
|
|
81
|
+
DocumentPart(
|
|
82
|
+
type="document", media_type=mime_type, document=data
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
85
|
else:
|
|
86
86
|
raise ValueError(
|
|
87
|
-
f"Unsupported inline_data mime type: {
|
|
87
|
+
f"Unsupported inline_data mime type: {mime_type}. Cannot convert to BaseMessageParam."
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
elif part.file_data:
|
|
91
|
-
|
|
90
|
+
elif file_data := part.file_data:
|
|
91
|
+
mime_type = file_data.mime_type or ""
|
|
92
|
+
if mime_type.startswith("image/"):
|
|
92
93
|
content_list.append(
|
|
93
94
|
ImageURLPart(
|
|
94
95
|
type="image_url",
|
|
@@ -96,7 +97,7 @@ class GoogleMessageParamConverter(BaseMessageParamConverter):
|
|
|
96
97
|
detail=None,
|
|
97
98
|
)
|
|
98
99
|
)
|
|
99
|
-
elif
|
|
100
|
+
elif mime_type.startswith("audio/"):
|
|
100
101
|
content_list.append(
|
|
101
102
|
AudioURLPart(
|
|
102
103
|
type="audio_url",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Utilities for validating supported media types for Google models."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _check_image_media_type(media_type: str) -> None:
|
|
5
|
+
"""Raises a `ValueError` if the image media type is not supported."""
|
|
6
|
+
if media_type not in [
|
|
7
|
+
"image/jpeg",
|
|
8
|
+
"image/png",
|
|
9
|
+
"image/webp",
|
|
10
|
+
"image/heic",
|
|
11
|
+
"image/heif",
|
|
12
|
+
]:
|
|
13
|
+
raise ValueError(
|
|
14
|
+
f"Unsupported image media type: {media_type}. "
|
|
15
|
+
"Google currently only supports JPEG, PNG, WebP, HEIC, "
|
|
16
|
+
"and HEIF images."
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _check_audio_media_type(media_type: str) -> None:
|
|
21
|
+
"""Raises a `ValueError` if the audio media type is not supported."""
|
|
22
|
+
if media_type not in [
|
|
23
|
+
"audio/wav",
|
|
24
|
+
"audio/mp3",
|
|
25
|
+
"audio/aiff",
|
|
26
|
+
"audio/aac",
|
|
27
|
+
"audio/ogg",
|
|
28
|
+
"audio/flac",
|
|
29
|
+
]:
|
|
30
|
+
raise ValueError(
|
|
31
|
+
f"Unsupported audio media type: {media_type}. "
|
|
32
|
+
"Google currently only supports WAV, MP3, AIFF, AAC, OGG, "
|
|
33
|
+
"and FLAC audio file types."
|
|
34
|
+
)
|
|
@@ -11,6 +11,7 @@ from google.genai.types import (
|
|
|
11
11
|
ContentListUnionDict,
|
|
12
12
|
FunctionResponseDict,
|
|
13
13
|
GenerateContentResponse,
|
|
14
|
+
GenerateContentResponseUsageMetadata,
|
|
14
15
|
PartDict,
|
|
15
16
|
# Import manually SchemaDict to avoid Pydantic error
|
|
16
17
|
SchemaDict, # noqa: F401
|
|
@@ -68,11 +69,13 @@ class GoogleCallResponse(
|
|
|
68
69
|
|
|
69
70
|
_provider = "google"
|
|
70
71
|
|
|
72
|
+
@computed_field
|
|
71
73
|
@property
|
|
72
74
|
def content(self) -> str:
|
|
73
75
|
"""Returns the contained string content for the 0th choice."""
|
|
74
76
|
return self.response.candidates[0].content.parts[0].text # pyright: ignore [reportOptionalSubscript, reportReturnType, reportOptionalMemberAccess, reportOptionalIterable]
|
|
75
77
|
|
|
78
|
+
@computed_field
|
|
76
79
|
@property
|
|
77
80
|
def finish_reasons(self) -> list[str]:
|
|
78
81
|
"""Returns the finish reasons of the response."""
|
|
@@ -83,6 +86,7 @@ class GoogleCallResponse(
|
|
|
83
86
|
if candidate and candidate.finish_reason is not None
|
|
84
87
|
]
|
|
85
88
|
|
|
89
|
+
@computed_field
|
|
86
90
|
@property
|
|
87
91
|
def model(self) -> str:
|
|
88
92
|
"""Returns the model name.
|
|
@@ -90,8 +94,11 @@ class GoogleCallResponse(
|
|
|
90
94
|
google.generativeai does not return model, so we return the model provided by
|
|
91
95
|
the user.
|
|
92
96
|
"""
|
|
93
|
-
return
|
|
97
|
+
return (
|
|
98
|
+
self.response.model_version if self.response.model_version else self._model
|
|
99
|
+
)
|
|
94
100
|
|
|
101
|
+
@computed_field
|
|
95
102
|
@property
|
|
96
103
|
def id(self) -> str | None:
|
|
97
104
|
"""Returns the id of the response.
|
|
@@ -101,27 +108,50 @@ class GoogleCallResponse(
|
|
|
101
108
|
return None
|
|
102
109
|
|
|
103
110
|
@property
|
|
104
|
-
def usage(self) -> None:
|
|
111
|
+
def usage(self) -> GenerateContentResponseUsageMetadata | None:
|
|
105
112
|
"""Returns the usage of the chat completion.
|
|
106
113
|
|
|
107
114
|
google.generativeai does not have Usage, so we return None
|
|
108
115
|
"""
|
|
109
|
-
return
|
|
116
|
+
return self.response.usage_metadata
|
|
110
117
|
|
|
118
|
+
@computed_field
|
|
111
119
|
@property
|
|
112
|
-
def input_tokens(self) -> None:
|
|
120
|
+
def input_tokens(self) -> int | None:
|
|
113
121
|
"""Returns the number of input tokens."""
|
|
114
|
-
return
|
|
122
|
+
return (
|
|
123
|
+
self.response.usage_metadata.prompt_token_count
|
|
124
|
+
if self.response.usage_metadata
|
|
125
|
+
else None
|
|
126
|
+
)
|
|
115
127
|
|
|
128
|
+
@computed_field
|
|
116
129
|
@property
|
|
117
|
-
def
|
|
130
|
+
def cached_tokens(self) -> int | None:
|
|
131
|
+
"""Returns the number of cached tokens."""
|
|
132
|
+
return (
|
|
133
|
+
self.response.usage_metadata.cached_content_token_count
|
|
134
|
+
if self.response.usage_metadata
|
|
135
|
+
else None
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@computed_field
|
|
139
|
+
@property
|
|
140
|
+
def output_tokens(self) -> int | None:
|
|
118
141
|
"""Returns the number of output tokens."""
|
|
119
|
-
return
|
|
142
|
+
return (
|
|
143
|
+
self.response.usage_metadata.candidates_token_count
|
|
144
|
+
if self.response.usage_metadata
|
|
145
|
+
else None
|
|
146
|
+
)
|
|
120
147
|
|
|
148
|
+
@computed_field
|
|
121
149
|
@property
|
|
122
150
|
def cost(self) -> float | None:
|
|
123
151
|
"""Returns the cost of the call."""
|
|
124
|
-
return calculate_cost(
|
|
152
|
+
return calculate_cost(
|
|
153
|
+
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
154
|
+
)
|
|
125
155
|
|
|
126
156
|
@computed_field
|
|
127
157
|
@cached_property
|
|
@@ -129,7 +159,6 @@ class GoogleCallResponse(
|
|
|
129
159
|
"""Returns the models's response as a message parameter."""
|
|
130
160
|
return {"role": "model", "parts": self.response.candidates[0].content.parts} # pyright: ignore [reportReturnType, reportOptionalSubscript, reportOptionalMemberAccess]
|
|
131
161
|
|
|
132
|
-
@computed_field
|
|
133
162
|
@cached_property
|
|
134
163
|
def tools(self) -> list[GoogleTool] | None:
|
|
135
164
|
"""Returns the list of tools for the 0th candidate's 0th content part."""
|
|
@@ -146,7 +175,6 @@ class GoogleCallResponse(
|
|
|
146
175
|
|
|
147
176
|
return extracted_tools
|
|
148
177
|
|
|
149
|
-
@computed_field
|
|
150
178
|
@cached_property
|
|
151
179
|
def tool(self) -> GoogleTool | None:
|
|
152
180
|
"""Returns the 0th tool for the 0th candidate's 0th content part.
|
|
@@ -6,7 +6,10 @@ usage docs: learn/streams.md#handling-streamed-responses
|
|
|
6
6
|
from typing import cast
|
|
7
7
|
|
|
8
8
|
from google.genai.types import FinishReason as GoogleFinishReason
|
|
9
|
-
from google.genai.types import
|
|
9
|
+
from google.genai.types import (
|
|
10
|
+
GenerateContentResponse,
|
|
11
|
+
GenerateContentResponseUsageMetadata,
|
|
12
|
+
)
|
|
10
13
|
|
|
11
14
|
from mirascope.core.base.types import FinishReason
|
|
12
15
|
|
|
@@ -57,12 +60,12 @@ class GoogleCallResponseChunk(
|
|
|
57
60
|
]
|
|
58
61
|
|
|
59
62
|
@property
|
|
60
|
-
def model(self) -> None:
|
|
63
|
+
def model(self) -> str | None:
|
|
61
64
|
"""Returns the model name.
|
|
62
65
|
|
|
63
66
|
google.generativeai does not return model, so we return None
|
|
64
67
|
"""
|
|
65
|
-
return
|
|
68
|
+
return self.chunk.model_version
|
|
66
69
|
|
|
67
70
|
@property
|
|
68
71
|
def id(self) -> str | None:
|
|
@@ -73,22 +76,27 @@ class GoogleCallResponseChunk(
|
|
|
73
76
|
return None
|
|
74
77
|
|
|
75
78
|
@property
|
|
76
|
-
def usage(self) -> None:
|
|
79
|
+
def usage(self) -> GenerateContentResponseUsageMetadata | None:
|
|
77
80
|
"""Returns the usage of the chat completion.
|
|
78
81
|
|
|
79
82
|
google.generativeai does not have Usage, so we return None
|
|
80
83
|
"""
|
|
81
|
-
return
|
|
84
|
+
return self.chunk.usage_metadata
|
|
82
85
|
|
|
83
86
|
@property
|
|
84
|
-
def input_tokens(self) -> None:
|
|
87
|
+
def input_tokens(self) -> int | None:
|
|
85
88
|
"""Returns the number of input tokens."""
|
|
86
|
-
return None
|
|
89
|
+
return self.usage.prompt_token_count if self.usage else None
|
|
87
90
|
|
|
88
91
|
@property
|
|
89
|
-
def
|
|
92
|
+
def cached_tokens(self) -> int | None:
|
|
93
|
+
"""Returns the number of cached tokens."""
|
|
94
|
+
return self.usage.cached_content_token_count if self.usage else None
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def output_tokens(self) -> int | None:
|
|
90
98
|
"""Returns the number of output tokens."""
|
|
91
|
-
return None
|
|
99
|
+
return self.usage.candidates_token_count if self.usage else None
|
|
92
100
|
|
|
93
101
|
@property
|
|
94
102
|
def common_finish_reasons(self) -> list[FinishReason] | None:
|
mirascope/core/google/stream.py
CHANGED
|
@@ -14,6 +14,7 @@ from google.genai.types import (
|
|
|
14
14
|
FinishReason,
|
|
15
15
|
FunctionCall,
|
|
16
16
|
GenerateContentResponse,
|
|
17
|
+
GenerateContentResponseUsageMetadata,
|
|
17
18
|
PartDict,
|
|
18
19
|
Tool,
|
|
19
20
|
)
|
|
@@ -66,7 +67,9 @@ class GoogleStream(
|
|
|
66
67
|
@property
|
|
67
68
|
def cost(self) -> float | None:
|
|
68
69
|
"""Returns the cost of the call."""
|
|
69
|
-
return calculate_cost(
|
|
70
|
+
return calculate_cost(
|
|
71
|
+
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
72
|
+
)
|
|
70
73
|
|
|
71
74
|
def _construct_message_param(
|
|
72
75
|
self, tool_calls: list[FunctionCall] | None = None, content: str | None = None
|
|
@@ -98,6 +101,15 @@ class GoogleStream(
|
|
|
98
101
|
raise ValueError(
|
|
99
102
|
"No stream response, check if the stream has been consumed."
|
|
100
103
|
)
|
|
104
|
+
candidates_token_count = (
|
|
105
|
+
int(self.output_tokens) if self.output_tokens is not None else None
|
|
106
|
+
)
|
|
107
|
+
prompt_token_count = (
|
|
108
|
+
int(self.input_tokens) if self.input_tokens is not None else None
|
|
109
|
+
)
|
|
110
|
+
total_token_count = int(candidates_token_count or 0) + int(
|
|
111
|
+
prompt_token_count or 0
|
|
112
|
+
)
|
|
101
113
|
response = GenerateContentResponse(
|
|
102
114
|
candidates=[
|
|
103
115
|
Candidate(
|
|
@@ -109,7 +121,13 @@ class GoogleStream(
|
|
|
109
121
|
parts=self.message_param["parts"], # pyright: ignore [reportTypedDictNotRequiredAccess, reportArgumentType]
|
|
110
122
|
),
|
|
111
123
|
)
|
|
112
|
-
]
|
|
124
|
+
],
|
|
125
|
+
model_version=self.model,
|
|
126
|
+
usage_metadata=GenerateContentResponseUsageMetadata(
|
|
127
|
+
candidates_token_count=candidates_token_count,
|
|
128
|
+
prompt_token_count=prompt_token_count,
|
|
129
|
+
total_token_count=total_token_count,
|
|
130
|
+
),
|
|
113
131
|
)
|
|
114
132
|
|
|
115
133
|
return GoogleCallResponse(
|
mirascope/core/groq/__init__.py
CHANGED
|
@@ -18,13 +18,13 @@ GroqMessageParam: TypeAlias = ChatCompletionMessageParam | BaseMessageParam
|
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
20
20
|
"AsyncGroqDynamicConfig",
|
|
21
|
-
"call",
|
|
22
|
-
"GroqDynamicConfig",
|
|
23
21
|
"GroqCallParams",
|
|
24
22
|
"GroqCallResponse",
|
|
25
23
|
"GroqCallResponseChunk",
|
|
24
|
+
"GroqDynamicConfig",
|
|
26
25
|
"GroqMessageParam",
|
|
27
26
|
"GroqStream",
|
|
28
27
|
"GroqTool",
|
|
28
|
+
"call",
|
|
29
29
|
"groq_call",
|
|
30
30
|
]
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
def calculate_cost(
|
|
5
5
|
input_tokens: int | float | None,
|
|
6
|
+
cached_tokens: int | float | None,
|
|
6
7
|
output_tokens: int | float | None,
|
|
7
8
|
model: str = "mixtral-8x7b-32768",
|
|
8
9
|
) -> float | None:
|
|
@@ -10,17 +11,17 @@ def calculate_cost(
|
|
|
10
11
|
|
|
11
12
|
https://wow.groq.com/
|
|
12
13
|
|
|
13
|
-
Model Input
|
|
14
|
-
llama-3.1-405b-reasoning N/A
|
|
15
|
-
llama-3.1-70b-versatile N/A
|
|
16
|
-
llama-3.1-8b-instant N/A
|
|
17
|
-
llama3-groq-70b-8192-tool-use-preview $0.89 / 1M tokens
|
|
18
|
-
llama3-groq-8b-8192-tool-use-preview $0.19 / 1M tokens
|
|
19
|
-
llama3-70b-8192 $0.59 / 1M tokens
|
|
20
|
-
llama3-8b-8192 $0.05 / 1M tokens
|
|
21
|
-
mixtral-8x7b-32768 $0.27 / 1M tokens
|
|
22
|
-
gemma-7b-it $0.07 / 1M tokens
|
|
23
|
-
gemma2-9b-it $0.20 / 1M tokens
|
|
14
|
+
Model Input Cached Output
|
|
15
|
+
llama-3.1-405b-reasoning N/A N/A
|
|
16
|
+
llama-3.1-70b-versatile N/A N/A
|
|
17
|
+
llama-3.1-8b-instant N/A N/A
|
|
18
|
+
llama3-groq-70b-8192-tool-use-preview $0.89 / 1M tokens $0.89 / 1M tokens
|
|
19
|
+
llama3-groq-8b-8192-tool-use-preview $0.19 / 1M tokens $0.19 / 1M tokens
|
|
20
|
+
llama3-70b-8192 $0.59 / 1M tokens $0.79 / 1M tokens
|
|
21
|
+
llama3-8b-8192 $0.05 / 1M tokens $0.08 / 1M tokens
|
|
22
|
+
mixtral-8x7b-32768 $0.27 / 1M tokens $0.27 / 1M tokens
|
|
23
|
+
gemma-7b-it $0.07 / 1M tokens $0.07 / 1M tokens
|
|
24
|
+
gemma2-9b-it $0.20 / 1M tokens $0.20 / 1M tokens
|
|
24
25
|
"""
|
|
25
26
|
pricing = {
|
|
26
27
|
"llama3-groq-70b-8192-tool-use-preview": {
|
|
@@ -98,6 +98,12 @@ class GroqCallResponse(
|
|
|
98
98
|
"""Returns the number of input tokens."""
|
|
99
99
|
return self.usage.prompt_tokens if self.usage else None
|
|
100
100
|
|
|
101
|
+
@computed_field
|
|
102
|
+
@property
|
|
103
|
+
def cached_tokens(self) -> int | None:
|
|
104
|
+
"""Returns the number of cached tokens."""
|
|
105
|
+
return 0
|
|
106
|
+
|
|
101
107
|
@computed_field
|
|
102
108
|
@property
|
|
103
109
|
def output_tokens(self) -> int | None:
|
|
@@ -108,7 +114,9 @@ class GroqCallResponse(
|
|
|
108
114
|
@property
|
|
109
115
|
def cost(self) -> float | None:
|
|
110
116
|
"""Returns the cost of the call."""
|
|
111
|
-
return calculate_cost(
|
|
117
|
+
return calculate_cost(
|
|
118
|
+
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
119
|
+
)
|
|
112
120
|
|
|
113
121
|
@computed_field
|
|
114
122
|
@cached_property
|
|
@@ -119,7 +127,6 @@ class GroqCallResponse(
|
|
|
119
127
|
)
|
|
120
128
|
return ChatCompletionAssistantMessageParam(**message_param)
|
|
121
129
|
|
|
122
|
-
@computed_field
|
|
123
130
|
@cached_property
|
|
124
131
|
def tools(self) -> list[GroqTool] | None:
|
|
125
132
|
"""Returns any available tool calls as their `GroqTool` definition.
|
|
@@ -140,7 +147,6 @@ class GroqCallResponse(
|
|
|
140
147
|
|
|
141
148
|
return extracted_tools
|
|
142
149
|
|
|
143
|
-
@computed_field
|
|
144
150
|
@cached_property
|
|
145
151
|
def tool(self) -> GroqTool | None:
|
|
146
152
|
"""Returns the 0th tool for the 0th choice message.
|
|
@@ -81,6 +81,11 @@ class GroqCallResponseChunk(BaseCallResponseChunk[ChatCompletionChunk, FinishRea
|
|
|
81
81
|
return self.usage.prompt_tokens
|
|
82
82
|
return None
|
|
83
83
|
|
|
84
|
+
@property
|
|
85
|
+
def cached_tokens(self) -> int | None:
|
|
86
|
+
"""Returns the number of cached tokens."""
|
|
87
|
+
return 0
|
|
88
|
+
|
|
84
89
|
@property
|
|
85
90
|
def output_tokens(self) -> int | None:
|
|
86
91
|
"""Returns the number of output tokens."""
|
mirascope/core/groq/stream.py
CHANGED
|
@@ -66,7 +66,9 @@ class GroqStream(
|
|
|
66
66
|
@property
|
|
67
67
|
def cost(self) -> float | None:
|
|
68
68
|
"""Returns the cost of the call."""
|
|
69
|
-
return calculate_cost(
|
|
69
|
+
return calculate_cost(
|
|
70
|
+
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
71
|
+
)
|
|
70
72
|
|
|
71
73
|
def _construct_message_param(
|
|
72
74
|
self,
|
|
@@ -16,7 +16,6 @@ LiteLLMMessageParam: TypeAlias = OpenAIMessageParam
|
|
|
16
16
|
|
|
17
17
|
__all__ = [
|
|
18
18
|
"AsyncLiteLLMDynamicConfig",
|
|
19
|
-
"call",
|
|
20
19
|
"LiteLLMCallParams",
|
|
21
20
|
"LiteLLMCallResponse",
|
|
22
21
|
"LiteLLMCallResponseChunk",
|
|
@@ -24,5 +23,6 @@ __all__ = [
|
|
|
24
23
|
"LiteLLMMessageParam",
|
|
25
24
|
"LiteLLMStream",
|
|
26
25
|
"LiteLLMTool",
|
|
26
|
+
"call",
|
|
27
27
|
"litellm_call",
|
|
28
28
|
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""This module contains the setup_call function for OpenAI tools."""
|
|
2
2
|
|
|
3
3
|
from collections.abc import Awaitable, Callable
|
|
4
|
-
from typing import Any, cast, overload
|
|
4
|
+
from typing import Any, TypeAlias, cast, overload
|
|
5
5
|
|
|
6
6
|
from litellm import acompletion, completion
|
|
7
7
|
from openai import OpenAI
|
|
@@ -21,12 +21,16 @@ from ...openai import (
|
|
|
21
21
|
from ...openai._call_kwargs import OpenAICallKwargs
|
|
22
22
|
from ...openai._utils import setup_call as setup_call_openai
|
|
23
23
|
|
|
24
|
+
# Note: MyPy doesn't like `client: ...` so we use these aliases instead.
|
|
25
|
+
_AsyncLiteLLMClient: TypeAlias = Any
|
|
26
|
+
_SyncLiteLLMClient: TypeAlias = Any
|
|
27
|
+
|
|
24
28
|
|
|
25
29
|
@overload
|
|
26
30
|
def setup_call(
|
|
27
31
|
*,
|
|
28
32
|
model: str,
|
|
29
|
-
client:
|
|
33
|
+
client: _AsyncLiteLLMClient | None,
|
|
30
34
|
fn: Callable[..., Awaitable[AsyncOpenAIDynamicConfig]],
|
|
31
35
|
fn_args: dict[str, Any],
|
|
32
36
|
dynamic_config: AsyncOpenAIDynamicConfig,
|
|
@@ -48,7 +52,7 @@ def setup_call(
|
|
|
48
52
|
def setup_call(
|
|
49
53
|
*,
|
|
50
54
|
model: str,
|
|
51
|
-
client:
|
|
55
|
+
client: _SyncLiteLLMClient | None,
|
|
52
56
|
fn: Callable[..., OpenAIDynamicConfig],
|
|
53
57
|
fn_args: dict[str, Any],
|
|
54
58
|
dynamic_config: OpenAIDynamicConfig,
|
|
@@ -24,13 +24,13 @@ MistralMessageParam: TypeAlias = (
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
__all__ = [
|
|
27
|
-
"call",
|
|
28
|
-
"MistralDynamicConfig",
|
|
29
27
|
"MistralCallParams",
|
|
30
28
|
"MistralCallResponse",
|
|
31
29
|
"MistralCallResponseChunk",
|
|
30
|
+
"MistralDynamicConfig",
|
|
32
31
|
"MistralMessageParam",
|
|
33
32
|
"MistralStream",
|
|
34
33
|
"MistralTool",
|
|
34
|
+
"call",
|
|
35
35
|
"mistral_call",
|
|
36
36
|
]
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
def calculate_cost(
|
|
5
5
|
input_tokens: int | float | None,
|
|
6
|
+
cached_tokens: int | float | None,
|
|
6
7
|
output_tokens: int | float | None,
|
|
7
8
|
model: str = "open-mistral-7b",
|
|
8
9
|
) -> float | None:
|
|
@@ -10,15 +11,15 @@ def calculate_cost(
|
|
|
10
11
|
|
|
11
12
|
https://mistral.ai/technology/#pricing
|
|
12
13
|
|
|
13
|
-
Model Input Output
|
|
14
|
-
open-mistral-nemo $0.3/1M tokens
|
|
15
|
-
mistral-large-latest $3/1M tokens
|
|
16
|
-
codestral-2405 $1/1M tokens
|
|
17
|
-
open-mistral-7b $0.25/1M tokens
|
|
18
|
-
open-mixtral-8x7b $0.7/1M tokens
|
|
19
|
-
open-mixtral-8x22b $2/1M tokens
|
|
20
|
-
mistral-small-latest $2/1M tokens
|
|
21
|
-
mistral-medium-latest $2.75/1M tokens
|
|
14
|
+
Model Input Cached Output
|
|
15
|
+
open-mistral-nemo $0.3/1M tokens $0.3/1M tokens
|
|
16
|
+
mistral-large-latest $3/1M tokens $9/1M tokens
|
|
17
|
+
codestral-2405 $1/1M tokens $3/1M tokens
|
|
18
|
+
open-mistral-7b $0.25/1M tokens $0.25/1M tokens
|
|
19
|
+
open-mixtral-8x7b $0.7/1M tokens $0.7/1M tokens
|
|
20
|
+
open-mixtral-8x22b $2/1M tokens $6/1M tokens
|
|
21
|
+
mistral-small-latest $2/1M tokens $6/1M tokens
|
|
22
|
+
mistral-medium-latest $2.75/1M tokens $8.1/1M tokens
|
|
22
23
|
"""
|
|
23
24
|
pricing = {
|
|
24
25
|
"open-mistral-nemo": {"prompt": 0.000_000_3, "completion": 0.000_000_3},
|
|
@@ -107,6 +107,12 @@ class MistralCallResponse(
|
|
|
107
107
|
"""Returns the number of input tokens."""
|
|
108
108
|
return self.usage.prompt_tokens
|
|
109
109
|
|
|
110
|
+
@computed_field
|
|
111
|
+
@property
|
|
112
|
+
def cached_tokens(self) -> int:
|
|
113
|
+
"""Returns the number of cached tokens."""
|
|
114
|
+
return 0
|
|
115
|
+
|
|
110
116
|
@computed_field
|
|
111
117
|
@property
|
|
112
118
|
def output_tokens(self) -> int | None:
|
|
@@ -117,7 +123,9 @@ class MistralCallResponse(
|
|
|
117
123
|
@property
|
|
118
124
|
def cost(self) -> float | None:
|
|
119
125
|
"""Returns the cost of the call."""
|
|
120
|
-
return calculate_cost(
|
|
126
|
+
return calculate_cost(
|
|
127
|
+
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
128
|
+
)
|
|
121
129
|
|
|
122
130
|
@computed_field
|
|
123
131
|
@cached_property
|
|
@@ -125,7 +133,6 @@ class MistralCallResponse(
|
|
|
125
133
|
"""Returns the assistants's response as a message parameter."""
|
|
126
134
|
return self._response_choices[0].message
|
|
127
135
|
|
|
128
|
-
@computed_field
|
|
129
136
|
@cached_property
|
|
130
137
|
def tools(self) -> list[MistralTool] | None:
|
|
131
138
|
"""Returns the tools for the 0th choice message.
|
|
@@ -146,7 +153,6 @@ class MistralCallResponse(
|
|
|
146
153
|
|
|
147
154
|
return extracted_tools
|
|
148
155
|
|
|
149
|
-
@computed_field
|
|
150
156
|
@cached_property
|
|
151
157
|
def tool(self) -> MistralTool | None:
|
|
152
158
|
"""Returns the 0th tool for the 0th choice message.
|
|
@@ -80,6 +80,11 @@ class MistralCallResponseChunk(BaseCallResponseChunk[CompletionChunk, FinishReas
|
|
|
80
80
|
return self.usage.prompt_tokens
|
|
81
81
|
return None
|
|
82
82
|
|
|
83
|
+
@property
|
|
84
|
+
def cached_tokens(self) -> int:
|
|
85
|
+
"""Returns the number of cached tokens."""
|
|
86
|
+
return 0
|
|
87
|
+
|
|
83
88
|
@property
|
|
84
89
|
def output_tokens(self) -> int | None:
|
|
85
90
|
"""Returns the number of output tokens."""
|
mirascope/core/mistral/stream.py
CHANGED
|
@@ -65,7 +65,9 @@ class MistralStream(
|
|
|
65
65
|
@property
|
|
66
66
|
def cost(self) -> float | None:
|
|
67
67
|
"""Returns the cost of the call."""
|
|
68
|
-
return calculate_cost(
|
|
68
|
+
return calculate_cost(
|
|
69
|
+
self.input_tokens, self.cached_tokens, self.output_tokens, self.model
|
|
70
|
+
)
|
|
69
71
|
|
|
70
72
|
def _construct_message_param(
|
|
71
73
|
self, tool_calls: list | None = None, content: str | None = None
|
|
@@ -18,14 +18,14 @@ OpenAIMessageParam: TypeAlias = ChatCompletionMessageParam | BaseMessageParam
|
|
|
18
18
|
|
|
19
19
|
__all__ = [
|
|
20
20
|
"AsyncOpenAIDynamicConfig",
|
|
21
|
-
"call",
|
|
22
|
-
"OpenAIDynamicConfig",
|
|
23
21
|
"OpenAICallParams",
|
|
24
22
|
"OpenAICallResponse",
|
|
25
23
|
"OpenAICallResponseChunk",
|
|
24
|
+
"OpenAIDynamicConfig",
|
|
26
25
|
"OpenAIMessageParam",
|
|
27
26
|
"OpenAIStream",
|
|
28
27
|
"OpenAITool",
|
|
29
28
|
"OpenAIToolConfig",
|
|
29
|
+
"call",
|
|
30
30
|
"openai_call",
|
|
31
31
|
]
|