mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 11:19:50 +08:00
fix(provider): clamp reasoning effort and filter chat extensions
This commit is contained in:
@@ -165,6 +165,7 @@ mod tests {
|
|||||||
convert_openai_chat_request_to_claude_request,
|
convert_openai_chat_request_to_claude_request,
|
||||||
convert_openai_chat_request_to_openai_responses_request,
|
convert_openai_chat_request_to_openai_responses_request,
|
||||||
normalize_claude_request_to_openai_chat_request,
|
normalize_claude_request_to_openai_chat_request,
|
||||||
|
normalize_gemini_request_to_openai_chat_request,
|
||||||
normalize_openai_responses_request_to_openai_chat_request,
|
normalize_openai_responses_request_to_openai_chat_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,6 +220,43 @@ mod tests {
|
|||||||
assert_eq!(converted["messages"][0]["content"], "hello");
|
assert_eq!(converted["messages"][0]["content"], "hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn claude_request_to_chat_clamps_max_reasoning_effort_to_high() {
|
||||||
|
let body = json!({
|
||||||
|
"model": "claude-sonnet",
|
||||||
|
"messages": [{"role": "user", "content": "hello"}],
|
||||||
|
"thinking": {"type": "enabled", "budget_tokens": 1024},
|
||||||
|
"output_config": {"effort": "max"},
|
||||||
|
"max_tokens": 128,
|
||||||
|
});
|
||||||
|
|
||||||
|
let converted =
|
||||||
|
normalize_claude_request_to_openai_chat_request(&body).expect("openai chat request");
|
||||||
|
|
||||||
|
assert_eq!(converted["reasoning_effort"], "high");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gemini_request_to_chat_clamps_xhigh_reasoning_effort_to_high() {
|
||||||
|
let body = json!({
|
||||||
|
"contents": [{
|
||||||
|
"role": "user",
|
||||||
|
"parts": [{"text": "hello"}]
|
||||||
|
}],
|
||||||
|
"generationConfig": {
|
||||||
|
"thinkingConfig": {"thinkingBudget": 8192}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let converted = normalize_gemini_request_to_openai_chat_request(
|
||||||
|
&body,
|
||||||
|
"/v1beta/models/gemini-2.5-pro:generateContent",
|
||||||
|
)
|
||||||
|
.expect("openai chat request");
|
||||||
|
|
||||||
|
assert_eq!(converted["reasoning_effort"], "high");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn responses_request_normalizer_keeps_tool_history_chat_safe() {
|
fn responses_request_normalizer_keeps_tool_history_chat_safe() {
|
||||||
let call_id_one = "call_weather_123";
|
let call_id_one = "call_weather_123";
|
||||||
@@ -333,6 +371,34 @@ mod tests {
|
|||||||
assert_eq!(messages[0]["content"], "");
|
assert_eq!(messages[0]["content"], "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn responses_request_normalizer_clamps_chat_reasoning_effort_and_filters_extensions() {
|
||||||
|
let body = json!({
|
||||||
|
"model": "gpt-5.1",
|
||||||
|
"input": "hello",
|
||||||
|
"reasoning": {"effort": "xhigh"},
|
||||||
|
"text": {"verbosity": "high"},
|
||||||
|
"include": ["reasoning.encrypted_content"],
|
||||||
|
"store": false,
|
||||||
|
"service_tier": "priority",
|
||||||
|
"prompt_cache_key": "cache_123",
|
||||||
|
"safety_identifier": "user_123"
|
||||||
|
});
|
||||||
|
|
||||||
|
let converted = normalize_openai_responses_request_to_openai_chat_request(&body)
|
||||||
|
.expect("openai chat request");
|
||||||
|
|
||||||
|
assert_eq!(converted["reasoning_effort"], "high");
|
||||||
|
assert_eq!(converted["verbosity"], "high");
|
||||||
|
assert_eq!(converted["service_tier"], "priority");
|
||||||
|
assert_eq!(converted["prompt_cache_key"], "cache_123");
|
||||||
|
assert_eq!(converted["safety_identifier"], "user_123");
|
||||||
|
assert!(converted.get("include").is_none());
|
||||||
|
assert!(converted.get("store").is_none());
|
||||||
|
assert!(converted.get("text").is_none());
|
||||||
|
assert!(converted.get("reasoning").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn request_normalizer_preserves_multiple_claude_tool_results() {
|
fn request_normalizer_preserves_multiple_claude_tool_results() {
|
||||||
let body = json!({
|
let body = json!({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Map, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
formats::context::FormatContext,
|
formats::context::FormatContext,
|
||||||
@@ -194,6 +194,7 @@ pub fn to_raw(canonical: &CanonicalRequest) -> Value {
|
|||||||
.and_then(|value| value.get("effort"))
|
.and_then(|value| value.get("effort"))
|
||||||
.and_then(Value::as_str)
|
.and_then(Value::as_str)
|
||||||
})
|
})
|
||||||
|
.and_then(openai_chat_reasoning_effort)
|
||||||
{
|
{
|
||||||
output.insert(
|
output.insert(
|
||||||
"reasoning_effort".to_string(),
|
"reasoning_effort".to_string(),
|
||||||
@@ -206,12 +207,12 @@ pub fn to_raw(canonical: &CanonicalRequest) -> Value {
|
|||||||
"openai",
|
"openai",
|
||||||
&output,
|
&output,
|
||||||
));
|
));
|
||||||
output.extend(namespace_extension_object(
|
output.extend(chat_compatible_openai_responses_extension_object(
|
||||||
&canonical.extensions,
|
&canonical.extensions,
|
||||||
OPENAI_RESPONSES_EXTENSION_NAMESPACE,
|
OPENAI_RESPONSES_EXTENSION_NAMESPACE,
|
||||||
&output,
|
&output,
|
||||||
));
|
));
|
||||||
output.extend(namespace_extension_object(
|
output.extend(chat_compatible_openai_responses_extension_object(
|
||||||
&canonical.extensions,
|
&canonical.extensions,
|
||||||
OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
OPENAI_RESPONSES_LEGACY_EXTENSION_NAMESPACE,
|
||||||
&output,
|
&output,
|
||||||
@@ -219,6 +220,31 @@ pub fn to_raw(canonical: &CanonicalRequest) -> Value {
|
|||||||
Value::Object(output)
|
Value::Object(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn openai_chat_reasoning_effort(value: &str) -> Option<&'static str> {
|
||||||
|
match value.trim().to_ascii_lowercase().as_str() {
|
||||||
|
"low" => Some("low"),
|
||||||
|
"medium" => Some("medium"),
|
||||||
|
"high" | "xhigh" | "max" => Some("high"),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chat_compatible_openai_responses_extension_object(
|
||||||
|
extensions: &std::collections::BTreeMap<String, Value>,
|
||||||
|
namespace: &str,
|
||||||
|
existing: &Map<String, Value>,
|
||||||
|
) -> Map<String, Value> {
|
||||||
|
namespace_extension_object(extensions, namespace, existing)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(key, _)| {
|
||||||
|
matches!(
|
||||||
|
key.as_str(),
|
||||||
|
"verbosity" | "service_tier" | "prompt_cache_key" | "safety_identifier" | "user"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn force_stream_options(body: &mut Value, upstream_is_stream: bool) {
|
fn force_stream_options(body: &mut Value, upstream_is_stream: bool) {
|
||||||
if !upstream_is_stream {
|
if !upstream_is_stream {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ impl ReasoningEffort {
|
|||||||
Self::Low => "low",
|
Self::Low => "low",
|
||||||
Self::Medium => "medium",
|
Self::Medium => "medium",
|
||||||
Self::High => "high",
|
Self::High => "high",
|
||||||
Self::XHigh => "xhigh",
|
Self::XHigh | Self::Max => "high",
|
||||||
Self::Max => "xhigh",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,7 +523,7 @@ mod tests {
|
|||||||
"gpt-5.4-xhigh",
|
"gpt-5.4-xhigh",
|
||||||
)
|
)
|
||||||
.expect("directive should apply");
|
.expect("directive should apply");
|
||||||
assert_eq!(openai_chat["reasoning_effort"], "xhigh");
|
assert_eq!(openai_chat["reasoning_effort"], "high");
|
||||||
|
|
||||||
let mut responses = json!({
|
let mut responses = json!({
|
||||||
"model": "gpt-5-upstream",
|
"model": "gpt-5-upstream",
|
||||||
@@ -597,7 +596,7 @@ mod tests {
|
|||||||
"gpt-5.4-fast-xhigh",
|
"gpt-5.4-fast-xhigh",
|
||||||
)
|
)
|
||||||
.expect("directive should apply");
|
.expect("directive should apply");
|
||||||
assert_eq!(openai_chat["reasoning_effort"], "xhigh");
|
assert_eq!(openai_chat["reasoning_effort"], "high");
|
||||||
assert_eq!(openai_chat["service_tier"], "priority");
|
assert_eq!(openai_chat["service_tier"], "priority");
|
||||||
|
|
||||||
let mut reversed = json!({"model": "gpt-5-upstream", "reasoning_effort": "low"});
|
let mut reversed = json!({"model": "gpt-5-upstream", "reasoning_effort": "low"});
|
||||||
|
|||||||
@@ -738,7 +738,7 @@ mod tests {
|
|||||||
.expect("openai chat body should build");
|
.expect("openai chat body should build");
|
||||||
|
|
||||||
assert_eq!(provider_request_body["model"], "gpt-5-upstream");
|
assert_eq!(provider_request_body["model"], "gpt-5-upstream");
|
||||||
assert_eq!(provider_request_body["reasoning_effort"], "xhigh");
|
assert_eq!(provider_request_body["reasoning_effort"], "high");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user