revert(proxy): drop Anthropic system-message hoisting (#3775)

The unrelated Windows test fixes from #3775 are kept; tool-thinking-history normalization is unchanged.

Refs #4297
This commit is contained in:
Jason
2026-06-16 22:25:29 +08:00
parent 69341db284
commit b724f5dde9
+12 -77
View File
@@ -225,78 +225,12 @@ pub fn normalize_anthropic_messages_for_provider(
return false;
}
let mut changed = normalize_anthropic_system_role_messages(body);
changed |= normalize_anthropic_tool_thinking_history_for_provider(body, provider, api_format);
let mut changed =
normalize_anthropic_tool_thinking_history_for_provider(body, provider, api_format);
changed |= normalize_deepseek_thinking_disabled_strip_effort(body, provider);
changed
}
fn normalize_anthropic_system_role_messages(body: &mut Value) -> bool {
let mut system_parts = Vec::new();
let changed = {
let Some(messages) = body.get_mut("messages").and_then(Value::as_array_mut) else {
return false;
};
let original_len = messages.len();
let mut kept_messages = Vec::with_capacity(messages.len());
for message in std::mem::take(messages) {
if message.get("role").and_then(Value::as_str) == Some("system") {
if let Some(content) = message.get("content") {
append_anthropic_system_parts(content, &mut system_parts);
}
} else {
kept_messages.push(message);
}
}
let changed = kept_messages.len() != original_len;
*messages = kept_messages;
changed
};
if !changed || system_parts.is_empty() {
return changed;
}
let mut merged_parts = Vec::new();
if let Some(existing) = body.get("system") {
append_anthropic_system_parts(existing, &mut merged_parts);
}
merged_parts.extend(system_parts);
if !merged_parts.is_empty() {
body["system"] = Value::Array(merged_parts);
}
true
}
fn append_anthropic_system_parts(content: &Value, parts: &mut Vec<Value>) {
match content {
Value::String(text) if !text.trim().is_empty() => {
parts.push(json!({
"type": "text",
"text": text
}));
}
Value::Array(items) => {
for item in items {
append_anthropic_system_parts(item, parts);
}
}
Value::Object(obj)
if obj
.get("text")
.and_then(Value::as_str)
.is_some_and(|text| !text.trim().is_empty()) =>
{
parts.push(Value::Object(obj.clone()));
}
_ => {}
}
}
fn normalize_anthropic_tool_thinking_history(body: &mut Value) -> bool {
let Some(messages) = body.get_mut("messages").and_then(Value::as_array_mut) else {
return false;
@@ -2189,7 +2123,10 @@ mod tests {
}
#[test]
fn test_anthropic_system_role_messages_move_to_top_level_system() {
fn test_anthropic_messages_no_longer_hoists_system_role_messages() {
// After reverting #3775, role=system messages are left in `messages[]`
// (DeepSeek's endpoint accepts them natively) and the top-level `system`
// field is untouched, preserving the request prefix.
let provider = create_provider(json!({
"env": {
"ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
@@ -2211,15 +2148,13 @@ mod tests {
let changed = normalize_anthropic_messages_for_provider(&mut body, &provider, "anthropic");
assert!(changed);
assert!(!changed);
let messages = body["messages"].as_array().unwrap();
assert_eq!(messages.len(), 1);
assert_eq!(messages[0]["role"], "user");
let system = body["system"].as_array().unwrap();
assert_eq!(system[0]["text"], "Existing top-level system.");
assert_eq!(system[1]["text"], "Message system one.");
assert_eq!(system[2]["text"], "Message system two.");
assert_eq!(messages.len(), 3);
assert_eq!(messages[0]["role"], "system");
assert_eq!(messages[1]["role"], "user");
assert_eq!(messages[2]["role"], "system");
assert_eq!(body["system"], "Existing top-level system.");
}
#[test]