mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-10 05:00:19 +08:00
fix(gateway): support current Codex Realtime live routes
This commit is contained in:
+65
-6
@@ -1,13 +1,15 @@
|
||||
# WebSocket transports
|
||||
|
||||
Aether exposes three independent WebSocket surfaces. They share transport
|
||||
Aether exposes several independent WebSocket surfaces. They share transport
|
||||
machinery, but not request schemas or continuation state:
|
||||
|
||||
| Public route | API format | Protocol |
|
||||
| --- | --- | --- |
|
||||
| `GET /v1/responses` | `openai:responses` | Responses WebSocket mode; every turn starts with `response.create`. |
|
||||
| `GET /v1/realtime?model=...` | `openai:realtime` | OpenAI Realtime JSON events, including Base64 audio events. |
|
||||
| `GET /v1/live[/{call_id}]` | `codex:live` | Codex Frameless/Live direct and WebRTC-sideband transport. |
|
||||
| `GET /v1/realtime?model=...` with a first-party Codex `originator` | `codex:live` | Current Codex Realtime v2 direct WebSocket transport. |
|
||||
| `POST /v1/realtime/calls`, `GET /v1/realtime?intent=quicksilver&call_id=...` | `codex:live` | Codex Realtime v1 AVAS WebRTC call creation and sideband transport. |
|
||||
| `GET/POST /v1/live[/{call_id}]` | `codex:live` | Legacy Codex Frameless direct and WebRTC compatibility transport. |
|
||||
|
||||
Do not point one surface at an endpoint configured for another. In particular,
|
||||
a Realtime or Live event is not passed through the Responses
|
||||
@@ -62,13 +64,29 @@ Codex clients. It is related to the OpenAI Realtime API, but it is not the
|
||||
Responses WebSocket protocol and never enters Aether's `response.create`
|
||||
state machine:
|
||||
|
||||
- Direct WebSocket: `GET /v1/live?model=<global-model>`. The first client text
|
||||
- Current WebRTC call creation: `POST /v1/realtime/calls?intent=quicksilver&architecture=avas`
|
||||
with bounded `sdp` and `session` multipart parts. Aether normalizes those
|
||||
selectors, applies the global-to-provider mapping, and rewrites the upstream
|
||||
`Location` to `/v1/realtime/calls/<call-id>`.
|
||||
- Current WebRTC sideband: `GET /v1/realtime?intent=quicksilver&call_id=<call-id>`.
|
||||
The unique `intent=quicksilver` selector classifies it as Codex Live;
|
||||
`call_id` alone remains on the separate OpenAI Realtime surface.
|
||||
- Current direct WebSocket (Realtime v2):
|
||||
`GET /v1/realtime?model=<authorized-global-model>`. V2 intentionally omits
|
||||
`intent=quicksilver`; Aether uses the first-party Codex `originator` header
|
||||
to distinguish it from ordinary OpenAI Realtime. The first client event is
|
||||
still the opaque `session.update` frame generated by Codex; Aether forwards
|
||||
it without converting it into a Responses `response.create` event.
|
||||
- Realtime v1 direct WebSocket uses
|
||||
`GET /v1/realtime?intent=quicksilver&model=<authorized-global-model>` and
|
||||
sends `openai-alpha: quicksilver=v1` upstream. V2 does not send that header.
|
||||
- Legacy direct WebSocket: `GET /v1/live?model=<global-model>`. The first client text
|
||||
frame must be `session.update`; later text, binary, ping, pong, and close
|
||||
frames are relayed opaquely.
|
||||
- WebRTC call creation: `POST /v1/live` with bounded `sdp` and `session`
|
||||
- Legacy WebRTC call creation: `POST /v1/live` with bounded `sdp` and `session`
|
||||
multipart parts. Aether applies the existing global-to-provider model
|
||||
mapping and rewrites the upstream `Location` to `/v1/live/<call-id>`.
|
||||
- WebRTC sideband: `GET /v1/live/<call-id>`. Frameless sideband attaches to an
|
||||
- Legacy WebRTC sideband: `GET /v1/live/<call-id>`. Frameless sideband attaches to an
|
||||
already initialized call, so Aether neither waits for nor sends a second
|
||||
`session.update` frame.
|
||||
|
||||
@@ -78,6 +96,45 @@ custom providers can add it in the endpoint editor. The
|
||||
`responses_websocket.enabled` provider option belongs only to
|
||||
`openai:responses` WebSocket mode and is not reused as the Live permission.
|
||||
|
||||
Codex Live also requires an authorized model mapping; adding the endpoint alone
|
||||
is not enough. For WebRTC, Codex sends the selected model in the multipart
|
||||
`session.model`. Aether treats that value as the downstream global model,
|
||||
selects an existing mapping whose provider endpoint is `codex:live`, and
|
||||
rewrites only `session.model` to the mapped upstream model. Configure Codex's
|
||||
realtime model selection to an authorized global alias (the current Codex
|
||||
default may be `gpt-realtime-1.5`, but that value is client-version dependent),
|
||||
or create an authorized mapping for the alias the client already sends. Aether
|
||||
does not invent a Live model name or add a bundled hard-coded model merely
|
||||
because the endpoint is enabled. For Codex providers only, an existing model mapping scoped to
|
||||
`openai:responses` (including the historical `/v1/responses` alias) can be
|
||||
reused for Live. The provider endpoint and key must still explicitly allow
|
||||
`codex:live`; OpenAI and custom providers do not receive this compatibility
|
||||
rule.
|
||||
|
||||
For Codex Desktop/app-server, point both the call-creation and sideband
|
||||
overrides at the same Aether origin when using a custom provider. Explicitly
|
||||
setting both avoids a client-version-dependent fallback to the OpenAI origin:
|
||||
|
||||
```toml
|
||||
model_provider = "aether"
|
||||
experimental_realtime_webrtc_call_base_url = "https://<aether-host>/v1"
|
||||
experimental_realtime_ws_base_url = "https://<aether-host>/v1"
|
||||
experimental_realtime_ws_model = "<authorized-global-live-alias>"
|
||||
|
||||
[model_providers.aether]
|
||||
name = "Aether"
|
||||
base_url = "https://<aether-host>/v1"
|
||||
wire_api = "responses"
|
||||
```
|
||||
|
||||
The two experimental overrides are optional when the selected provider's
|
||||
`base_url` already points at Aether, but if either is set they must resolve to
|
||||
the same deployment so the authenticated call binding can be found. A missing
|
||||
call-create request in Aether means the client did not select this provider or
|
||||
failed before gateway routing; a call-create request without the matching
|
||||
sideband usually means the sideband origin or credential differs. These
|
||||
settings may change with newer Codex releases.
|
||||
|
||||
API-key and bearer providers can use direct WebSocket or WebRTC. ChatGPT OAuth
|
||||
uses the official Codex backend for WebRTC call creation and the OpenAI Live
|
||||
origin for its sideband; direct OAuth WebSocket and custom OAuth backend
|
||||
@@ -109,7 +166,9 @@ created call that never attaches a sideband is not held against provider
|
||||
concurrency after call creation.
|
||||
|
||||
For the public GA Realtime API's connection and session concepts, see the
|
||||
[OpenAI Realtime guide](https://developers.openai.com/api/docs/guides/realtime).
|
||||
[OpenAI Realtime guide](https://developers.openai.com/api/docs/guides/realtime),
|
||||
[WebRTC connection guide](https://developers.openai.com/api/docs/guides/realtime-webrtc),
|
||||
and [server-side controls guide](https://developers.openai.com/api/docs/guides/realtime-server-controls).
|
||||
|
||||
OpenAI's current WebSocket service supports named `stream_id` lanes: requests on
|
||||
the same lane are FIFO, while different lanes may run concurrently. Aether's
|
||||
|
||||
Reference in New Issue
Block a user