vtcode_llm/providers/gemini/wire/models/
request.rs1use super::{Content, SystemInstruction, Tool, ToolConfig};
2use serde::{Deserialize, Deserializer, Serialize};
3use serde_json::Value;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct GenerateContentRequest {
7 pub(crate) contents: Vec<Content>,
8 #[serde(skip_serializing_if = "Option::is_none")]
9 pub(crate) tools: Option<Vec<Tool>>,
10 #[serde(skip_serializing_if = "Option::is_none", rename = "toolConfig")]
11 pub(crate) tool_config: Option<ToolConfig>,
12 #[serde(skip_serializing_if = "Option::is_none", rename = "systemInstruction")]
13 pub(crate) system_instruction: Option<SystemInstruction>,
14 #[serde(skip_serializing_if = "Option::is_none", rename = "cachedContent")]
17 pub(crate) cached_content: Option<String>,
18 #[serde(
19 default,
20 skip_serializing_if = "Option::is_none",
21 rename = "generationConfig",
22 deserialize_with = "deserialize_boxed_generation_config_opt"
23 )]
24 pub(crate) generation_config: Option<Box<GenerationConfig>>,
25}
26
27#[derive(Debug, Clone, Default, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29pub struct GenerationConfig {
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub(crate) temperature: Option<f32>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub(crate) top_p: Option<f32>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub(crate) top_k: Option<i32>,
36 #[serde(skip_serializing_if = "Option::is_none", rename = "candidateCount")]
37 pub(crate) candidate_count: Option<i32>,
38 #[serde(skip_serializing_if = "Option::is_none", rename = "maxOutputTokens")]
39 pub(crate) max_output_tokens: Option<u32>,
40 #[serde(skip_serializing_if = "Option::is_none", rename = "stopSequences")]
41 pub(crate) stop_sequences: Option<Vec<String>>,
42 #[serde(skip_serializing_if = "Option::is_none", rename = "responseMimeType")]
43 pub(crate) response_mime_type: Option<String>,
44 #[serde(skip_serializing_if = "Option::is_none", rename = "responseSchema")]
45 pub(crate) response_schema: Option<Value>,
46 #[serde(skip_serializing_if = "Option::is_none", rename = "presencePenalty")]
47 pub(crate) presence_penalty: Option<f32>,
48 #[serde(skip_serializing_if = "Option::is_none", rename = "frequencyPenalty")]
49 pub(crate) frequency_penalty: Option<f32>,
50 #[serde(skip_serializing_if = "Option::is_none", rename = "thinkingConfig")]
51 pub(crate) thinking_config: Option<ThinkingConfig>,
52}
53
54impl GenerationConfig {
55 fn is_empty(&self) -> bool {
56 self.temperature.is_none()
57 && self.top_p.is_none()
58 && self.top_k.is_none()
59 && self.candidate_count.is_none()
60 && self.max_output_tokens.is_none()
61 && self.stop_sequences.is_none()
62 && self.response_mime_type.is_none()
63 && self.response_schema.is_none()
64 && self.presence_penalty.is_none()
65 && self.frequency_penalty.is_none()
66 && self.thinking_config.is_none()
67 }
68
69 fn into_boxed_if_non_empty(self) -> Option<Box<Self>> {
70 (!self.is_empty()).then_some(Box::new(self))
71 }
72}
73
74#[derive(Debug, Clone, Default, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct ThinkingConfig {
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub(crate) thinking_level: Option<String>,
79}
80
81fn deserialize_boxed_generation_config_opt<'de, D>(deserializer: D) -> Result<Option<Box<GenerationConfig>>, D::Error>
82where
83 D: Deserializer<'de>,
84{
85 Option::<GenerationConfig>::deserialize(deserializer)
86 .map(|value| value.and_then(GenerationConfig::into_boxed_if_non_empty))
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn empty_generation_config_deserializes_to_none() {
95 let request: GenerateContentRequest = serde_json::from_str(
96 r#"{
97 "contents": [],
98 "generationConfig": {}
99 }"#,
100 )
101 .unwrap();
102
103 assert!(request.generation_config.is_none());
104 }
105
106 #[test]
107 fn boxed_generation_config_is_smaller_than_inline_option() {
108 use std::mem::size_of;
109
110 assert!(size_of::<Option<Box<GenerationConfig>>>() < size_of::<Option<GenerationConfig>>());
111 }
112}