Skip to content

feat(voice): realtime_langwatch_session context manager for live OpenAI Realtime apps (#673)#676

Merged
drewdrewthis merged 8 commits into
mainfrom
feat/issue673-live-realtime-tracing
Jun 17, 2026
Merged

feat(voice): realtime_langwatch_session context manager for live OpenAI Realtime apps (#673)#676
drewdrewthis merged 8 commits into
mainfrom
feat/issue673-live-realtime-tracing

Conversation

@drewdrewthis

@drewdrewthis drewdrewthis commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Why

Production OpenAI Realtime apps have no path into LangWatch today — scenario.run() handles test-time tracing, but live calls fly blind. Closes #673.

What changed

  • RealtimeLangWatchSession (exported as realtime_langwatch_session) — async context manager that wraps a live Realtime session and records one child LLM span per log_turn() call, matching the per-turn shape scenario tests already produce.
  • No-op when keyless — if LANGWATCH_API_KEY is absent the context manager is a safe no-op (no raise, no spans); safe to ship in keyless envs without a conditional.
  • Attaches to existing provider, doesn't clobber — if langwatch.setup() or scenario.run() already installed a TracerProvider, the helper attaches to it; idempotent guard prevents duplicate BatchSpanProcessor registration across sequential sessions.
  • Spec-first, unit-tested — 14 ACs in specs/realtime-live-tracing.feature, covered by 20 unit tests in tests/test_live_tracing.py; no live API calls needed.

How it works

scenario/
  __init__.py              ← exports realtime_langwatch_session
  _tracing/
    live.py                ← RealtimeLangWatchSession + helpers (_get_concrete_provider, _add_langwatch_exporter)
specs/
  realtime-live-tracing.feature   ← 14 ACs
tests/
  test_live_tracing.py            ← 20 unit tests

__aenter__ checks the global OTel provider: fresh → builds + installs its own TracerProvider; existing → attaches an OTLP exporter. A root span is opened for the session; log_turn() creates a child realtime_turn span with type=llm, input, output, model, latency_ms. __aexit__ ends the root span and force-flushes only provider it owns.

Test plan

cd python
uv run pytest tests/test_live_tracing.py -v
# → 20 passed
uv run pyright tests/test_live_tracing.py scenario/_tracing/live.py
# → 0 errors

How I can prove I was successful

No playable artifact — see Test plan. 20 unit tests pass covering all 14 ACs; pyright clean; all CI checks green.

Anything surprising?

The live module intentionally duplicates two small helpers from scenario._tracing.setup (_get_concrete_provider, _add_langwatch_exporter). The design constraint is that scenario._tracing.setup is the test-runner's lazy-init path — importing it from a production context would be wrong. The duplication is documented in both files.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds RealtimeLangWatchSession, a new async context manager in python/scenario/_tracing/live.py, that records LangWatch-compatible OpenTelemetry spans for live OpenAI Realtime sessions. It is exported from the package root, specified via a Gherkin feature file with 14 acceptance criteria, covered by a matching unit test suite, and documented in the existing voice guide.

Changes

Live Tracing for OpenAI Realtime

Layer / File(s) Summary
Gherkin feature spec (AC1–AC14)
specs/realtime-live-tracing.feature
Defines all 14 acceptance criteria covering importability, span emission, no-op mode, OTel provider lifecycle, export failure robustness, log_turn lifecycle enforcement, documentation assertions, coexistence with ensure_tracing_initialized, and flush/teardown semantics.
RealtimeLangWatchSession implementation and public export
python/scenario/_tracing/live.py, python/scenario/__init__.py
Adds live.py with module design constraints doc, _get_concrete_provider helper, _add_langwatch_exporter helper, and RealtimeLangWatchSession (__init__, __aenter__, log_turn, __aexit__). Exports realtime_langwatch_session via __init__.py.
Unit test suite (AC1–AC14)
python/tests/test_live_tracing.py
Adds OTel provider reset autouse fixtures, an in-memory exporter helper, and async tests for all 14 acceptance criteria including span attributes, no-op mode, provider creation/attachment, export failure swallowing, RuntimeError lifecycle checks, edge cases (empty transcripts, multiple turns), and __aexit__ flush visibility.
Live app tracing documentation
docs/docs/pages/voice/happy-path-openai-realtime.mdx
Adds a "Getting LangWatch traces from your live app" section with a realtime_langwatch_session code example, log_turn field descriptions, no-op behavior note, and langwatch.setup() compatibility note.

Sequence Diagram

sequenceDiagram
  participant App as Live App
  participant RLS as RealtimeLangWatchSession
  participant OTel as TracerProvider
  participant LW as LangWatch OTLP

  App->>RLS: async with realtime_langwatch_session(name, model, api_key)
  RLS->>OTel: detect existing concrete provider
  alt no concrete provider and API key present
    RLS->>OTel: install new TracerProvider + BatchSpanProcessor → LangWatch OTLP
  else concrete provider already exists
    RLS->>OTel: attach BatchSpanProcessor to existing provider
  else no API key
    RLS-->>App: enter no-op mode (zero spans, no raises)
  end
  RLS->>OTel: start root span, attach to context
  RLS-->>App: yield self

  loop per turn
    App->>RLS: await log_turn(user_transcript, agent_transcript, model, latency_ms)
    RLS->>OTel: create + end child "realtime_turn" span with attributes
    OTel->>LW: BatchSpanProcessor.on_end (export, failures swallowed)
  end

  App->>RLS: exit async with
  RLS->>OTel: end root span, detach context token
  RLS->>OTel: force_flush() if session owns provider
Loading

Possibly related issues

  • #673: This PR directly implements all objectives from issue #673 — the RealtimeLangWatchSession async context manager, log_turn() method, OTel provider lifecycle handling, graceful degradation without LANGWATCH_API_KEY, public export from the scenario package, documentation section, and AC1–AC14 test coverage.
  • #674: The realtime_langwatch_session helper and documentation section introduced here address the live-app tracing workflow, providing a packaged production-friendly wrapper as an alternative to manually wiring OpenTelemetry providers.
  • #675: Requests critical documentation enhancements to the per-turn tracing feature introduced in this PR, including topology tradeoffs and VAD over-counting mitigation notes for the realtime_langwatch_session documentation.

Poem

🐇 Hippity-hop, a new trace in the fold,
Real-time sessions now shimmer with gold.
log_turn whispers each agent reply,
No key? No worries — just hop on by.
The spans flush clean when the context departs,
A rabbit-approved start for live tracing arts! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a realtime_langwatch_session context manager for live OpenAI Realtime voice apps, with proper scope indication (#673).
Linked Issues check ✅ Passed The PR fully implements issue #673 requirements: realtime_langwatch_session context manager with log_turn() method, no imports from setup, LangWatch trace emission, OTel state detection, documentation section, graceful missing-credential handling, and 20 unit tests covering 14 acceptance criteria.
Out of Scope Changes check ✅ Passed All changes align with issue #673 scope: new live.py module, package exports, documentation, and tests for the context manager. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The PR description clearly explains why the changes are needed, what was changed, and how it works. It directly addresses the feature request to enable LangWatch tracing for live OpenAI Realtime apps, which is the core purpose of this changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue673-live-realtime-tracing

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.

@drewdrewthis drewdrewthis added prove-it-clean All ACs verified by /prove-it at this HEAD in-ai-review Workflow: in-ai-review labels Jun 15, 2026
Comment thread python/tests/test_live_tracing.py Fixed
Comment thread python/tests/test_live_tracing.py Fixed
@drewdrewthis

Copy link
Copy Markdown
Collaborator Author

No description provided.

@drewdrewthis

drewdrewthis commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

Review verdict: READY

All CI checks pass. Both Fix concerns resolved. All 14 ACs covered. PR is assigned and human reviewer requested.


Fixes applied since 0669c9db

  1. [security] python/scenario/_tracing/live.py:182 — Removed exc_info=True from __aenter__ exception handler; now logs type(exc).__name__ only, eliminating the Bearer token exposure path. ✅ Resolved
  2. [test] python/tests/test_live_tracing.py:124-130 — Added parent-child span assertion: parent_ctx.span_id == root_ctx.span_id. Pyright-narrowed both SpanContext | None values before comparison. ✅ Resolved

Non-blocking (for author awareness)

  • Design [principles][uncle-bob][metz-beck][fowler]_noop/_active/_own_provider flags encode three states; Null Object split recommended as follow-up issue
  • Dead assert [principles]assert self._tracer is not None at live.py:216 is guarded by _noop and dead under python -O; delete or annotate
  • Snake_case export [principles]RealtimeLangWatchSession as realtime_langwatch_session convention mismatch; consider factory function
  • reset_otel fixture [hygiene] — missing trace._PROXY_TRACER_PROVIDER reset; could leak state between tests
  • import re placement [hygiene] — inside function bodies at lines 158, 354; move to module level
  • Span schema [fowler]log_turn hard-codes LangWatch attribute keys; verify they match scenario-test span constants

Completeness (issue #673)

All 14 ACs (AC1–AC14, with AC5 split a/b) covered by tests. Proof-reviewer ran the suite: 20 passed in 2.84s.

@drewdrewthis drewdrewthis self-assigned this Jun 15, 2026
@drewdrewthis drewdrewthis marked this pull request as ready for review June 15, 2026 18:34
@drewdrewthis

Copy link
Copy Markdown
Collaborator Author

Live behavior demonstration

realtime_langwatch_session exercised against the installed package (uv run python3) with an InMemorySpanExporter to capture spans without requiring a live LangWatch server:

$ LANGWATCH_API_KEY=test-demo-key uv run python3 -c "
  ...
  async with realtime_langwatch_session(name='demo-session', model='gpt-4o-realtime-preview') as session:
      await session.log_turn('I want to cancel my subscription', 'Of course, I can help with that.', model='gpt-4o-realtime-preview', latency_ms=430)
      await session.log_turn('What is the refund policy?', 'Full refund within 30 days.', model='gpt-4o-realtime-preview', latency_ms=310)
  spans = exporter.get_finished_spans()
  for i, s in enumerate(spans): print(f'[{i}] name={s.name!r} attrs={dict(s.attributes)}')
"

=== realtime_langwatch_session demo ===
Spans produced: 3
  [0] name='realtime_turn' attrs={'type': 'llm', 'input': 'I want to cancel my subscription', 'output': 'Of course, I can help with that.', 'model': 'gpt-4o-realtime-preview', 'latency_ms': 430}
  [1] name='realtime_turn' attrs={'type': 'llm', 'input': 'What is the refund policy?', 'output': 'Full refund within 30 days.', 'model': 'gpt-4o-realtime-preview', 'latency_ms': 310}
  [2] name='demo-session' attrs={'model': 'gpt-4o-realtime-preview'}

Confirmed:

  • Two child realtime_turn spans with correct type, input, output, model, latency_ms attributes
  • Root demo-session span with model attribute (wired this session after dead-state review fix)
  • 20/20 unit tests pass at HEAD 8899f9ea

@drewdrewthis drewdrewthis added prove-it-clean All ACs verified by /prove-it at this HEAD and removed prove-it-clean All ACs verified by /prove-it at this HEAD labels Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
specs/realtime-live-tracing.feature (1)

67-70: ⚡ Quick win

AC6 is over-coupled to a non-production failure hook.

Line 69 hardcodes on_end failure, but the live path uses BatchSpanProcessor + OTLP exporter; this AC can pass while the real export path regresses. Consider rewording AC6 to validate exporter/BatchSpanProcessor failure containment, then align the test to that path.

🤖 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 `@specs/realtime-live-tracing.feature` around lines 67 - 70, The test for AC6
(Scenario: OTLP export failure does not propagate to the live app) is mocking
the span processor's on_end method to raise RuntimeError, but this does not
reflect the real production failure path which uses BatchSpanProcessor with OTLP
exporter. Reword the scenario description to explicitly state it validates
exporter/BatchSpanProcessor failure containment, then update the test setup to
mock the actual OTLP exporter failure or BatchSpanProcessor failure instead of
the on_end hook, ensuring the test validates the real export path rather than a
non-production failure point.
🤖 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.

Inline comments:
In `@python/scenario/_tracing/live.py`:
- Around line 57-86: The _add_langwatch_exporter function registers a new
BatchSpanProcessor unconditionally each time it's called, causing duplicate
processors and resource leaks when the same provider is reused across multiple
sessions. Add an idempotent guard before the provider.add_span_processor call to
check whether a BatchSpanProcessor has already been registered on the provider
instance. If one already exists, return early without adding another. This
ensures that repeated invocations (such as from multiple __aenter__ calls in
realtime_langwatch_session) don't accumulate duplicate processors.

In `@python/tests/test_live_tracing.py`:
- Around line 33-35: The module-level import of scenario at lines 33-35 prevents
the test_ac9_importing_scenario_does_not_create_tracer_provider test from
validating fresh-import behavior because scenario is already loaded before the
reset_otel fixture runs. Remove the module-level imports of scenario and
RealtimeLangWatchSession from lines 33-35, then modify the
test_ac9_importing_scenario_does_not_create_tracer_provider test (currently at
lines 93-99) to import scenario fresh within the test body itself, either by
clearing sys.modules['scenario'] and reimporting or by using an isolated
subprocess approach similar to how test_ac1_importable_from_scenario_package
correctly tests fresh import at line 86. This ensures the test can detect if
scenario creates a TracerProvider at import time.

---

Nitpick comments:
In `@specs/realtime-live-tracing.feature`:
- Around line 67-70: The test for AC6 (Scenario: OTLP export failure does not
propagate to the live app) is mocking the span processor's on_end method to
raise RuntimeError, but this does not reflect the real production failure path
which uses BatchSpanProcessor with OTLP exporter. Reword the scenario
description to explicitly state it validates exporter/BatchSpanProcessor failure
containment, then update the test setup to mock the actual OTLP exporter failure
or BatchSpanProcessor failure instead of the on_end hook, ensuring the test
validates the real export path rather than a non-production failure point.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: faa58f40-8c42-4824-bc1f-6a6595deddfb

📥 Commits

Reviewing files that changed from the base of the PR and between 21226c0 and 8899f9e.

📒 Files selected for processing (5)
  • docs/docs/pages/voice/happy-path-openai-realtime.mdx
  • python/scenario/__init__.py
  • python/scenario/_tracing/live.py
  • python/tests/test_live_tracing.py
  • specs/realtime-live-tracing.feature

Comment thread python/scenario/_tracing/live.py
Comment thread python/tests/test_live_tracing.py Outdated
Ubuntu and others added 3 commits June 16, 2026 10:33
- docs: add "Getting LangWatch traces from your live app" section
- specs: add realtime-live-tracing.feature covering AC1-AC14
- new scenario/_tracing/live.py with RealtimeLangWatchSession
- export realtime_langwatch_session from scenario package
- unit tests covering AC1-AC14 in python/tests/test_live_tracing.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eturn type

- Remove self._entered flag (set but never read)
- Wire self._model as root span attribute when provided (was stored but
  never used; log_turn still accepts per-turn model override)
- Change __aexit__ return annotation from -> bool to -> None and drop
  explicit `return False` (None is falsy, same semantics, matches codebase convention)
- Remove redundant span.name assertion in test_ac2 (span already filtered by name)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@drewdrewthis drewdrewthis force-pushed the feat/issue673-live-realtime-tracing branch from 8899f9e to 603c28a Compare June 16, 2026 10:33
Ubuntu and others added 2 commits June 16, 2026 10:37
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Consolidate to single import style for scenario._tracing.setup
- Move module-level `import scenario` to file scope (was local in test_ac1)
- Guard _add_langwatch_exporter against duplicate BatchSpanProcessor registration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread python/tests/test_live_tracing.py
Comment thread python/tests/test_live_tracing.py Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
python/scenario/_tracing/live.py (2)

33-33: ⚠️ Potential issue | 🟠 Major

Add explicit parameter types to satisfy strict typing requirements.

Line 33: provider parameter in _get_concrete_provider() is untyped.
Line 235: exc_type, exc, and tb parameters in __aexit__() are untyped.

These violations conflict with coding guidelines requiring explicit type annotations for all function parameters and strict pyright compatibility.

Suggested patch
+from types import TracebackType
 from typing import Optional
 
 from opentelemetry import context, trace
 from opentelemetry.sdk.trace import TracerProvider
 from opentelemetry.sdk.trace.export import SpanExporter
 
 logger = logging.getLogger("scenario.tracing")
 
 
-def _get_concrete_provider(provider) -> Optional[TracerProvider]:
+def _get_concrete_provider(provider: object) -> Optional[TracerProvider]:
     """Return the concrete ``TracerProvider`` if one exists, else ``None``.
     
     Checks the provider itself and one level of delegation (the
     ``ProxyTracerProvider`` pattern OTel uses before any provider is set).
     
     NOTE: intentionally duplicated from ``scenario._tracing.setup`` — see the
     module docstring for why this file must not import from setup.
     """
-    async def __aexit__(self, exc_type, exc, tb) -> None:
+    async def __aexit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc: BaseException | None,
+        tb: TracebackType | None,
+    ) -> None:
🤖 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 `@python/scenario/_tracing/live.py` at line 33, Add explicit type annotations
to the untyped parameters in two functions to satisfy strict typing
requirements. In the `_get_concrete_provider()` function, add a type annotation
to the `provider` parameter (specify the appropriate type based on how it's used
in the function). In the `__aexit__()` method, add type annotations to the three
parameters `exc_type`, `exc`, and `tb` to properly document the exception
handling context (these typically follow the standard exception handler
signature with Optional types for exception values).

Source: Coding guidelines


137-150: ⚠️ Potential issue | 🟠 Major

Annotate instance attributes explicitly in __init__ for pyright strict compliance.

self._tracer, self._root_span, and self._ctx_token lack explicit type annotations. Per coding guidelines, class attributes must be explicitly annotated and code must pass pyright strict mode without errors.

Suggested patch
+from opentelemetry.context.context import Context, Token
+from opentelemetry.trace import Span, Tracer
@@
         self._provider: Optional[TracerProvider] = None
         self._own_provider = False  # did WE create/install the provider?
-        self._tracer = None
-        self._root_span = None
-        self._ctx_token = None
+        self._tracer: Tracer | None = None
+        self._root_span: Span | None = None
+        self._ctx_token: Token[Context] | None = None
🤖 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 `@python/scenario/_tracing/live.py` around lines 137 - 150, Add explicit type
annotations to the three instance attributes that are currently initialized to
None without type annotations in the __init__ method: self._tracer,
self._root_span, and self._ctx_token. Each should be annotated as
Optional[AppropriateType] where AppropriateType corresponds to the actual type
that will be assigned to each attribute later in the lifecycle methods. This
ensures the code complies with pyright strict mode, consistent with how
self._provider is already annotated as Optional[TracerProvider].
🤖 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.

Outside diff comments:
In `@python/scenario/_tracing/live.py`:
- Line 33: Add explicit type annotations to the untyped parameters in two
functions to satisfy strict typing requirements. In the
`_get_concrete_provider()` function, add a type annotation to the `provider`
parameter (specify the appropriate type based on how it's used in the function).
In the `__aexit__()` method, add type annotations to the three parameters
`exc_type`, `exc`, and `tb` to properly document the exception handling context
(these typically follow the standard exception handler signature with Optional
types for exception values).
- Around line 137-150: Add explicit type annotations to the three instance
attributes that are currently initialized to None without type annotations in
the __init__ method: self._tracer, self._root_span, and self._ctx_token. Each
should be annotated as Optional[AppropriateType] where AppropriateType
corresponds to the actual type that will be assigned to each attribute later in
the lifecycle methods. This ensures the code complies with pyright strict mode,
consistent with how self._provider is already annotated as
Optional[TracerProvider].

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 65a9be2e-c9b9-43af-8980-7facee03bfae

📥 Commits

Reviewing files that changed from the base of the PR and between 72044b2 and 6d8fc7d.

📒 Files selected for processing (2)
  • python/scenario/_tracing/live.py
  • python/tests/test_live_tracing.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • python/tests/test_live_tracing.py

…_session throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread python/scenario/_tracing/live.py Outdated
Comment thread python/tests/test_live_tracing.py
Ubuntu and others added 2 commits June 16, 2026 11:37
…an assertion

exc_info=True in __aenter__ could expose the Bearer token if the OTLP
exporter constructor raises an exception whose repr includes the headers
dict. Switch to logging type(exc).__name__ only.

AC2 test now verifies the turn span's parent.span_id equals the root
span's span_id, closing the gap where context-attachment could silently
break without the test catching it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parent_ctx and root_ctx are both Optional[SpanContext] per OTel type
stubs; extract and assert-narrow each before accessing .span_id so
pyright's reportOptionalMemberAccess check passes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Automated low-risk assessment

This PR was evaluated against the repository's Low-Risk Pull Requests procedure and does not qualify as low risk.

The PR adds a new runtime tracing helper that attaches an OTLP exporter and may send spans to LangWatch (an external API) and also alters/globalizes TracerProvider behavior. That constitutes a change to integrations with a third‑party system and global runtime behavior, which violates the policy's "no integrations with third‑party systems or external APIs" requirement. Although it is no‑op without a key and includes docs/tests, the introduced runtime integration and side effects make it ineligible for low‑risk labeling.

This PR requires a manual review before merging.

@drewdrewthis drewdrewthis merged commit e89d00c into main Jun 17, 2026
18 checks passed
@drewdrewthis drewdrewthis deleted the feat/issue673-live-realtime-tracing branch June 17, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in-ai-review Workflow: in-ai-review prove-it-clean All ACs verified by /prove-it at this HEAD

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(voice): first-class LangWatch tracing for live OpenAI Realtime apps (not just scenario tests)

2 participants