Skip to content

feat(suppress): expose SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION as env var#336

Draft
adityamehra wants to merge 13 commits into
mainfrom
fix/remove-suppress-language-model-key
Draft

feat(suppress): expose SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION as env var#336
adityamehra wants to merge 13 commits into
mainfrom
fix/remove-suppress-language-model-key

Conversation

@adityamehra

@adityamehra adityamehra commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Exposes the existing suppress_language_model_instrumentation OTel context key as a runtime environment variable, giving zero-code deployments a global suppression knob without introducing any new concept or constant.

  • util-genai/attributes.py — Updated docstring on SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY to document that its uppercase form (SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION) is also honoured as an environment variable. No new constant added.
  • openai-v2/patch.py — New _is_instrumentation_suppressed() helper checks both surfaces (OTel context key or env var). All four traced entry-points (chat, async chat, embeddings, async embeddings) use it. No change to the LangChain instrumentor.
  • openai-v2/tests/test_suppression.py — 15 new tests covering env var truthy/falsey parsing and two VCR integration tests.

Motivation

The LangChain instrumentor's _OpenAITracingWrapper sets the suppress_language_model_instrumentation context key per-request to prevent duplicate LLM spans when both instrumentors are active. For zero-code deployments the preferred pattern going forward is OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=openai (excludes openai-v2 at startup). The env var provides a runtime fallback when startup-time exclusion is not possible — same concept, environment variable form — without introducing any new LangChain-specific flag.

Changes

File Change
util-genai/attributes.py Extended docstring on SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_KEY to describe env var usage
openai-v2/patch.py _is_instrumentation_suppressed(): checks context key first, then env var; four call-sites updated
openai-v2/tests/test_suppression.py 15 new tests: truthy/falsey env var parsing + 2 VCR end-to-end tests
openai-v2/tests/cassettes/test_chat_completion_suppressed_via_env_var.yaml New VCR cassette
openai-v2/tests/cassettes/test_chat_completion_not_suppressed_when_env_var_false.yaml New VCR cassette
openai-v2/CHANGELOG.md Unreleased Added entry
util-genai/CHANGELOG.md Unreleased Added entry

Usage

Preferred zero-code path — exclude openai-v2 at startup:

export OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=openai
opentelemetry-instrument python myapp.py

Runtime suppression via env var — openai-v2 is loaded but skips span creation:

export SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION=true

Use this when startup-time exclusion is not possible or as belt-and-suspenders.

Explicit opt-in to spans (override any env var default):

export SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION=false

Per-request suppression via context key (LangChain, unchanged):
The _OpenAITracingWrapper in the LangChain instrumentor continues to set the context key on every OpenAI call — no code changes there.

Suppression precedence

_is_instrumentation_suppressed() checks in order:

  1. OTel context key (suppress_language_model_instrumentation) — set per-request by LangChain
  2. Environment variable SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION — global, checked at call time

Either surface alone is sufficient to suppress. Truthy values: true, 1, yes, on (case-insensitive). Unset or any other value → not suppressed.

Testing notes

Validated with the SRE Incident Copilot demo (LangChain + openai-v2, manual and zero-code):

  • All chat gpt-4o-mini / embedding spans carry scope opentelemetry.util.genai.handler (LangChain) — zero direct openai-v2 spans in both SUPPRESS=false and SUPPRESS=true runs, confirming no duplicate telemetry regardless of env var value.
  • SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION=false with OTEL_PYTHON_DISABLED_INSTRUMENTATIONS="" (openai-v2 loaded): copilot runs successfully; LangChain context-key suppression prevents duplicates as before.

Test plan

  • pytest instrumentation-genai/opentelemetry-instrumentation-openai-v2/tests/test_suppression.py -v — 19/19 pass
  • Existing context-key suppression tests still pass (path untouched)
  • New env var tests: truthy/falsey parsing + VCR integration
  • make lint passes

adityamehra and others added 11 commits May 13, 2026 16:34
…ributes

StreamWrapper was missing __getattr__, causing AttributeError when
LiteLLM (and other clients) access raw_response.headers after calling
with_raw_response.create(stream=True).

Ports the fix from upstream opentelemetry-python-contrib PR #4184
(fixes issue #4113).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…or with_raw_response streaming

When LiteLLM calls with_raw_response.create(stream=True), the OpenAI SDK
returns a LegacyAPIResponse (with .headers). SDOT's _parse_response was
calling .parse() on it to get the AsyncStream before wrapping in StreamWrapper,
discarding the headers. LiteLLM then accesses raw_response.headers, which
failed with AttributeError since AsyncStream has no .headers attribute.

Fix:
- Capture LegacyAPIResponse.headers before _parse_response discards it
- Store captured headers as StreamWrapper.headers (direct attribute, not proxied)
- Add parse() returning self so callers can treat StreamWrapper as a raw response
- Keep __getattr__ for any other unknown attribute proxying to the underlying stream

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…aming headers bug

Reproduces the production error reported in lab0:
  AttributeError: 'StreamWrapper' object has no attribute 'headers'

Mirrors the pattern from upstream issues:
  #4032 - StreamWrapper missing .parse()
  #4113 - StreamWrapper missing .headers

Co-authored-by: Cursor <cursoragent@cursor.com>
…im in reproducer

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@adityamehra adityamehra requested review from a team as code owners May 19, 2026 20:30
@adityamehra adityamehra force-pushed the fix/remove-suppress-language-model-key branch from 3b66c84 to 399915b Compare May 19, 2026 20:38
@adityamehra adityamehra changed the title feat(langchain): add OTEL_INSTRUMENTATION_LANGCHAIN_SUPPRESS_OPENAI env var feat(suppress): expose SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION as env var May 19, 2026
@adityamehra adityamehra force-pushed the fix/remove-suppress-language-model-key branch 2 times, most recently from b8aab3a to d0eff63 Compare May 19, 2026 20:46
… var

Add SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION_ENV_VAR to util-genai attributes
as the environment variable counterpart of the existing
suppress_language_model_instrumentation OTel context key.

The openai-v2 instrumentor now checks both surfaces via a single
_is_instrumentation_suppressed() helper:
  1. OTel context key (existing) — set per-request by the LangChain
     instrumentor to prevent duplicate LLM spans.
  2. New env var — set globally for zero-code deployments together with
     OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=openai.

One concept, two surfaces. No new flags introduced in the LangChain package.

Co-authored-by: Cursor <cursoragent@cursor.com>
@adityamehra adityamehra force-pushed the fix/remove-suppress-language-model-key branch from d0eff63 to 8f324d4 Compare May 19, 2026 20:46
…anguage-model-key

Co-authored-by: Cursor <cursoragent@cursor.com>
@adityamehra adityamehra marked this pull request as draft May 19, 2026 22:19
the per-request `suppress_language_model_instrumentation` OTel context key
(the env var is the uppercase form of the same string). When set to a truthy
value (`true`, `1`, `yes`, `on`), the openai-v2 instrumentor skips creating
spans entirely. Intended for zero-code deployments alongside

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.

Is this an alternative instead of "alongside"?

by the LangChain instrumentor to prevent duplicate LLM spans when both
instrumentors are active simultaneously.
2. SUPPRESS_LANGUAGE_MODEL_INSTRUMENTATION environment variable — set globally
for zero-code deployments alongside OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=openai.

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.

same here

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.

2 participants