fix(compact): 移除 OpenAI Compact 请求中不受支持的 store 参数

- 在 OpenAI Compact 请求规范化流程中剥离 `store`
- 将 compact 默认 body rules 与 Codex CLI 默认规则拆分
- 补充 same-format 和转换链路的 compact 回归测试
This commit is contained in:
AAEE86
2026-04-12 01:52:25 +08:00
parent 335e440cc5
commit def8135118
10 changed files with 97 additions and 18 deletions

View File

@@ -26,7 +26,7 @@ fn applies_codex_defaults_when_body_rules_do_not_handle_fields() {
} }
#[test] #[test]
fn defers_to_user_body_rules_for_handled_fields() { fn strips_store_for_compact_even_when_body_rules_handle_it() {
let body_rules = json!([ let body_rules = json!([
{"action":"set","path":"store","value":true}, {"action":"set","path":"store","value":true},
{"action":"set","path":"instructions","value":"Keep custom"}, {"action":"set","path":"instructions","value":"Keep custom"},
@@ -51,7 +51,7 @@ fn defers_to_user_body_rules_for_handled_fields() {
); );
assert!(body.get("max_output_tokens").is_none()); assert!(body.get("max_output_tokens").is_none());
assert_eq!(body["store"], true); assert!(body.get("store").is_none());
assert_eq!(body["instructions"], "Keep custom"); assert_eq!(body["instructions"], "Keep custom");
assert_eq!(body["metadata"]["mode"], "custom"); assert_eq!(body["metadata"]["mode"], "custom");
assert_eq!(body["top_p"], 0.5); assert_eq!(body["top_p"], 0.5);

View File

@@ -267,4 +267,32 @@ mod tests {
assert_eq!(converted["store"], false); assert_eq!(converted["store"], false);
assert_eq!(converted["instructions"], "You are GPT-5."); assert_eq!(converted["instructions"], "You are GPT-5.");
} }
#[test]
fn strips_store_for_openai_compact_requests() {
let request = json!({
"model": "gpt-5",
"messages": [{
"role": "user",
"content": "Hello from OpenAI Chat"
}],
"store": true
});
let converted = build_standard_request_body(
&request,
"openai:chat",
"gpt-5",
"openai",
"openai:compact",
"/v1/chat/completions",
false,
None,
None,
)
.expect("openai chat should convert to openai compact");
assert_eq!(converted["model"], "gpt-5");
assert!(converted.get("store").is_none());
}
} }

View File

@@ -1,6 +1,5 @@
use serde_json::Value; use serde_json::Value;
use super::super::codex::apply_codex_openai_cli_special_body_edits;
use crate::ai_pipeline::conversion::{request_conversion_kind, RequestConversionKind}; use crate::ai_pipeline::conversion::{request_conversion_kind, RequestConversionKind};
use crate::ai_pipeline::transport::apply_local_body_rules; use crate::ai_pipeline::transport::apply_local_body_rules;
use crate::ai_pipeline::transport::url::{ use crate::ai_pipeline::transport::url::{
@@ -8,6 +7,7 @@ use crate::ai_pipeline::transport::url::{
build_openai_cli_url, build_passthrough_path_url, build_openai_cli_url, build_passthrough_path_url,
}; };
use crate::ai_pipeline::{ use crate::ai_pipeline::{
apply_codex_openai_cli_special_body_edits, apply_openai_compact_special_body_edits,
build_cross_format_openai_chat_request_body as pipeline_build_cross_format_openai_chat_request_body, build_cross_format_openai_chat_request_body as pipeline_build_cross_format_openai_chat_request_body,
build_local_openai_chat_request_body as pipeline_build_local_openai_chat_request_body, build_local_openai_chat_request_body as pipeline_build_local_openai_chat_request_body,
GatewayProviderTransportSnapshot, GatewayProviderTransportSnapshot,
@@ -74,6 +74,7 @@ pub(crate) fn build_cross_format_openai_chat_request_body(
body_rules, body_rules,
user_api_key_id, user_api_key_id,
); );
apply_openai_compact_special_body_edits(&mut provider_request_body, provider_api_format);
Some(provider_request_body) Some(provider_request_body)
} }

View File

@@ -3,7 +3,6 @@ use std::collections::BTreeMap;
use serde_json::Value; use serde_json::Value;
use url::form_urlencoded; use url::form_urlencoded;
use super::super::codex::apply_codex_openai_cli_special_body_edits;
use crate::ai_pipeline::conversion::{request_conversion_kind, RequestConversionKind}; use crate::ai_pipeline::conversion::{request_conversion_kind, RequestConversionKind};
use crate::ai_pipeline::transport::antigravity::{ use crate::ai_pipeline::transport::antigravity::{
build_antigravity_v1internal_url, AntigravityRequestUrlAction, build_antigravity_v1internal_url, AntigravityRequestUrlAction,
@@ -14,6 +13,7 @@ use crate::ai_pipeline::transport::url::{
build_openai_cli_url, build_passthrough_path_url, build_openai_cli_url, build_passthrough_path_url,
}; };
use crate::ai_pipeline::{ use crate::ai_pipeline::{
apply_codex_openai_cli_special_body_edits, apply_openai_compact_special_body_edits,
build_cross_format_openai_cli_request_body as pipeline_build_cross_format_openai_cli_request_body, build_cross_format_openai_cli_request_body as pipeline_build_cross_format_openai_cli_request_body,
build_local_openai_cli_request_body as pipeline_build_local_openai_cli_request_body, build_local_openai_cli_request_body as pipeline_build_local_openai_cli_request_body,
GatewayProviderTransportSnapshot, GatewayProviderTransportSnapshot,
@@ -40,6 +40,7 @@ pub(crate) fn build_local_openai_cli_request_body(
body_rules, body_rules,
user_api_key_id, user_api_key_id,
); );
apply_openai_compact_special_body_edits(&mut provider_request_body, provider_api_format);
Some(provider_request_body) Some(provider_request_body)
} }
@@ -70,6 +71,7 @@ pub(crate) fn build_cross_format_openai_cli_request_body(
body_rules, body_rules,
user_api_key_id, user_api_key_id,
); );
apply_openai_compact_special_body_edits(&mut provider_request_body, provider_api_format);
Some(provider_request_body) Some(provider_request_body)
} }

