use crate::proxy::mappers::claude::models::*;
use serde_json::Value;
pub(super) fn clean_cache_control_from_messages(messages: &mut [Message]) {
tracing::info!(
"[DEBUG-593] Starting cache_control cleanup for {} messages",
messages.len()
);
let mut total_cleaned = 0;
for (idx, msg) in messages.iter_mut().enumerate() {
if let MessageContent::Array(blocks) = &mut msg.content {
for (block_idx, block) in blocks.iter_mut().enumerate() {
match block {
ContentBlock::Thinking { cache_control, .. } => {
if cache_control.is_some() {
tracing::info!(
" Found cache_control in Thinking block at message[{}].content[{}]: {:?}",
idx,
block_idx,
cache_control
);
*cache_control = None;
total_cleaned += 1;
}
}
ContentBlock::Image { cache_control, .. } => {
if cache_control.is_some() {
tracing::debug!(
"[Cache-Control-Cleaner] Removed cache_control from Image block at message[{}].content[{}]",
idx,
block_idx
);
*cache_control = None;
total_cleaned += 1;
}
}
ContentBlock::Document { cache_control, .. } => {
if cache_control.is_some() {
tracing::debug!(
"[Cache-Control-Cleaner] Removed cache_control from Document block at message[{}].content[{}]",
idx,
block_idx
);
*cache_control = None;
total_cleaned += 1;
}
}
ContentBlock::ToolUse { cache_control, .. } => {
if cache_control.is_some() {
tracing::debug!(
"[Cache-Control-Cleaner] Removed cache_control from ToolUse block at message[{}].content[{}]",
idx,
block_idx
);
*cache_control = None;
total_cleaned += 1;
}
}
_ => {}
}
}
}
}
if total_cleaned > 0 {
tracing::info!(
"[DEBUG-593] Cache control cleanup complete: removed {} cache_control fields",
total_cleaned
);
} else {
tracing::debug!("[DEBUG-593] No cache_control fields found");
}
}
pub(super) fn deep_clean_cache_control(value: &mut Value) {
match value {
Value::Object(map) => {
if map.remove("cache_control").is_some() {
tracing::debug!("[DEBUG-593] Removed cache_control from nested JSON object");
}
for (_, v) in map.iter_mut() {
deep_clean_cache_control(v);
}
}
Value::Array(arr) => {
for item in arr.iter_mut() {
deep_clean_cache_control(item);
}
}
_ => {}
}
}
pub(super) fn sort_thinking_blocks_first(messages: &mut [Message]) {
for msg in messages.iter_mut() {
if msg.role == "assistant" {
if let MessageContent::Array(blocks) = &mut msg.content {
let mut thinking_blocks: Vec<ContentBlock> = Vec::new();
let mut text_blocks: Vec<ContentBlock> = Vec::new();
let mut tool_blocks: Vec<ContentBlock> = Vec::new();
let mut other_blocks: Vec<ContentBlock> = Vec::new();
let original_len = blocks.len();
let mut needs_reorder = false;
let mut saw_non_thinking = false;
for block in blocks.iter() {
match block {
ContentBlock::Thinking { .. } | ContentBlock::RedactedThinking { .. } => {
if saw_non_thinking {
needs_reorder = true;
}
}
ContentBlock::Text { .. } => {
saw_non_thinking = true;
}
ContentBlock::ToolUse { .. } => {
saw_non_thinking = true;
}
_ => saw_non_thinking = true,
}
}
if needs_reorder || original_len > 1 {
for block in blocks.drain(..) {
match &block {
ContentBlock::Thinking { .. }
| ContentBlock::RedactedThinking { .. } => {
thinking_blocks.push(block);
}
ContentBlock::Text { text } => {
if !text.trim().is_empty() && text != "(no content)" {
text_blocks.push(block);
}
}
ContentBlock::ToolUse { .. } => {
tool_blocks.push(block);
}
_ => {
other_blocks.push(block);
}
}
}
blocks.extend(thinking_blocks);
blocks.extend(text_blocks);
blocks.extend(other_blocks);
blocks.extend(tool_blocks);
if needs_reorder {
tracing::warn!(
"Reordered assistant messages to [Thinking, Text, Tool] structure."
);
}
}
}
}
}
}
pub(super) fn merge_consecutive_messages(messages: &mut Vec<Message>) {
if messages.len() <= 1 {
return;
}
let mut merged: Vec<Message> = Vec::with_capacity(messages.len());
let old_messages = std::mem::take(messages);
let mut messages_iter = old_messages.into_iter();
if let Some(mut current) = messages_iter.next() {
for next in messages_iter {
if current.role == next.role {
match (&mut current.content, next.content) {
(MessageContent::Array(current_blocks), MessageContent::Array(next_blocks)) => {
current_blocks.extend(next_blocks);
}
(MessageContent::Array(current_blocks), MessageContent::String(next_text)) => {
current_blocks.push(ContentBlock::Text { text: next_text });
}
(MessageContent::String(current_text), MessageContent::String(next_text)) => {
*current_text = format!("{}\n\n{}", current_text, next_text);
}
(MessageContent::String(current_text), MessageContent::Array(next_blocks)) => {
let mut new_blocks = vec![ContentBlock::Text {
text: current_text.clone(),
}];
new_blocks.extend(next_blocks);
current.content = MessageContent::Array(new_blocks);
}
}
} else {
merged.push(current);
current = next;
}
}
merged.push(current);
}
*messages = merged;
}
pub(super) fn reorder_gemini_parts(parts: &mut Vec<Value>) {
if parts.len() <= 1 {
return;
}
let mut thinking_parts = Vec::new();
let mut text_parts = Vec::new();
let mut tool_parts = Vec::new();
let mut other_parts = Vec::new();
for part in parts.drain(..) {
if part.get("thought").and_then(|t| t.as_bool()) == Some(true) {
thinking_parts.push(part);
} else if part.get("functionCall").is_some() {
tool_parts.push(part);
} else if let Some(text) = part.get("text").and_then(|t| t.as_str()) {
if !text.trim().is_empty() && text != "(no content)" {
text_parts.push(part);
}
} else {
other_parts.push(part);
}
}
parts.extend(thinking_parts);
parts.extend(text_parts);
parts.extend(other_parts);
parts.extend(tool_parts);
}