Skip to content

feat(aws): expose Nova Sonic session ID on the realtime session - #7327

Open
guiruggiero wants to merge 2 commits into
livekit:mainfrom
guiruggiero:feat/aws-nova-sonic-session-id
Open

guiruggiero wants to merge 2 commits into
livekit:mainfrom
guiruggiero:feat/aws-nova-sonic-session-id

Conversation

@guiruggiero

Copy link
Copy Markdown
Contributor

What

Stores Nova Sonic's sessionId on RealtimeSession, exposes it as a session_id property, and logs it once per stream at INFO level.

Purely additive — no change to the existing event flow.

Why

I'm the PM for Amazon Nova Sonic. When a customer running Sonic through LiveKit files a support ticket with us, the first thing we ask for is the session ID, and today there's no easy way for them to get it — which blocks debugging on our side.

Nova Sonic stamps sessionId on every server event and it's the canonical identifier for tracing a session on the service side, but the plugin never stores it. The only place it currently surfaces is inside the DEBUG-level log_event_data() call on completionStart, so users have to run the worker at --log-level debug and scrape the raw JSON out of the lk.pii.event_data field. The log_event_data() call in _handle_usage_event is commented out, so the earliest occurrence isn't logged at all.

Details

Why usageEvent rather than completionStart: usageEvent is the earliest server event carrying sessionId. I confirmed this against the live Bedrock API with a session that sends only the init sequence (sessionStartpromptStart → SYSTEM → SYSTEM_SPEECH) and no user input at all: four usageEvents arrived, each carrying sessionId, before any completionStart. Capturing it here means the ID is available before the user has said anything, so it survives a session that fails early.

I also traced a full session (100+ events — usageEvent, completionStart, contentStart, textOutput, audioOutput, contentEnd, completionEnd) and every one carried an identical sessionId, so reading it off any single event is equivalent. usageEvent is simply the first opportunity.

Why log on change rather than once: _session_recycle_timer and initialize_streams(is_restart=True) open a new Bedrock stream — ahead of the service's session-duration limit and on credential refresh — and each new stream is assigned a new sessionId. So a single RealtimeSession legitimately spans several IDs. The != guard emits one line per ID instead of one per usageEvent.

Naming: session_id -> str | None follows the existing convention in the codebase — the OpenAI realtime plugin (gpt_live_model.py), the AssemblyAI and Speechmatics STT plugins, and inference/avatar.py all expose the same shape.

Checks

  • ruff check and ruff format --check pass, verified against the 0.15.22 pinned in uv.lock
  • The new property is documented, per CONTRIBUTING.md
  • No CHANGELOG.md or manifest edits, per CONTRIBUTING.md

@guiruggiero
guiruggiero requested a review from a team as a code owner September 17, 2026 21:57
devin-ai-integration[bot]

This comment was marked as resolved.

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.

1 participant