Skip to content

Fix OTel invoke_agent span: prefix the operation name and omit empty agent name/description attributes#655

Open
PratikDhanave wants to merge 1 commit into
microsoft:mainfrom
PratikDhanave:otel-invoke-agent-span-name
Open

Fix OTel invoke_agent span: prefix the operation name and omit empty agent name/description attributes#655
PratikDhanave wants to merge 1 commit into
microsoft:mainfrom
PratikDhanave:otel-invoke-agent-span-name

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

What

Two GenAI-semantic-convention fixes in the invoke_agent span setup in provider/otelprovider/otel.go:

  1. Span name. The span was started with the raw agent name (tracer.Start(ctx, a.Name(), ...)); invoke_agent was only used for the gen_ai.operation.name attribute, never the span name. It is now named <operation> <target> (e.g. invoke_agent helper), falling back to the agent id when the name is empty so the span still carries a target.

  2. Empty attributes. gen_ai.agent.name and gen_ai.agent.description were set unconditionally, so an agent with no name/description produced empty-string attributes. They are now appended only when non-empty. The provider name still keeps its unknown default (parity with Python).

Why

The GenAI semantic conventions require the span be named operation + target. The sibling tool span already does this — startToolSpan in toolautocall/autocall.go builds execute_tool <tool> — so the agent span was inconsistent within this repo and diverged from the .NET and Python SDKs. Both .NET and Python also omit gen_ai.agent.name/gen_ai.agent.description when they are empty rather than emitting empty strings.

Tests

Extended provider/otelprovider/otel_test.go using the existing tracetest in-memory exporter harness:

  • TestOtel_Run_SpanNamePrefixedWithOperation — asserts the span Name is invoke_agent helper.
  • TestOtel_Run_OmitsEmptyAgentNameAndDescription — asserts no gen_ai.agent.name/gen_ai.agent.description keys for an unnamed/undescribed agent.
  • Updated TestOtel_Run_SpanHasCorrectAttributes to expect the prefixed span name.

go build ./..., go vet ./provider/otelprovider/..., and go test ./provider/otelprovider/... all pass.

Copilot AI review requested due to automatic review settings July 23, 2026 07:56
@PratikDhanave
PratikDhanave requested a review from a team as a code owner July 23, 2026 07:56

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 pull request updates the OpenTelemetry invoke_agent span emitted by the OTel middleware to better align with GenAI semantic conventions (span naming and attribute emission), and extends the existing in-memory trace exporter tests to lock in the behavior.

Changes:

  • Rename the invoke_agent span to "<operation> <target>" (e.g., invoke_agent helper) with fallback from agent name to agent ID.
  • Stop emitting gen_ai.agent.name and gen_ai.agent.description attributes when those values are empty.
  • Extend/update unit tests in provider/otelprovider to validate the new span naming and attribute omission behavior.

Reviewed changes

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

File Description
provider/otelprovider/otel.go Adjusts invoke_agent span naming and conditionally emits agent name/description attributes.
provider/otelprovider/otel_test.go Updates existing assertions and adds new tests for span name and omitted empty attributes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +71 to +74
name := opInvoke
if n := cmp.Or(a.Name(), a.ID()); n != "" {
name += " " + n
}
Comment on lines +162 to +163

func TestOtel_Run_OmitsEmptyAgentNameAndDescription(t *testing.T) {
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added the parity-approved Go API consistency review found no parity issues label Jul 23, 2026
Name the agent span "<operation> <target>" (invoke_agent <name>) to
match the GenAI semantic conventions and the sibling execute_tool span,
instead of using the raw agent name. Fall back to the agent id when the
name is empty so the span still carries a target.

Only set gen_ai.agent.name and gen_ai.agent.description when non-empty,
matching .NET and Python which omit these attributes for an
unnamed/undescribed agent rather than emitting empty strings.
@PratikDhanave
PratikDhanave force-pushed the otel-invoke-agent-span-name branch from a95d1e3 to 09349fc Compare July 23, 2026 15:41
@github-actions

Copy link
Copy Markdown
Contributor

Cross-Repo Parity Review — ✅ Aligned

This PR fixes two OTel invoke_agent span issues in provider/otelprovider/otel.go.

Change 1: Span name follows <operation> <target> convention

The span is now named invoke_agent <agent_name_or_id> instead of the bare agent name. This matches:

  • Python (observability.py_start_streaming_span): builds the span name as f"{operation} {span_name}" where operation = OtelAttr.AGENT_INVOKE_OPERATION ("invoke_agent") and the agent name/id is the target.
  • .NET (OpenTelemetryAgent.cs): delegates to OpenTelemetryChatClient which follows the same GenAI semantic convention <operation> <target> pattern.
  • The sibling execute_tool <name> span in toolautocall/autocall.go already used this convention, so this also restores internal consistency.

Change 2: gen_ai.agent.name / gen_ai.agent.description omitted when empty

Attributes are now conditional on being non-empty. This matches upstream:

  • Python: passes agent_name=getattr(self, "name", None) or getattr(self, "id", "unknown") — empty string names fall back to agent ID rather than being emitted as empty strings; agent_description=getattr(self, "description", None) is filtered out by _get_span_attributes when None, so absent descriptions are never emitted.
  • .NET tests (OpenTelemetryAgentTests.cs): confirms gen_ai.agent.name presence is conditional on the agent having a name.

Both changes are consistent with the GenAI semantic conventions and restore alignment with the Python and .NET SDKs.

No parity issues found. The parity-approved label is retained.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by Go API Consistency Review Agent · 62.5 AIC · ⌖ 5.6 AIC · ⊞ 5.7K ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parity-approved Go API consistency review found no parity issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants