Merge branch 'fawney19:main' into main

This commit is contained in:
ZheFox
2026-06-16 12:17:28 +08:00
committed by GitHub
160 changed files with 24102 additions and 1311 deletions
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
use super::{
apply_codex_openai_responses_special_body_edits, apply_codex_openai_responses_special_headers,
};
use crate::ai_serving::planner::standard::build_local_openai_responses_request_body;
use http::{HeaderMap, HeaderValue};
use serde_json::json;
@@ -36,6 +37,40 @@ fn applies_codex_defaults_when_body_rules_do_not_handle_fields() {
assert!(body.get("reasoning").is_none());
}
#[test]
fn local_openai_responses_codex_body_wraps_string_input_for_backend() {
let body = json!({
"model": "gpt-5",
"input": "hello"
});
let provider_request_body = build_local_openai_responses_request_body(
&body,
"gpt-5-upstream",
false,
false,
"codex",
"openai:responses",
None,
Some("key-123"),
&HeaderMap::new(),
false,
)
.expect("codex local openai responses body should build");
assert_eq!(
provider_request_body["input"],
json!([{
"type": "message",
"role": "user",
"content": [{
"type": "input_text",
"text": "hello"
}]
}])
);
}
#[test]
fn strips_store_for_compact_even_when_body_rules_handle_it() {
let body_rules = json!([
@@ -15,7 +15,8 @@ use crate::ai_serving::planner::report_context::{
use crate::ai_serving::planner::spec_metadata::local_standard_spec_metadata;
use crate::ai_serving::planner::CandidateFailureDiagnostic;
use crate::ai_serving::planner::{
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
AiExecutionDecisionResponseParts,
};
use crate::ai_serving::transport::{
resolve_transport_execution_timeouts, resolve_transport_profile,
@@ -175,6 +176,7 @@ pub(super) async fn maybe_build_local_standard_decision_payload_for_candidate(
transport_profile: _,
request_redacted: _,
} = resolved;
let request_gzip = resolve_transport_request_gzip_policy(&transport);
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
decision_is_stream: spec_metadata.require_streaming,
@@ -201,6 +203,8 @@ pub(super) async fn maybe_build_local_standard_decision_payload_for_candidate(
provider_request_body: Some(provider_request_body),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip,
proxy,
transport_profile,
timeouts,
@@ -23,6 +23,7 @@ use crate::ai_serving::planner::spec_metadata::local_standard_spec_metadata;
use crate::ai_serving::planner::standard::{
apply_codex_openai_responses_special_headers, apply_deepseek_tool_call_thinking_compat,
is_deepseek_provider, request_body_build_failure_extra_data,
request_conversion_failure_extra_data,
};
use crate::ai_serving::transport::kiro::{
build_kiro_provider_headers, build_kiro_provider_request_body,
@@ -599,10 +600,14 @@ pub(crate) async fn resolve_local_standard_candidate_payload_parts(
attempt.candidate_index,
&attempt.candidate_id,
"provider_request_body_build_failed",
request_body_build_failure_extra_data(
request_conversion_failure_extra_data(
body_json,
spec_metadata.api_format,
provider_api_format,
Some(prepared_candidate.mapped_model.as_str()),
Some(parts.uri.path()),
upstream_is_stream,
"standard_family_request_conversion",
),
)
.await;
@@ -62,7 +62,8 @@ pub(crate) use crate::ai_serving::{
normalize_openai_responses_request_to_openai_chat_request, parse_openai_tool_result_content,
};
pub(crate) use aether_ai_serving::{
request_body_build_failure_extra_data, same_format_provider_request_body_failure_extra_data,
request_body_build_failure_extra_data, request_conversion_failure_extra_data,
same_format_provider_request_body_failure_extra_data,
};
pub(crate) fn build_standard_upstream_url(
@@ -6,7 +6,8 @@ use crate::ai_serving::planner::report_context::{
insert_provider_stream_event_api_format, LocalExecutionReportContextParts,
};
use crate::ai_serving::planner::{
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
AiExecutionDecisionResponseParts,
};
use crate::ai_serving::transport::{
resolve_transport_execution_timeouts, resolve_transport_profile,
@@ -104,15 +105,6 @@ pub(crate) async fn maybe_build_local_openai_chat_decision_payload_for_candidate
.eq_ignore_ascii_case("chatgpt_web")
{
extra_fields.insert("chatgpt_web_image".to_string(), serde_json::json!(true));
extra_fields.insert(
"local_failover_policy".to_string(),
serde_json::json!({
"stop_status_codes": [400, 401, 403, 429, 500, 502, 503, 504],
"error_stop_patterns": [
{ "pattern": ".*" }
]
}),
);
}
let super::request::LocalOpenAiChatCandidatePayloadParts {
client_api_format,
@@ -192,6 +184,7 @@ pub(crate) async fn maybe_build_local_openai_chat_decision_payload_for_candidate
),
&transport,
);
let request_gzip = resolve_transport_request_gzip_policy(&transport);
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
decision_is_stream,
@@ -218,6 +211,8 @@ pub(crate) async fn maybe_build_local_openai_chat_decision_payload_for_candidate
provider_request_body: Some(provider_request_body),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip,
proxy,
transport_profile,
timeouts,
@@ -25,6 +25,7 @@ use crate::ai_serving::planner::standard::{
apply_deepseek_tool_call_thinking_compat, build_cross_format_openai_chat_request_body,
build_cross_format_openai_chat_upstream_url, build_local_openai_chat_request_body,
build_local_openai_chat_upstream_url, request_body_build_failure_extra_data,
request_conversion_failure_extra_data,
};
use crate::ai_serving::transport::auth::resolve_local_openai_bearer_auth;
use crate::ai_serving::transport::kiro::{
@@ -601,10 +602,14 @@ pub(crate) async fn resolve_local_openai_chat_candidate_payload_parts(
candidate_index,
candidate_id,
"provider_request_body_build_failed",
request_body_build_failure_extra_data(
request_conversion_failure_extra_data(
body_json,
"openai:chat",
provider_api_format.as_str(),
Some(prepared_candidate.mapped_model.as_str()),
Some(parts.uri.path()),
upstream_is_stream,
"openai_chat_request_conversion",
),
)
.await;
@@ -326,6 +326,8 @@ mod tests {
})),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip: None,
proxy: None,
transport_profile: None,
timeouts: None,
@@ -422,6 +424,8 @@ mod tests {
provider_request_body: Some(json!({"model":"gpt-5.4","messages":[],"stream":true})),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip: None,
proxy: None,
transport_profile: None,
timeouts: None,
@@ -488,6 +492,8 @@ mod tests {
provider_request_body,
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip: None,
proxy: None,
transport_profile: None,
timeouts: None,
@@ -595,6 +601,8 @@ mod tests {
),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip: None,
proxy: None,
transport_profile: None,
timeouts: None,
@@ -292,6 +292,8 @@ mod tests {
})),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip: None,
proxy: None,
transport_profile: None,
timeouts: None,
@@ -387,6 +389,8 @@ mod tests {
provider_request_body: Some(json!({"model":"gpt-5.4","messages":[],"stream":false})),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip: None,
proxy: None,
transport_profile: None,
timeouts: None,
@@ -458,6 +462,8 @@ mod tests {
),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip: None,
proxy: None,
transport_profile: None,
timeouts: None,
@@ -9,7 +9,8 @@ use crate::ai_serving::planner::report_context::{
};
use crate::ai_serving::planner::spec_metadata::local_openai_responses_spec_metadata;
use crate::ai_serving::planner::{
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
AiExecutionDecisionResponseParts,
};
use crate::ai_serving::transport::{
resolve_transport_execution_timeouts, resolve_transport_profile,
@@ -101,15 +102,6 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
.eq_ignore_ascii_case("chatgpt_web")
{
extra_fields.insert("chatgpt_web_image".to_string(), json!(true));
extra_fields.insert(
"local_failover_policy".to_string(),
json!({
"stop_status_codes": [400, 401, 403, 429, 500, 502, 503, 504],
"error_stop_patterns": [
{ "pattern": ".*" }
]
}),
);
}
insert_provider_stream_event_api_format(
&mut extra_fields,
@@ -212,6 +204,7 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
image_request_summary: _,
request_redacted: _,
} = resolved;
let request_gzip = resolve_transport_request_gzip_policy(&transport);
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
decision_is_stream: spec_metadata.require_streaming,
@@ -238,6 +231,8 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
provider_request_body: Some(provider_request_body),
provider_request_body_base64: None,
content_type: Some("application/json".to_string()),
content_encoding: None,
request_gzip,
proxy,
transport_profile,
timeouts,
@@ -27,6 +27,7 @@ use crate::ai_serving::planner::standard::{
apply_deepseek_tool_call_thinking_compat, build_cross_format_openai_responses_request_body,
build_cross_format_openai_responses_upstream_url, build_local_openai_responses_request_body,
build_local_openai_responses_upstream_url, request_body_build_failure_extra_data,
request_conversion_failure_extra_data,
};
use crate::ai_serving::transport::antigravity::{
build_antigravity_safe_v1internal_request, build_antigravity_static_identity_headers,
@@ -371,10 +372,14 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts(
candidate_index,
candidate_id,
"provider_request_body_build_failed",
request_body_build_failure_extra_data(
request_conversion_failure_extra_data(
body_json,
spec_metadata.api_format,
provider_api_format,
Some(mapped_model.as_str()),
Some(parts.uri.path()),
upstream_is_stream,
"openai_responses_request_conversion",
),
)
.await;