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.
- mirascope/__init__.py +2 -2
- mirascope/api/__init__.py +6 -0
- mirascope/api/_generated/README.md +207 -0
- mirascope/api/_generated/__init__.py +141 -0
- mirascope/api/_generated/client.py +163 -0
- mirascope/api/_generated/core/__init__.py +52 -0
- mirascope/api/_generated/core/api_error.py +23 -0
- mirascope/api/_generated/core/client_wrapper.py +58 -0
- mirascope/api/_generated/core/datetime_utils.py +30 -0
- mirascope/api/_generated/core/file.py +70 -0
- mirascope/api/_generated/core/force_multipart.py +16 -0
- mirascope/api/_generated/core/http_client.py +619 -0
- mirascope/api/_generated/core/http_response.py +55 -0
- mirascope/api/_generated/core/jsonable_encoder.py +102 -0
- mirascope/api/_generated/core/pydantic_utilities.py +310 -0
- mirascope/api/_generated/core/query_encoder.py +60 -0
- mirascope/api/_generated/core/remove_none_from_dict.py +11 -0
- mirascope/api/_generated/core/request_options.py +35 -0
- mirascope/api/_generated/core/serialization.py +282 -0
- mirascope/api/_generated/docs/__init__.py +4 -0
- mirascope/api/_generated/docs/client.py +95 -0
- mirascope/api/_generated/docs/raw_client.py +132 -0
- mirascope/api/_generated/environment.py +9 -0
- mirascope/api/_generated/errors/__init__.py +17 -0
- mirascope/api/_generated/errors/bad_request_error.py +15 -0
- mirascope/api/_generated/errors/conflict_error.py +15 -0
- mirascope/api/_generated/errors/forbidden_error.py +15 -0
- mirascope/api/_generated/errors/internal_server_error.py +15 -0
- mirascope/api/_generated/errors/not_found_error.py +15 -0
- mirascope/api/_generated/health/__init__.py +7 -0
- mirascope/api/_generated/health/client.py +96 -0
- mirascope/api/_generated/health/raw_client.py +129 -0
- mirascope/api/_generated/health/types/__init__.py +8 -0
- mirascope/api/_generated/health/types/health_check_response.py +24 -0
- mirascope/api/_generated/health/types/health_check_response_status.py +5 -0
- mirascope/api/_generated/organizations/__init__.py +25 -0
- mirascope/api/_generated/organizations/client.py +380 -0
- mirascope/api/_generated/organizations/raw_client.py +876 -0
- mirascope/api/_generated/organizations/types/__init__.py +23 -0
- mirascope/api/_generated/organizations/types/organizations_create_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_create_response_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_get_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_get_response_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_update_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_update_response_role.py +7 -0
- mirascope/api/_generated/projects/__init__.py +17 -0
- mirascope/api/_generated/projects/client.py +458 -0
- mirascope/api/_generated/projects/raw_client.py +1016 -0
- mirascope/api/_generated/projects/types/__init__.py +15 -0
- mirascope/api/_generated/projects/types/projects_create_response.py +30 -0
- mirascope/api/_generated/projects/types/projects_get_response.py +30 -0
- mirascope/api/_generated/projects/types/projects_list_response_item.py +30 -0
- mirascope/api/_generated/projects/types/projects_update_response.py +30 -0
- mirascope/api/_generated/reference.md +753 -0
- mirascope/api/_generated/traces/__init__.py +55 -0
- mirascope/api/_generated/traces/client.py +162 -0
- mirascope/api/_generated/traces/raw_client.py +168 -0
- mirascope/api/_generated/traces/types/__init__.py +95 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +36 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +31 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +25 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +54 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +28 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +35 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +35 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +27 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +54 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +28 -0
- 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
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +60 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +29 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +54 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +28 -0
- 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
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_response.py +27 -0
- mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +28 -0
- mirascope/api/_generated/types/__init__.py +37 -0
- mirascope/api/_generated/types/already_exists_error.py +24 -0
- mirascope/api/_generated/types/already_exists_error_tag.py +5 -0
- mirascope/api/_generated/types/database_error.py +24 -0
- mirascope/api/_generated/types/database_error_tag.py +5 -0
- mirascope/api/_generated/types/http_api_decode_error.py +29 -0
- mirascope/api/_generated/types/http_api_decode_error_tag.py +5 -0
- mirascope/api/_generated/types/issue.py +40 -0
- mirascope/api/_generated/types/issue_tag.py +17 -0
- mirascope/api/_generated/types/not_found_error_body.py +24 -0
- mirascope/api/_generated/types/not_found_error_tag.py +5 -0
- mirascope/api/_generated/types/permission_denied_error.py +24 -0
- mirascope/api/_generated/types/permission_denied_error_tag.py +7 -0
- mirascope/api/_generated/types/property_key.py +7 -0
- mirascope/api/_generated/types/property_key_key.py +27 -0
- mirascope/api/_generated/types/property_key_key_tag.py +5 -0
- mirascope/api/client.py +255 -0
- mirascope/api/settings.py +81 -0
- mirascope/llm/__init__.py +45 -11
- mirascope/llm/calls/calls.py +81 -57
- mirascope/llm/calls/decorator.py +121 -115
- mirascope/llm/content/__init__.py +3 -2
- mirascope/llm/context/_utils.py +19 -6
- mirascope/llm/exceptions.py +30 -16
- mirascope/llm/formatting/_utils.py +9 -5
- mirascope/llm/formatting/format.py +2 -2
- mirascope/llm/formatting/from_call_args.py +2 -2
- mirascope/llm/messages/message.py +13 -5
- mirascope/llm/models/__init__.py +2 -2
- mirascope/llm/models/models.py +189 -81
- mirascope/llm/prompts/__init__.py +13 -12
- mirascope/llm/prompts/_utils.py +27 -24
- mirascope/llm/prompts/decorator.py +133 -204
- mirascope/llm/prompts/prompts.py +424 -0
- mirascope/llm/prompts/protocols.py +25 -59
- mirascope/llm/providers/__init__.py +44 -0
- mirascope/llm/{clients → providers}/_missing_import_stubs.py +8 -6
- mirascope/llm/providers/anthropic/__init__.py +29 -0
- mirascope/llm/providers/anthropic/_utils/__init__.py +23 -0
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +271 -0
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +216 -0
- mirascope/llm/{clients → providers}/anthropic/_utils/decode.py +44 -11
- mirascope/llm/providers/anthropic/_utils/encode.py +356 -0
- mirascope/llm/providers/anthropic/beta_provider.py +322 -0
- mirascope/llm/providers/anthropic/model_id.py +23 -0
- mirascope/llm/providers/anthropic/model_info.py +87 -0
- mirascope/llm/providers/anthropic/provider.py +416 -0
- mirascope/llm/{clients → providers}/base/__init__.py +3 -3
- mirascope/llm/{clients → providers}/base/_utils.py +25 -8
- mirascope/llm/{clients/base/client.py → providers/base/base_provider.py} +255 -126
- mirascope/llm/providers/google/__init__.py +21 -0
- mirascope/llm/{clients → providers}/google/_utils/decode.py +61 -7
- mirascope/llm/{clients → providers}/google/_utils/encode.py +44 -30
- mirascope/llm/providers/google/model_id.py +22 -0
- mirascope/llm/providers/google/model_info.py +62 -0
- mirascope/llm/providers/google/provider.py +442 -0
- mirascope/llm/providers/load_provider.py +54 -0
- mirascope/llm/providers/mlx/__init__.py +24 -0
- mirascope/llm/providers/mlx/_utils.py +129 -0
- mirascope/llm/providers/mlx/encoding/__init__.py +8 -0
- mirascope/llm/providers/mlx/encoding/base.py +69 -0
- mirascope/llm/providers/mlx/encoding/transformers.py +147 -0
- mirascope/llm/providers/mlx/mlx.py +237 -0
- mirascope/llm/providers/mlx/model_id.py +17 -0
- mirascope/llm/providers/mlx/provider.py +415 -0
- mirascope/llm/providers/model_id.py +16 -0
- mirascope/llm/providers/ollama/__init__.py +19 -0
- mirascope/llm/providers/ollama/provider.py +71 -0
- mirascope/llm/providers/openai/__init__.py +6 -0
- mirascope/llm/providers/openai/completions/__init__.py +25 -0
- mirascope/llm/{clients → providers}/openai/completions/_utils/__init__.py +2 -0
- mirascope/llm/{clients → providers}/openai/completions/_utils/decode.py +60 -6
- mirascope/llm/{clients → providers}/openai/completions/_utils/encode.py +37 -26
- mirascope/llm/providers/openai/completions/base_provider.py +513 -0
- mirascope/llm/providers/openai/completions/provider.py +22 -0
- mirascope/llm/providers/openai/model_id.py +31 -0
- mirascope/llm/providers/openai/model_info.py +303 -0
- mirascope/llm/providers/openai/provider.py +398 -0
- mirascope/llm/providers/openai/responses/__init__.py +21 -0
- mirascope/llm/{clients → providers}/openai/responses/_utils/decode.py +59 -6
- mirascope/llm/{clients → providers}/openai/responses/_utils/encode.py +34 -23
- mirascope/llm/providers/openai/responses/provider.py +469 -0
- mirascope/llm/providers/provider_id.py +23 -0
- mirascope/llm/providers/provider_registry.py +169 -0
- mirascope/llm/providers/together/__init__.py +19 -0
- mirascope/llm/providers/together/provider.py +40 -0
- mirascope/llm/responses/__init__.py +3 -0
- mirascope/llm/responses/base_response.py +14 -5
- mirascope/llm/responses/base_stream_response.py +35 -6
- mirascope/llm/responses/finish_reason.py +1 -0
- mirascope/llm/responses/response.py +33 -13
- mirascope/llm/responses/root_response.py +12 -13
- mirascope/llm/responses/stream_response.py +35 -23
- mirascope/llm/responses/usage.py +95 -0
- mirascope/llm/tools/__init__.py +9 -2
- mirascope/llm/tools/_utils.py +12 -3
- mirascope/llm/tools/protocols.py +4 -4
- mirascope/llm/tools/tool_schema.py +44 -9
- mirascope/llm/tools/tools.py +10 -9
- mirascope/ops/__init__.py +156 -0
- mirascope/ops/_internal/__init__.py +5 -0
- mirascope/ops/_internal/closure.py +1118 -0
- mirascope/ops/_internal/configuration.py +126 -0
- mirascope/ops/_internal/context.py +76 -0
- mirascope/ops/_internal/exporters/__init__.py +26 -0
- mirascope/ops/_internal/exporters/exporters.py +342 -0
- mirascope/ops/_internal/exporters/processors.py +104 -0
- mirascope/ops/_internal/exporters/types.py +165 -0
- mirascope/ops/_internal/exporters/utils.py +29 -0
- mirascope/ops/_internal/instrumentation/__init__.py +8 -0
- mirascope/ops/_internal/instrumentation/llm/__init__.py +8 -0
- mirascope/ops/_internal/instrumentation/llm/encode.py +238 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/__init__.py +38 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_input_messages.py +31 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_output_messages.py +38 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_system_instructions.py +18 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/shared.py +100 -0
- mirascope/ops/_internal/instrumentation/llm/llm.py +1288 -0
- mirascope/ops/_internal/propagation.py +198 -0
- mirascope/ops/_internal/protocols.py +51 -0
- mirascope/ops/_internal/session.py +139 -0
- mirascope/ops/_internal/spans.py +232 -0
- mirascope/ops/_internal/traced_calls.py +371 -0
- mirascope/ops/_internal/traced_functions.py +394 -0
- mirascope/ops/_internal/tracing.py +276 -0
- mirascope/ops/_internal/types.py +13 -0
- mirascope/ops/_internal/utils.py +75 -0
- mirascope/ops/_internal/versioned_calls.py +512 -0
- mirascope/ops/_internal/versioned_functions.py +346 -0
- mirascope/ops/_internal/versioning.py +303 -0
- mirascope/ops/exceptions.py +21 -0
- {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/METADATA +78 -3
- mirascope-2.0.0a4.dist-info/RECORD +247 -0
- {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/WHEEL +1 -1
- mirascope/graphs/__init__.py +0 -22
- mirascope/graphs/finite_state_machine.py +0 -625
- mirascope/llm/agents/__init__.py +0 -15
- mirascope/llm/agents/agent.py +0 -97
- mirascope/llm/agents/agent_template.py +0 -45
- mirascope/llm/agents/decorator.py +0 -176
- mirascope/llm/calls/base_call.py +0 -33
- mirascope/llm/clients/__init__.py +0 -34
- mirascope/llm/clients/anthropic/__init__.py +0 -25
- mirascope/llm/clients/anthropic/_utils/encode.py +0 -243
- mirascope/llm/clients/anthropic/clients.py +0 -819
- mirascope/llm/clients/anthropic/model_ids.py +0 -8
- mirascope/llm/clients/google/__init__.py +0 -20
- mirascope/llm/clients/google/clients.py +0 -853
- mirascope/llm/clients/google/model_ids.py +0 -15
- mirascope/llm/clients/openai/__init__.py +0 -25
- mirascope/llm/clients/openai/completions/__init__.py +0 -28
- mirascope/llm/clients/openai/completions/_utils/model_features.py +0 -81
- mirascope/llm/clients/openai/completions/clients.py +0 -833
- mirascope/llm/clients/openai/completions/model_ids.py +0 -8
- mirascope/llm/clients/openai/responses/__init__.py +0 -26
- mirascope/llm/clients/openai/responses/_utils/__init__.py +0 -13
- mirascope/llm/clients/openai/responses/_utils/model_features.py +0 -87
- mirascope/llm/clients/openai/responses/clients.py +0 -832
- mirascope/llm/clients/openai/responses/model_ids.py +0 -8
- mirascope/llm/clients/openai/shared/__init__.py +0 -7
- mirascope/llm/clients/openai/shared/_utils.py +0 -55
- mirascope/llm/clients/providers.py +0 -175
- mirascope-2.0.0a2.dist-info/RECORD +0 -102
- /mirascope/llm/{clients → providers}/base/kwargs.py +0 -0
- /mirascope/llm/{clients → providers}/base/params.py +0 -0
- /mirascope/llm/{clients/anthropic → providers/google}/_utils/__init__.py +0 -0
- /mirascope/llm/{clients → providers}/google/message.py +0 -0
- /mirascope/llm/{clients/google → providers/openai/responses}/_utils/__init__.py +0 -0
- {mirascope-2.0.0a2.dist-info → mirascope-2.0.0a4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Google client implementation."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .model_id import GoogleModelId
|
|
7
|
+
from .provider import GoogleProvider
|
|
8
|
+
else:
|
|
9
|
+
try:
|
|
10
|
+
from .model_id import GoogleModelId
|
|
11
|
+
from .provider import GoogleProvider
|
|
12
|
+
except ImportError: # pragma: no cover
|
|
13
|
+
from .._missing_import_stubs import (
|
|
14
|
+
create_import_error_stub,
|
|
15
|
+
create_provider_stub,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
GoogleProvider = create_provider_stub("google", "GoogleProvider")
|
|
19
|
+
GoogleModelId = str
|
|
20
|
+
|
|
21
|
+
__all__ = ["GoogleModelId", "GoogleProvider"]
|
|
@@ -29,8 +29,10 @@ from ....responses import (
|
|
|
29
29
|
FinishReasonChunk,
|
|
30
30
|
RawMessageChunk,
|
|
31
31
|
RawStreamEventChunk,
|
|
32
|
+
Usage,
|
|
33
|
+
UsageDeltaChunk,
|
|
32
34
|
)
|
|
33
|
-
from ..
|
|
35
|
+
from ..model_id import GoogleModelId, model_name
|
|
34
36
|
from .encode import UNKNOWN_TOOL_ID
|
|
35
37
|
|
|
36
38
|
GOOGLE_FINISH_REASON_MAP = {
|
|
@@ -43,6 +45,30 @@ GOOGLE_FINISH_REASON_MAP = {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
def _decode_usage(
|
|
49
|
+
usage: genai_types.GenerateContentResponseUsageMetadata | None,
|
|
50
|
+
) -> Usage | None:
|
|
51
|
+
"""Convert Google UsageMetadata to Mirascope Usage."""
|
|
52
|
+
if (
|
|
53
|
+
usage is None
|
|
54
|
+
or usage.prompt_token_count is None
|
|
55
|
+
or usage.candidates_token_count is None
|
|
56
|
+
): # pragma: no cover
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
reasoning_tokens = usage.thoughts_token_count or 0
|
|
60
|
+
output_tokens = usage.candidates_token_count + reasoning_tokens
|
|
61
|
+
|
|
62
|
+
return Usage(
|
|
63
|
+
input_tokens=usage.prompt_token_count,
|
|
64
|
+
output_tokens=output_tokens,
|
|
65
|
+
cache_read_tokens=usage.cached_content_token_count or 0,
|
|
66
|
+
cache_write_tokens=0,
|
|
67
|
+
reasoning_tokens=usage.thoughts_token_count or 0,
|
|
68
|
+
raw=usage,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
46
72
|
def _decode_content_part(part: genai_types.Part) -> AssistantContentPart | None:
|
|
47
73
|
"""Returns an `AssistantContentPart` (or `None`) decoded from a google `Part`"""
|
|
48
74
|
if part.thought and part.text:
|
|
@@ -88,7 +114,7 @@ def _decode_candidate_content(
|
|
|
88
114
|
candidate: genai_types.Candidate,
|
|
89
115
|
) -> Sequence[AssistantContentPart]:
|
|
90
116
|
"""Returns a sequence of `AssistantContentPart` decoded from a google `Candidate`"""
|
|
91
|
-
content_parts = []
|
|
117
|
+
content_parts: list[AssistantContentPart] = []
|
|
92
118
|
if candidate.content and candidate.content.parts:
|
|
93
119
|
for part in candidate.content.parts:
|
|
94
120
|
decoded_part = _decode_content_part(part)
|
|
@@ -98,9 +124,10 @@ def _decode_candidate_content(
|
|
|
98
124
|
|
|
99
125
|
|
|
100
126
|
def decode_response(
|
|
101
|
-
response: genai_types.GenerateContentResponse,
|
|
102
|
-
|
|
103
|
-
|
|
127
|
+
response: genai_types.GenerateContentResponse,
|
|
128
|
+
model_id: GoogleModelId,
|
|
129
|
+
) -> tuple[AssistantMessage, FinishReason | None, Usage | None]:
|
|
130
|
+
"""Returns an `AssistantMessage`, `FinishReason`, and `Usage` extracted from a `GenerateContentResponse`"""
|
|
104
131
|
content: Sequence[AssistantContentPart] = []
|
|
105
132
|
candidate_content: genai_types.Content | None = None
|
|
106
133
|
finish_reason: FinishReason | None = None
|
|
@@ -115,12 +142,14 @@ def decode_response(
|
|
|
115
142
|
|
|
116
143
|
assistant_message = AssistantMessage(
|
|
117
144
|
content=content,
|
|
118
|
-
|
|
145
|
+
provider_id="google",
|
|
119
146
|
model_id=model_id,
|
|
147
|
+
provider_model_name=model_name(model_id),
|
|
120
148
|
raw_message=candidate_content.model_dump(),
|
|
121
149
|
)
|
|
122
150
|
|
|
123
|
-
|
|
151
|
+
usage = _decode_usage(response.usage_metadata)
|
|
152
|
+
return assistant_message, finish_reason, usage
|
|
124
153
|
|
|
125
154
|
|
|
126
155
|
class _GoogleChunkProcessor:
|
|
@@ -130,6 +159,8 @@ class _GoogleChunkProcessor:
|
|
|
130
159
|
self.current_content_type: Literal["text", "tool_call", "thought"] | None = None
|
|
131
160
|
self.accumulated_parts: list[genai_types.Part] = []
|
|
132
161
|
self.reconstructed_content = genai_types.Content(parts=[])
|
|
162
|
+
# Track previous cumulative usage to compute deltas
|
|
163
|
+
self.prev_usage = Usage()
|
|
133
164
|
|
|
134
165
|
def process_chunk(
|
|
135
166
|
self, chunk: genai_types.GenerateContentResponse
|
|
@@ -205,6 +236,29 @@ class _GoogleChunkProcessor:
|
|
|
205
236
|
if finish_reason is not None:
|
|
206
237
|
yield FinishReasonChunk(finish_reason=finish_reason)
|
|
207
238
|
|
|
239
|
+
# Emit usage delta if usage metadata is present
|
|
240
|
+
if chunk.usage_metadata:
|
|
241
|
+
usage_metadata = chunk.usage_metadata
|
|
242
|
+
current_input = usage_metadata.prompt_token_count or 0
|
|
243
|
+
current_output = usage_metadata.candidates_token_count or 0
|
|
244
|
+
current_cache_read = usage_metadata.cached_content_token_count or 0
|
|
245
|
+
current_reasoning = usage_metadata.thoughts_token_count or 0
|
|
246
|
+
|
|
247
|
+
yield UsageDeltaChunk(
|
|
248
|
+
input_tokens=current_input - self.prev_usage.input_tokens,
|
|
249
|
+
output_tokens=current_output - self.prev_usage.output_tokens,
|
|
250
|
+
cache_read_tokens=current_cache_read
|
|
251
|
+
- self.prev_usage.cache_read_tokens,
|
|
252
|
+
cache_write_tokens=0,
|
|
253
|
+
reasoning_tokens=current_reasoning - self.prev_usage.reasoning_tokens,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Update previous usage
|
|
257
|
+
self.prev_usage.input_tokens = current_input
|
|
258
|
+
self.prev_usage.output_tokens = current_output
|
|
259
|
+
self.prev_usage.cache_read_tokens = current_cache_read
|
|
260
|
+
self.prev_usage.reasoning_tokens = current_reasoning
|
|
261
|
+
|
|
208
262
|
def raw_message_chunk(self) -> RawMessageChunk:
|
|
209
263
|
content = genai_types.Content(role="model", parts=self.accumulated_parts)
|
|
210
264
|
return RawMessageChunk(raw_message=content.model_dump())
|
|
@@ -4,7 +4,8 @@ import base64
|
|
|
4
4
|
import json
|
|
5
5
|
from collections.abc import Sequence
|
|
6
6
|
from functools import lru_cache
|
|
7
|
-
from typing import Any, cast
|
|
7
|
+
from typing import Any, TypedDict, cast
|
|
8
|
+
from typing_extensions import Required
|
|
8
9
|
|
|
9
10
|
from google.genai import types as genai_types
|
|
10
11
|
|
|
@@ -17,15 +18,20 @@ from ....formatting import (
|
|
|
17
18
|
resolve_format,
|
|
18
19
|
)
|
|
19
20
|
from ....messages import AssistantMessage, Message, UserMessage
|
|
20
|
-
from ....tools import FORMAT_TOOL_NAME,
|
|
21
|
-
from ...base import
|
|
22
|
-
from ..
|
|
21
|
+
from ....tools import FORMAT_TOOL_NAME, AnyToolSchema, BaseToolkit
|
|
22
|
+
from ...base import Params, _utils as _base_utils
|
|
23
|
+
from ..model_id import GoogleModelId, model_name
|
|
24
|
+
from ..model_info import MODELS_WITHOUT_STRUCTURED_OUTPUT_AND_TOOLS_SUPPORT
|
|
23
25
|
|
|
24
26
|
UNKNOWN_TOOL_ID = "google_unknown_tool_id"
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
class GoogleKwargs(
|
|
28
|
-
"""Google's
|
|
29
|
+
class GoogleKwargs(TypedDict, total=False):
|
|
30
|
+
"""Kwargs for Google's generate_content method."""
|
|
31
|
+
|
|
32
|
+
model: Required[str]
|
|
33
|
+
contents: Required[genai_types.ContentListUnionDict]
|
|
34
|
+
config: genai_types.GenerateContentConfigDict
|
|
29
35
|
|
|
30
36
|
|
|
31
37
|
def _resolve_refs(
|
|
@@ -52,7 +58,7 @@ def _encode_content(
|
|
|
52
58
|
content: Sequence[ContentPart], encode_thoughts: bool
|
|
53
59
|
) -> list[genai_types.PartDict]:
|
|
54
60
|
"""Returns a list of google `PartDicts` converted from a sequence of Mirascope `ContentPart`s"""
|
|
55
|
-
result = []
|
|
61
|
+
result: list[genai_types.PartDict] = []
|
|
56
62
|
|
|
57
63
|
for part in content:
|
|
58
64
|
if part.type == "text":
|
|
@@ -121,7 +127,7 @@ def _encode_message(
|
|
|
121
127
|
"""Returns a Google `ContentDict` converted from a Mirascope `Message`"""
|
|
122
128
|
if (
|
|
123
129
|
message.role == "assistant"
|
|
124
|
-
and message.
|
|
130
|
+
and message.provider_id == "google"
|
|
125
131
|
and message.model_id == model_id
|
|
126
132
|
and message.raw_message
|
|
127
133
|
and not encode_thoughts
|
|
@@ -144,7 +150,7 @@ def _encode_messages(
|
|
|
144
150
|
|
|
145
151
|
@lru_cache(maxsize=128)
|
|
146
152
|
def _convert_tool_to_function_declaration(
|
|
147
|
-
tool:
|
|
153
|
+
tool: AnyToolSchema,
|
|
148
154
|
) -> genai_types.FunctionDeclarationDict:
|
|
149
155
|
"""Convert a single Mirascope tool to Google FunctionDeclaration format with caching."""
|
|
150
156
|
schema_dict = tool.parameters.model_dump(by_alias=True, exclude_none=True)
|
|
@@ -170,21 +176,22 @@ def encode_request(
|
|
|
170
176
|
*,
|
|
171
177
|
model_id: GoogleModelId,
|
|
172
178
|
messages: Sequence[Message],
|
|
173
|
-
tools: Sequence[
|
|
179
|
+
tools: Sequence[AnyToolSchema] | BaseToolkit[AnyToolSchema] | None,
|
|
174
180
|
format: type[FormattableT] | Format[FormattableT] | None,
|
|
175
181
|
params: Params,
|
|
176
|
-
) -> tuple[
|
|
177
|
-
Sequence[Message],
|
|
178
|
-
Format[FormattableT] | None,
|
|
179
|
-
genai_types.ContentListUnionDict,
|
|
180
|
-
GoogleKwargs,
|
|
181
|
-
]:
|
|
182
|
+
) -> tuple[Sequence[Message], Format[FormattableT] | None, GoogleKwargs]:
|
|
182
183
|
"""Prepares a request for the genai `Client.models.generate_content` method."""
|
|
183
|
-
|
|
184
|
+
if not model_id.startswith("google/"): # pragma: no cover
|
|
185
|
+
raise ValueError(f"Model ID must start with 'google/' prefix, got: {model_id}")
|
|
186
|
+
|
|
187
|
+
google_config: genai_types.GenerateContentConfigDict = (
|
|
188
|
+
genai_types.GenerateContentConfigDict()
|
|
189
|
+
)
|
|
184
190
|
encode_thoughts = False
|
|
191
|
+
google_model_name = model_name(model_id)
|
|
185
192
|
|
|
186
193
|
with _base_utils.ensure_all_params_accessed(
|
|
187
|
-
params=params,
|
|
194
|
+
params=params, provider_id="google"
|
|
188
195
|
) as param_accessor:
|
|
189
196
|
if param_accessor.temperature is not None:
|
|
190
197
|
google_config["temperature"] = param_accessor.temperature
|
|
@@ -214,17 +221,23 @@ def encode_request(
|
|
|
214
221
|
tools = tools.tools if isinstance(tools, BaseToolkit) else tools or []
|
|
215
222
|
google_tools: list[genai_types.ToolDict] = []
|
|
216
223
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
# Google does not support strict outputs when tools are present
|
|
220
|
-
# (Gemini 2.5 will error, 2.0 and below will ignore tools)
|
|
221
|
-
default_mode="strict" if not tools else "tool",
|
|
224
|
+
allows_strict_mode_with_tools = (
|
|
225
|
+
google_model_name not in MODELS_WITHOUT_STRUCTURED_OUTPUT_AND_TOOLS_SUPPORT
|
|
222
226
|
)
|
|
227
|
+
# Older google models do not allow strict mode when using tools; if so, we use tool
|
|
228
|
+
# mode when tools are present by default for compatibility. Otherwise, prefer strict mode.
|
|
229
|
+
default_mode = "tool" if tools and not allows_strict_mode_with_tools else "strict"
|
|
230
|
+
format = resolve_format(format, default_mode=default_mode)
|
|
223
231
|
if format is not None:
|
|
224
|
-
if
|
|
232
|
+
if (
|
|
233
|
+
format.mode in ("strict", "json")
|
|
234
|
+
and tools
|
|
235
|
+
and not allows_strict_mode_with_tools
|
|
236
|
+
):
|
|
225
237
|
raise FeatureNotSupportedError(
|
|
226
238
|
feature=f"formatting_mode:{format.mode} with tools",
|
|
227
|
-
|
|
239
|
+
provider_id="google",
|
|
240
|
+
model_id=model_id,
|
|
228
241
|
)
|
|
229
242
|
|
|
230
243
|
if format.mode == "strict":
|
|
@@ -271,9 +284,10 @@ def encode_request(
|
|
|
271
284
|
if system_message_content:
|
|
272
285
|
google_config["system_instruction"] = system_message_content
|
|
273
286
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
google_config,
|
|
287
|
+
kwargs = GoogleKwargs(
|
|
288
|
+
model=model_name(model_id),
|
|
289
|
+
contents=_encode_messages(remaining_messages, model_id, encode_thoughts),
|
|
290
|
+
config=google_config,
|
|
279
291
|
)
|
|
292
|
+
|
|
293
|
+
return messages, format, kwargs
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Google registered LLM models."""
|
|
2
|
+
|
|
3
|
+
from typing import TypeAlias, get_args
|
|
4
|
+
|
|
5
|
+
from .model_info import GoogleKnownModels
|
|
6
|
+
|
|
7
|
+
GoogleModelId: TypeAlias = GoogleKnownModels | str
|
|
8
|
+
"""The Google model ids registered with Mirascope."""
|
|
9
|
+
|
|
10
|
+
GOOGLE_KNOWN_MODELS: set[str] = set(get_args(GoogleKnownModels))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def model_name(model_id: GoogleModelId) -> str:
|
|
14
|
+
"""Extract the google model name from a full model ID.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
model_id: Full model ID (e.g. "google/gemini-2.5-flash")
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Provider-specific model ID (e.g. "gemini-2.5-flash")
|
|
21
|
+
"""
|
|
22
|
+
return model_id.removeprefix("google/")
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Google model information.
|
|
2
|
+
|
|
3
|
+
This file is auto-generated by scripts/model_features/codegen_google.py
|
|
4
|
+
Do not edit manually - run the codegen script to update."""
|
|
5
|
+
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
GoogleKnownModels = Literal[
|
|
9
|
+
"google/gemini-2.0-flash",
|
|
10
|
+
"google/gemini-2.0-flash-001",
|
|
11
|
+
"google/gemini-2.0-flash-exp",
|
|
12
|
+
"google/gemini-2.0-flash-exp-image-generation",
|
|
13
|
+
"google/gemini-2.0-flash-lite",
|
|
14
|
+
"google/gemini-2.0-flash-lite-001",
|
|
15
|
+
"google/gemini-2.0-flash-lite-preview",
|
|
16
|
+
"google/gemini-2.0-flash-lite-preview-02-05",
|
|
17
|
+
"google/gemini-2.5-flash",
|
|
18
|
+
"google/gemini-2.5-flash-image",
|
|
19
|
+
"google/gemini-2.5-flash-image-preview",
|
|
20
|
+
"google/gemini-2.5-flash-lite",
|
|
21
|
+
"google/gemini-2.5-flash-lite-preview-09-2025",
|
|
22
|
+
"google/gemini-2.5-flash-preview-09-2025",
|
|
23
|
+
"google/gemini-2.5-pro",
|
|
24
|
+
"google/gemini-3-pro-image-preview",
|
|
25
|
+
"google/gemini-3-pro-preview",
|
|
26
|
+
"google/gemini-flash-latest",
|
|
27
|
+
"google/gemini-flash-lite-latest",
|
|
28
|
+
"google/gemini-pro-latest",
|
|
29
|
+
"google/gemini-robotics-er-1.5-preview",
|
|
30
|
+
"google/gemma-3-12b-it",
|
|
31
|
+
"google/gemma-3-1b-it",
|
|
32
|
+
"google/gemma-3-27b-it",
|
|
33
|
+
"google/gemma-3-4b-it",
|
|
34
|
+
"google/gemma-3n-e2b-it",
|
|
35
|
+
"google/gemma-3n-e4b-it",
|
|
36
|
+
"google/nano-banana-pro-preview",
|
|
37
|
+
]
|
|
38
|
+
"""Valid Google model IDs."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
MODELS_WITHOUT_STRUCTURED_OUTPUT_AND_TOOLS_SUPPORT: set[str] = {
|
|
42
|
+
"gemini-2.5-flash",
|
|
43
|
+
"gemini-2.5-flash-image",
|
|
44
|
+
"gemini-2.5-flash-image-preview",
|
|
45
|
+
"gemini-2.5-flash-lite",
|
|
46
|
+
"gemini-2.5-flash-lite-preview-09-2025",
|
|
47
|
+
"gemini-2.5-flash-preview-09-2025",
|
|
48
|
+
"gemini-2.5-pro",
|
|
49
|
+
"gemini-3-pro-image-preview",
|
|
50
|
+
"gemini-flash-latest",
|
|
51
|
+
"gemini-flash-lite-latest",
|
|
52
|
+
"gemini-pro-latest",
|
|
53
|
+
"gemini-robotics-er-1.5-preview",
|
|
54
|
+
"gemma-3-12b-it",
|
|
55
|
+
"gemma-3-1b-it",
|
|
56
|
+
"gemma-3-27b-it",
|
|
57
|
+
"gemma-3-4b-it",
|
|
58
|
+
"gemma-3n-e2b-it",
|
|
59
|
+
"gemma-3n-e4b-it",
|
|
60
|
+
"nano-banana-pro-preview",
|
|
61
|
+
}
|
|
62
|
+
"""Models that do not support structured outputs when tools are present."""
|