Adopt shared Role enum across instrumentations - #575
Conversation
Replace hardcoded role literals and google-genai's private `Role` enum with `opentelemetry.util.genai.types.Role` in every instrumentation that emits a role it controls: google-genai, smolagents, openai, agno, qwen-agent, bedrock, portkey, and llama-index. Anthropic is passthrough-only and unchanged. Bump portkey's `opentelemetry-util-genai` floor to 1.2b0.dev, where `Role` was added, matching every other package that uses it; its `oldest` test env gains the same `-e util/opentelemetry-util-genai` workaround the others already carry. No behaviour change: the enum values are identical to the literals they replace. Assisted-by: Claude Opus 5
Pull request dashboard statusMerged · refreshed 2026-09-03 20:22 UTC Status above doesn't look right?
|
Assisted-by: Claude Opus 5
There was a problem hiding this comment.
🟢 Approval recommended
The changes consistently adopt the shared Role enum as intended, with only a minor comment/doc mismatch noted in Portkey’s oldest requirements file.
Pull request overview
This PR standardizes GenAI message role values produced by instrumentations by replacing local/hardcoded role literals (including google-genai’s private enum) with the shared opentelemetry.util.genai.types.Role enum, keeping provider-supplied roles as passthrough.
Changes:
- Replace synthesized role literals (
"user","assistant", etc.) withRole.<...>.valueacross multiple instrumentations. - Remove google-genai’s private
Roleenum in favor of the shared util enum. - Bump Portkey’s
opentelemetry-util-genaidependency floor and align its oldest-test env with the repo’s existing “install util from workspace” pattern.
File summaries
| File | Description |
|---|---|
| instrumentation/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/message.py | Drops local Role enum and maps SDK roles to shared Role values. |
| instrumentation/opentelemetry-instrumentation-google-genai/src/opentelemetry/instrumentation/google_genai/interactions.py | Uses shared Role for synthesized input/output messages. |
| instrumentation/opentelemetry-instrumentation-genai-smolagents/src/opentelemetry/instrumentation/genai/smolagents/patch.py | Uses shared Role for streamed output message role default. |
| instrumentation/opentelemetry-instrumentation-genai-smolagents/src/opentelemetry/instrumentation/genai/smolagents/_messages.py | Uses shared Role in role normalization/defaults and mapping. |
| instrumentation/opentelemetry-instrumentation-genai-qwen-agent/src/opentelemetry/instrumentation/genai/qwen_agent/utils.py | Uses shared Role for default input role and output role. |
| instrumentation/opentelemetry-instrumentation-genai-portkey/tests/requirements.oldest.txt | Adds editable util install for oldest env until util release is published. |
| instrumentation/opentelemetry-instrumentation-genai-portkey/src/opentelemetry/instrumentation/genai/portkey/wrappers.py | Uses shared Role for synthesized assistant output messages. |
| instrumentation/opentelemetry-instrumentation-genai-portkey/src/opentelemetry/instrumentation/genai/portkey/utils.py | Uses shared Role for synthesized assistant role defaults. |
| instrumentation/opentelemetry-instrumentation-genai-portkey/pyproject.toml | Raises opentelemetry-util-genai floor to include Role. |
| instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/response_extractors.py | Uses shared Role for synthesized input/output messages. |
| instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/chat_wrappers.py | Uses shared Role for synthesized assistant output messages. |
| instrumentation/opentelemetry-instrumentation-genai-llama-index/src/opentelemetry/instrumentation/genai/llama_index/_handler.py | Uses shared Role for synthesized user input message. |
| instrumentation/opentelemetry-instrumentation-genai-bedrock/src/opentelemetry/instrumentation/genai/bedrock/stream.py | Uses shared Role for stream wrapper role default. |
| instrumentation/opentelemetry-instrumentation-genai-bedrock/src/opentelemetry/instrumentation/genai/bedrock/extractors.py | Uses shared Role for default role values when extracting. |
| instrumentation/opentelemetry-instrumentation-genai-agno/src/opentelemetry/instrumentation/genai/agno/patch.py | Uses shared Role for synthesized input/output messages. |
Review details
- Files reviewed: 16/16 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The file said there was nothing to pin, directly above an editable util-genai install. That line is a temporary workaround, not a pin. Assisted-by: Claude Opus 5
| @@ -217,7 +218,7 @@ def convert_to_output_messages( | |||
|
|
|||
| output_messages.append( | |||
| OutputMessage( | |||
There was a problem hiding this comment.
nit: one bare literal is left in this file - convert_to_final_output_messages (line 250) still defaults role to "assistant". It only feeds the local filter and is never emitted, but it is the last one left in the repo, so converting it keeps the sweep complete.
There was a problem hiding this comment.
thanks for catching that, switched to use enum there as well
convert_to_final_output_messages still defaulted role to "assistant", the last bare literal left after the initial sweep. Extend the same treatment to the role literals the instrumentations compare against, so every role in the repo that has an enum member goes through it. Values with no member stay literals: Google's "model" and qwen-agent's "function", neither of which is a spec role. Assisted-by: Claude Opus 5
Description
The GenAI message schemas constrain
roletosystem,user,assistant, andtool, but they are not code-generated intoopentelemetry-semconv, so each instrumentation spelled the values out its own way: a privateRoleenum in google-genai, a local_ROLE_MAPin smolagents, bare literals elsewhere. The schemas accept any string, so an off-spec value passes weaver and only surfaces downstream, whereroledrives the role badge and the System/User/Assistant/Tool filter — that is how"role": "AIMessageChunk"reached langchain spans in #504.#506 added a shared
Roleenum toopentelemetry-util-genaiand adopted it in langchain. This PR adopts it in google-genai (deleting its private copy), smolagents, openai, agno, qwen-agent, bedrock, portkey, and llama-index; anthropic is passthrough-only and is unchanged. Only roles an instrumentation picks itself change — provider-supplied roles still pass through, and the enum values are identical to the literals they replace, so there is no behaviour change.Portkey's
opentelemetry-util-genaifloor moves to1.2b0.dev, whereRolewas added, matching every other package that depends on it; itsoldesttox env gains the same-e util/opentelemetry-util-genaiworkaround the others already carry.Fixes #534.
Type of change
How has this been tested?
No new tests: the change is a literal-for-enum substitution, and the existing suites already assert the emitted role values, so they are the regression check.
latestsuites pass for google-genai, smolagents, agno, qwen-agent, portkey, openai, and llama-indexlatestsuite fails identically onmainand on this branch (15 pre-existing VCR failures), so it is unaffectedtox -e ruffcleantox -e changelog-previewrenders the portkey entryChecklist