diff --git a/google/genai/batches.py b/google/genai/batches.py index aa4dd36ee..3d1182eb1 100644 --- a/google/genai/batches.py +++ b/google/genai/batches.py @@ -2164,7 +2164,7 @@ def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2264,7 +2264,7 @@ def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2794,7 +2794,7 @@ async def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2894,7 +2894,7 @@ async def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value diff --git a/google/genai/caches.py b/google/genai/caches.py index 9c441447e..b6045d223 100644 --- a/google/genai/caches.py +++ b/google/genai/caches.py @@ -1419,7 +1419,7 @@ def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -1579,7 +1579,7 @@ def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -1880,7 +1880,7 @@ async def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2042,7 +2042,7 @@ async def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value diff --git a/google/genai/files.py b/google/genai/files.py index 271c066ca..90d91f3bf 100644 --- a/google/genai/files.py +++ b/google/genai/files.py @@ -244,7 +244,7 @@ def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -491,7 +491,7 @@ def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -862,7 +862,7 @@ async def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -1111,7 +1111,7 @@ async def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value diff --git a/google/genai/models.py b/google/genai/models.py index 7a35a8b6c..eb93071f2 100644 --- a/google/genai/models.py +++ b/google/genai/models.py @@ -1109,6 +1109,9 @@ def _EmbedContentResponse_from_mldev( if getv(from_object, ['metadata']) is not None: setv(to_object, ['metadata'], getv(from_object, ['metadata'])) + if getv(from_object, ['usageMetadata']) is not None: + setv(to_object, ['usage_metadata'], getv(from_object, ['usageMetadata'])) + return to_object @@ -1136,6 +1139,9 @@ def _EmbedContentResponse_from_vertex( if getv(from_object, ['metadata']) is not None: setv(to_object, ['metadata'], getv(from_object, ['metadata'])) + if getv(from_object, ['usageMetadata']) is not None: + setv(to_object, ['usage_metadata'], getv(from_object, ['usageMetadata'])) + if getv(root_object, ['embedding_api_type']) == 'EMBED_CONTENT': embedding = getv(from_object, ['embedding']) usage_metadata = getv(from_object, ['usageMetadata']) @@ -5025,7 +5031,7 @@ def _generate_content( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -5126,7 +5132,7 @@ def _generate_content_stream( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) yield return_value @@ -5247,7 +5253,7 @@ def _embed_content( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -5338,7 +5344,7 @@ def _generate_images( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -5422,7 +5428,7 @@ def _edit_image( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -5506,7 +5512,7 @@ def _upscale_image( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -5881,7 +5887,7 @@ def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -6049,7 +6055,7 @@ def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -6159,7 +6165,7 @@ def count_tokens( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -6262,7 +6268,7 @@ def compute_tokens( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -7224,7 +7230,7 @@ async def _generate_content( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -7328,7 +7334,7 @@ async def async_generator(): # type: ignore[no-untyped-def] else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) yield return_value @@ -7451,7 +7457,7 @@ async def _embed_content( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -7542,7 +7548,7 @@ async def _generate_images( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -7626,7 +7632,7 @@ async def _edit_image( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -7710,7 +7716,7 @@ async def _upscale_image( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -8092,7 +8098,7 @@ async def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -8260,7 +8266,7 @@ async def delete( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -8370,7 +8376,7 @@ async def count_tokens( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -8472,7 +8478,7 @@ async def compute_tokens( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value diff --git a/google/genai/tests/types/test_embed_content_response_fields.py b/google/genai/tests/types/test_embed_content_response_fields.py new file mode 100644 index 000000000..b3e616c47 --- /dev/null +++ b/google/genai/tests/types/test_embed_content_response_fields.py @@ -0,0 +1,64 @@ +"""Regression tests for #2658 — EmbedContentResponse should surface usageMetadata +and HttpResponse in sdk_http_response should include body. + +Background: ``EmbedContentResponse`` lacks a ``usage_metadata`` field. Separately, +every ``HttpResponse`` construction across the SDK passes only +``headers=response.headers``, silently dropping ``body=response.body`` — +users inspecting ``response.sdk_http_response.body`` always see ``None``. + +Tests: + +1. ``test_embed_content_response_has_usage_metadata_field`` — the type + declares ``usage_metadata`` (model_fields contains the name). +2. ``test_embed_content_response_dict_has_usage_metadata_field`` — the + corresponding TypedDict also declares the field. +3. ``test_http_response_body_construction_through_wrapper`` — using the + `_common` machinery that wraps sdk_http_response, body should be + preserved when present. +""" + +from __future__ import annotations + + +def test_embed_content_response_has_usage_metadata_field(): + """The EmbedContentResponse Pydantic model must expose usage_metadata + as a field so calling code can access ``response.usage_metadata``. + """ + from google.genai import types + + field_names = types.EmbedContentResponse.model_fields.keys() + assert "usage_metadata" in field_names, ( + "#2658 regression: EmbedContentResponse must declare a " + "'usage_metadata' field so callers can read it. " + f"Declared fields: {sorted(field_names)}" + ) + + +def test_embed_content_response_dict_has_usage_metadata_field(): + """The corresponding TypedDict must declare usage_metadata too.""" + from google.genai import types + + # TypedDict exposes annotations via __annotations__ + annotations = types.EmbedContentResponseDict.__annotations__ + assert "usage_metadata" in annotations, ( + "#2658 regression: EmbedContentResponseDict must declare " + "'usage_metadata' for dict-style callers. " + f"Declared annotations: {sorted(annotations.keys())}" + ) + + +def test_http_response_accepts_body(): + """HttpResponse type must accept body and round-trip it through + Pydantic. (The bug is in call sites, but the type must support + passing body= for the fix to take effect.) + """ + from google.genai import types + + resp = types.HttpResponse( + headers={"content-type": "application/json"}, + body='{"embeddings": []}', + ) + assert resp.body == '{"embeddings": []}', ( + f"#2658 regression: HttpResponse must preserve body. Got: {resp.body!r}" + ) + assert resp.headers == {"content-type": "application/json"} diff --git a/google/genai/tunings.py b/google/genai/tunings.py index 8709df040..4daf8d245 100644 --- a/google/genai/tunings.py +++ b/google/genai/tunings.py @@ -2520,7 +2520,7 @@ def _get( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2592,7 +2592,7 @@ def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2685,7 +2685,7 @@ def cancel( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2778,7 +2778,7 @@ def _tune( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2871,7 +2871,7 @@ def _tune_mldev( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -2988,7 +2988,7 @@ def validate_reward( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -3215,7 +3215,7 @@ async def _get( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -3289,7 +3289,7 @@ async def _list( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -3382,7 +3382,7 @@ async def cancel( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -3475,7 +3475,7 @@ async def _tune( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -3568,7 +3568,7 @@ async def _tune_mldev( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value @@ -3685,7 +3685,7 @@ async def validate_reward( else {}, ) return_value.sdk_http_response = types.HttpResponse( - headers=response.headers + headers=response.headers, body=response.body ) self._api_client._verify_response(return_value) return return_value diff --git a/google/genai/types.py b/google/genai/types.py index e0dcee515..ead833c91 100644 --- a/google/genai/types.py +++ b/google/genai/types.py @@ -8817,6 +8817,13 @@ class EmbedContentResponse(_common.BaseModel): description="""Gemini Enterprise Agent Platform only. Metadata about the request. """, ) + usage_metadata: Optional["UsageMetadata"] = Field( + default=None, + description="""Usage metadata about the embed_content request, including token counts. + Used to surface the wire-format ``usageMetadata`` field that the API + returns but the type previously discarded. Fix for #2658. + """, + ) class EmbedContentResponseDict(TypedDict, total=False): @@ -8834,6 +8841,11 @@ class EmbedContentResponseDict(TypedDict, total=False): """Gemini Enterprise Agent Platform only. Metadata about the request. """ + usage_metadata: Optional["UsageMetadataDict"] + """Usage metadata about the embed_content request. Mirrors the + wire-format ``usageMetadata`` field. Fix for #2658. + """ + EmbedContentResponseOrDict = Union[ EmbedContentResponse, EmbedContentResponseDict