Skip to main content

gproxy_protocol/transform/claude/generate_content/gemini/
utils.rs

1use crate::claude::count_tokens::types::{
2    BetaOutputEffort, BetaSystemPrompt, BetaThinkingConfigParam, BetaToolChoice, BetaToolUnion,
3};
4use crate::gemini::count_tokens::types::{
5    GeminiCodeExecution, GeminiComputerUse, GeminiContent, GeminiEnvironment, GeminiFileSearch,
6    GeminiFunctionCallingConfig, GeminiFunctionCallingMode, GeminiFunctionDeclaration,
7    GeminiGoogleSearch, GeminiPart, GeminiThinkingConfig, GeminiThinkingLevel, GeminiTool,
8    GeminiToolConfig, GeminiUrlContext,
9};
10use crate::transform::claude::utils::beta_system_prompt_to_text;
11
12pub fn gemini_system_instruction_from_claude(
13    system: Option<BetaSystemPrompt>,
14) -> Option<GeminiContent> {
15    beta_system_prompt_to_text(system).map(|text| GeminiContent {
16        parts: vec![GeminiPart {
17            text: Some(text),
18            ..GeminiPart::default()
19        }],
20        role: None,
21    })
22}
23
24pub fn gemini_tools_from_claude(
25    tools: Option<Vec<BetaToolUnion>>,
26    include_custom_schema: bool,
27) -> Option<Vec<GeminiTool>> {
28    let mut converted_tools = Vec::new();
29
30    if let Some(tools) = tools {
31        for tool in tools {
32            match tool {
33                BetaToolUnion::Custom(tool) => {
34                    let parameters_json_schema = if include_custom_schema {
35                        serde_json::to_value(tool.input_schema).ok()
36                    } else {
37                        None
38                    };
39                    converted_tools.push(GeminiTool {
40                        function_declarations: Some(vec![GeminiFunctionDeclaration {
41                            name: tool.name,
42                            description: tool.description.unwrap_or_default(),
43                            behavior: None,
44                            parameters: None,
45                            parameters_json_schema,
46                            response: None,
47                            response_json_schema: None,
48                        }]),
49                        ..GeminiTool::default()
50                    });
51                }
52                BetaToolUnion::CodeExecution20250522(_)
53                | BetaToolUnion::CodeExecution20250825(_) => {
54                    converted_tools.push(GeminiTool {
55                        code_execution: Some(GeminiCodeExecution {}),
56                        ..GeminiTool::default()
57                    });
58                }
59                BetaToolUnion::ComputerUse20241022(_)
60                | BetaToolUnion::ComputerUse20250124(_)
61                | BetaToolUnion::ComputerUse20251124(_) => {
62                    converted_tools.push(GeminiTool {
63                        computer_use: Some(GeminiComputerUse {
64                            environment: GeminiEnvironment::EnvironmentBrowser,
65                            excluded_predefined_functions: None,
66                        }),
67                        ..GeminiTool::default()
68                    });
69                }
70                BetaToolUnion::WebSearch20250305(_) => {
71                    converted_tools.push(GeminiTool {
72                        google_search: Some(GeminiGoogleSearch::default()),
73                        ..GeminiTool::default()
74                    });
75                }
76                BetaToolUnion::WebFetch20250910(_) => {
77                    converted_tools.push(GeminiTool {
78                        url_context: Some(GeminiUrlContext {}),
79                        ..GeminiTool::default()
80                    });
81                }
82                BetaToolUnion::ToolSearchBm25_20251119(_)
83                | BetaToolUnion::ToolSearchRegex20251119(_) => {
84                    converted_tools.push(GeminiTool {
85                        file_search: Some(GeminiFileSearch::default()),
86                        ..GeminiTool::default()
87                    });
88                }
89                BetaToolUnion::Bash20241022(_)
90                | BetaToolUnion::Bash20250124(_)
91                | BetaToolUnion::TextEditor20241022(_)
92                | BetaToolUnion::TextEditor20250124(_)
93                | BetaToolUnion::TextEditor20250429(_)
94                | BetaToolUnion::TextEditor20250728(_) => {
95                    converted_tools.push(GeminiTool {
96                        code_execution: Some(GeminiCodeExecution {}),
97                        ..GeminiTool::default()
98                    });
99                }
100                BetaToolUnion::McpToolset(_) | BetaToolUnion::Memory20250818(_) => {}
101            }
102        }
103    }
104
105    if converted_tools.is_empty() {
106        None
107    } else {
108        Some(converted_tools)
109    }
110}
111
112pub fn gemini_tool_config_from_claude(
113    tool_choice: Option<BetaToolChoice>,
114) -> Option<GeminiToolConfig> {
115    let function_calling_config = match tool_choice {
116        Some(BetaToolChoice::Auto(_)) => Some(GeminiFunctionCallingConfig {
117            mode: Some(GeminiFunctionCallingMode::Auto),
118            allowed_function_names: None,
119        }),
120        Some(BetaToolChoice::Any(_)) => Some(GeminiFunctionCallingConfig {
121            mode: Some(GeminiFunctionCallingMode::Any),
122            allowed_function_names: None,
123        }),
124        Some(BetaToolChoice::None(_)) => Some(GeminiFunctionCallingConfig {
125            mode: Some(GeminiFunctionCallingMode::None),
126            allowed_function_names: None,
127        }),
128        Some(BetaToolChoice::Tool(choice)) => Some(GeminiFunctionCallingConfig {
129            mode: Some(GeminiFunctionCallingMode::Any),
130            allowed_function_names: Some(vec![choice.name]),
131        }),
132        None => None,
133    };
134
135    function_calling_config.map(|config| GeminiToolConfig {
136        function_calling_config: Some(config),
137        retrieval_config: None,
138    })
139}
140
141pub fn gemini_thinking_config_from_claude(
142    thinking: Option<BetaThinkingConfigParam>,
143    output_effort: Option<&BetaOutputEffort>,
144) -> Option<GeminiThinkingConfig> {
145    let mut thinking_config = GeminiThinkingConfig::default();
146    let mut has_thinking_config = false;
147
148    match thinking {
149        Some(BetaThinkingConfigParam::Enabled(config)) => {
150            thinking_config.include_thoughts = Some(true);
151            thinking_config.thinking_budget =
152                Some(i64::try_from(config.budget_tokens).unwrap_or(i64::MAX));
153            has_thinking_config = true;
154        }
155        Some(BetaThinkingConfigParam::Disabled(_)) => {
156            thinking_config.include_thoughts = Some(false);
157            has_thinking_config = true;
158        }
159        Some(BetaThinkingConfigParam::Adaptive(_)) => {
160            thinking_config.thinking_level = Some(GeminiThinkingLevel::Medium);
161            has_thinking_config = true;
162        }
163        None => {}
164    }
165
166    if let Some(effort) = output_effort {
167        thinking_config.thinking_level = Some(match effort {
168            BetaOutputEffort::Low => GeminiThinkingLevel::Low,
169            BetaOutputEffort::Medium => GeminiThinkingLevel::Medium,
170            BetaOutputEffort::High => GeminiThinkingLevel::High,
171            BetaOutputEffort::XHigh => GeminiThinkingLevel::High,
172            BetaOutputEffort::Max => GeminiThinkingLevel::High,
173        });
174        has_thinking_config = true;
175    }
176
177    if has_thinking_config {
178        Some(thinking_config)
179    } else {
180        None
181    }
182}