gproxy-transform 2.4.4

Pairwise request/response/stream transforms between the OpenAI, Anthropic Claude, and Google Gemini APIs
Documentation
use crate::protocol::{claude, gemini, openai};

pub(in crate::transform::count_tokens) fn openai_tool_config_to_gemini(
    tool_choice: Option<openai::ResponseToolChoice>,
) -> Option<gemini::ToolConfig> {
    let function_calling_config = match tool_choice? {
        openai::ResponseToolChoice::Mode(mode) => {
            Some(crate::protocol::wire!(gemini::FunctionCallingConfig {
                mode: Some(openai_tool_choice_mode_to_gemini(mode)),
                allowed_function_names: Vec::new(),
                extra: Default::default(),
            }))
        }
        openai::ResponseToolChoice::Allowed(choice) => {
            Some(crate::protocol::wire!(gemini::FunctionCallingConfig {
                mode: Some(openai_allowed_tools_mode_to_gemini(choice.mode)),
                allowed_function_names: choice
                    .tools
                    .into_iter()
                    .filter_map(openai_allowed_tool_name)
                    .collect(),
                extra: Default::default(),
            }))
        }
        openai::ResponseToolChoice::Function(choice) => {
            Some(crate::protocol::wire!(gemini::FunctionCallingConfig {
                mode: Some(gemini::FunctionCallingMode::Known(
                    gemini::FunctionCallingModeKnown::Any,
                )),
                allowed_function_names: vec![choice.name],
                extra: Default::default(),
            }))
        }
        _ => None,
    }?;

    Some(crate::protocol::wire!(gemini::ToolConfig {
        function_calling_config: Some(function_calling_config),
        retrieval_config: None,
        include_server_side_tool_invocations: None,
        extra: Default::default(),
    }))
}

fn openai_tool_choice_mode_to_gemini(mode: openai::ToolChoiceMode) -> gemini::FunctionCallingMode {
    match mode {
        openai::ToolChoiceMode::None => {
            gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::None)
        }
        openai::ToolChoiceMode::Auto => {
            gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Auto)
        }
        openai::ToolChoiceMode::Required => {
            gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Any)
        }
        _ => {
            unreachable!("new non-exhaustive protocol variant requires a lockstep transform update")
        }
    }
}

fn openai_allowed_tools_mode_to_gemini(
    mode: openai::AllowedToolsMode,
) -> gemini::FunctionCallingMode {
    match mode {
        openai::AllowedToolsMode::Auto => {
            gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Auto)
        }
        openai::AllowedToolsMode::Required => {
            gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Any)
        }
        _ => {
            unreachable!("new non-exhaustive protocol variant requires a lockstep transform update")
        }
    }
}

fn openai_allowed_tool_name(tool: openai::ResponseAllowedTool) -> Option<String> {
    match tool {
        openai::ResponseAllowedTool::Function { name, .. } => Some(name),
        _ => None,
    }
}

pub(in crate::transform::count_tokens) fn claude_tool_config_to_gemini(
    tool_choice: Option<claude::ToolChoice>,
) -> Option<gemini::ToolConfig> {
    let function_calling_config = match tool_choice? {
        claude::ToolChoice::Auto(_) => {
            Some(crate::protocol::wire!(gemini::FunctionCallingConfig {
                mode: Some(gemini::FunctionCallingMode::Known(
                    gemini::FunctionCallingModeKnown::Auto,
                )),
                allowed_function_names: Vec::new(),
                extra: Default::default(),
            }))
        }
        claude::ToolChoice::Any(_) => Some(crate::protocol::wire!(gemini::FunctionCallingConfig {
            mode: Some(gemini::FunctionCallingMode::Known(
                gemini::FunctionCallingModeKnown::Any,
            )),
            allowed_function_names: Vec::new(),
            extra: Default::default(),
        })),
        claude::ToolChoice::Tool(choice) => {
            Some(crate::protocol::wire!(gemini::FunctionCallingConfig {
                mode: Some(gemini::FunctionCallingMode::Known(
                    gemini::FunctionCallingModeKnown::Any,
                )),
                allowed_function_names: vec![choice.name],
                extra: Default::default(),
            }))
        }
        claude::ToolChoice::None(_) => {
            Some(crate::protocol::wire!(gemini::FunctionCallingConfig {
                mode: Some(gemini::FunctionCallingMode::Known(
                    gemini::FunctionCallingModeKnown::None,
                )),
                allowed_function_names: Vec::new(),
                extra: Default::default(),
            }))
        }
        claude::ToolChoice::Unknown(_) => None,
        _ => {
            unreachable!("new non-exhaustive protocol variant requires a lockstep transform update")
        }
    }?;

    Some(crate::protocol::wire!(gemini::ToolConfig {
        function_calling_config: Some(function_calling_config),
        retrieval_config: None,
        include_server_side_tool_invocations: None,
        extra: Default::default(),
    }))
}