View File

@@ -81,6 +81,28 @@ fn local_openai_cli_wrapper_preserves_body_order_after_edits() {
); );
} }
#[test]
fn local_openai_compact_wrapper_strips_store_for_same_format_requests() {
let body_json = json!({
"model": "gpt-5.4",
"input": [],
"store": true
});
let provider_request_body = build_local_openai_cli_request_body(
&body_json,
"gpt-5.4",
false,
"openai",
"openai:compact",
None,
None,
)
.expect("local openai compact body should build");
assert!(provider_request_body.get("store").is_none());
}
#[test] #[test]
fn strips_metadata_for_codex_openai_cli_requests() { fn strips_metadata_for_codex_openai_cli_requests() {
let body_json = json!({ let body_json = json!({

View File

@@ -3,15 +3,15 @@ pub(crate) use aether_ai_pipeline::api::{
aggregate_openai_chat_stream_sync_response, aggregate_openai_cli_stream_sync_response, aggregate_openai_chat_stream_sync_response, aggregate_openai_cli_stream_sync_response,
aggregate_standard_chat_stream_sync_response, aggregate_standard_cli_stream_sync_response, aggregate_standard_chat_stream_sync_response, aggregate_standard_cli_stream_sync_response,
apply_codex_openai_cli_special_body_edits, apply_codex_openai_cli_special_headers, apply_codex_openai_cli_special_body_edits, apply_codex_openai_cli_special_headers,
augment_sync_report_context, build_core_error_body_for_client_format, apply_openai_compact_special_body_edits, augment_sync_report_context,
build_cross_format_openai_chat_request_body, build_cross_format_openai_cli_request_body, build_core_error_body_for_client_format, build_cross_format_openai_chat_request_body,
build_generated_tool_call_id, build_kiro_final_message_sse_events, build_cross_format_openai_cli_request_body, build_generated_tool_call_id,
build_kiro_initial_sse_events, build_kiro_stream_error_sse_events, build_kiro_final_message_sse_events, build_kiro_initial_sse_events,
build_local_openai_chat_request_body, build_local_openai_cli_request_body, build_kiro_stream_error_sse_events, build_local_openai_chat_request_body,
build_local_success_background_report, build_local_success_conversion_background_report, build_local_openai_cli_request_body, build_local_success_background_report,
build_openai_cli_response, build_standard_request_body, build_local_success_conversion_background_report, build_openai_cli_response,
build_standard_request_body_from_canonical, build_standard_upstream_url, build_standard_request_body, build_standard_request_body_from_canonical,
calculate_kiro_context_input_tokens, canonicalize_tool_arguments, build_standard_upstream_url, calculate_kiro_context_input_tokens, canonicalize_tool_arguments,
convert_claude_chat_response_to_openai_chat, convert_claude_cli_response_to_openai_cli, convert_claude_chat_response_to_openai_chat, convert_claude_cli_response_to_openai_cli,
convert_gemini_chat_response_to_openai_chat, convert_gemini_cli_response_to_openai_cli, convert_gemini_chat_response_to_openai_chat, convert_gemini_cli_response_to_openai_cli,
convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request, convert_openai_chat_request_to_claude_request, convert_openai_chat_request_to_gemini_request,

View File

@@ -139,9 +139,9 @@ pub use crate::planner::specialized::{
}; };
pub use crate::planner::standard::{ pub use crate::planner::standard::{
apply_codex_openai_cli_special_body_edits, apply_codex_openai_cli_special_headers, apply_codex_openai_cli_special_body_edits, apply_codex_openai_cli_special_headers,
build_cross_format_openai_chat_request_body, build_cross_format_openai_cli_request_body, apply_openai_compact_special_body_edits, build_cross_format_openai_chat_request_body,
build_local_openai_chat_request_body, build_local_openai_cli_request_body, build_cross_format_openai_cli_request_body, build_local_openai_chat_request_body,
build_standard_request_body, build_standard_upstream_url, build_local_openai_cli_request_body, build_standard_request_body, build_standard_upstream_url,
claude::{ claude::{
resolve_stream_spec as resolve_claude_stream_spec, resolve_stream_spec as resolve_claude_stream_spec,
resolve_sync_spec as resolve_claude_sync_spec, resolve_sync_spec as resolve_claude_sync_spec,

View File

@@ -20,6 +20,12 @@ fn is_codex_openai_cli_request(provider_type: &str, provider_api_format: &str) -
) )
} }
fn is_openai_compact_request(provider_api_format: &str) -> bool {
provider_api_format
.trim()
.eq_ignore_ascii_case("openai:compact")
}
fn build_stable_codex_prompt_cache_key(user_api_key_id: &str) -> Option<String> { fn build_stable_codex_prompt_cache_key(user_api_key_id: &str) -> Option<String> {
let normalized = user_api_key_id.trim(); let normalized = user_api_key_id.trim();
if normalized.is_empty() { if normalized.is_empty() {
@@ -135,6 +141,22 @@ fn maybe_inject_codex_prompt_cache_key(
); );
} }
pub fn apply_openai_compact_special_body_edits(
provider_request_body: &mut Value,
provider_api_format: &str,
) {
if !is_openai_compact_request(provider_api_format) {
return;
}
let Some(body_object) = provider_request_body.as_object_mut() else {
return;
};
// `/v1/responses/compact` does not accept `store`.
body_object.remove("store");
}
pub fn apply_codex_openai_cli_special_body_edits( pub fn apply_codex_openai_cli_special_body_edits(
provider_request_body: &mut Value, provider_request_body: &mut Value,
provider_type: &str, provider_type: &str,
@@ -162,7 +184,9 @@ pub fn apply_codex_openai_cli_special_body_edits(
if !body_rules_handle_path(body_rules, "metadata") { if !body_rules_handle_path(body_rules, "metadata") {
body_object.remove("metadata"); body_object.remove("metadata");
} }
if !body_rules_handle_path(body_rules, "store") { if is_openai_compact_request(provider_api_format) {
body_object.remove("store");
} else if !body_rules_handle_path(body_rules, "store") {
body_object.insert("store".to_string(), json!(false)); body_object.insert("store".to_string(), json!(false));
} }
if !body_rules_handle_path(body_rules, "instructions") if !body_rules_handle_path(body_rules, "instructions")

View File

@@ -14,7 +14,7 @@ use crate::conversion::request::{
}; };
use super::{ use super::{
codex::apply_codex_openai_cli_special_body_edits, apply_openai_compact_special_body_edits, codex::apply_codex_openai_cli_special_body_edits,
normalize::build_local_openai_chat_request_body, normalize::build_local_openai_chat_request_body,
}; };
@@ -52,6 +52,7 @@ pub fn build_standard_request_body(
body_rules, body_rules,
user_api_key_id, user_api_key_id,
); );
apply_openai_compact_special_body_edits(&mut provider_request_body, provider_api_format);
Some(provider_request_body) Some(provider_request_body)
} }

View File

@@ -8,6 +8,7 @@ pub mod openai_cli;
pub use codex::{ pub use codex::{
apply_codex_openai_cli_special_body_edits, apply_codex_openai_cli_special_headers, apply_codex_openai_cli_special_body_edits, apply_codex_openai_cli_special_headers,
apply_openai_compact_special_body_edits,
}; };
pub use family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec}; pub use family::{LocalStandardSourceFamily, LocalStandardSourceMode, LocalStandardSpec};
pub use matrix::{ pub use matrix::{