Skip to content

feat(google-genai): capture Interactions request configuration - #752

Merged
lmolkova merged 13 commits into
open-telemetry:mainfrom
JacksonWeber:feat/google-interactions-config
Sep 22, 2026
Merged

lmolkova merged 13 commits into
open-telemetry:mainfrom
JacksonWeber:feat/google-interactions-config

Conversation

@JacksonWeber

@JacksonWeber JacksonWeber commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

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.body models, 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

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How has this been tested?

  • tox -e precommit,typecheck
  • Google oldest, v2, and latest suites, including Python 3.14 latest.
  • Google conformance scenarios with Weaver 0.26.1 and VCR replay.
  • Offline SDK-model and HTTP-transport coverage for parameters, streaming normalization, and error paths. No live Gemini calls or API key.

Checklist

See CONTRIBUTING.md
for the style guide, changelog guidance, and more.

  • Followed the style guidelines of this project
  • Changelog updated if the change requires an entry
  • Unit tests added
  • Documentation updated

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>
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Sep 21, 2026

Copy link
Copy Markdown

Pull request dashboard status

Merged · refreshed 2026-09-22 05:40 UTC

Status above doesn't look right?
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Medium severity

Open (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.body shapes, and apply it onto the util-genai invocation before completion/stream finalization.
  • Derive gen_ai.output.type from response_format / response_mime_type when 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.

JacksonWeber and others added 2 commits September 21, 2026 00:42
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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation is behavior-preserving, semconv-aligned, and comprehensively tested.

Review effort: Balanced
Findings: None

Resolved since last review (1)

If both variables are set, the includes list is applied first, then the
excludes list filters the result further.

Interactions request parameters

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, removed this section to clean this up

Comment on lines +585 to +594
if format_type in (
"object",
"array",
"string",
"number",
"integer",
"boolean",
"null",
):
return output_types.JSON.value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added json, json_object, and json_schema here and updated the test request params

Comment on lines +603 to +610
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))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use math.isfinite

Comment on lines +619 to +622
if isinstance(stop_sequences, (list, tuple)) and all(
isinstance(item, str) for item in stop_sequences
):
invocation.stop_sequences = list(stop_sequences)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a check on stop_sequences.

@eternalcuriouslearner

Copy link
Copy Markdown
Contributor

Sorry overlooked one small thing, should we add assertions to verify these attributes to vcr tests and conformance test suite?

JacksonWeber and others added 6 commits September 21, 2026 09:07
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a specific type we can use instead of object?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this to Mapping[str, object] and updated the request helpers to match.

@JacksonWeber

Copy link
Copy Markdown
Contributor Author

Sorry overlooked one small thing, should we add assertions to verify these attributes to vcr tests and conformance test suite?

Added assertions to the sync/async VCR tests and the conformance scenario.

JacksonWeber and others added 4 commits September 21, 2026 10:25
…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>
@lmolkova
lmolkova added this pull request to the merge queue Sep 22, 2026
Merged via the queue into open-telemetry:main with commit 6f69095 Sep 22, 2026
78 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

[google-genai] Capture generation_config request parameters on interactions.create

4 participants