fix(openai): preserve deployment account routing policy

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-11-03 11:33:53 +08:00
co-authored by Codex
parent 3b4b547e9c
commit e7c24781fb
9 changed files with 41 additions and 45 deletions
@@ -32,7 +32,6 @@ impl CodexBackendClient {
content_type: Option<&str>,
) -> CodexClientResult<HeaderMap> {
let mut headers = self.model_request_headers(&self.profile.snapshot(), context)?;
let mut passthrough = HeaderMap::new();
for header in extra_protocol_headers {
let name = HeaderName::from_bytes(header.name().as_bytes())?;
if is_managed_identity_header(name.as_str())
@@ -58,9 +57,8 @@ impl CodexBackendClient {
} else {
HeaderValue::from_bytes(header.value())?
};
passthrough.append(name, value);
headers.append(name, value);
}
headers.extend(passthrough);
if let Some(content_type) = content_type {
headers.insert(
reqwest::header::CONTENT_TYPE,
@@ -34,7 +34,9 @@ pub(super) fn is_managed_identity_header(name: &str) -> bool {
| "originator"
| "user-agent"
| "version"
// 地域和账号路由统一按部署配置生成
| "x-openai-internal-codex-residency"
| "x-openai-account-routing-override"
| "x-oai-attestation"
| "x-oai-is"
| "x-oai-is-update"
@@ -45,7 +45,6 @@ const DOWNSTREAM_CLIENT_HEADERS: &[(&str, &str)] = &[
];
const BUSINESS_EXTENSION_HEADERS: &[(&str, &str)] = &[
("X-OpenAI-Account-Routing-Override", "first-route"),
("x-openai-fedramp", "true"),
("x-grok-turn-idx", "7"),
("X-Grok-Model-Override", "synthetic-model"),
@@ -79,6 +78,10 @@ fn request_with_opaque_headers(use_websocket: bool) -> CodexResponsesRequest {
"x-openai-internal-codex-residency",
STANDARD.encode(b"future-region")
],
[
"X-OpenAI-Account-Routing-Override",
STANDARD.encode(b"client-route")
],
["x-codex-turn-state", STANDARD.encode(b"turn-ascii")],
["x-codex-turn-state", STANDARD.encode(b"turn-\x80")],
["bad header name", STANDARD.encode(b"ignored")],
@@ -286,6 +289,10 @@ async fn backend_http_should_preserve_business_headers_without_downstream_transp
raw_header_values(&raw, "traceparent"),
vec![b"synthetic-trace".to_vec()]
);
assert_eq!(
raw_header_values(&raw, "x-openai-account-routing-override"),
vec![b"configured-route".to_vec()]
);
assert_eq!(
raw_header_values(&raw, "x-codex-installation-id"),
vec![b"lease-installation".to_vec()]
@@ -475,6 +482,10 @@ async fn backend_websocket_should_preserve_business_headers_without_downstream_t
assert_eq!(values(name), vec![value.to_vec()], "missing {name}");
}
assert_eq!(values("traceparent"), vec![b"synthetic-trace".to_vec()]);
assert_eq!(
values("x-openai-account-routing-override"),
vec![b"configured-route".to_vec()]
);
assert_eq!(
values("x-codex-installation-id"),
vec![b"lease-installation".to_vec()]
@@ -71,20 +71,15 @@ fn assert_isolated_request(
body: &Value,
owner: Option<&str>,
) {
for (name, values) in [
("x-openai-account-routing-override", ["us", "us_cr"]),
("x-openai-fedramp", ["true", "false"]),
] {
assert_eq!(
headers
.get_all(name)
.iter()
.map(|value| value.to_str().unwrap())
.collect::<Vec<_>>(),
values,
"changed {name} with owner {owner:?}"
);
}
assert!(!headers.contains_key("x-openai-account-routing-override"));
assert_eq!(
headers
.get_all("x-openai-fedramp")
.iter()
.map(|value| value.to_str().unwrap())
.collect::<Vec<_>>(),
["true", "false"]
);
assert_eq!(headers["x-codex-guardian"], "true");
assert_eq!(
headers
@@ -120,7 +115,7 @@ fn assert_isolated_request(
}
#[tokio::test]
async fn http_preserves_routing_headers_and_only_scopes_guardian_parent_reference() {
async fn http_applies_deployment_routing_and_only_scopes_guardian_parent_reference() {
for owner in [Some(SELECTED_ACCOUNT), Some("acct_scope_old"), None] {
let store = Arc::new(MemoryAccountStore::default());
create_account(&store, SELECTED_ACCOUNT).await;
@@ -163,7 +158,7 @@ async fn http_preserves_routing_headers_and_only_scopes_guardian_parent_referenc
}
#[tokio::test]
async fn websocket_preserves_routing_headers_and_only_scopes_guardian_parent_reference() {
async fn websocket_applies_deployment_routing_and_only_scopes_guardian_parent_reference() {
for owner in [Some(SELECTED_ACCOUNT), Some("acct_scope_old"), None] {
let store = Arc::new(MemoryAccountStore::default());
create_account(&store, SELECTED_ACCOUNT).await;
@@ -142,8 +142,12 @@ async fn independent_endpoints_send_opaque_headers_without_client_identity() {
let sent = requests.last().unwrap();
assert_eq!(sent.url.path(), endpoint);
uuid::Uuid::parse_str(sent.headers["x-codex-installation-id"].to_str().unwrap()).unwrap();
assert!(
!sent
.headers
.contains_key("x-openai-account-routing-override")
);
for (name, expected) in [
("x-openai-account-routing-override", "client-route"),
("x-openai-fedramp", "true"),
("traceparent", "client-trace"),
("tracestate", "vendor=client"),
@@ -457,14 +457,6 @@ fn assert_live_correlation(headers: &reqwest::header::HeaderMap) {
assert_eq!(headers["thread-id"], session);
assert_eq!(headers["openai-alpha"], "live-test");
assert_eq!(headers["x-openai-fedramp"], "true");
assert_eq!(
headers
.get_all("x-openai-account-routing-override")
.iter()
.map(|value| value.to_str().unwrap())
.collect::<Vec<_>>(),
["first-route", "second-route"]
);
assert_eq!(
headers
.get_all("x-test-business")
@@ -478,6 +470,7 @@ fn assert_live_correlation(headers: &reqwest::header::HeaderMap) {
"x-oai-attestation",
"x-forwarded-for",
"x-stainless-runtime",
"x-openai-account-routing-override",
"x-codex-turn-state",
"x-codex-routing-hint",
] {
@@ -143,7 +143,7 @@ fn deployment_routing_survives_artifact_updates_and_profile_selection() {
}
#[tokio::test]
async fn model_transports_apply_residency_and_client_or_default_routing() {
async fn model_transports_apply_deployment_routing_with_or_without_client_headers() {
use base64::{Engine as _, engine::general_purpose::STANDARD};
use gateway_core::operation::{GenerateRequest, ProtocolPayload};
@@ -266,19 +266,13 @@ async fn model_transports_apply_residency_and_client_or_default_routing() {
.expect("model response");
let headers = capture.await;
for (name, value) in HEADERS.into_iter().zip(expected) {
let expected_values =
if name == "x-openai-account-routing-override" && has_downstream_headers {
vec!["downstream-first", "downstream-second"]
} else {
value.into_iter().collect::<Vec<_>>()
};
assert_eq!(
headers
.get_all(name)
.iter()
.map(|value| value.to_str().unwrap())
.collect::<Vec<_>>(),
expected_values,
value.into_iter().collect::<Vec<_>>(),
"{document}, websocket={websocket}, downstream={has_downstream_headers}, {name}",
);
}