mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
fix(gateway): support current Codex Realtime live routes
This commit is contained in:
@@ -1708,14 +1708,22 @@ async fn receive_realtime_message(socket: &mut wreq::ws::WebSocket) -> WreqWsMes
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_creates_bound_codex_live_oauth_calls_with_opaque_session_fields() {
|
||||
fn gateway_creates_bound_codex_live_oauth_calls_with_legacy_responses_mapping() {
|
||||
super::run_frontdoor_async_test(
|
||||
"codex-live-oauth-frontdoor",
|
||||
run_codex_live_oauth_frontdoor_scenario(),
|
||||
run_codex_live_oauth_frontdoor_scenario(CodexLiveWebRtcTestDialect::LegacyLive),
|
||||
);
|
||||
}
|
||||
|
||||
async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
#[test]
|
||||
fn gateway_creates_bound_codex_realtime_oauth_calls_with_legacy_responses_mapping() {
|
||||
super::run_frontdoor_async_test(
|
||||
"codex-realtime-oauth-frontdoor",
|
||||
run_codex_live_oauth_frontdoor_scenario(CodexLiveWebRtcTestDialect::Realtime),
|
||||
);
|
||||
}
|
||||
|
||||
async fn run_codex_live_oauth_frontdoor_scenario(dialect: CodexLiveWebRtcTestDialect) {
|
||||
const PROVIDER_ID: &str = "provider-codex-live";
|
||||
const ENDPOINT_ID: &str = "endpoint-provider-codex-live";
|
||||
const UPSTREAM_KEY_ID: &str = "key-provider-codex-live";
|
||||
@@ -1724,6 +1732,15 @@ async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
const CALL_ID: &str = "rtc_frontdoor_live";
|
||||
|
||||
let mut row = sample_codex_live_candidate_row(PROVIDER_ID, CLIENT_MODEL, PROVIDER_MODEL);
|
||||
// Existing Codex associations predate the dedicated Live format. The
|
||||
// provider-aware compatibility rule must carry their Responses-scoped
|
||||
// source mapping through the complete scheduler, not only the repository
|
||||
// fast path.
|
||||
if let Some(mappings) = row.model_provider_model_mappings.as_mut() {
|
||||
for mapping in mappings {
|
||||
mapping.api_formats = Some(vec!["openai:responses".to_string()]);
|
||||
}
|
||||
}
|
||||
row.key_allowed_models = Some(vec![PROVIDER_MODEL.to_string()]);
|
||||
let candidate_repository =
|
||||
Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
|
||||
@@ -1755,6 +1772,8 @@ async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
vec![endpoint],
|
||||
vec![upstream_key],
|
||||
));
|
||||
let request_candidate_repository = Arc::new(InMemoryRequestCandidateRepository::default());
|
||||
let usage_repository = Arc::new(InMemoryUsageReadRepository::default());
|
||||
|
||||
let captured_plan = Arc::new(Mutex::new(None::<aether_contracts::ExecutionPlan>));
|
||||
let captured_plan_for_runtime = Arc::clone(&captured_plan);
|
||||
@@ -1804,13 +1823,19 @@ async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
let (execution_runtime_url, execution_runtime_handle) = start_server(execution_runtime).await;
|
||||
let state = build_state_with_execution_runtime_override(execution_runtime_url)
|
||||
.with_data_state_for_tests(
|
||||
crate::data::GatewayDataState::with_minimal_candidate_selection_and_auth_for_tests(
|
||||
candidate_repository,
|
||||
crate::data::GatewayDataState::with_auth_candidate_selection_provider_catalog_request_candidates_and_usage_for_tests(
|
||||
auth_repository,
|
||||
)
|
||||
.attach_provider_catalog_repository_for_tests(provider_catalog_repository)
|
||||
.with_encryption_key_for_tests(DEVELOPMENT_ENCRYPTION_KEY),
|
||||
);
|
||||
candidate_repository,
|
||||
provider_catalog_repository,
|
||||
request_candidate_repository,
|
||||
Arc::clone(&usage_repository),
|
||||
DEVELOPMENT_ENCRYPTION_KEY,
|
||||
),
|
||||
)
|
||||
.with_usage_runtime_for_tests(crate::usage::UsageRuntimeConfig {
|
||||
enabled: true,
|
||||
..crate::usage::UsageRuntimeConfig::default()
|
||||
});
|
||||
let gateway = build_router_with_state(state);
|
||||
let (gateway_url, gateway_handle) = start_server(gateway).await;
|
||||
|
||||
@@ -1829,7 +1854,10 @@ async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
session
|
||||
);
|
||||
let response = reqwest::Client::new()
|
||||
.post(format!("{gateway_url}/v1/live"))
|
||||
.post(format!(
|
||||
"{gateway_url}{}",
|
||||
dialect.call_create_request_target()
|
||||
))
|
||||
.header("authorization", "Bearer sk-codex-live")
|
||||
.header(
|
||||
"content-type",
|
||||
@@ -1842,7 +1870,7 @@ async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
.await
|
||||
.expect("Codex Live call creation should complete");
|
||||
assert_eq!(response.status(), StatusCode::CREATED);
|
||||
let downstream_location = format!("/v1/live/{CALL_ID}");
|
||||
let downstream_location = dialect.call_location(CALL_ID);
|
||||
assert_eq!(
|
||||
response
|
||||
.headers()
|
||||
@@ -1896,7 +1924,7 @@ async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
assert_eq!(provider_body["session"]["instructions"], "Keep this opaque");
|
||||
assert_eq!(
|
||||
plan.headers.get("openai-alpha").map(String::as_str),
|
||||
Some("quicksilver=v2")
|
||||
Some(dialect.alpha_header())
|
||||
);
|
||||
assert_eq!(
|
||||
plan.headers.get("originator").map(String::as_str),
|
||||
@@ -1918,6 +1946,49 @@ async fn run_codex_live_oauth_frontdoor_scenario() {
|
||||
assert_eq!(plan.headers.get("thread-id"), Some(converged_session));
|
||||
uuid::Uuid::parse_str(converged_session).expect("converged session ID must be a UUID");
|
||||
|
||||
let call_create_usage = tokio::time::timeout(std::time::Duration::from_secs(5), async {
|
||||
loop {
|
||||
if let Some(usage) = usage_repository
|
||||
.find_by_request_id(plan.request_id.as_str())
|
||||
.await
|
||||
.expect("Live call-create usage read should succeed")
|
||||
{
|
||||
break usage;
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("Live call-create usage should be persisted before timeout");
|
||||
assert_eq!(call_create_usage.status, "completed");
|
||||
assert_eq!(call_create_usage.billing_status, "void");
|
||||
assert_eq!(call_create_usage.status_code, Some(201));
|
||||
assert_eq!(call_create_usage.request_type.as_deref(), Some("live"));
|
||||
assert_eq!(call_create_usage.api_format.as_deref(), Some("codex:live"));
|
||||
assert_eq!(call_create_usage.model, CLIENT_MODEL);
|
||||
assert!(!call_create_usage.is_stream);
|
||||
assert!(!call_create_usage.is_websocket());
|
||||
assert_eq!(call_create_usage.websocket_transport(), None);
|
||||
assert!(!call_create_usage.usage_available());
|
||||
assert!(!call_create_usage.usage_pricing_available());
|
||||
assert_eq!(call_create_usage.total_tokens, 0);
|
||||
assert_eq!(call_create_usage.total_cost_usd, 0.0);
|
||||
assert_eq!(call_create_usage.actual_total_cost_usd, 0.0);
|
||||
assert!(call_create_usage.request_headers.is_none());
|
||||
assert!(call_create_usage.request_body.is_none());
|
||||
assert!(call_create_usage.provider_request_headers.is_none());
|
||||
assert!(call_create_usage.provider_request_body.is_none());
|
||||
let serialized_call_create_usage =
|
||||
serde_json::to_string(&call_create_usage).expect("Live call-create usage should serialize");
|
||||
for sentinel in [
|
||||
offer_sdp,
|
||||
"Keep this opaque",
|
||||
"sk-codex-live",
|
||||
"oauth-upstream-secret",
|
||||
] {
|
||||
assert!(!serialized_call_create_usage.contains(sentinel));
|
||||
}
|
||||
|
||||
gateway_handle.abort();
|
||||
execution_runtime_handle.abort();
|
||||
}
|
||||
@@ -1927,20 +1998,78 @@ struct ObservedCodexLiveWebSocket {
|
||||
request_target: String,
|
||||
authorization: Option<String>,
|
||||
alpha: Option<String>,
|
||||
originator: Option<String>,
|
||||
session_id: Option<String>,
|
||||
initial_event: serde_json::Value,
|
||||
event_after_turn_done: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum CodexLiveDirectTestDialect {
|
||||
LegacyLive,
|
||||
RealtimeV1,
|
||||
RealtimeV2,
|
||||
}
|
||||
|
||||
impl CodexLiveDirectTestDialect {
|
||||
fn public_path(self) -> &'static str {
|
||||
match self {
|
||||
Self::LegacyLive => "/v1/live",
|
||||
Self::RealtimeV1 | Self::RealtimeV2 => "/v1/realtime",
|
||||
}
|
||||
}
|
||||
|
||||
fn query_prefix(self) -> &'static str {
|
||||
match self {
|
||||
Self::RealtimeV1 => "intent=quicksilver&",
|
||||
Self::LegacyLive | Self::RealtimeV2 => "",
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_upstream_target(self, provider_model: &str) -> String {
|
||||
match self {
|
||||
Self::LegacyLive => format!("/v1/live?model={provider_model}"),
|
||||
Self::RealtimeV1 => {
|
||||
format!("/v1/realtime?intent=quicksilver&model={provider_model}")
|
||||
}
|
||||
Self::RealtimeV2 => format!("/v1/realtime?model={provider_model}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_alpha(self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::LegacyLive => Some("quicksilver=v2"),
|
||||
Self::RealtimeV1 => Some("quicksilver=v1"),
|
||||
Self::RealtimeV2 => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_relays_codex_live_api_key_websocket_opaquely() {
|
||||
super::run_frontdoor_async_test(
|
||||
"codex-live-api-key-websocket-frontdoor",
|
||||
run_codex_live_api_key_websocket_frontdoor_scenario(),
|
||||
run_codex_live_api_key_websocket_frontdoor_scenario(CodexLiveDirectTestDialect::LegacyLive),
|
||||
);
|
||||
}
|
||||
|
||||
async fn run_codex_live_api_key_websocket_frontdoor_scenario() {
|
||||
#[test]
|
||||
fn gateway_relays_codex_realtime_v2_api_key_websocket_opaquely() {
|
||||
super::run_frontdoor_async_test(
|
||||
"codex-realtime-v2-api-key-websocket-frontdoor",
|
||||
run_codex_live_api_key_websocket_frontdoor_scenario(CodexLiveDirectTestDialect::RealtimeV2),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_relays_codex_realtime_v1_api_key_websocket_opaquely() {
|
||||
super::run_frontdoor_async_test(
|
||||
"codex-realtime-v1-api-key-websocket-frontdoor",
|
||||
run_codex_live_api_key_websocket_frontdoor_scenario(CodexLiveDirectTestDialect::RealtimeV1),
|
||||
);
|
||||
}
|
||||
|
||||
async fn run_codex_live_api_key_websocket_frontdoor_scenario(dialect: CodexLiveDirectTestDialect) {
|
||||
const PROVIDER_ID: &str = "provider-codex-live-api-key";
|
||||
const ENDPOINT_ID: &str = "endpoint-provider-codex-live-api-key";
|
||||
const UPSTREAM_KEY_ID: &str = "key-provider-codex-live-api-key";
|
||||
@@ -1951,6 +2080,7 @@ async fn run_codex_live_api_key_websocket_frontdoor_scenario() {
|
||||
let upstream_state = Arc::new(Mutex::new(Some(observed_tx)));
|
||||
let upstream = Router::new()
|
||||
.route("/v1/live", get(mock_codex_live_websocket))
|
||||
.route("/v1/realtime", get(mock_codex_live_websocket))
|
||||
.with_state(upstream_state);
|
||||
let (upstream_url, upstream_handle) = start_server(upstream).await;
|
||||
|
||||
@@ -2014,10 +2144,18 @@ async fn run_codex_live_api_key_websocket_frontdoor_scenario() {
|
||||
http::HeaderName::from_static("openai-alpha"),
|
||||
http::HeaderValue::from_static("client-value-must-be-replaced"),
|
||||
);
|
||||
if matches!(dialect, CodexLiveDirectTestDialect::RealtimeV2) {
|
||||
handshake_headers.insert(
|
||||
http::HeaderName::from_static("originator"),
|
||||
http::HeaderValue::from_static("codex_work_desktop"),
|
||||
);
|
||||
}
|
||||
let invalid_model_response = wreq::Client::new()
|
||||
.websocket(format!(
|
||||
"{}/v1/live?model={CLIENT_MODEL}&model=second-model",
|
||||
gateway_url.replacen("http://", "ws://", 1)
|
||||
"{}{}?{}model={CLIENT_MODEL}&model=second-model",
|
||||
gateway_url.replacen("http://", "ws://", 1),
|
||||
dialect.public_path(),
|
||||
dialect.query_prefix(),
|
||||
))
|
||||
.headers(handshake_headers.clone())
|
||||
.send()
|
||||
@@ -2026,8 +2164,10 @@ async fn run_codex_live_api_key_websocket_frontdoor_scenario() {
|
||||
assert_eq!(invalid_model_response.status(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let websocket_url = format!(
|
||||
"{}/v1/live?foo=bar&model={CLIENT_MODEL}&trace=1",
|
||||
gateway_url.replacen("http://", "ws://", 1)
|
||||
"{}{}?{}foo=bar&model={CLIENT_MODEL}&trace=1",
|
||||
gateway_url.replacen("http://", "ws://", 1),
|
||||
dialect.public_path(),
|
||||
dialect.query_prefix(),
|
||||
);
|
||||
let response = wreq::Client::new()
|
||||
.websocket(websocket_url)
|
||||
@@ -2091,13 +2231,17 @@ async fn run_codex_live_api_key_websocket_frontdoor_scenario() {
|
||||
.expect("mock upstream observation channel should remain open");
|
||||
assert_eq!(
|
||||
observed.request_target,
|
||||
format!("/v1/live?model={PROVIDER_MODEL}")
|
||||
dialect.expected_upstream_target(PROVIDER_MODEL)
|
||||
);
|
||||
assert_eq!(
|
||||
observed.authorization.as_deref(),
|
||||
Some("Bearer oauth-upstream-secret")
|
||||
);
|
||||
assert_eq!(observed.alpha.as_deref(), Some("quicksilver=v2"));
|
||||
assert_eq!(observed.alpha.as_deref(), dialect.expected_alpha());
|
||||
assert_eq!(
|
||||
observed.originator.as_deref(),
|
||||
matches!(dialect, CodexLiveDirectTestDialect::RealtimeV2).then_some("codex_work_desktop")
|
||||
);
|
||||
assert_eq!(observed.session_id.as_deref(), Some("stable-live-session"));
|
||||
let mut expected_initial_event = initial_event;
|
||||
expected_initial_event["session"]["model"] = json!(PROVIDER_MODEL);
|
||||
@@ -2119,15 +2263,63 @@ struct ObservedCodexLiveSideband {
|
||||
session_update: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum CodexLiveWebRtcTestDialect {
|
||||
LegacyLive,
|
||||
Realtime,
|
||||
}
|
||||
|
||||
impl CodexLiveWebRtcTestDialect {
|
||||
fn call_create_path(self) -> &'static str {
|
||||
match self {
|
||||
Self::LegacyLive => "/v1/live",
|
||||
Self::Realtime => "/v1/realtime/calls",
|
||||
}
|
||||
}
|
||||
|
||||
fn call_location(self, call_id: &str) -> String {
|
||||
format!("{}/{call_id}", self.call_create_path())
|
||||
}
|
||||
|
||||
fn call_create_request_target(self) -> &'static str {
|
||||
match self {
|
||||
Self::LegacyLive => "/v1/live",
|
||||
Self::Realtime => "/v1/realtime/calls?intent=quicksilver&architecture=avas",
|
||||
}
|
||||
}
|
||||
|
||||
fn alpha_header(self) -> &'static str {
|
||||
match self {
|
||||
Self::LegacyLive => "quicksilver=v2",
|
||||
Self::Realtime => "quicksilver=v1",
|
||||
}
|
||||
}
|
||||
|
||||
fn sideband_path(self, call_id: &str) -> String {
|
||||
match self {
|
||||
Self::LegacyLive => format!("/v1/live/{call_id}"),
|
||||
Self::Realtime => format!("/v1/realtime?intent=quicksilver&call_id={call_id}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_creates_and_relays_bound_codex_live_api_key_sideband() {
|
||||
super::run_frontdoor_async_test(
|
||||
"codex-live-api-key-sideband-frontdoor",
|
||||
run_codex_live_api_key_sideband_frontdoor_scenario(),
|
||||
run_codex_live_api_key_sideband_frontdoor_scenario(CodexLiveWebRtcTestDialect::LegacyLive),
|
||||
);
|
||||
}
|
||||
|
||||
async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
#[test]
|
||||
fn gateway_creates_and_relays_bound_codex_live_api_key_realtime_calls_sideband() {
|
||||
super::run_frontdoor_async_test(
|
||||
"codex-live-api-key-realtime-calls-sideband-frontdoor",
|
||||
run_codex_live_api_key_sideband_frontdoor_scenario(CodexLiveWebRtcTestDialect::Realtime),
|
||||
);
|
||||
}
|
||||
|
||||
async fn run_codex_live_api_key_sideband_frontdoor_scenario(dialect: CodexLiveWebRtcTestDialect) {
|
||||
const PROVIDER_ID: &str = "provider-codex-live-sideband";
|
||||
const ENDPOINT_ID: &str = "endpoint-provider-codex-live-sideband";
|
||||
const UPSTREAM_KEY_ID: &str = "key-provider-codex-live-sideband";
|
||||
@@ -2142,12 +2334,13 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
"/v1/live/{call_id}",
|
||||
get(mock_codex_live_sideband_websocket),
|
||||
)
|
||||
.route("/v1/realtime", get(mock_codex_live_sideband_websocket))
|
||||
.with_state(upstream_state);
|
||||
let (upstream_url, upstream_handle) = start_server(upstream).await;
|
||||
|
||||
let captured_plan = Arc::new(Mutex::new(None::<aether_contracts::ExecutionPlan>));
|
||||
let captured_plan_for_runtime = Arc::clone(&captured_plan);
|
||||
let upstream_location = format!("{upstream_url}/v1/live/{CALL_ID}");
|
||||
let upstream_location = format!("{upstream_url}{}", dialect.call_location(CALL_ID));
|
||||
let execution_runtime = Router::new().route(
|
||||
"/v1/execute/sync",
|
||||
any(move |request: Request| {
|
||||
@@ -2237,9 +2430,11 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
let gateway = build_router_with_state(state);
|
||||
let (gateway_url, gateway_handle) = start_server(gateway).await;
|
||||
|
||||
let sideband_path = dialect.sideband_path(CALL_ID);
|
||||
let sideband_url = format!(
|
||||
"{}/v1/live/{CALL_ID}",
|
||||
gateway_url.replacen("http://", "ws://", 1)
|
||||
"{}{}",
|
||||
gateway_url.replacen("http://", "ws://", 1),
|
||||
sideband_path
|
||||
);
|
||||
let mut sideband_headers = HeaderMap::new();
|
||||
sideband_headers.insert(
|
||||
@@ -2273,7 +2468,10 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
session
|
||||
);
|
||||
let response = reqwest::Client::new()
|
||||
.post(format!("{gateway_url}/v1/live"))
|
||||
.post(format!(
|
||||
"{gateway_url}{}",
|
||||
dialect.call_create_request_target()
|
||||
))
|
||||
.header("authorization", "Bearer sk-codex-live-sideband")
|
||||
.header(
|
||||
"content-type",
|
||||
@@ -2291,7 +2489,7 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
.headers()
|
||||
.get(http::header::LOCATION)
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
Some(format!("/v1/live/{CALL_ID}").as_str())
|
||||
Some(dialect.call_location(CALL_ID).as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
response
|
||||
@@ -2315,8 +2513,17 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
.clone()
|
||||
.expect("Live API-key call should reach execution runtime");
|
||||
let plan_url = url::Url::parse(plan.url.as_str()).expect("Live API-key URL should parse");
|
||||
assert_eq!(plan_url.path(), "/v1/live");
|
||||
assert!(plan_url.query().is_none());
|
||||
assert_eq!(plan_url.path(), dialect.call_create_path());
|
||||
match dialect {
|
||||
CodexLiveWebRtcTestDialect::LegacyLive => assert!(plan_url.query().is_none()),
|
||||
CodexLiveWebRtcTestDialect::Realtime => assert_eq!(
|
||||
plan_url.query_pairs().collect::<HashMap<_, _>>(),
|
||||
HashMap::from([
|
||||
("intent".into(), "quicksilver".into()),
|
||||
("architecture".into(), "avas".into()),
|
||||
])
|
||||
),
|
||||
}
|
||||
assert_eq!(plan.method, "POST");
|
||||
assert!(!plan.stream);
|
||||
assert!(plan
|
||||
@@ -2344,7 +2551,7 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
);
|
||||
assert_eq!(
|
||||
plan.headers.get("openai-alpha").map(String::as_str),
|
||||
Some("quicksilver=v2")
|
||||
Some(dialect.alpha_header())
|
||||
);
|
||||
assert_eq!(
|
||||
plan.headers.get("x-session-id").map(String::as_str),
|
||||
@@ -2380,8 +2587,9 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
);
|
||||
let conflicting_response = wreq::Client::new()
|
||||
.websocket(format!(
|
||||
"{}/v1/live/{CALL_ID}",
|
||||
gateway_url.replacen("http://", "ws://", 1)
|
||||
"{}{}",
|
||||
gateway_url.replacen("http://", "ws://", 1),
|
||||
dialect.sideband_path(CALL_ID)
|
||||
))
|
||||
.headers(sideband_headers)
|
||||
.send()
|
||||
@@ -2414,12 +2622,12 @@ async fn run_codex_live_api_key_sideband_frontdoor_scenario() {
|
||||
.await
|
||||
.expect("mock sideband should observe the opaque command before timeout")
|
||||
.expect("mock sideband observation channel should remain open");
|
||||
assert_eq!(observed.request_target, format!("/v1/live/{CALL_ID}"));
|
||||
assert_eq!(observed.request_target, sideband_path);
|
||||
assert_eq!(
|
||||
observed.authorization.as_deref(),
|
||||
Some("Bearer oauth-upstream-secret")
|
||||
);
|
||||
assert_eq!(observed.alpha.as_deref(), Some("quicksilver=v2"));
|
||||
assert_eq!(observed.alpha.as_deref(), Some(dialect.alpha_header()));
|
||||
assert_eq!(
|
||||
observed.session_id.as_deref(),
|
||||
Some("stable-live-sideband-session")
|
||||
@@ -2501,6 +2709,10 @@ async fn mock_codex_live_websocket(
|
||||
.get("openai-alpha")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::to_string);
|
||||
let originator = headers
|
||||
.get("originator")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::to_string);
|
||||
let session_id = headers
|
||||
.get("x-session-id")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
@@ -2535,6 +2747,7 @@ async fn mock_codex_live_websocket(
|
||||
request_target,
|
||||
authorization,
|
||||
alpha,
|
||||
originator,
|
||||
session_id,
|
||||
initial_event,
|
||||
event_after_turn_done,
|
||||
|
||||
Reference in New Issue
Block a user