fix: patch aiohttp SSL verification for OpenAI Realtime STT WebSocket

The OpenAI Realtime STT uses aiohttp WebSocket connections (not httpx),
so the existing httpx verify=False fix does not apply. LiveKit's
http_context creates aiohttp.TCPConnector without ssl=False, causing
SSL certificate verification errors when OPENAI_BASE_URL points to a
proxy with a self-signed certificate.

Monkey-patch http_context._new_session_ctx to inject ssl=False into the
aiohttp connector, fixing the "CERTIFICATE_VERIFY_FAILED" error for
Realtime STT WebSocket connections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-03-01 18:29:59 -08:00
parent 5460be8c04
commit a5c95b460a
1 changed files with 49 additions and 0 deletions

View File

@ -10,7 +10,9 @@ Usage:
import json
import logging
import ssl
import aiohttp
from livekit.agents import (
Agent,
AgentServer,
@ -20,9 +22,56 @@ from livekit.agents import (
cli,
room_io,
)
from livekit.agents.utils import http_context
from livekit.plugins import silero
from .config import settings
# ---------------------------------------------------------------------------
# Monkey-patch: disable SSL verification for aiohttp sessions.
#
# The OpenAI Realtime STT uses aiohttp WebSocket (not httpx), so passing
# verify=False to the httpx/OpenAI client does NOT help. LiveKit's
# http_context._new_session_ctx creates an aiohttp.TCPConnector without
# ssl=False, causing SSL errors when OPENAI_BASE_URL points to a proxy
# with a self-signed certificate.
#
# We replace _new_session_ctx to inject ssl=False into the connector.
# ---------------------------------------------------------------------------
_original_new_session_ctx = http_context._new_session_ctx
_no_verify_ssl = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
_no_verify_ssl.check_hostname = False
_no_verify_ssl.verify_mode = ssl.CERT_NONE
def _patched_new_session_ctx():
"""Same as the original but with ssl verification disabled."""
_g_session = None
def _new_session():
nonlocal _g_session
if _g_session is None or _g_session.closed:
from livekit.agents.job import get_job_context
try:
http_proxy = get_job_context().proc.http_proxy
except RuntimeError:
http_proxy = None
connector = aiohttp.TCPConnector(
limit_per_host=50,
keepalive_timeout=120,
ssl=_no_verify_ssl,
)
_g_session = aiohttp.ClientSession(proxy=http_proxy, connector=connector)
return _g_session
http_context._ContextVar.set(_new_session)
return _new_session
http_context._new_session_ctx = _patched_new_session_ctx
from .plugins.agent_llm import AgentServiceLLM
from .plugins.whisper_stt import LocalWhisperSTT
from .plugins.kokoro_tts import LocalKokoroTTS, patch_misaki_compat