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
@@ -1188,7 +1188,7 @@ async fn gateway_creates_admin_provider_locally_with_trusted_admin_principal() {
.list_endpoints_by_provider_ids(std::slice::from_ref(&created.id))
.await
.expect("endpoints should list");
assert_eq!(endpoints.len(), 4);
assert_eq!(endpoints.len(), 5);
let responses_endpoint = endpoints
.iter()
.find(|endpoint| endpoint.api_format == "openai:responses")
@@ -1205,6 +1205,10 @@ async fn gateway_creates_admin_provider_locally_with_trusted_admin_principal() {
.iter()
.find(|endpoint| endpoint.api_format == "openai:image")
.expect("image endpoint should exist");
let live_endpoint = endpoints
.iter()
.find(|endpoint| endpoint.api_format == "codex:live")
.expect("Codex Live endpoint should exist");
assert_eq!(
responses_endpoint.base_url,
"https://chatgpt.com/backend-api/codex"
@@ -1225,6 +1229,14 @@ async fn gateway_creates_admin_provider_locally_with_trusted_admin_principal() {
assert_eq!(compact_endpoint.max_retries, Some(7));
assert_eq!(search_endpoint.max_retries, Some(7));
assert_eq!(image_endpoint.max_retries, Some(7));
assert_eq!(live_endpoint.api_family.as_deref(), Some("codex"));
assert_eq!(live_endpoint.endpoint_kind.as_deref(), Some("live"));
assert_eq!(
crate::api::ai::public_api_format_local_path(&live_endpoint.api_format),
"/v1/live"
);
assert!(live_endpoint.custom_path.is_none());
assert_eq!(live_endpoint.max_retries, Some(7));
assert_eq!(
responses_endpoint
.config
@@ -1253,6 +1265,7 @@ async fn gateway_creates_admin_provider_locally_with_trusted_admin_principal() {
assert!(compact_endpoint.body_rules.is_none());
assert!(search_endpoint.body_rules.is_none());
assert!(image_endpoint.body_rules.is_none());
assert!(live_endpoint.body_rules.is_none());
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
gateway_handle.abort();
@@ -1296,6 +1309,14 @@ async fn gateway_updates_fixed_provider_and_reconciles_template_managed_endpoint
);
cli_endpoint.max_retries = Some(2);
cli_endpoint.config = Some(json!({"upstream_stream_policy": "force_stream"}));
let mut live_endpoint = sample_endpoint(
"endpoint-codex-live",
"provider-codex",
"codex:live",
"https://chatgpt.com/backend-api/codex",
);
live_endpoint.max_retries = Some(2);
live_endpoint.custom_path = Some("/custom/live".to_string());
let mut key = sample_key(
"key-codex-oauth",
"provider-codex",
@@ -1307,7 +1328,7 @@ async fn gateway_updates_fixed_provider_and_reconciles_template_managed_endpoint
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
vec![provider],
vec![cli_endpoint],
vec![cli_endpoint, live_endpoint],
vec![key],
));
@@ -1367,7 +1388,7 @@ async fn gateway_updates_fixed_provider_and_reconciles_template_managed_endpoint
.list_endpoints_by_provider_ids(&["provider-codex".to_string()])
.await
.expect("endpoints should list");
assert_eq!(endpoints.len(), 4);
assert_eq!(endpoints.len(), 5);
let responses_endpoint = endpoints
.iter()
.find(|endpoint| endpoint.api_format == "openai:responses")
@@ -1384,11 +1405,23 @@ async fn gateway_updates_fixed_provider_and_reconciles_template_managed_endpoint
.iter()
.find(|endpoint| endpoint.api_format == "openai:image")
.expect("image endpoint should exist");
let live_endpoint = endpoints
.iter()
.find(|endpoint| endpoint.api_format == "codex:live")
.expect("Codex Live endpoint should exist");
assert_eq!(responses_endpoint.max_retries, Some(9));
assert_eq!(compact_endpoint.max_retries, Some(9));
assert_eq!(search_endpoint.max_retries, Some(9));
assert_eq!(image_endpoint.max_retries, Some(9));
assert_eq!(live_endpoint.api_family.as_deref(), Some("codex"));
assert_eq!(live_endpoint.endpoint_kind.as_deref(), Some("live"));
assert_eq!(
crate::api::ai::public_api_format_local_path(&live_endpoint.api_format),
"/v1/live"
);
assert_eq!(live_endpoint.custom_path.as_deref(), Some("/custom/live"));
assert_eq!(live_endpoint.max_retries, Some(9));
assert_eq!(
responses_endpoint
.config