mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 16:06:08 +00:00
fix(openai): preserve deployment account routing policy
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -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}",
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user