feat: harden public OpenAI API contracts
Quality gate / backend (push) Canceled after 0s
Quality gate / frontend (push) Canceled after 0s

This commit is contained in:
Xin Wang
2026-09-07 14:25:48 +08:00
parent 8025ed8aa0
commit 2649d64b55
36 changed files with 16643 additions and 236 deletions
+27 -7
View File
@@ -34,6 +34,7 @@ from services.openai_realtime.events import (
normalize_turn_detection,
require_event,
)
from services.openai_realtime.schemas import SERVER_EVENT_TYPES
from services.openai_realtime.session import OpenAIRealtimeSession
from services.pipecat.turn_config import create_vad_params
from services.realtime.protocol import PipelineProtocolRuntime
@@ -47,7 +48,10 @@ def _event_id() -> str:
def _server_event(event_type: str, **payload: Any) -> dict[str, Any]:
return {"type": event_type, "event_id": _event_id(), **payload}
event = {"type": event_type, "event_id": _event_id(), **payload}
if event_type.startswith("x.interactive_media."):
event["version"] = "1"
return event
def _decode_base64(value: object, *, param: str) -> bytes:
@@ -129,9 +133,13 @@ class OpenAIRealtimeInputProcessor(FrameProcessor):
),
direction,
)
client_event: dict[str, Any] | None = None
try:
await self._bridge.handle_client_event(require_event(frame.message))
client_event = require_event(frame.message)
await self._bridge.handle_client_event(client_event)
except RealtimeEventError as exc:
if exc.event_id is None and client_event is not None:
exc.event_id = str(client_event.get("event_id") or "") or None
await self._bridge.emit(error_event(exc))
except Exception as exc: # noqa: BLE001 - event errors must stay connection-local
client_event_id = (
@@ -682,10 +690,16 @@ class OpenAIRealtimeBridge:
def translate_server_message(self, message: dict[str, Any]) -> list[dict[str, Any]]:
message_type = str(message.get("type") or "")
if message_type == "error":
return [message]
if message_type in SERVER_EVENT_TYPES:
event = dict(message)
event.setdefault("event_id", _event_id())
if message_type.startswith("x.interactive_media."):
event["version"] = "1"
return [event]
if "." in message_type:
return [message]
# Dotted wire events outside the documented subset must not leak
# through from a provider-specific adapter.
return []
if message_type == "transcript":
return self._translate_transcript(message)
if message_type == "assistant-text-start":
@@ -903,7 +917,10 @@ class OpenAIRealtimeBridge:
"workflow-variables": "x.interactive_media.workflow.variables.updated",
"workflow-error": "x.interactive_media.workflow.error",
}
return _server_event(names[kind], **{k: v for k, v in message.items() if k != "type"})
return _server_event(
names[kind],
**{k: v for k, v in message.items() if k != "type"},
)
if kind in {"handoff-requested", "call-ended"}:
if "handoff" not in self.session.capabilities:
return None
@@ -912,5 +929,8 @@ class OpenAIRealtimeBridge:
if kind == "handoff-requested"
else "x.interactive_media.call.ended"
)
return _server_event(event_type, **{k: v for k, v in message.items() if k != "type"})
return _server_event(
event_type,
**{k: v for k, v in message.items() if k != "type"},
)
return None