mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-10 13:10:21 +08:00
fix(codex): 服务端压缩使用标准 Responses 合约
This commit is contained in:
@@ -122,8 +122,9 @@ pub(crate) fn apply_provider_request_routing_policy_to_decision(
|
||||
input.requested_model.as_str(),
|
||||
)
|
||||
});
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_with_capabilities(
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
&mut decision.provider_request_headers,
|
||||
decision.provider_request_body.as_ref(),
|
||||
provider_type.as_str(),
|
||||
provider_api_format.as_str(),
|
||||
terminal_provider_model,
|
||||
@@ -254,8 +255,9 @@ pub(crate) fn apply_provider_request_routing_policy_to_decision(
|
||||
input.requested_model.as_str(),
|
||||
)
|
||||
});
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_with_capabilities(
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
&mut provider_request_headers,
|
||||
Some(&provider_request_body),
|
||||
provider_type.as_str(),
|
||||
provider_api_format.as_str(),
|
||||
provider_model,
|
||||
|
||||
@@ -531,8 +531,9 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
|
||||
.get("model")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or(prepared.mapped_model.as_str());
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_with_capabilities(
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
&mut provider_request_headers,
|
||||
Some(&provider_request_body),
|
||||
transport.provider.provider_type.as_str(),
|
||||
prepared.provider_api_format.as_str(),
|
||||
provider_model,
|
||||
|
||||
+2
-1
@@ -685,8 +685,9 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts(
|
||||
Some(trace_id),
|
||||
transport.key.decrypted_auth_config.as_deref(),
|
||||
);
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_with_capabilities(
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
&mut provider_request_headers,
|
||||
Some(&provider_request_body),
|
||||
transport.provider.provider_type.as_str(),
|
||||
provider_api_format,
|
||||
mapped_model.as_str(),
|
||||
|
||||
@@ -4,6 +4,7 @@ pub(crate) use aether_ai_formats::api::{
|
||||
aggregate_standard_chat_stream_sync_response, aggregate_standard_cli_stream_sync_response,
|
||||
api_format_alias_matches, api_format_storage_aliases,
|
||||
apply_codex_openai_compact_terminal_headers, apply_codex_openai_responses_chat_body_edits,
|
||||
apply_codex_openai_responses_lite_header_for_request_body_with_capabilities,
|
||||
apply_codex_openai_responses_lite_header_with_capabilities,
|
||||
apply_codex_openai_responses_special_body_edits,
|
||||
apply_codex_openai_responses_special_body_edits_with_source_model_and_capabilities,
|
||||
|
||||
@@ -3464,8 +3464,9 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
.get("model")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or(request_model);
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_with_capabilities(
|
||||
crate::ai_serving::apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
&mut request_headers,
|
||||
Some(&provider_request_body),
|
||||
transport.provider.provider_type.as_str(),
|
||||
provider_api_format,
|
||||
final_provider_model,
|
||||
|
||||
@@ -705,18 +705,15 @@ async fn gateway_executes_codex_cli_stream_via_local_decision_gate_after_oauth_r
|
||||
seen_execution_runtime_request.thread_id,
|
||||
seen_execution_runtime_request.prompt_cache_key
|
||||
);
|
||||
assert_eq!(seen_execution_runtime_request.responses_lite, "true");
|
||||
assert!(!seen_execution_runtime_request.has_top_level_tools);
|
||||
assert!(!seen_execution_runtime_request.has_top_level_instructions);
|
||||
assert!(seen_execution_runtime_request.has_additional_tools);
|
||||
assert!(!seen_execution_runtime_request.parallel_tool_calls);
|
||||
assert!(seen_execution_runtime_request.responses_lite.is_empty());
|
||||
assert!(seen_execution_runtime_request.has_top_level_tools);
|
||||
assert!(seen_execution_runtime_request.has_top_level_instructions);
|
||||
assert!(!seen_execution_runtime_request.has_additional_tools);
|
||||
assert!(seen_execution_runtime_request.parallel_tool_calls);
|
||||
assert_eq!(seen_execution_runtime_request.reasoning_effort, "low");
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.reasoning_context,
|
||||
"all_turns"
|
||||
);
|
||||
assert!(seen_execution_runtime_request.reasoning_context.is_empty());
|
||||
assert!(seen_execution_runtime_request.has_compaction_trigger);
|
||||
assert!(!seen_execution_runtime_request.has_context_management);
|
||||
assert!(seen_execution_runtime_request.has_context_management);
|
||||
|
||||
let stored_candidates = request_candidate_repository
|
||||
.list_by_request_id("trace-codex-cli-stream-local-123")
|
||||
|
||||
@@ -173,6 +173,7 @@ pub use crate::formats::{
|
||||
codex::{
|
||||
apply_codex_openai_compact_terminal_headers,
|
||||
apply_codex_openai_responses_chat_body_edits,
|
||||
apply_codex_openai_responses_lite_header_for_request_body_with_capabilities,
|
||||
apply_codex_openai_responses_lite_header_with_capabilities,
|
||||
apply_codex_openai_responses_special_body_edits,
|
||||
apply_codex_openai_responses_special_body_edits_with_source_model_and_capabilities,
|
||||
|
||||
@@ -607,10 +607,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}],
|
||||
"context_management": [{
|
||||
"type": "compaction",
|
||||
"compact_threshold": 128000
|
||||
}],
|
||||
"parallel_tool_calls": true
|
||||
});
|
||||
|
||||
@@ -633,7 +629,6 @@ mod tests {
|
||||
assert_eq!(body, first);
|
||||
assert!(body.get("instructions").is_none());
|
||||
assert!(body.get("tools").is_none());
|
||||
assert!(body.get("context_management").is_none());
|
||||
assert_eq!(body["input"][0]["type"], "additional_tools");
|
||||
assert_eq!(body["input"][0]["role"], "developer");
|
||||
assert_eq!(body["input"][0]["tools"][0]["name"], "lookup");
|
||||
@@ -656,6 +651,51 @@ mod tests {
|
||||
assert!(body["reasoning"].get("summary").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gpt_5_6_sol_uses_standard_responses_contract_for_server_side_compaction() {
|
||||
let mut body = json!({
|
||||
"model": "gpt-5.6-sol",
|
||||
"instructions": "Preserve the standard Responses request shape.",
|
||||
"input": [{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [{"type": "input_text", "text": "compact"}]
|
||||
}],
|
||||
"tools": [{"type": "function", "name": "lookup"}],
|
||||
"context_management": [{
|
||||
"type": "compaction",
|
||||
"compact_threshold": 128000
|
||||
}],
|
||||
"parallel_tool_calls": true
|
||||
});
|
||||
let finalization = OpenAiProviderRequestFinalization {
|
||||
source_api_format: "openai:responses",
|
||||
provider_api_format: "openai:responses",
|
||||
provider_type: "codex",
|
||||
provider_model: "gpt-5.6-sol",
|
||||
source_model: "gpt-5.6-sol",
|
||||
body_rules: None,
|
||||
upstream_is_stream: true,
|
||||
require_body_stream_field: true,
|
||||
};
|
||||
|
||||
finalize_openai_provider_request(&mut body, finalization)
|
||||
.expect("server-side compaction should use the standard Responses contract");
|
||||
let first = body.clone();
|
||||
finalize_openai_provider_request(&mut body, finalization)
|
||||
.expect("standard Responses finalization should be idempotent");
|
||||
|
||||
assert_eq!(body, first);
|
||||
assert_eq!(body["context_management"][0]["compact_threshold"], 128000);
|
||||
assert_eq!(
|
||||
body["instructions"],
|
||||
"Preserve the standard Responses request shape."
|
||||
);
|
||||
assert_eq!(body["tools"][0]["name"], "lookup");
|
||||
assert_eq!(body["parallel_tool_calls"], true);
|
||||
assert!(body["reasoning"].get("context").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opaque_codex_deployments_use_the_exact_source_model_card() {
|
||||
let mut body = json!({
|
||||
|
||||
@@ -1290,6 +1290,16 @@ fn strip_codex_responses_lite_image_details(item: &mut Value) {
|
||||
}
|
||||
}
|
||||
|
||||
fn codex_responses_lite_supports_context_management(context_management: Option<&Value>) -> bool {
|
||||
context_management.is_none_or(Value::is_null)
|
||||
}
|
||||
|
||||
fn codex_responses_lite_supports_request_body(provider_request_body: Option<&Value>) -> bool {
|
||||
provider_request_body.is_none_or(|body| {
|
||||
codex_responses_lite_supports_context_management(body.get("context_management"))
|
||||
})
|
||||
}
|
||||
|
||||
fn apply_codex_responses_lite_body_contract(
|
||||
body_object: &mut serde_json::Map<String, Value>,
|
||||
capabilities: &CodexResponsesModelCapabilities,
|
||||
@@ -1298,8 +1308,6 @@ fn apply_codex_responses_lite_body_contract(
|
||||
return;
|
||||
}
|
||||
|
||||
body_object.remove("context_management");
|
||||
|
||||
let tools_are_valid = body_object
|
||||
.get("tools")
|
||||
.is_none_or(|tools| tools.is_null() || tools.is_array());
|
||||
@@ -1573,6 +1581,17 @@ pub fn apply_codex_openai_responses_special_body_edits_with_source_model_and_cap
|
||||
);
|
||||
&bundled_capabilities
|
||||
};
|
||||
let standard_contract_capabilities = (capabilities.use_responses_lite
|
||||
&& !codex_responses_lite_supports_context_management(
|
||||
body_object.get("context_management"),
|
||||
))
|
||||
.then(|| CodexResponsesModelCapabilities {
|
||||
use_responses_lite: false,
|
||||
..capabilities.clone()
|
||||
});
|
||||
let capabilities = standard_contract_capabilities
|
||||
.as_ref()
|
||||
.unwrap_or(capabilities);
|
||||
let supports_reasoning_mode =
|
||||
crate::formats::shared::model_directives::openai_model_resolves_to_gpt_5_6(
|
||||
effective_provider_model.as_str(),
|
||||
@@ -1697,6 +1716,26 @@ pub fn apply_codex_openai_responses_lite_header_with_capabilities(
|
||||
provider_model: &str,
|
||||
source_model: &str,
|
||||
model_capabilities: Option<&CodexResponsesModelCapabilities>,
|
||||
) {
|
||||
apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
provider_request_headers,
|
||||
None,
|
||||
provider_type,
|
||||
provider_api_format,
|
||||
provider_model,
|
||||
source_model,
|
||||
model_capabilities,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
provider_request_headers: &mut BTreeMap<String, String>,
|
||||
provider_request_body: Option<&Value>,
|
||||
provider_type: &str,
|
||||
provider_api_format: &str,
|
||||
provider_model: &str,
|
||||
source_model: &str,
|
||||
model_capabilities: Option<&CodexResponsesModelCapabilities>,
|
||||
) {
|
||||
if !is_codex_openai_responses_request(provider_type, provider_api_format) {
|
||||
return;
|
||||
@@ -1710,7 +1749,9 @@ pub fn apply_codex_openai_responses_lite_header_with_capabilities(
|
||||
resolve_codex_responses_model_capabilities(provider_model, source_model, None);
|
||||
&bundled_capabilities
|
||||
};
|
||||
if capabilities.use_responses_lite {
|
||||
if capabilities.use_responses_lite
|
||||
&& codex_responses_lite_supports_request_body(provider_request_body)
|
||||
{
|
||||
provider_request_headers
|
||||
.insert(CODEX_RESPONSES_LITE_HEADER.to_string(), "true".to_string());
|
||||
}
|
||||
@@ -1770,12 +1811,14 @@ pub fn apply_codex_openai_special_headers(
|
||||
.get("model")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default();
|
||||
apply_codex_openai_responses_lite_header(
|
||||
apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
provider_request_headers,
|
||||
Some(provider_request_body),
|
||||
provider_type,
|
||||
provider_api_format,
|
||||
provider_model,
|
||||
provider_model,
|
||||
None,
|
||||
);
|
||||
|
||||
apply_codex_openai_compact_terminal_headers(
|
||||
@@ -1790,6 +1833,7 @@ mod tests {
|
||||
use super::{
|
||||
apply_codex_openai_responses_chat_body_edits,
|
||||
apply_codex_openai_responses_compact_body_edits,
|
||||
apply_codex_openai_responses_lite_header_for_request_body_with_capabilities,
|
||||
apply_codex_openai_responses_lite_header_with_capabilities,
|
||||
apply_codex_openai_responses_special_body_edits,
|
||||
apply_codex_openai_responses_special_body_edits_with_source_model_and_capabilities,
|
||||
@@ -1897,6 +1941,33 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_responses_lite_header_is_omitted_for_server_side_compaction() {
|
||||
let mut headers = std::collections::BTreeMap::from([(
|
||||
CODEX_RESPONSES_LITE_HEADER.to_string(),
|
||||
"true".to_string(),
|
||||
)]);
|
||||
let body = json!({
|
||||
"model": "gpt-5.6-sol",
|
||||
"context_management": [{
|
||||
"type": "compaction",
|
||||
"compact_threshold": 128000
|
||||
}]
|
||||
});
|
||||
|
||||
apply_codex_openai_responses_lite_header_for_request_body_with_capabilities(
|
||||
&mut headers,
|
||||
Some(&body),
|
||||
"codex",
|
||||
"openai:responses",
|
||||
"gpt-5.6-sol",
|
||||
"gpt-5.6-sol",
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!headers.contains_key(CODEX_RESPONSES_LITE_HEADER));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn model_card_without_summary_parameter_support_keeps_reasoning_and_ids() {
|
||||
let metadata = build_codex_model_catalog_metadata(&[json!({
|
||||
|
||||
Reference in New Issue
Block a user