Skip to content

feat(sdk): expose use_legacy_attributes via Traceloop.init()#4133

Open
dvirski wants to merge 1 commit into
mainfrom
dr/feat(sdk)-expose-use_legacy_attributes-via-Traceloop.init()
Open

feat(sdk): expose use_legacy_attributes via Traceloop.init()#4133
dvirski wants to merge 1 commit into
mainfrom
dr/feat(sdk)-expose-use_legacy_attributes-via-Traceloop.init()

Conversation

@dvirski
Copy link
Copy Markdown

@dvirski dvirski commented May 12, 2026

Fixes #3236.

Problem: use_legacy_attributes existed on every instrumentation but was
unreachable through the SDK — users were locked to legacy gen_ai.prompt/
gen_ai.completion span attributes with no way to opt into the new
event-based format.

Fix: thread use_legacy_attributes=True (default, no behaviour change) from
Traceloop.init() through TracerWrapper, init_instrumentations(), and all
8 instrumentations that support the flag: openai, anthropic, bedrock,
sagemaker, groq, langchain, together, watsonx.

Summary by CodeRabbit

  • New Features
    • Added use_legacy_attributes parameter to SDK initialization, enabling control over tracing attribute formatting. Defaults to True for backward compatibility. This setting propagates across all supported AI service instrumentors for consistent behavior.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

The PR adds a use_legacy_attributes parameter to Traceloop.init() and threads it through the initialization chain to all supported instrumentors, enabling users to control legacy attribute formatting without manual setup.

Changes

Legacy Attributes Parameter Threading

Layer / File(s) Summary
Public API parameter
packages/traceloop-sdk/traceloop/sdk/__init__.py
Traceloop.init() gains a new use_legacy_attributes: bool = True parameter and forwards it to TracerWrapper via constructor argument.
Core initialization propagation
packages/traceloop-sdk/traceloop/sdk/tracing/tracing.py
TracerWrapper.__new__ accepts use_legacy_attributes and passes it to init_instrumentations, which routes it through a centralized instrument selection loop to all active initializers.
Instrumentor implementations
packages/traceloop-sdk/traceloop/sdk/tracing/tracing.py
Eight instrument initializers (init_openai_instrumentor, init_anthropic_instrumentor, init_langchain_instrumentor, init_together_instrumentor, init_bedrock_instrumentor, init_sagemaker_instrumentor, init_watsonx_instrumentor, init_groq_instrumentor) now accept the parameter and pass it to their corresponding OpenTelemetry instrumentor constructors.
Test coverage
packages/traceloop-sdk/tests/test_sdk_initialization.py
A new test test_use_legacy_attributes_false_propagates_to_instrumentors() verifies that calling Traceloop.init(use_legacy_attributes=False) propagates the setting to OpenAIConfig.use_legacy_attributes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A rabbit hops through parameters with glee,
Threading use_legacy_attributes through the tree,
From API to wrapper to every instrumentor bright,
Each one now knows when to emit prompts just right! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: exposing use_legacy_attributes parameter through Traceloop.init() method.
Linked Issues check ✅ Passed The PR successfully addresses issue #3236 by exposing use_legacy_attributes parameter through Traceloop.init() and propagating it through TracerWrapper and init_instrumentations to eight supported instrumentations.
Out of Scope Changes check ✅ Passed All changes are directly related to the objective of exposing use_legacy_attributes via Traceloop.init(), with no extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dr/feat(sdk)-expose-use_legacy_attributes-via-Traceloop.init()

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Dvir Rezenman seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/traceloop-sdk/tests/test_sdk_initialization.py (1)

232-253: 💤 Low value

Test correctly verifies propagation to OpenAI instrumentor.

The test properly isolates itself by saving/restoring the TracerWrapper instance and verifies that use_legacy_attributes=False reaches the OpenAI instrumentor configuration.

Consider adding complementary test coverage:

Optional test improvements
  1. Verify the default case: Test that when use_legacy_attributes is not specified (or set to True), the config defaults to True.

  2. Test multiple instrumentors: While testing OpenAI is representative, you could verify that the flag propagates to at least one other instrumentor (e.g., Anthropic or Groq) to ensure the pattern holds across different code paths.

Example for default case:

def test_use_legacy_attributes_defaults_to_true():
    """Verify use_legacy_attributes defaults to True for backward compatibility."""
    from opentelemetry.instrumentation.openai.shared.config import Config as OpenAIConfig
    
    _instance = None
    if hasattr(TracerWrapper, "instance"):
        _instance = TracerWrapper.instance
        del TracerWrapper.instance
    
    exporter = InMemorySpanExporter()
    Traceloop.init(
        exporter=exporter,
        disable_batch=True,
        # use_legacy_attributes not specified, should default to True
    )
    
    assert OpenAIConfig.use_legacy_attributes is True
    
    if _instance is not None:
        TracerWrapper.instance = _instance
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/traceloop-sdk/tests/test_sdk_initialization.py` around lines 232 -
253, Add complementary tests: create
test_use_legacy_attributes_defaults_to_true() that mirrors
test_use_legacy_attributes_false_propagates_to_instrumentors but omits the
use_legacy_attributes argument when calling
Traceloop.init(exporter=InMemorySpanExporter(), disable_batch=True) and assert
OpenAIConfig.use_legacy_attributes is True; also add an additional test (e.g.,
test_use_legacy_attributes_propagates_to_other_instrumentor) that initializes
the SDK with use_legacy_attributes=False and asserts the same flag reached
another instrumentor's Config (e.g., AnthropicConfig or GroqConfig) to confirm
propagation; keep the same TracerWrapper instance save/restore pattern used in
the existing test to avoid global state leaks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/traceloop-sdk/tests/test_sdk_initialization.py`:
- Around line 232-253: Add complementary tests: create
test_use_legacy_attributes_defaults_to_true() that mirrors
test_use_legacy_attributes_false_propagates_to_instrumentors but omits the
use_legacy_attributes argument when calling
Traceloop.init(exporter=InMemorySpanExporter(), disable_batch=True) and assert
OpenAIConfig.use_legacy_attributes is True; also add an additional test (e.g.,
test_use_legacy_attributes_propagates_to_other_instrumentor) that initializes
the SDK with use_legacy_attributes=False and asserts the same flag reached
another instrumentor's Config (e.g., AnthropicConfig or GroqConfig) to confirm
propagation; keep the same TracerWrapper instance save/restore pattern used in
the existing test to avoid global state leaks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc9db1b3-3297-4a1c-bd8b-32022889f21c

