feat(gateway): add Codex Live and OpenAI Realtime

Implement preflighted Live/Realtime WebSocket transports, protocol-aware authentication, usage auditing, UI filtering, and legacy Codex permission migration.
This commit is contained in:
ZheFox
2026-08-21 04:27:34 +08:00
parent fe38dcd294
commit 2c89202001
105 changed files with 7553 additions and 947 deletions
@@ -41,6 +41,8 @@ pub fn apply_codex_oauth_fingerprint_convergence(
provider_request_headers: &mut BTreeMap<String, String>,
provider_request_body: &mut Value,
) -> bool {
let is_responses = aether_ai_formats::is_openai_responses_format(provider_api_format);
let is_live = aether_ai_formats::api_format_alias_matches(provider_api_format, "codex:live");
if !transport
.provider
.provider_type
@@ -48,11 +50,12 @@ pub fn apply_codex_oauth_fingerprint_convergence(
.eq_ignore_ascii_case("codex")
|| !transport.key.auth_type.trim().eq_ignore_ascii_case("oauth")
|| crate::agent_identity::is_codex_agent_identity_transport(transport)
|| !aether_ai_formats::is_openai_responses_format(provider_api_format)
|| aether_ai_formats::openai_responses_request_operation(
provider_api_format,
provider_request_body,
) == Some(aether_ai_formats::OPENAI_RESPONSES_OPERATION_COMPACT)
|| (!is_responses && !is_live)
|| is_responses
&& aether_ai_formats::openai_responses_request_operation(
provider_api_format,
provider_request_body,
) == Some(aether_ai_formats::OPENAI_RESPONSES_OPERATION_COMPACT)
|| !codex_fingerprint_convergence_enabled(
transport.provider.provider_type.as_str(),
transport.provider.config.as_ref(),
@@ -74,7 +77,12 @@ pub fn apply_codex_oauth_fingerprint_convergence(
let fingerprint = resolve_converged_fingerprint(account_seed, original_client_session_id);
apply_converged_headers(provider_request_headers, &fingerprint);
apply_converged_client_metadata(provider_request_body, &fingerprint);
// Live uses the converged identity on the WebSocket/call-control headers.
// Its event/session payload is an independent opaque protocol and must not
// receive Responses-only `client_metadata` fields.
if is_responses {
apply_converged_client_metadata(provider_request_body, &fingerprint);
}
true
}
@@ -477,6 +485,27 @@ mod tests {
assert_ne!(first.installation_id, other_account.installation_id);
}
#[test]
fn live_convergence_sets_the_websocket_identity_without_mutating_the_payload() {
let transport = sample_transport();
let original_body = json!({"model": "gpt-live", "future_live_field": true});
let mut body = original_body.clone();
let mut headers = BTreeMap::new();
assert!(apply_codex_oauth_fingerprint_convergence(
&transport,
"codex:live",
Some("client-live-session"),
&mut headers,
&mut body,
));
assert_eq!(body, original_body);
assert_eq!(headers.get("x-session-id"), headers.get("thread-id"));
assert!(headers.contains_key("x-codex-installation-id"));
assert!(headers.contains_key("x-codex-window-id"));
}
#[test]
fn disabled_or_out_of_scope_requests_are_unchanged() {
let mut transport = sample_transport();
@@ -291,7 +291,7 @@ const CLAUDE_CODE_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProvider
const CODEX_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
provider_type: "codex",
version: 1,
version: 2,
base_url: "https://chatgpt.com/backend-api/codex",
endpoints: &[
FixedProviderEndpointTemplate {
@@ -318,6 +318,12 @@ const CODEX_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTempla
custom_path: None,
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
},
FixedProviderEndpointTemplate {
item_key: "codex:live",
api_format: "codex:live",
custom_path: None,
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
},
],
runtime_policy: CODEX_RUNTIME_POLICY,
};
@@ -656,7 +662,7 @@ mod tests {
fn codex_fixed_provider_template_includes_codex_companion_endpoints() {
let template = fixed_provider_template("codex").expect("codex template should exist");
assert_eq!(template.base_url, "https://chatgpt.com/backend-api/codex");
assert_eq!(template.version, 1);
assert_eq!(template.version, 2);
assert_eq!(
template
.endpoints
@@ -667,7 +673,8 @@ mod tests {
"openai:responses",
"openai:responses:compact",
"openai:search",
"openai:image"
"openai:image",
"codex:live"
]
);
@@ -680,6 +687,12 @@ mod tests {
fixed_provider_endpoint_template_by_api_format("codex", "openai:search")
.expect("codex search endpoint should exist");
assert!(search_template.config_defaults.is_empty());
let live_template = fixed_provider_endpoint_template_by_api_format("codex", "codex:live")
.expect("codex Live endpoint should exist");
assert_eq!(live_template.item_key, "codex:live");
assert_eq!(live_template.custom_path, None);
assert!(live_template.config_defaults.is_empty());
}
#[test]
@@ -4,7 +4,7 @@ use std::sync::OnceLock;
use aether_ai_formats::ApiOperation;
use regex::Regex;
use serde_json::Value;
use url::form_urlencoded;
use url::{form_urlencoded, Url};
use crate::antigravity::{
build_antigravity_v1internal_url, is_antigravity_provider_transport,
@@ -128,7 +128,10 @@ fn build_transport_request_url_inner(
.ends_with("/messages/count_tokens");
let blocked_keys = if normalized_provider_api_format.starts_with("gemini:")
|| normalized_provider_api_format == "claude:messages"
{
|| matches!(
normalized_provider_api_format.as_str(),
"openai:realtime" | "codex:live"
) {
GATEWAY_CREDENTIAL_QUERY_KEYS
} else {
&[][..]
@@ -153,6 +156,11 @@ fn build_transport_request_url_inner(
{
url = build_claude_messages_url(&url, None);
}
let url = if normalized_provider_api_format == "openai:realtime" {
replace_realtime_model_query(url, params.mapped_model?)?
} else {
url
};
return Some(maybe_add_gemini_stream_alt_sse(
url,
&provider_api_format,
@@ -179,6 +187,19 @@ fn build_transport_request_url_inner(
&transport.endpoint.base_url,
params.request_query,
)),
"openai:realtime" => build_passthrough_path_url(
&transport.endpoint.base_url,
"/v1/realtime",
params.request_query,
GATEWAY_CREDENTIAL_QUERY_KEYS,
)
.and_then(|url| replace_realtime_model_query(url, params.mapped_model?)),
"codex:live" => build_passthrough_path_url(
&transport.endpoint.base_url,
"/live",
params.request_query,
GATEWAY_CREDENTIAL_QUERY_KEYS,
),
"openai:embedding" | "jina:embedding" => {
build_provider_embedding_v1_url(&transport.endpoint.base_url, params.request_query)
}
@@ -228,6 +249,24 @@ fn build_transport_request_url_inner(
))
}
fn replace_realtime_model_query(raw_url: String, mapped_model: &str) -> Option<String> {
let mut url = Url::parse(raw_url.as_str()).ok()?;
let retained = url
.query_pairs()
.filter(|(name, _)| !name.eq_ignore_ascii_case("model"))
.map(|(name, value)| (name.into_owned(), value.into_owned()))
.collect::<Vec<_>>();
url.set_query(None);
{
let mut query = url.query_pairs_mut();
for (name, value) in retained {
query.append_pair(name.as_str(), value.as_str());
}
query.append_pair("model", mapped_model);
}
Some(url.to_string())
}
pub fn build_local_openai_chat_upstream_url(
transport: &GatewayProviderTransportSnapshot,
request_query: Option<&str>,
@@ -786,6 +825,118 @@ mod tests {
}
}
#[test]
fn realtime_url_uses_mapped_model_and_drops_downstream_credentials() {
let transport = sample_transport(
"custom",
"openai:realtime",
"https://api.openai.com/v1",
None,
);
let url = build_transport_request_url(
&transport,
TransportRequestUrlParams {
provider_api_format: "openai:realtime",
mapped_model: Some("gpt-realtime-provider"),
upstream_is_stream: true,
request_query: Some("model=client-alias&trace=1&key=downstream-secret"),
kiro_api_region: None,
api_operation: None,
},
)
.expect("realtime URL");
assert_eq!(
url,
"https://api.openai.com/v1/realtime?trace=1&model=gpt-realtime-provider"
);
assert!(!url.contains("downstream-secret"));
assert!(!url.contains("client-alias"));
}
#[test]
fn realtime_custom_path_still_rewrites_the_model_query() {
let transport = sample_transport(
"custom",
"openai:realtime",
"https://voice.example.test/api",
Some("/socket/realtime?deployment=future"),
);
let url = build_transport_request_url(
&transport,
TransportRequestUrlParams {
provider_api_format: "openai:realtime",
mapped_model: Some("provider-model"),
upstream_is_stream: true,
request_query: Some("model=client-model&tenant=alpha"),
kiro_api_region: None,
api_operation: None,
},
)
.expect("custom realtime URL");
assert_eq!(
url,
"https://voice.example.test/api/socket/realtime?deployment=future&tenant=alpha&model=provider-model"
);
}
#[test]
fn codex_live_url_uses_the_dedicated_endpoint_without_forwarding_credentials() {
let transport = sample_transport(
"custom",
"codex:live",
"https://voice.example.test/v1",
None,
);
let url = build_transport_request_url(
&transport,
TransportRequestUrlParams {
provider_api_format: "codex:live",
mapped_model: Some("provider-model"),
upstream_is_stream: true,
request_query: Some("trace=1&key=downstream-secret"),
kiro_api_region: None,
api_operation: None,
},
)
.expect("Codex Live URL");
assert_eq!(url, "https://voice.example.test/v1/live?trace=1");
assert!(!url.contains("downstream-secret"));
}
#[test]
fn codex_live_custom_path_remains_an_explicit_protocol_endpoint() {
let transport = sample_transport(
"custom",
"codex:live",
"https://voice.example.test/v1",
Some("/socket/live?deployment=future"),
);
let url = build_transport_request_url(
&transport,
TransportRequestUrlParams {
provider_api_format: "codex:live",
mapped_model: Some("provider-model"),
upstream_is_stream: true,
request_query: Some("tenant=alpha&key=downstream-secret"),
kiro_api_region: None,
api_operation: None,
},
)
.expect("custom Codex Live URL");
assert_eq!(
url,
"https://voice.example.test/v1/socket/live?deployment=future&tenant=alpha"
);
}
#[test]
fn uses_vertex_hook_before_custom_path_for_custom_aiplatform_transport() {
let transport = sample_transport(
@@ -835,6 +835,8 @@ fn resolve_same_format_standard_direct_auth(
) -> Option<(String, String)> {
if aether_ai_formats::api_format_alias_matches(provider_api_format, "openai:embedding")
|| aether_ai_formats::api_format_alias_matches(provider_api_format, "openai:search")
|| aether_ai_formats::api_format_alias_matches(provider_api_format, "openai:realtime")
|| aether_ai_formats::api_format_alias_matches(provider_api_format, "codex:live")
{
resolve_local_openai_bearer_auth(transport)
} else {
@@ -1431,6 +1433,56 @@ mod tests {
);
}
#[test]
fn resolves_openai_realtime_direct_auth_with_bearer_header() {
let mut transport = sample_transport("openai");
transport.endpoint.api_format = "openai:realtime".to_string();
transport.key.auth_type = "api_key".to_string();
let behavior = classify_same_format_provider_request_behavior(
&transport,
SameFormatProviderRequestBehaviorParams {
require_streaming: true,
provider_api_format: "openai:realtime",
report_kind: "openai_realtime_websocket_success",
},
);
assert_eq!(
resolve_same_format_provider_direct_auth(
&behavior,
&transport,
SameFormatProviderFamily::Standard,
"openai:realtime",
),
Some(("authorization".to_string(), "Bearer secret".to_string()))
);
}
#[test]
fn resolves_codex_live_direct_auth_with_bearer_header() {
let mut transport = sample_transport("openai");
transport.endpoint.api_format = "codex:live".to_string();
transport.key.auth_type = "api_key".to_string();
let behavior = classify_same_format_provider_request_behavior(
&transport,
SameFormatProviderRequestBehaviorParams {
require_streaming: true,
provider_api_format: "codex:live",
report_kind: "codex_live_websocket_success",
},
);
assert_eq!(
resolve_same_format_provider_direct_auth(
&behavior,
&transport,
SameFormatProviderFamily::Standard,
"codex:live",
),
Some(("authorization".to_string(), "Bearer secret".to_string()))
);
}
#[test]
fn keeps_claude_same_format_api_key_on_x_api_key_header() {
let mut transport = sample_transport("custom");