Skip to main content

vtcode_llm/providers/gemini/wire/interactions/
mod.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3
4use super::GenerationConfig;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct InteractionRequest {
8    pub(crate) model: String,
9    pub(crate) input: InteractionInput,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub(crate) tools: Option<Vec<InteractionTool>>,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub(crate) system_instruction: Option<String>,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub(crate) response_format: Option<Value>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub(crate) response_mime_type: Option<String>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub(crate) stream: Option<bool>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub(crate) store: Option<bool>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub(crate) generation_config: Option<InteractionGenerationConfig>,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub(crate) tool_choice: Option<InteractionToolChoice>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub(crate) previous_interaction_id: Option<String>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(untagged)]
32pub enum InteractionInput {
33    Text(String),
34    Content(Vec<InteractionContent>),
35    Turns(Vec<InteractionTurn>),
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct InteractionTurn {
40    pub(crate) role: String,
41    pub(crate) content: InteractionTurnContent,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45#[serde(untagged)]
46pub enum InteractionTurnContent {
47    Text(String),
48    Content(Vec<InteractionContent>),
49}
50
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52#[serde(tag = "type", rename_all = "snake_case")]
53pub enum InteractionContent {
54    Text {
55        text: String,
56    },
57    Image {
58        data: String,
59        mime_type: String,
60    },
61    FunctionCall {
62        id: String,
63        name: String,
64        arguments: Value,
65        #[serde(skip_serializing_if = "Option::is_none")]
66        signature: Option<String>,
67    },
68    FunctionResult {
69        call_id: String,
70        #[serde(skip_serializing_if = "Option::is_none")]
71        name: Option<String>,
72        result: InteractionResult,
73        #[serde(skip_serializing_if = "Option::is_none")]
74        is_error: Option<bool>,
75        #[serde(skip_serializing_if = "Option::is_none")]
76        signature: Option<String>,
77    },
78    /// Catch-all for unknown content types added by the Gemini API.
79    #[serde(other)]
80    Unknown,
81}
82
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84#[serde(untagged)]
85pub enum InteractionResult {
86    String(String),
87    Json(Value),
88    Content(Vec<InteractionContent>),
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct InteractionTool {
93    #[serde(rename = "type")]
94    pub(crate) tool_type: String,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub(crate) name: Option<String>,
97    #[serde(skip_serializing_if = "Option::is_none")]
98    description: Option<String>,
99    #[serde(skip_serializing_if = "Option::is_none")]
100    parameters: Option<Value>,
101    #[serde(flatten, default)]
102    pub(crate) extra: Map<String, Value>,
103}
104
105impl InteractionTool {
106    pub(crate) fn built_in(tool_type: &str, config: Option<&Value>) -> Self {
107        let extra = config.and_then(Value::as_object).cloned().unwrap_or_default();
108        Self {
109            tool_type: tool_type.to_string(),
110            name: None,
111            description: None,
112            parameters: None,
113            extra,
114        }
115    }
116
117    pub(crate) fn function(name: String, description: String, parameters: Value) -> Self {
118        Self {
119            tool_type: "function".to_string(),
120            name: Some(name),
121            description: Some(description),
122            parameters: Some(parameters),
123            extra: Map::new(),
124        }
125    }
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct InteractionToolChoice {
130    pub(crate) mode: String,
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub(crate) tools: Option<Vec<String>>,
133}
134
135impl InteractionToolChoice {
136    pub(crate) fn new(mode: impl Into<String>) -> Self {
137        Self { mode: mode.into(), tools: None }
138    }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct InteractionGenerationConfig {
143    #[serde(skip_serializing_if = "Option::is_none")]
144    temperature: Option<f32>,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    top_p: Option<f32>,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    max_output_tokens: Option<u32>,
149    #[serde(skip_serializing_if = "Option::is_none")]
150    stop_sequences: Option<Vec<String>>,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    thinking_level: Option<String>,
153}
154
155impl From<GenerationConfig> for InteractionGenerationConfig {
156    fn from(value: GenerationConfig) -> Self {
157        Self {
158            temperature: value.temperature,
159            top_p: value.top_p,
160            max_output_tokens: value.max_output_tokens,
161            stop_sequences: value.stop_sequences,
162            thinking_level: value.thinking_config.and_then(|cfg| cfg.thinking_level),
163        }
164    }
165}
166
167impl From<InteractionGenerationConfig> for GenerationConfig {
168    fn from(value: InteractionGenerationConfig) -> Self {
169        GenerationConfig {
170            temperature: value.temperature,
171            top_p: value.top_p,
172            max_output_tokens: value.max_output_tokens,
173            stop_sequences: value.stop_sequences,
174            thinking_config: value
175                .thinking_level
176                .map(|thinking_level| super::ThinkingConfig { thinking_level: Some(thinking_level) }),
177            ..Default::default()
178        }
179    }
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct Interaction {
184    pub(crate) id: String,
185    pub(crate) model: String,
186    #[serde(default)]
187    pub(crate) status: Option<String>,
188    #[serde(default)]
189    pub(crate) outputs: Vec<InteractionOutput>,
190    #[serde(default)]
191    pub(crate) usage: Option<InteractionUsage>,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct InteractionOutput {
196    #[serde(rename = "type")]
197    pub(crate) output_type: String,
198    #[serde(default)]
199    pub(crate) text: Option<String>,
200    #[serde(default)]
201    pub(crate) summary: Option<String>,
202    #[serde(default)]
203    pub(crate) id: Option<String>,
204    #[serde(default)]
205    pub(crate) name: Option<String>,
206    #[serde(default)]
207    pub(crate) arguments: Option<Value>,
208    #[serde(default)]
209    pub(crate) signature: Option<String>,
210    #[serde(default)]
211    pub(crate) function_call: Option<InteractionFunctionCall>,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct InteractionFunctionCall {
216    pub(crate) name: String,
217    pub(crate) arguments: Value,
218    #[serde(default)]
219    pub(crate) id: Option<String>,
220    #[serde(default)]
221    pub(crate) signature: Option<String>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct InteractionUsage {
226    #[serde(default)]
227    pub(crate) total_input_tokens: Option<u32>,
228    #[serde(default)]
229    pub(crate) total_output_tokens: Option<u32>,
230    #[serde(default)]
231    pub(crate) total_tokens: Option<u32>,
232    #[serde(default)]
233    pub(crate) total_cached_tokens: Option<u32>,
234}