Skip to content

Python: Add OpenTelemetry instrumentation to ClaudeAgent (#4278)#4326

Open
amitmukh wants to merge 2 commits intomicrosoft:mainfrom
amitmukh:fix/claude-telemetry
Open

Python: Add OpenTelemetry instrumentation to ClaudeAgent (#4278)#4326
amitmukh wants to merge 2 commits intomicrosoft:mainfrom
amitmukh:fix/claude-telemetry

Conversation

@amitmukh
Copy link
Contributor

@amitmukh amitmukh commented Feb 26, 2026

Summary

  • Fixes Python: [Bug]: ClaudeAgent missing telemetry - inherits from BaseAgent instead of Agent #4278: ClaudeAgent inherits from BaseAgent directly, bypassing AgentTelemetryLayer, so enable_instrumentation() has no effect
  • Adds inline OpenTelemetry telemetry to ClaudeAgent.run() using the same observability helpers as AgentTelemetryLayer
  • Covers both streaming and non-streaming paths with invoke_agent spans, duration tracking, exception capture, and sensitive data support

Approach

Instead of changing the inheritance chain (which would cause MRO conflicts since ClaudeAgent defines its own run() and doesn't use a standard chat client), this PR integrates telemetry directly into ClaudeAgent.run() using the framework's existing observability helpers (_get_span, _get_span_attributes, _capture_messages, _capture_response, capture_exception). This is the safest approach with zero risk of breaking existing behavior.

Changes

  • python/packages/claude/agent_framework_claude/_agent.py: Add telemetry wrapping in run(), with two new private methods _run_with_telemetry() and _run_with_telemetry_stream()
  • python/packages/claude/tests/test_claude_agent.py: Add 5 new tests covering span emission, disabled instrumentation, streaming spans, exception capture, and provider name verification

Test plan

  • All 49 existing tests pass unchanged
  • 5 new telemetry tests pass (54 total)
  • Verify invoke_agent span appears in App Insights when enable_instrumentation() is called
  • Verify no spans are emitted when instrumentation is disabled

Future Work: Tool-level telemetry granularity

This PR provides the outer invoke_agent span for ClaudeAgent. However, visibility into
individual tool calls (Read, Write, Bash, etc.) and per-LLM-call spans that happen inside
the Claude CLI subprocess is not possible at the MAF layer — these are opaque to the framework.

A related issue has been raised upstream: anthropics/claude-agent-sdk-python#611
requesting OpenTelemetry instrumentation in the Claude Agent SDK itself.

Once Anthropic ships SDK-level telemetry, a follow-up PR can wire those events as child spans
under the invoke_agent span created here. The trace context parent is already in place —
the integration should be a small, targeted change once the Anthropic SDK defines its
telemetry surface.

Note: Custom @tool-decorated functions already get tool-level spans today via
FunctionTool.invoke() in core. Only Claude's built-in CLI tools require the upstream fix.

)

Add inline telemetry to ClaudeAgent.run() so that enable_instrumentation()
emits invoke_agent spans and metrics. Covers both streaming and
non-streaming paths using the same observability helpers as
AgentTelemetryLayer. Adds 5 unit tests for telemetry behavior.

Co-Authored-By: amitmukh <amimukherjee@microsoft.com>
Copilot AI review requested due to automatic review settings February 26, 2026 17:18
@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Feb 26, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
TOTAL22174276287% 
report-only-changed-files is enabled. No files were changed during this commit :)

Python Unit Test Overview

Tests Skipped Failures Errors Time
4677 247 💤 0 ❌ 0 🔥 1m 17s ⏱️

Copy link
Contributor

Copilot AI left a comment

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 adds OpenTelemetry span emission to ClaudeAgent.run() so enable_instrumentation() affects Claude agents the same way it affects core Agent implementations, covering both streaming and non-streaming execution paths.

Changes:

  • Wrap ClaudeAgent.run() with OTEL span creation, duration tracking, response capture, and exception capture.
  • Add streaming-specific span finalization via ResponseStream cleanup hooks and weakref.finalize.
  • Add new Claude telemetry unit tests for enabled/disabled instrumentation, streaming, exceptions, and provider name.

Reviewed changes

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

File Description
python/packages/claude/agent_framework_claude/_agent.py Adds inline OTEL instrumentation to ClaudeAgent.run() via new _run_with_telemetry* helpers.
python/packages/claude/tests/test_claude_agent.py Adds unit tests validating ClaudeAgent telemetry behavior across key run paths.

- Add justification comment for private observability API imports
- Pass system_instructions to capture_messages for system prompt capture
- Use monkeypatch instead of try/finally for test global state isolation

Co-Authored-By: amitmukh <amitmukh@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: ClaudeAgent missing telemetry - inherits from BaseAgent instead of Agent

3 participants