📥 Commits

Reviewing files that changed from the base of the PR and between 6d3e696 and 7541daa.

⛔ Files ignored due to path filters (2)
  • packages/sample-app/uv.lock is excluded by !**/*.lock
  • packages/traceloop-sdk/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • packages/traceloop-sdk/tests/test_sdk_initialization.py
  • packages/traceloop-sdk/traceloop/sdk/__init__.py
  • packages/traceloop-sdk/traceloop/sdk/tracing/tracing.py

@doronkopit5
Copy link
Copy Markdown
Member

doronkopit5 commented May 13, 2026

The PR cleanly threads the flag through the SDK, and the mechanical scope is right (all 8 instrumentors that accept the kwarg are covered). Before merging though, I want to flag that the world shifted under issue #3236 in a way that affects what this flag should be named and what it should do.

The OTel semconv decision reversed

When #3236 was filed, the assumption was: "legacy attributes (gen_ai.prompt/gen_ai.completion) → events" is the migration path. That's no longer true. Semconv issue #2010 was re-litigated and closed in favor of attributes on spans, not events. The new spec-compliant attributes are gen_ai.input.messages / gen_ai.output.messages / gen_ai.system_instructions — and events failed to gain adoption across the GenAI tracing ecosystem.

OpenLLMetry has already migrated

Spot-checking the instrumentors, they all emit the new attributes today by default:

  • opentelemetry-instrumentation-openai/.../shared/chat_wrappers.py:552, 612GEN_AI_INPUT_MESSAGES / GEN_AI_OUTPUT_MESSAGES
  • opentelemetry-instrumentation-anthropic/.../span_utils.py:147, 318
  • opentelemetry-instrumentation-groq/.../span_utils.py:140, 183
  • opentelemetry-instrumentation-bedrock/.../span_utils.py:185, 376
  • opentelemetry-instrumentation-langchain/.../span_utils.py:231, 379

So use_legacy_attributes=True (the default) is already the current spec-compliant behavior.

What use_legacy_attributes=False actually does

Looking at opentelemetry-instrumentation-openai/.../utils.py:191-198 and the call sites in chat_wrappers.py:273-287:

if should_emit_events():        # not Config.use_legacy_attributes AND event_logger set
    emit_event(...)             # emit prompts as OTel log events
elif should_send_prompts():
    await _set_prompts(...)     # emit gen_ai.input.messages on the span

So the real semantics today are:

Flag value What gets emitted Status
use_legacy_attributes=True (default) gen_ai.input.messages on span Current spec
use_legacy_attributes=False (this PR exposes) OTel log events instead Abandoned direction

The name is now actively misleading: a user reading the SDK signature would reasonably assume False means "non-legacy / modern", but it actually opts them off the current spec and onto the events path.

Proposal: rename to use_attributes

This matches @nirga's original suggestion in #3236 ("use_attributes (or use_events)") and reflects reality without flipping defaults:

Old New
use_legacy_attributes: bool = True use_attributes: bool = True

Default stays True (no behavior change for existing users). The "legacy" word — which is the inaccurate part — goes away. False continues to mean "emit as events instead."

To keep the SDK and instrumentor-level APIs consistent, I'd suggest renaming at both layers in this PR:

  1. Rename the kwarg on the 8 instrumentor __init__s to use_attributes, accepting use_legacy_attributes as a deprecated alias that emits a DeprecationWarning and forwards to the new name.
  2. Same on Traceloop.init().
  3. Remove the alias in the next major version.

This adds maybe ~15 lines of deprecation-shim code but gives us a clean, accurate public API without breaking anyone.

Two related items worth deciding before merge

  1. EventLoggerProvider plumbing. The original issue also asked how to configure the event logger when opting into events. Today, use_legacy_attributes=False silently relies on the global logger provider being configured. Worth documenting clearly that users must configure one themselves — otherwise setting the flag silently does nothing useful.

  2. Test robustness. The new test in tests/test_sdk_initialization.py:232-253 manually mutates TracerWrapper.instance instead of using the existing fixture pattern from conftest.py. It also only verifies one instrumentor (OpenAI). Worth parametrizing across 2-3 instrumentors and using the fixture pattern to avoid global-state leakage if Traceloop.init() ever raises mid-call.

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.

🐛 Bug Report: No way to set use_legacy_attributes using TraceLoop.init()

3 participants