Skip to content

Don't call set attribute twice for the same key and make start attributes unsettable#150

Open
lmolkova wants to merge 4 commits into
open-telemetry:mainfrom
lmolkova:dont-duplicate-attributes
Open

Don't call set attribute twice for the same key and make start attributes unsettable#150
lmolkova wants to merge 4 commits into
open-telemetry:mainfrom
lmolkova:dont-duplicate-attributes

Conversation

@lmolkova

Copy link
Copy Markdown
Member

No description provided.

)
invocation.stop()

assert invocation.request_model == "text-embedding-3-small"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

attributes validated below


def _get_base_attributes(self) -> dict[str, Any]:
@property
def agent_name(self) -> str | None:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this one is used by langchain, convert to read-only property

@lmolkova lmolkova changed the title Don't call set attribute twice for the same key and make start attributed unsettable Don't call set attribute twice for the same key and make start attributes unsettable Jun 18, 2026
self.request_model = request_model
self.server_address = server_address
self.server_port = server_port
self._provider: str = provider

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.

so _ means don't directly set this, instead pass it in on initialization.. Is that enforced somehow or is it just a convention ? Does it cause a lint error at least ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

it a conventions, pylint can detect it, but is not configured to do it in this and contrib repos. I can look into enabling it, but it's a common convention in python to mark internal API, it's what we do for all private methods on these classes. I don't think it needs to be bullet-proof

@lmolkova lmolkova force-pushed the dont-duplicate-attributes branch from 56db4f0 to d7f45d9 Compare June 19, 2026 23:20
@lmolkova lmolkova marked this pull request as ready for review June 19, 2026 23:28
@lmolkova lmolkova requested a review from a team as a code owner June 19, 2026 23:28
Copilot AI review requested due to automatic review settings June 19, 2026 23:28

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.

Pull request overview

This PR refactors GenAI invocation types to (1) only set sampling-relevant attributes at span start (avoiding re-setting the same attribute keys again at finish) and (2) encourage providing span-name/sampling inputs at invocation construction time (e.g., passing agent_name into invoke_local_agent(...)).

Changes:

  • Split “start-time” vs “finish-time” attribute setting by introducing _get_start_attributes() and removing duplicated set_attributes(...) keys at finish.
  • Make several start-time fields “construction-time only” (e.g., agent name/model/server info) and update tests to pass them at construction.
  • Update OpenAI response streaming wrapper to populate response_model_name instead of mutating request_model.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_workflow_invocation.py Uses start-only attributes and stores workflow name internally (_name).
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_tool_invocation.py Moves tool metadata to start-time attributes and avoids re-setting at finish.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_retrieval_invocation.py Moves retrieval sampling attributes to start-time, avoids duplication at finish.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py Renames “start attributes” helper and removes syncing deprecated container fields that are now start-only.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_embedding_invocation.py Restricts provider/model/server fields to start-time attributes; finish sets only non-start fields.
util/opentelemetry-util-genai/src/opentelemetry/util/genai/_agent_invocation.py Moves sampling-relevant agent fields to start-time; updates finish-time attribute grouping.
util/opentelemetry-util-genai/tests/test_workflow_invocation.py Updates workflow invocation tests for the new name storage/behavior.
util/opentelemetry-util-genai/tests/test_handler_workflow.py Updates workflow handler tests for the new name storage/behavior.
util/opentelemetry-util-genai/tests/test_handler_retrieval.py Updates retrieval handler tests for the new data source id storage/behavior.
util/opentelemetry-util-genai/tests/test_handler_completion_hook.py Updates completion-hook test to pass agent_name at construction.
util/opentelemetry-util-genai/tests/test_handler_agent.py Updates agent tests to pass agent_name at construction and reflect start-only fields.
util/opentelemetry-util-genai/.changelog/150.changed Adds a towncrier fragment describing the behavioral change.
instrumentation/opentelemetry-instrumentation-genai-openai/tests/test_embedding_invocation_unit.py Drops assertions tied to mutable request_model state.
instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/response_wrappers.py Sets response_model_name (actual model) instead of mutating request_model.

Comment on lines +55 to +58
self._name: str | None = name
self.input_messages: list[InputMessage] = []
self.output_messages: list[OutputMessage] = []
self._start(self._get_base_attributes())
self._start(self._get_start_attributes())
Comment on lines +71 to +75
self._data_source_id: str | None = data_source_id
self._provider: str | None = provider
self._request_model: str | None = request_model
self._server_address: str | None = server_address
self._server_port: int | None = server_port
Comment on lines +70 to +73
self._provider: str = provider
self._request_model: str | None = request_model
self._server_address: str | None = server_address
self._server_port: int | None = server_port
Comment on lines +51 to +55
# e.g., azure.ai.openai, openai, aws.bedrock
self._provider: str = provider
self._request_model: str | None = request_model
self._server_address: str | None = server_address
self._server_port: int | None = server_port
Comment on lines 64 to +75
self.should_capture_content_on_span = should_capture_content_on_spans()
self.name = name
self._name: str = name
self.tool_result: AttributeValue | None = None
# Since arguments and tool_result can be expensive to serialize,
# it's recommended to check the content capture flag in the
# instrumentation library before assigning these attributes
# to the invocation.
self.arguments: AttributeValue | None = None
self.tool_call_id = tool_call_id
self.tool_type = tool_type
self.tool_description = tool_description
self._start(self._get_base_attributes())
self._tool_call_id: str | None = tool_call_id
self._tool_type: str | None = tool_type
self._tool_description: str | None = tool_description
self._start(self._get_start_attributes())
invocation.output_messages = [out]
invocation.stop()
assert invocation.name == "my_workflow"
assert invocation._name == "my_workflow"
with self.handler.workflow() as inv:
self.assertIsInstance(inv, WorkflowInvocation)
self.assertIsNone(inv.name)
self.assertIsNone(inv._name)
with self.handler.retrieval() as inv:
self.assertIsInstance(inv, RetrievalInvocation)
self.assertIsNone(inv.data_source_id)
self.assertIsNone(inv._data_source_id)
assert invocation._operation_name == "invoke_agent"
assert invocation.agent_name is None
assert invocation.request_model is None
assert invocation._request_model is None
Comment on lines +1 to +2
Restricted start-time sampling attributes to invocation construction time;
they should not be settable after start.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants