feat(format): audit same-format compatibility rewrites

This commit is contained in:
elky
2026-06-08 16:12:37 +08:00
parent 6155ffefcc
commit 84f41dae77
8 changed files with 297 additions and 23 deletions
@@ -107,6 +107,11 @@ pub(crate) async fn maybe_build_local_same_format_provider_decision_payload_for_
json!(crate::ai_serving::transport::GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME),
);
}
if !resolved.compatibility_edits.is_empty() {
if let Ok(value) = serde_json::to_value(&resolved.compatibility_edits) {
extra_fields.insert("request_body_compatibility_edits".to_string(), value);
}
}
let provider_api_format = resolved.provider_api_format.clone();
let effective_headers = input.effective_headers(&parts.headers);
let report_context = append_local_failover_policy_to_value(
@@ -175,6 +180,7 @@ pub(crate) async fn maybe_build_local_same_format_provider_decision_payload_for_
provider_request_headers,
provider_request_body,
transport_profile: _,
compatibility_edits: _,
request_redacted: _,
} = resolved;
@@ -19,7 +19,9 @@ use crate::ai_serving::transport::{
build_gemini_cli_v1internal_request, build_grok_browser_headers, build_grok_upstream_url,
build_same_format_provider_headers, resolve_local_gemini_cli_request_auth,
GeminiCliRequestAuth, GeminiCliRequestAuthSupport, GeminiCliRequestEnvelopeSupport,
GrokHeaderInput, SameFormatProviderHeadersInput, GEMINI_CLI_USER_AGENT, GROK_CHAT_PATH,
GrokHeaderInput, SameFormatProviderCompatibilityEdit,
SameFormatProviderCompatibilityEditAction, SameFormatProviderHeadersInput,
GEMINI_CLI_USER_AGENT, GROK_CHAT_PATH,
};
use crate::ai_serving::{CandidateFailureDiagnostic, GatewayProviderTransportSnapshot};
use crate::{AppState, GatewayError};
@@ -107,6 +109,7 @@ pub(crate) struct LocalSameFormatProviderCandidatePayloadParts {
pub(super) provider_request_headers: BTreeMap<String, String>,
pub(super) provider_request_body: Value,
pub(super) transport_profile: Option<ResolvedTransportProfile>,
pub(super) compatibility_edits: Vec<SameFormatProviderCompatibilityEdit>,
pub(super) request_redacted: bool,
}
@@ -153,8 +156,8 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
let body_json = redaction.body_json.as_ref();
let mut transport = Arc::clone(&prepared.transport);
let Some(mut base_provider_request_body) =
super::super::request::build_same_format_provider_request_body(
let Some(base_provider_request) =
super::super::request::build_same_format_provider_request_body_with_compatibility_report(
body_json,
prepared.provider_api_format.as_str(),
&prepared.mapped_model,
@@ -190,6 +193,8 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
.await;
return Ok(None);
};
let mut base_provider_request_body = base_provider_request.body;
let mut compatibility_edits = base_provider_request.compatibility_edits;
if let Some(mapping) =
crate::system_features::reasoning_model_directive_mapping_for_api_format_and_model(
state,
@@ -198,10 +203,18 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
)
.await
{
let before_mapping = base_provider_request_body.clone();
crate::ai_serving::apply_model_directive_mapping_patch(
&mut base_provider_request_body,
&mapping,
);
if before_mapping != base_provider_request_body {
compatibility_edits.push(SameFormatProviderCompatibilityEdit {
field: "model_directive_mapping".to_string(),
action: SameFormatProviderCompatibilityEditAction::RuntimeRewrite,
detail: "applied configured model directive mapping patch".to_string(),
});
}
// Directive mapping is a deep-merge patch and may overwrite/add `stream`;
// re-enforce stream-field policy afterward.
// Kiro behavior classification already hard-requires upstream streaming,
@@ -452,6 +465,7 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
provider_request_headers,
provider_request_body,
transport_profile,
compatibility_edits,
request_redacted: redaction.redacted,
}))
}
@@ -2,4 +2,5 @@ mod body;
mod url;
pub(super) use self::body::build_same_format_provider_request_body;
pub(super) use self::body::build_same_format_provider_request_body_with_compatibility_report;
pub(super) use self::url::build_same_format_upstream_url;
@@ -3,7 +3,9 @@ use serde_json::Value;
use super::super::LocalSameFormatProviderSpec;
use crate::ai_serving::transport::{
build_same_format_provider_request_body as build_same_format_provider_request_body_impl,
build_same_format_provider_request_body_with_compatibility_report as build_same_format_provider_request_body_with_compatibility_report_impl,
SameFormatProviderFamily, SameFormatProviderRequestBodyInput,
SameFormatProviderRequestBodyOutput,
};
pub(crate) fn build_same_format_provider_request_body(
@@ -36,6 +38,38 @@ pub(crate) fn build_same_format_provider_request_body(
})
}
pub(crate) fn build_same_format_provider_request_body_with_compatibility_report(
body_json: &Value,
provider_api_format: &str,
mapped_model: &str,
spec: LocalSameFormatProviderSpec,
body_rules: Option<&Value>,
request_headers: Option<&http::HeaderMap>,
upstream_is_stream: bool,
force_body_stream_field: bool,
kiro_auth: Option<&crate::ai_serving::transport::kiro::KiroRequestAuth>,
is_claude_code: bool,
enable_model_directives: bool,
) -> Option<SameFormatProviderRequestBodyOutput> {
build_same_format_provider_request_body_with_compatibility_report_impl(
SameFormatProviderRequestBodyInput {
body_json,
mapped_model,
client_api_format: spec.api_format,
provider_api_format,
source_model: body_json.get("model").and_then(Value::as_str),
family: same_format_provider_family(spec.family),
body_rules,
request_headers,
upstream_is_stream,
force_body_stream_field,
kiro_auth_config: kiro_auth.map(|auth| &auth.auth_config),
is_claude_code,
enable_model_directives,
},
)
}
fn same_format_provider_family(
family: super::super::LocalSameFormatProviderFamily,
) -> SameFormatProviderFamily {
@@ -72,8 +72,10 @@ pub(crate) use aether_provider_transport::{
build_local_openai_chat_upstream_url, build_local_openai_responses_upstream_url,
build_openai_image_headers, build_openai_image_upstream_url, build_passthrough_headers,
build_request_trace_proxy_value, build_same_format_provider_headers,
build_same_format_provider_request_body, build_same_format_provider_upstream_url,
build_standard_plan_fallback_headers, build_standard_plan_fallback_openai_chat_url,
build_same_format_provider_request_body,
build_same_format_provider_request_body_with_compatibility_report,
build_same_format_provider_upstream_url, build_standard_plan_fallback_headers,
build_standard_plan_fallback_openai_chat_url,
build_standard_plan_fallback_openai_responses_url, build_standard_provider_request_headers,
build_transport_request_url, build_transport_request_url_for_request_body,
build_video_create_headers, build_video_create_request_body, build_video_create_upstream_url,
@@ -106,12 +108,14 @@ pub(crate) use aether_provider_transport::{
GeminiCliRequestAuthUnsupportedReason, GeminiCliRequestEnvelopeSupport,
GeminiFilesHeadersInput, GeminiFilesRequestBodyError, GeminiFilesRequestBodyParts,
GrokHeaderInput, LocalResolvedOAuthRequestAuth, ProviderOpenAiImageHeadersInput,
ProviderVideoCreateFamily, ProviderVideoCreateHeadersInput, SameFormatProviderFamily,
SameFormatProviderHeadersInput, SameFormatProviderRequestBehavior,
ProviderVideoCreateFamily, ProviderVideoCreateHeadersInput,
SameFormatProviderCompatibilityEdit, SameFormatProviderCompatibilityEditAction,
SameFormatProviderFamily, SameFormatProviderHeadersInput, SameFormatProviderRequestBehavior,
SameFormatProviderRequestBehaviorParams, SameFormatProviderRequestBodyInput,
SameFormatProviderUpstreamUrlParams, StandardPlanFallbackAcceptPolicy,
StandardPlanFallbackHeadersInput, StandardProviderRequestHeaders,
StandardProviderRequestHeadersInput, TransportRequestBodySemanticsError,
TransportRequestUrlParams, GEMINI_CLI_USER_AGENT, GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,
GROK_CHAT_PATH, GROK_INTERNAL_HEADER, GROK_RATE_LIMITS_PATH, WINDSURF_ENVELOPE_NAME,
SameFormatProviderRequestBodyOutput, SameFormatProviderUpstreamUrlParams,
StandardPlanFallbackAcceptPolicy, StandardPlanFallbackHeadersInput,
StandardProviderRequestHeaders, StandardProviderRequestHeadersInput,
TransportRequestBodySemanticsError, TransportRequestUrlParams, GEMINI_CLI_USER_AGENT,
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME, GROK_CHAT_PATH, GROK_INTERNAL_HEADER,
GROK_RATE_LIMITS_PATH, WINDSURF_ENVELOPE_NAME,
};