Skip to main content

gproxy_protocol/transform/claude/count_tokens/gemini/
request.rs

1use crate::claude::count_tokens::request::ClaudeCountTokensRequest;
2use crate::claude::count_tokens::types::BetaMessageRole;
3use crate::gemini::count_tokens::request::{
4    GeminiCountTokensRequest, PathParameters, QueryParameters, RequestBody, RequestHeaders,
5};
6use crate::gemini::count_tokens::types::{
7    GeminiContent, GeminiContentRole, GeminiGenerateContentRequest, GeminiGenerationConfig,
8    GeminiPart, HttpMethod,
9};
10use crate::transform::claude::count_tokens::utils::{
11    beta_message_content_to_text, claude_model_to_string,
12};
13use crate::transform::claude::generate_content::gemini::utils::{
14    gemini_system_instruction_from_claude, gemini_thinking_config_from_claude,
15    gemini_tool_config_from_claude, gemini_tools_from_claude,
16};
17use crate::transform::claude::model_list::gemini::utils::ensure_models_prefix;
18use crate::transform::utils::TransformError;
19
20impl TryFrom<ClaudeCountTokensRequest> for GeminiCountTokensRequest {
21    type Error = TransformError;
22
23    fn try_from(value: ClaudeCountTokensRequest) -> Result<Self, TransformError> {
24        let model = ensure_models_prefix(&claude_model_to_string(&value.body.model));
25        let contents = value
26            .body
27            .messages
28            .into_iter()
29            .map(|message| GeminiContent {
30                parts: vec![GeminiPart {
31                    text: Some(beta_message_content_to_text(&message.content)),
32                    ..GeminiPart::default()
33                }],
34                role: Some(match message.role {
35                    BetaMessageRole::User => GeminiContentRole::User,
36                    BetaMessageRole::Assistant => GeminiContentRole::Model,
37                }),
38            })
39            .collect::<Vec<_>>();
40        let tools = gemini_tools_from_claude(value.body.tools, false);
41        let tool_config = gemini_tool_config_from_claude(value.body.tool_choice);
42        let thinking_config = gemini_thinking_config_from_claude(
43            value.body.thinking,
44            value
45                .body
46                .output_config
47                .as_ref()
48                .and_then(|config| config.effort.as_ref()),
49        );
50        let system_instruction = gemini_system_instruction_from_claude(value.body.system);
51        let json_output_requested = value
52            .body
53            .output_config
54            .as_ref()
55            .and_then(|config| config.format.as_ref())
56            .is_some();
57        let generation_config = if thinking_config.is_some() || json_output_requested {
58            Some(GeminiGenerationConfig {
59                response_mime_type: if json_output_requested {
60                    Some("application/json".to_string())
61                } else {
62                    None
63                },
64                thinking_config,
65                ..GeminiGenerationConfig::default()
66            })
67        } else {
68            None
69        };
70
71        Ok(Self {
72            method: HttpMethod::Post,
73            path: PathParameters {
74                model: model.clone(),
75            },
76            query: QueryParameters::default(),
77            headers: RequestHeaders::default(),
78            body: RequestBody {
79                contents: None,
80                generate_content_request: Some(GeminiGenerateContentRequest {
81                    model,
82                    contents,
83                    tools,
84                    tool_config,
85                    safety_settings: None,
86                    system_instruction,
87                    generation_config,
88                    cached_content: None,
89                }),
90            },
91        })
92    }
93}