pub(in crate::transform::count_tokens) fn claude_tool_choice_to_openai(
    tool_choice: Option<claude::ToolChoice>,
) -> Option<openai::ResponseToolChoice> {
    match tool_choice? {
        claude::ToolChoice::Auto(_) => Some(openai::ResponseToolChoice::Mode(
            openai::ToolChoiceMode::Auto,
        )),
        claude::ToolChoice::Any(_) => Some(openai::ResponseToolChoice::Mode(
            openai::ToolChoiceMode::Required,
        )),
        claude::ToolChoice::Tool(choice) => Some(openai::ResponseToolChoice::Function(
            crate::protocol::wire!(openai::ResponseFunctionToolChoice {
                type_: openai::FunctionToolChoiceType::Function,
                name: choice.name,
                extra: Default::default(),
            }),
        )),
        claude::ToolChoice::None(_) => Some(openai::ResponseToolChoice::Mode(
            openai::ToolChoiceMode::None,
        )),
        claude::ToolChoice::Unknown(_) => None,
        _ => {
            unreachable!("new non-exhaustive protocol variant requires a lockstep transform update")
        }
    }
}

pub(in crate::transform::count_tokens) fn gemini_tool_config_to_openai(
    tool_config: Option<gemini::ToolConfig>,
) -> Option<openai::ResponseToolChoice> {
    let config = tool_config?.function_calling_config?;
    let names = config.allowed_function_names;
    match config.mode? {
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::None) => Some(
            openai::ResponseToolChoice::Mode(openai::ToolChoiceMode::None),
        ),
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Auto) => {
            if names.is_empty() {
                Some(openai::ResponseToolChoice::Mode(
                    openai::ToolChoiceMode::Auto,
                ))
            } else {
                Some(openai::ResponseToolChoice::Allowed(
                    openai_allowed_function_choice(openai::AllowedToolsMode::Auto, names),
                ))
            }
        }
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Any)
        | gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Validated) => {
            if names.len() == 1 {
                Some(openai::ResponseToolChoice::Function(
                    crate::protocol::wire!(openai::ResponseFunctionToolChoice {
                        type_: openai::FunctionToolChoiceType::Function,
                        name: names.into_iter().next().unwrap_or_default(),
                        extra: Default::default(),
                    }),
                ))
            } else if names.is_empty() {
                Some(openai::ResponseToolChoice::Mode(
                    openai::ToolChoiceMode::Required,
                ))
            } else {
                Some(openai::ResponseToolChoice::Allowed(
                    openai_allowed_function_choice(openai::AllowedToolsMode::Required, names),
                ))
            }
        }
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::ModeUnspecified)
        | gemini::FunctionCallingMode::Unknown(_) => None,
        _ => {
            unreachable!("new non-exhaustive protocol variant requires a lockstep transform update")
        }
    }
}

pub(in crate::transform::count_tokens) fn gemini_tool_config_to_claude(
    tool_config: Option<gemini::ToolConfig>,
) -> Option<claude::ToolChoice> {
    let config = tool_config?.function_calling_config?;
    let names = config.allowed_function_names;
    match config.mode? {
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::None) => Some(
            claude::ToolChoice::None(crate::protocol::wire!(claude::ToolChoiceNone {
                type_: claude::ToolChoiceNoneType::None,
                extra: Default::default(),
            })),
        ),
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Auto) => Some(
            claude::ToolChoice::Auto(crate::protocol::wire!(claude::ToolChoiceAuto {
                type_: claude::ToolChoiceAutoType::Auto,
                disable_parallel_tool_use: None,
                extra: Default::default(),
            })),
        ),
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Any)
        | gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::Validated) => {
            if names.len() == 1 {
                Some(claude::ToolChoice::Tool(crate::protocol::wire!(
                    claude::ToolChoiceTool {
                        name: names.into_iter().next().unwrap_or_default(),
                        type_: claude::ToolChoiceToolType::Tool,
                        disable_parallel_tool_use: None,
                        extra: Default::default(),
                    }
                )))
            } else {
                Some(claude::ToolChoice::Any(crate::protocol::wire!(
                    claude::ToolChoiceAny {
                        type_: claude::ToolChoiceAnyType::Any,
                        disable_parallel_tool_use: None,
                        extra: Default::default(),
                    }
                )))
            }
        }
        gemini::FunctionCallingMode::Known(gemini::FunctionCallingModeKnown::ModeUnspecified)
        | gemini::FunctionCallingMode::Unknown(_) => None,
        _ => {
            unreachable!("new non-exhaustive protocol variant requires a lockstep transform update")
        }
    }
}

