feat(google-genai): capture Interactions request configuration - #752
Conversation
Record generation configuration and output-format attributes for sync, async, and streaming Interactions calls, including newer request.body inputs. Preserve SDK stream normalization, zero values, and lazy request content. Closes open-telemetry#563 Assisted-by: GPT-6 Astra Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pull request dashboard statusMerged · refreshed 2026-09-22 05:40 UTC Status above doesn't look right?
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The request-body extraction currently uses __dict__, which can include defaulted/unset model fields and may record configuration that was not explicitly supplied.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
What changed in this PR
This PR extends the Google GenAI Interactions instrumentation to capture explicitly supplied request configuration (generation parameters and output format) as gen_ai.request.* and gen_ai.output.type attributes across sync/async/streaming and error paths, and adds focused tests and documentation for the new telemetry.
Changes:
- Extract request configuration from both legacy kwargs and newer
request.bodyshapes, and apply it onto the util-genai invocation before completion/stream finalization. - Derive
gen_ai.output.typefromresponse_format/response_mime_typewhen representable by a single semconv output type. - Add a comprehensive Interactions request-parameter test suite plus README documentation for the new behavior.
| File | Description |
|---|---|
| instrumentation/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/interactions.py | Adds request-shape normalization + attribute extraction for generation config and output type; adjusts stream detection to work with request-body models. |
| instrumentation/opentelemetry-instrumentation-google-genai/tests/interactions/test_request_parameters.py | New tests covering sync/async/streaming/error paths, request-body vs kwargs shapes, and output-type mapping. |
| instrumentation/opentelemetry-instrumentation-google-genai/README.rst | Documents which Interactions parameters are captured and when output type is set/omitted. |
| instrumentation/opentelemetry-instrumentation-google-genai/.changelog/0.added | Adds a changelog fragment describing the new captured attributes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Replace the placeholder fragment number with the assigned upstream PR number required by the changelog workflow. Assisted-by: GPT-6 Astra Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use Pydantic field metadata to capture only explicitly supplied request and generation-config fields, including explicit extras. Avoid model serialization so lazy content and SDK argument objects remain untouched. Assisted-by: GPT-6 Astra Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| If both variables are set, the includes list is applied first, then the | ||
| excludes list filters the result further. | ||
|
|
||
| Interactions request parameters |
There was a problem hiding this comment.
Can we de-AI this section a bit? I think it can be either removed (we don't list the details for each operation) or collapsed to 1-2 sentences.
There was a problem hiding this comment.
Makes sense, removed this section to clean this up
| if format_type in ( | ||
| "object", | ||
| "array", | ||
| "string", | ||
| "number", | ||
| "integer", | ||
| "boolean", | ||
| "null", | ||
| ): | ||
| return output_types.JSON.value |
There was a problem hiding this comment.
| if format_type in ( | |
| "object", | |
| "array", | |
| "string", | |
| "number", | |
| "integer", | |
| "boolean", | |
| "null", | |
| ): | |
| return output_types.JSON.value | |
| if format_type in ( | |
| "object", | |
| "array", | |
| "string", | |
| "number", | |
| "integer", | |
| "boolean", | |
| "null", | |
| "json", | |
| "json_object", | |
| "json_schema", | |
| ): | |
| return output_types.JSON.value |
Might be worth including common JSON format names too.
There was a problem hiding this comment.
Added json, json_object, and json_schema here and updated the test request params
| for name in ("temperature", "top_p"): | ||
| value = _get_field(config, name) | ||
| if ( | ||
| isinstance(value, (int, float)) | ||
| and not isinstance(value, bool) | ||
| and -float_info.max <= value <= float_info.max | ||
| ): | ||
| setattr(invocation, name, float(value)) |
There was a problem hiding this comment.
| for name in ("temperature", "top_p"): | |
| value = _get_field(config, name) | |
| if ( | |
| isinstance(value, (int, float)) | |
| and not isinstance(value, bool) | |
| and -float_info.max <= value <= float_info.max | |
| ): | |
| setattr(invocation, name, float(value)) | |
| for name in ("temperature", "top_p"): | |
| value = _get_field(config, name) | |
| if ( | |
| isinstance(value, (int, float)) | |
| and not isinstance(value, bool) | |
| and math.isfinite(value) | |
| and value >= 0 | |
| ): | |
| setattr(invocation, name, float(value)) |
math.isfinite could be simpler than checking against float_info.
There was a problem hiding this comment.
Updated to use math.isfinite
| if isinstance(stop_sequences, (list, tuple)) and all( | ||
| isinstance(item, str) for item in stop_sequences | ||
| ): | ||
| invocation.stop_sequences = list(stop_sequences) |
There was a problem hiding this comment.
| if isinstance(stop_sequences, (list, tuple)) and all( | |
| isinstance(item, str) for item in stop_sequences | |
| ): | |
| invocation.stop_sequences = list(stop_sequences) | |
| if ( | |
| isinstance(stop_sequences, (list, tuple)) | |
| and stop_sequences | |
| and all(isinstance(item, str) for item in stop_sequences) | |
| ): | |
| invocation.stop_sequences = list(stop_sequences) |
Consider checking that stop_sequences is non-empty so [] is not recorded as an attribute.
There was a problem hiding this comment.
Added a check on stop_sequences.
|
Sorry overlooked one small thing, should we add assertions to verify these attributes to vcr tests and conformance test suite? |
Assisted-by: GPT-6 Astra Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…/opentelemetry/instrumentation/google_genai/interactions.py Co-authored-by: Liudmila Molkova <neskazu@gmail.com>
Assisted-by: GPT-6 Astra Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-6 Astra Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-6 Astra Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
| for name in ("temperature", "top_p"): | ||
| value = _get_field(config, name) | ||
| if isinstance(value, int) and value > float_info.max: | ||
| continue |
There was a problem hiding this comment.
btw why do we even need to protect from it, if top_p is invalid float, the SDK would throw, why do we need to validate boundaries?
There was a problem hiding this comment.
We convert the value before calling the SDK, so a huge integer could make our instrumentation throw first. This guard just skips recording it and lets the SDK handle the original value. Although it's definitely an unlikely edge case.
| request: object, | ||
| ) -> None: | ||
| config = _explicit_request_fields(_get_field(request, "generation_config")) | ||
| for name in ("temperature", "top_p"): |
There was a problem hiding this comment.
why do we have a loop here? please copy over method _coerse_float from some other place in this repo and call it for individual properties.
And the let's use typed invocatio.top_p | temperature setters.
There was a problem hiding this comment.
Moved the existing conversion checks into _coerce_float and replaced the loop with explicit invocation.temperature and invocation.top_p assignments
| telemetry_handler: TelemetryHandler, | ||
| instance: InteractionsResource | AsyncInteractionsResource, | ||
| kwargs: dict[str, Any], | ||
| request: object, |
There was a problem hiding this comment.
is there a specific type we can use instead of object?
There was a problem hiding this comment.
Changed this to Mapping[str, object] and updated the request helpers to match.
Added assertions to the sync/async VCR tests and the conformance scenario. |
…tests Assisted-by: GPT-6 Astra Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-6 Astra Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Assisted-by: GPT-6 Astra Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Description
Captures explicitly supplied generation configuration and output-format attributes for Google GenAI Interactions calls, including sync, async, streaming, and failed requests.
These settings were previously omitted from telemetry. The implementation supports legacy keyword arguments and newer SDK
request.bodymodels, preserves zero values, and leaves SDK arguments and lazy iterators untouched.Closes #563
Field availability follows the SDK version. Mixed output formats are omitted when no single semantic-convention output type represents them.
Assisted-by: GPT-6 Astra
Type of change
How has this been tested?
tox -e precommit,typecheckChecklist
See CONTRIBUTING.md
for the style guide, changelog guidance, and more.