gproxy_protocol/transform/gemini/generate_content/claude/
request.rs1use crate::claude::create_message::request::{
2 ClaudeCreateMessageRequest, PathParameters, QueryParameters, RequestBody, RequestHeaders,
3};
4use crate::claude::create_message::types::{HttpMethod, Model};
5use crate::gemini::generate_content::request::GeminiGenerateContentRequest;
6use crate::transform::gemini::utils::{
7 claude_output_config_from_effort_and_format,
8 claude_thinking_effort_format_from_gemini_generation_config,
9 gemini_contents_to_claude_messages, gemini_system_instruction_to_claude,
10 gemini_tool_choice_to_claude, gemini_tools_to_claude, strip_models_prefix,
11};
12use crate::transform::utils::TransformError;
13
14impl TryFrom<GeminiGenerateContentRequest> for ClaudeCreateMessageRequest {
15 type Error = TransformError;
16
17 fn try_from(value: GeminiGenerateContentRequest) -> Result<Self, TransformError> {
18 let body = value.body;
19 let model = Model::Custom(strip_models_prefix(&value.path.model));
20 let messages = gemini_contents_to_claude_messages(body.contents);
21 let system = gemini_system_instruction_to_claude(body.system_instruction);
22 let tool_choice = gemini_tool_choice_to_claude(body.tool_config);
23 let tools = gemini_tools_to_claude(body.tools);
24
25 let generation_config = body.generation_config;
26 let max_tokens = generation_config
27 .as_ref()
28 .and_then(|config| config.max_output_tokens)
29 .map(u64::from)
30 .unwrap_or(8192);
31 let stop_sequences = generation_config
32 .as_ref()
33 .and_then(|config| config.stop_sequences.clone());
34 let temperature = generation_config
35 .as_ref()
36 .and_then(|config| config.temperature);
37 let top_k = generation_config
38 .as_ref()
39 .and_then(|config| config.top_k)
40 .map(u64::from);
41 let top_p = generation_config.as_ref().and_then(|config| config.top_p);
42
43 let (thinking, output_effort, output_format) =
44 claude_thinking_effort_format_from_gemini_generation_config(
45 generation_config.as_ref(),
46 Some(&model),
47 );
48 let output_config =
49 claude_output_config_from_effort_and_format(output_effort, output_format.clone());
50
51 Ok(ClaudeCreateMessageRequest {
52 method: HttpMethod::Post,
53 path: PathParameters::default(),
54 query: QueryParameters::default(),
55 headers: RequestHeaders::default(),
56 body: RequestBody {
57 max_tokens,
58 messages,
59 model,
60 container: None,
61 context_management: None,
62 inference_geo: None,
63 mcp_servers: None,
64 metadata: None,
65 cache_control: None,
66 output_config,
67 service_tier: None,
68 speed: None,
69 stop_sequences,
70 stream: None,
71 system,
72 temperature,
73 thinking,
74 tool_choice,
75 tools,
76 top_k,
77 top_p,
78 },
79 })
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::claude::count_tokens::types as ct;
87 use crate::gemini::count_tokens::types::{GeminiContentRole, GeminiPart};
88 use crate::gemini::generate_content::request::{
89 GeminiGenerateContentRequest, PathParameters as GeminiPathParameters,
90 QueryParameters as GeminiQueryParameters, RequestBody as GeminiRequestBody,
91 RequestHeaders as GeminiRequestHeaders,
92 };
93 use crate::gemini::generate_content::types::{
94 GeminiContent, GeminiGenerationConfig, GeminiThinkingConfig,
95 };
96
97 #[test]
98 fn opus_47_converts_budgeted_gemini_thinking_to_adaptive() {
99 let request = GeminiGenerateContentRequest {
100 method: crate::gemini::types::HttpMethod::Post,
101 path: GeminiPathParameters {
102 model: "models/claude-opus-4-7".to_string(),
103 },
104 query: GeminiQueryParameters::default(),
105 headers: GeminiRequestHeaders::default(),
106 body: GeminiRequestBody {
107 contents: vec![GeminiContent {
108 parts: vec![GeminiPart {
109 text: Some("hi".to_string()),
110 ..Default::default()
111 }],
112 role: Some(GeminiContentRole::User),
113 }],
114 generation_config: Some(GeminiGenerationConfig {
115 thinking_config: Some(GeminiThinkingConfig {
116 thinking_budget: Some(4_096),
117 ..Default::default()
118 }),
119 ..Default::default()
120 }),
121 ..Default::default()
122 },
123 };
124
125 let claude_request = ClaudeCreateMessageRequest::try_from(request).expect("transform");
126 assert!(matches!(
127 claude_request.body.thinking,
128 Some(ct::BetaThinkingConfigParam::Adaptive(_))
129 ));
130 }
131}