fn openai_allowed_function_choice(
    mode: openai::AllowedToolsMode,
    names: Vec<String>,
) -> openai::ResponseAllowedToolChoice {
    crate::protocol::wire!(openai::ResponseAllowedToolChoice {
        mode,
        tools: names
            .into_iter()
            .map(|name| openai::ResponseAllowedTool::Function {
                name,
                extra: Default::default(),
            })
            .collect(),
        type_: openai::AllowedToolsType::AllowedTools,
        extra: Default::default(),
    })
}

pub(in crate::transform::count_tokens) fn openai_tool_choice_to_claude(
    tool_choice: Option<openai::ResponseToolChoice>,
    parallel_tool_calls: Option<bool>,
) -> Option<claude::ToolChoice> {
    let disable_parallel_tool_use = parallel_tool_calls.map(|value| !value);
    match tool_choice {
        Some(openai::ResponseToolChoice::Mode(openai::ToolChoiceMode::None)) => Some(
            claude::ToolChoice::None(crate::protocol::wire!(claude::ToolChoiceNone {
                type_: claude::ToolChoiceNoneType::None,
                extra: Default::default(),
            })),
        ),
        Some(openai::ResponseToolChoice::Mode(openai::ToolChoiceMode::Auto)) | None => {
            disable_parallel_tool_use.map(|disable_parallel_tool_use| {
                claude::ToolChoice::Auto(crate::protocol::wire!(claude::ToolChoiceAuto {
                    type_: claude::ToolChoiceAutoType::Auto,
                    disable_parallel_tool_use: Some(disable_parallel_tool_use),
                    extra: Default::default(),
                }))
            })
        }
        Some(openai::ResponseToolChoice::Mode(openai::ToolChoiceMode::Required)) => Some(
            claude::ToolChoice::Any(crate::protocol::wire!(claude::ToolChoiceAny {
                type_: claude::ToolChoiceAnyType::Any,
                disable_parallel_tool_use,
                extra: Default::default(),
            })),
        ),
        Some(openai::ResponseToolChoice::Function(choice)) => Some(claude::ToolChoice::Tool(
            crate::protocol::wire!(claude::ToolChoiceTool {
                name: choice.name,
                type_: claude::ToolChoiceToolType::Tool,
                disable_parallel_tool_use,
                extra: Default::default(),
            }),
        )),
        Some(openai::ResponseToolChoice::Allowed(choice)) => {
            let names = choice
                .tools
                .into_iter()
                .filter_map(openai_allowed_tool_name)
                .collect::<Vec<_>>();
            if names.len() == 1 {
                Some(claude::ToolChoice::Tool(crate::protocol::wire!(
                    claude::ToolChoiceTool {
                        name: names.into_iter().next().unwrap_or_default(),
                        type_: claude::ToolChoiceToolType::Tool,
                        disable_parallel_tool_use,
                        extra: Default::default(),
                    }
                )))
            } else {
                Some(claude::ToolChoice::Any(crate::protocol::wire!(
                    claude::ToolChoiceAny {
                        type_: claude::ToolChoiceAnyType::Any,
                        disable_parallel_tool_use,
                        extra: Default::default(),
                    }
                )))
            }
        }
        _ => None,
    }
}

pub(in crate::transform::count_tokens) fn claude_parallel_tool_calls(
    tool_choice: Option<&claude::ToolChoice>,
) -> Option<bool> {
    match tool_choice? {
        claude::ToolChoice::Auto(choice) => choice.disable_parallel_tool_use.map(|value| !value),
        claude::ToolChoice::Any(choice) => choice.disable_parallel_tool_use.map(|value| !value),
        claude::ToolChoice::Tool(choice) => choice.disable_parallel_tool_use.map(|value| !value),
        claude::ToolChoice::None(_) | claude::ToolChoice::Unknown(_) => None,
        _ => {
            unreachable!("new non-exhaustive protocol variant requires a lockstep transform update")
        }
    }
}