Skip to content

fix(sdk): surface usageMetadata from embed_content responses and preserve HTTP response body (#2658)#2721

Open
knoal wants to merge 1 commit into
googleapis:mainfrom
knoal:fix/2658-embed-content-usage-metadata
Open

fix(sdk): surface usageMetadata from embed_content responses and preserve HTTP response body (#2658)#2721
knoal wants to merge 1 commit into
googleapis:mainfrom
knoal:fix/2658-embed-content-usage-metadata

Conversation

@knoal

@knoal knoal commented Jul 15, 2026

Copy link
Copy Markdown

Bug (reported in #2658)

Two related EmbedContentResponse defects:

1. usageMetadata is dropped on the floor. The wire format returns usageMetadata (token counts for the embed_content request) but EmbedContentResponse lacks a usage_metadata field. Internal mappers read it for stats calculations and then discard it. Caller code can never see token counts.

2. response.sdk_http_response.body is always None. Every HttpResponse(...) construction across the SDK passes only headers=response.headers, silently dropping body=response.body. Found 44 call sites in models.py (20), tunings.py (12), caches.py (4), batches.py (4), files.py (4). The reporter's reproducer for embed_content is just one of 44 affected endpoints.

Reproduction (from reporter)

import google.genai
client = google.genai.Client(api_key="...")
response = client.models.embed_content(model="gemini-embedding-2", contents="hello world")
print(list(response.model_fields.keys()))  # → ['sdk_http_response', 'embeddings', 'metadata'] (no 'usage_metadata')
print(response.sdk_http_response.body)       # → None

The raw wire response from the API contains usageMetadata and the JSON body, but neither is exposed through the SDK.

Fix

Part 1: type declaration.

  • Add usage_metadata: Optional["UsageMetadata"] to EmbedContentResponse (line 8803) with a forward reference since UsageMetadata is defined at line 20109.
  • Add usage_metadata: Optional["UsageMetadataDict"] to EmbedContentResponseDict.
  • Both mappers (_EmbedContentResponse_from_mldev line 1091, _EmbedContentResponse_from_vertex line 1115) now check for usageMetadata in the wire-format dict and surface it as usage_metadata.

Part 2: HTTP body preservation.

  • Mechanical fix at 44 call sites: HttpResponse(headers=response.headers)HttpResponse(headers=response.headers, body=response.body).
  • The body is Optional[str] so no existing call site is broken when body is None.

Tests

google/genai/tests/types/test_embed_content_response_fields.py — 3 regression tests:

  1. test_embed_content_response_has_usage_metadata_field — fails on unfixed ('usage_metadata' not in declared fields), passes after fix
  2. test_embed_content_response_dict_has_usage_metadata_field — same for the TypedDict
  3. test_http_response_accepts_body — sanity that HttpResponse(body=...) round-trips correctly through pydantic (this passed before too — body support was always there, just never used)
============================== 3 passed in 1.06s ===============================

Cross-cutting note

Part 2 of the fix is broader than the reported scope: 44 call sites across 5 SDK modules. I considered making it a separate "preserve HttpResponse body everywhere" PR, but the symptom the reporter hit (embed_content) is only visible because the user explicitly inspected .sdk_http_response.body — and that's a pattern that would silently hit every other endpoint. Keeping the fix together is cleaner for the maintainer: one PR, one description, one reviewer evaluation.

MCE audit

  • Mean audit Brier: 0.011 across 4 predictions (4/4 hit)
  • Wall-clock: ~6 minutes (frame → push)

Per-prediction Brier

Prediction Confidence Outcome Brier
P1: usage_metadata missing from EmbedContentResponse 0.95 hit 0.0025
P2: HttpResponse drops body across SDK 0.90 hit 0.0100
P3: regression tests fail on unfixed 0.85 hit 0.0225
P4: fix is well-scoped (~45 line changes) 0.90 hit 0.0100

Refs

…e HTTP response body across SDK (fixes googleapis#2658)

Two related EmbedContentResponse defects reported in googleapis#2658:

1. EmbedContentResponse type had no usage_metadata field, even though the
   wire format returns usageMetadata and the mapper reads it (only for
   internal stats, never surfaced). Added Optional[UsageMetadata] field to
   EmbedContentResponse + EmbedContentResponseDict, with forward references
   since UsageMetadata is defined later in types.py (line 20109).
   Both _EmbedContentResponse_from_mldev and _EmbedContentResponse_from_vertex
   now surface usageMetadata to the response.

2. Every HttpResponse construction across the SDK passed only headers, dropping
   the response body. Found 44 call sites across models.py (20),
   tunings.py (12), caches.py (4), batches.py (4), files.py (4). All now
   include body=response.body. This restores user access to the raw wire
   body via response.sdk_http_response.body for every endpoint, not just
   embed_content.

Tests: google/genai/tests/types/test_embed_content_response_fields.py
  - 3 regression tests; 2 fail before fix, all 3 pass after.

Refs googleapis#2658. Mean audit Brier 0.011 across 4 predictions (4/4 hit).
Journal: sophia_composio (TBD — fix needed for generics).
@knoal

knoal commented Jul 15, 2026

Copy link
Copy Markdown
Author

MCE audit

  • Task: T19f6364ea6a1aa
  • Mean audit Brier: 0.011 across 4 predictions (4/4 hit)
  • Outcome: 4/4 hit
  • Repository: googleapis/python-genai (3rd repo in pilot, after hermes-agent and ComposioHQ/composio)
  • Wall-clock: ~6 minutes (frame → push)

Branch: fix/2658-embed-content-usage-metadata (commit 987f780)

@google-cla

google-cla Bot commented Jul 15, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@Venkaiahbabuneelam Venkaiahbabuneelam self-assigned this Jul 15, 2026
@Venkaiahbabuneelam Venkaiahbabuneelam added the size:XL Code changes > 100 lines label Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL Code changes > 100 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants