boundless-author-7811
04/15/2026, 10:25 AMtall-airline-16756
04/15/2026, 10:25 AMtall-airline-16756
04/15/2026, 10:26 AMtrunk_id (it’s blank in the log), and whether room = voice_assistant_room_1 and customer_id = cust_001 are correct.
With those details I’ll either retrieve the event payload or give steps to access/subscribe to these notifications.
, with interactive elementsboundless-author-7811
04/15/2026, 10:28 AMtall-airline-16756
04/15/2026, 10:28 AMboundless-author-7811
04/15/2026, 10:29 AMtall-airline-16756
04/15/2026, 10:30 AMsilence_detected)?
5. Confirm identifiers from your log: is room = voice_assistant_room_1, customer_id = cust_001, and please provide trunk_id if available.
6. Do you want an example of the past event payload, a real-time handler, or both?
Reply with those details and I’ll provide the precise server-side handler code and subscription steps.
, with interactive elementsboundless-author-7811
04/15/2026, 10:31 AMtall-airline-16756
04/15/2026, 10:31 AMhttps://livekit.example.com)
2. Do you have Agents enabled to emit IVRActivity events in your deployment? (yes/no)
3. Do you want events for all rooms or only specific filters (list room names or leave blank for all)?
Once you confirm those, I’ll send the minimal Python code to subscribe and handle every IVRActivity event in real time, plus an example payload and parsing notes.
, with interactive elementsboundless-author-7811
04/15/2026, 10:41 AMtall-airline-16756
04/15/2026, 10:42 AMimport os
import jwt
import json
import time
import websocket # pip install websocket-client
LIVEKIT_URL = os.getenv("LIVEKIT_URL", "wss://livekit.example.com") # websocket endpoint
API_KEY = os.getenv("LIVEKIT_API_KEY", "your_api_key")
API_SECRET = os.getenv("LIVEKIT_API_SECRET", "your_api_secret")
def make_token(api_key, api_secret):
now = int(time.time())
payload = {
"iat": now,
"exp": now + 60,
"sub": api_key,
# include admin claims if needed by your deployment
"grant": {"system": {"admin": True}}
}
return jwt.encode(payload, api_secret, algorithm="HS256")
def on_message(ws, message):
try:
msg = json.loads(message)
except Exception:
print("Received non-JSON message:", message)
return
# Adjust this check depending on your event structure.
# This assumes events include a top-level "type" or "event" and an "activity" payload.
event_type = msg.get("type") or msg.get("event")
if not event_type:
return
# Example: catch any IVRActivity / agent activity events
if event_type.lower().startswith("ivr") or "ivr" in json.dumps(msg).lower():
print("IVRActivity event received:")
print(json.dumps(msg, indent=2))
else:
# optionally handle other events
pass
def on_open(ws):
print("Connected to LiveKit realtime websocket")
def on_error(ws, error):
print("WebSocket error:", error)
def on_close(ws, code, reason):
print("WebSocket closed:", code, reason)
if __name__ == "__main__":
token = make_token(API_KEY, API_SECRET)
# build ws url with token as query param if required by your deployment
ws_url = f"{LIVEKIT_URL}/rtc?access_token={token}"
ws = websocket.WebSocketApp(
ws_url,
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close,
)
ws.run_forever()
If you want, I can:
• Replace the generic checks with the exact IVRActivity event field names if you share a sample event payload.
• Show how to load credentials from environment variables or a secret store.
• Provide a version using your LiveKit host URL or handling TLS/custom certs., with interactive elementsboundless-author-7811
04/15/2026, 10:44 AMsession = AgentSession[CallState](
llm=openai.realtime.RealtimeModel.with_azure(
voice="alloy",
modalities=["audio", "text"],
turn_detection=None,
input_audio_noise_reduction="far_field",
azure_endpoint=deployment.base_url or "",
azure_deployment=deployment.azure_deployment_name,
api_version=deployment.api_version or "2024-10-01-preview"
),
vad=ctx.proc.userdata["vad"],
userdata=state,
ivr_detection=True,
user_away_timeout=15.0,
aec_warmup_duration=3.0,
turn_handling={
"interruption": {
"enabled": True,
"min_duration": 0.8,
"min_words": 2,
},
},
max_tool_steps=5,
)tall-airline-16756
04/15/2026, 10:44 AMsession.event_queue.get() or session.next_event()) — no extra package names needed. Replace a single placeholder if your session exposes a differently named queue method.
import asyncio
import json
from typing import Any, Dict
# async handler called for every IVRActivity event
async def handle_ivr_activity(evt: Dict[str, Any]):
print("IVRActivity event:")
print(json.dumps(evt, indent=2))
# --- Try to register a handler if AgentSession supports it ---
try:
# If your AgentSession exposes a registration API, this will wire the handler.
# Replace 'register_event_handler' and the event name if your API differs.
session.register_event_handler("ivr_activity", handle_ivr_activity)
except Exception:
# --- Fallback: poll a session event queue (works generically) ---
async def poll_session_events():
while True:
# Preferred: asyncio-compatible queue method many sessions expose
try:
evt = await session.event_queue.get() # replace only if your API differs
except AttributeError:
# Alternative common method name:
try:
evt = await session.next_event()
except AttributeError:
# No compatible event source found; stop polling
return
if not isinstance(evt, dict):
continue
evt_type = (evt.get("type") or evt.get("event") or "").lower()
if "ivr" in evt_type or "ivr" in json.dumps(evt).lower():
await handle_ivr_activity(evt)
asyncio.create_task(poll_session_events())
If you want, paste one small sample raw IVRActivity event JSON (redact secrets) and I’ll tighten the detection logic and show how to extract fields like room, customer_id, and trunk_id.
, with interactive elementsboundless-author-7811
04/16/2026, 9:29 AMtall-airline-16756
04/16/2026, 9:29 AMinput_tokens = input_audio_tokens + input_text_tokens and output_tokens = output_audio_tokens + output_text_tokens.
• Cached tokens (input_cached_tokens, input_cached_text_tokens) are portions of input_tokens and should not be double-counted.
1) Including cached tokens (total recorded tokens):
• Total = input_tokens + output_tokens = 3087 + 299 = 3386
2) Excluding cached input tokens (count only newly processed input tokens):
• New input = input_tokens - input_cached_tokens = 3087 - 2624 = 463
• Total (excluding cached) = New input + output_tokens = 463 + 299 = 762
Tell me which you prefer (include or exclude cached) and I’ll format this for billing or logging.
, with interactive elementsboundless-author-7811
04/16/2026, 9:30 AMtall-airline-16756
04/16/2026, 9:30 AMboundless-author-7811
04/16/2026, 9:31 AMtall-airline-16756
04/16/2026, 9:31 AM