Don't call set attribute twice for the same key and make start attributes unsettable#150
Don't call set attribute twice for the same key and make start attributes unsettable#150lmolkova wants to merge 4 commits into
Conversation
| ) | ||
| invocation.stop() | ||
|
|
||
| assert invocation.request_model == "text-embedding-3-small" |
There was a problem hiding this comment.
attributes validated below
|
|
||
| def _get_base_attributes(self) -> dict[str, Any]: | ||
| @property | ||
| def agent_name(self) -> str | None: |
There was a problem hiding this comment.
this one is used by langchain, convert to read-only property
| self.request_model = request_model | ||
| self.server_address = server_address | ||
| self.server_port = server_port | ||
| self._provider: str = provider |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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
hide or readonly
56db4f0 to
d7f45d9
Compare
There was a problem hiding this comment.
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 duplicatedset_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_nameinstead of mutatingrequest_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. |
| 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()) |
| 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 |
| 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 |
| # 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 |
| 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 |
| Restricted start-time sampling attributes to invocation construction time; | ||
| they should not be settable after start. |
No description provided.