Skip to content

Conversation

@vasantteja
Copy link
Contributor

@vasantteja vasantteja commented Dec 16, 2025

Description

Implements OpenTelemetry instrumentation for Anthropic's Messages.create API (sync, non-streaming). This adds automatic tracing for Anthropic SDK calls, capturing GenAI semantic convention attributes for observability.

P.S: LLM help was used for writing the code.

What's Implemented

  • patch.py: Wrapper function for Messages.create that creates spans with request/response attributes
  • utils.py: Helper functions for attribute extraction, error handling, and content capture configuration
  • __init__.py: Wiring for patching/unpatching via wrapt
  • Tests: Comprehensive test suite with VCR cassettes for API mocking
  • Updated minimum anthropic version to 0.16.0 - the instrumentation requires the modern SDK structure (anthropic.resources.messages module) which doesn't exist in versions prior to 0.18.0.

Semantic Convention Attributes Captured

Request:

  • gen_ai.operation.name (chat)
  • gen_ai.system (anthropic)
  • gen_ai.request.model
  • gen_ai.request.max_tokens
  • gen_ai.request.temperature
  • gen_ai.request.top_p
  • gen_ai.request.top_k
  • gen_ai.request.stop_sequences
  • server.address

Response:

  • gen_ai.response.id
  • gen_ai.response.model
  • gen_ai.response.finish_reasons
  • gen_ai.usage.input_tokens
  • gen_ai.usage.output_tokens

Error:

  • error.type (on exceptions)

Fixes #(#3949)

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Ran the full test suite with VCR cassettes for mocked API responses:

pytest instrumentation-genai/opentelemetry-instrumentation-anthropic/tests/ -v

Test cases:

  • Basic sync message creation with correct span attributes
  • All optional parameters (temperature, top_p, top_k, stop_sequences)
  • Token usage capture (input/output tokens)
  • Stop reason captured as finish_reasons array
  • Connection error handling (APIConnectionError)
  • API error handling (404 NotFoundError)
  • Uninstrumentation removes patching
  • Multiple instrument/uninstrument cycles

Results: 15 tests passed (8 new + 7 existing instrumentor tests)

Does This PR Require a Core Repo Change?

  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

@vasantteja vasantteja removed their assignment Dec 16, 2025
@vasantteja vasantteja removed their assignment Dec 16, 2025
@vasantteja vasantteja removed their assignment Dec 16, 2025
@vasantteja vasantteja removed their assignment Dec 16, 2025
@vasantteja vasantteja removed their assignment Dec 16, 2025
@vasantteja vasantteja removed their assignment Dec 16, 2025
@vasantteja vasantteja removed their assignment Dec 16, 2025
- Removed unused trace import from conftest.py and updated tracer_provider fixture to return the provider instead of yielding it.
- Enhanced test_instrument_with_no_providers to ensure proper instrument/uninstrument cycle while passing providers for a clean test environment.
- Improved readability by restructuring the assignment of request_model in the messages_create function.
- Ensured consistent handling of model attributes from both the attributes dictionary and kwargs.
…s function

- Updated the return type to include AttributeValue for better type safety.
- Expanded the docstring to detail the OpenTelemetry semantic conventions for LLM requests and server attributes.
- Included descriptions for GenAI attributes and server attributes to improve clarity and documentation.
@vasantteja vasantteja force-pushed the feat/anthropic-messages-create-sync branch from e011a91 to 643a282 Compare December 27, 2025 02:59
- Introduced a new test file for integration tests that require a valid ANTHROPIC_API_KEY.
- Implemented a pytest hook to skip integration tests if the API key is not set or is a placeholder.
- Updated pyproject.toml to include pytest configuration for test paths and markers for integration tests.
- Enhanced conftest.py to support skipping integration tests based on API key availability.
- Removed commented-out imports and improved assertion readability in test_integration.py.
- Streamlined attribute checks for spans to enhance clarity and maintainability.
- Ensured consistent validation of response attributes in integration tests.
- Added a pylint directive to disable the redefined-outer-name warning, improving code quality and maintainability.
- Updated the messages_create function to ensure request_model is explicitly converted to a string for better type consistency.
- Cleaned up the docstring in get_llm_request_attributes to remove unnecessary whitespace and enhance readability.
- Improved clarity in the documentation of GenAI and server attributes included in the returned dictionary.
- Added a comment to the AnthropicInstrumentor class to indicate the need for a logger_provider in the TelemetryHandler to capture content events.
@vasantteja
Copy link
Contributor Author

@aabmass Can you please take another look? I updated the pr with the suggested changes.

@vasantteja vasantteja removed their assignment Dec 30, 2025
@vasantteja vasantteja removed their assignment Jan 5, 2026
…nstrumentation package. The integration tests have been deleted, and the pytest configuration has been updated to reflect these changes.
@vasantteja vasantteja removed their assignment Jan 6, 2026
@vasantteja vasantteja removed their assignment Jan 6, 2026
@vasantteja vasantteja removed their assignment Jan 7, 2026
Comment on lines 36 to 41
def is_content_enabled() -> bool:
"""Check if content capture is enabled via environment variable."""
capture_content = environ.get(
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT, "false"
)
return capture_content.lower() == "true"
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like something we should expose in genai utils if it's not already.

Looking through this PR, it doesn't look like it's actually used? I see it's passed to the wrapper function, but the _capture_content is not accessed. Can this just be removed and let GenAI utils take care of removing it from the spans/events?

…d related references. Updated messages_create function to eliminate unused parameter.
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.

9 participants