feat: harden public OpenAI API contracts
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user