From f891241cbf063a5a53a2039974fca39ad1b27c7b Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Thu, 23 Oct 2025 11:13:07 +0200 Subject: [PATCH 1/5] support `parameters_json_schema` in function declaration Signed-off-by: Jean Mertz --- src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.rs b/src/types.rs index 0f4e993..0be73a3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -171,6 +171,7 @@ pub struct FunctionDeclaration { pub name: String, pub description: String, pub parameters: Option, + pub parameters_json_schema: Option, pub response: Option, } From 18bb9d94e69c4058fa79200f94ee489a69acf05c Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Thu, 20 Nov 2025 10:34:48 +0100 Subject: [PATCH 2/5] sometimes gemini returns no parts? Signed-off-by: Jean Mertz --- src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.rs b/src/types.rs index 0be73a3..aedc7d5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -99,6 +99,7 @@ pub enum FunctionCallingMode { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Content { + #[serde(default)] pub parts: Vec, // Optional. The producer of the content. Must be either 'user' or 'model'. // Useful to set for multi-turn conversations, otherwise can be left blank or unset. From 412184789f5ae9bff1822ec6edb06b7b9fde8dfa Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Thu, 20 Nov 2025 10:35:11 +0100 Subject: [PATCH 3/5] improve error debugging capabilities Signed-off-by: Jean Mertz --- src/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 007b29b..6cf8dca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,8 +43,12 @@ pub enum GeminiError { EventSource(#[from] reqwest_eventsource::Error), #[error("API Error: {0}")] Api(Value), - #[error("JSON Error: {0}")] - Json(#[from] serde_json::Error), + #[error("JSON Error: {error} (payload: {data})")] + Json { + data: String, + #[source] + error: serde_json::Error, + }, #[error("Function execution error: {0}")] FunctionExecution(String), } @@ -210,7 +214,10 @@ impl GeminiClient { Event::Open => (), Event::Message(event) => yield serde_json::from_str::(&event.data) - .map_err(Into::into), + .map_err(|error| GeminiError::Json { + data: event.data, + error, + }), }, Err(e) => match e { reqwest_eventsource::Error::StreamEnded => stream.close(), From 554e3f45b922077a27b7cdd141fb9d2a08148472 Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Thu, 20 Nov 2025 16:47:42 +0100 Subject: [PATCH 4/5] add thinking_level support Signed-off-by: Jean Mertz --- src/types.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/types.rs b/src/types.rs index aedc7d5..699e358 100644 --- a/src/types.rs +++ b/src/types.rs @@ -346,6 +346,25 @@ pub struct ThinkingConfig { pub include_thoughts: bool, /// The number of thoughts tokens that the model should generate. pub thinking_budget: Option, + /// Controls the maximum depth of the model's internal reasoning process + /// before it produces a response. If not specified, the default is HIGH. + /// Recommended for Gemini 3 or later models. Use with earlier models + /// results in an error. + pub thinking_level: Option, +} + +/// Allow user to specify how much to think using enum instead of integer +/// budget. +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum ThinkingLevel { + /// Unspecified thinking level. + #[default] + ThinkingLevelUnspecified, + /// High thinking level. + High, + /// Low thinking level. + Low, } /// A response candidate generated from the model. From c198dd843c2dccd877ac6839d0c9af3fd948d75b Mon Sep 17 00:00:00 2001 From: Jean Mertz Date: Wed, 26 Nov 2025 14:35:41 +0100 Subject: [PATCH 5/5] expand types Signed-off-by: Jean Mertz --- src/lib.rs | 1 + src/types.rs | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6cf8dca..78ded49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -268,6 +268,7 @@ impl GeminiClient { Ok(result) => { request.contents.push(Content { parts: vec![ContentData::FunctionResponse(FunctionResponse { + id: function_call.id.clone(), name: function_call.name.clone(), response: FunctionResponsePayload { content: result }, }) diff --git a/src/types.rs b/src/types.rs index 699e358..9d9882f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -728,6 +728,8 @@ pub struct ContentPart { pub data: ContentData, #[serde(skip_serializing)] pub metadata: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub thought_signature: Option, } impl ContentPart { @@ -736,6 +738,7 @@ impl ContentPart { data: ContentData::Text(text.to_string()), thought, metadata: None, + thought_signature: None, } } @@ -747,6 +750,7 @@ impl ContentPart { }), thought, metadata: None, + thought_signature: None, } } @@ -758,17 +762,25 @@ impl ContentPart { }), thought: false, metadata: None, + thought_signature: None, } } - pub fn new_function_call(name: &str, arguments: Value, thought: bool) -> Self { + pub fn new_function_call( + id: Option<&str>, + name: &str, + arguments: Value, + thought: bool, + ) -> Self { Self { data: ContentData::FunctionCall(FunctionCall { + id: id.map(|s| s.to_string()), name: name.to_string(), arguments, }), thought, metadata: None, + thought_signature: None, } } @@ -779,6 +791,7 @@ impl ContentPart { }), thought: false, metadata: None, + thought_signature: None, } } @@ -787,17 +800,20 @@ impl ContentPart { data: ContentData::CodeExecutionResult(content), thought: false, metadata: None, + thought_signature: None, } } - pub fn new_function_response(name: &str, content: Value) -> Self { + pub fn new_function_response(id: Option<&str>, name: &str, content: Value) -> Self { Self { data: ContentData::FunctionResponse(FunctionResponse { + id: id.map(|s| s.to_string()), name: name.to_string(), response: FunctionResponsePayload { content }, }), thought: false, metadata: None, + thought_signature: None, } } } @@ -812,6 +828,7 @@ impl From for ContentPart { data, thought: false, metadata: None, + thought_signature: None, } } } @@ -831,14 +848,18 @@ pub enum ContentData { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct FunctionCall { + #[serde(default)] + pub id: Option, pub name: String, - #[serde(rename = "args")] + #[serde(default, rename = "args")] pub arguments: serde_json::Value, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct FunctionResponse { + #[serde(default)] + pub id: Option, pub name: String, pub response: FunctionResponsePayload, }