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
@@ -210,6 +210,28 @@ mod tests {
.expect("endpoint transport should build");
responses.updated_at_unix_secs = Some(100);
let mut live = StoredProviderCatalogEndpoint::new(
"endpoint-codex-live".to_string(),
provider.id.clone(),
"codex:live".to_string(),
Some("codex".to_string()),
Some("live".to_string()),
false,
)
.expect("Live endpoint should build")
.with_transport_fields(
"https://voice-proxy.internal/v1".to_string(),
None,
None,
Some(6),
Some("/socket/live".to_string()),
Some(json!({"custom_transport_option": true})),
None,
Some(json!({"url": "http://voice-proxy.internal:8080"})),
)
.expect("Live endpoint transport should build");
live.updated_at_unix_secs = Some(100);
let mut key = StoredProviderCatalogKey::new(
"key-codex".to_string(),
provider.id.clone(),
@@ -228,10 +250,17 @@ mod tests {
"custom".to_string(),
)
.expect("unrelated provider should build");
let fresh_codex_provider = StoredProviderCatalogProvider::new(
"provider-codex-fresh".to_string(),
"Fresh Codex".to_string(),
None,
"codex".to_string(),
)
.expect("fresh Codex provider should build");
let repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
vec![provider, unrelated_provider],
vec![responses],
vec![provider, fresh_codex_provider, unrelated_provider],
vec![responses, live],
vec![key],
));
let state = AppState::new()
@@ -247,7 +276,7 @@ mod tests {
.list_endpoints_by_provider_ids(&["provider-codex".to_string()])
.await
.expect("endpoints should list");
assert_eq!(first_endpoints.len(), 4);
assert_eq!(first_endpoints.len(), 5);
let responses = first_endpoints
.iter()
.find(|endpoint| endpoint.api_format == "openai:responses")
@@ -272,6 +301,26 @@ mod tests {
assert!(first_endpoints
.iter()
.any(|endpoint| endpoint.api_format == "openai:search"));
let live = first_endpoints
.iter()
.find(|endpoint| endpoint.api_format == "codex:live")
.expect("Codex Live endpoint should be reconciled from the v2 template");
assert_eq!(live.api_family.as_deref(), Some("codex"));
assert_eq!(live.endpoint_kind.as_deref(), Some("live"));
assert_eq!(live.base_url, "https://voice-proxy.internal/v1");
assert_eq!(live.custom_path.as_deref(), Some("/socket/live"));
assert_eq!(live.max_retries, Some(6));
assert_eq!(
live.proxy,
Some(json!({"url": "http://voice-proxy.internal:8080"}))
);
assert_eq!(
live.config
.as_ref()
.and_then(|value| value.get("custom_transport_option")),
Some(&json!(true))
);
assert!(!live.is_active);
let keys = repository
.list_keys_by_provider_ids(&["provider-codex".to_string()])
.await
@@ -283,6 +332,17 @@ mod tests {
.await
.expect("unrelated endpoints should list")
.is_empty());
let fresh_endpoints = repository
.list_endpoints_by_provider_ids(&["provider-codex-fresh".to_string()])
.await
.expect("fresh Codex endpoints should list");
assert_eq!(fresh_endpoints.len(), 5);
let fresh_live = fresh_endpoints
.iter()
.find(|endpoint| endpoint.api_format == "codex:live")
.expect("v2 reconciliation should add a Codex Live endpoint");
assert_eq!(fresh_live.base_url, "https://chatgpt.com/backend-api/codex");
assert!(fresh_live.is_active);
assert!(perform_fixed_provider_reconciliation_once(&state)
.await
@@ -292,6 +352,11 @@ mod tests {
.await
.expect("endpoints should list again");
assert_eq!(second_endpoints, first_endpoints);
let second_fresh_endpoints = repository
.list_endpoints_by_provider_ids(&["provider-codex-fresh".to_string()])
.await
.expect("fresh Codex endpoints should list again");
assert_eq!(second_fresh_endpoints, fresh_endpoints);
}
#[tokio::test]