gproxy-transform 3.0.0-alpha.0

GPROXY v3 pairwise protocol transforms
Documentation
use gproxy_protocol::claude;

use crate::TransformError;

pub(crate) fn transform(body: bytes::Bytes, model: &str) -> Result<bytes::Bytes, TransformError> {
    let input: claude::CountTokensRequestBody = serde_json::from_slice(&body)?;
    let output = transform_typed(input, model)?;
    Ok(bytes::Bytes::from(serde_json::to_vec(&output)?))
}

pub(crate) fn transform_typed(
    mut input: claude::CountTokensRequestBody,
    model: &str,
) -> Result<gproxy_protocol::openai::ResponseInputTokensRequest, TransformError> {
    crate::common::claude_message_controls::apply(&mut input.messages, &mut input.output_config);
    let text = messages_text(std::mem::take(&mut input.messages));
    input.cache_control = None;
    input.context_management = None;
    input.mcp_servers = None;
    let mut output =
        crate::generate_content::claude_messages_to_openai_responses::request::count_tokens(
            input, model,
        )?;
    output.input = (!text.is_empty()).then_some(gproxy_protocol::openai::ResponseInput::Text(text));
    Ok(output)
}

fn messages_text(messages: Vec<claude::MessageParam>) -> String {
    messages
        .into_iter()
        .map(|message| {
            if let claude::StringOrArray::String(text) = &message.content {
                return text.clone();
            }
            if let claude::StringOrArray::Array(blocks) = message.content {
                return blocks
                    .into_iter()
                    .filter_map(|block| {
                        let claude::ContentBlockParam::Text(block) = block else {
                            return None;
                        };
                        Some(block.text)
                    })
                    .collect();
            }
            String::new()
        })
        .collect::<Vec<_>>()
        .join("\n")
}