use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct GeminiConfig {
pub api_key: String,
pub base_url: String,
}
impl GeminiConfig {
pub fn new(api_key: impl Into<String>) -> Self {
Self {
api_key: api_key.into(),
base_url: "https://generativelanguage.googleapis.com/v1beta/".to_string(),
}
}
pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
self.base_url = base_url.into();
self
}
}
impl Default for GeminiConfig {
fn default() -> Self {
Self::new(std::env::var("GEMINI_API_KEY").unwrap_or_else(|_| String::new()))
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiRequest {
pub contents: Vec<GeminiContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub generation_config: Option<GeminiGenerationConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub safety_settings: Option<Vec<GeminiSafetySetting>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<GeminiTool>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_instruction: Option<GeminiSystemInstruction>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_config: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_content: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GeminiSystemInstruction {
pub parts: Vec<GeminiPart>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GeminiTool {
pub function_declarations: Vec<GeminiFunctionDeclaration>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct GeminiFunctionDeclaration {
pub name: String,
pub description: String,
pub parameters_json_schema: Option<serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GeminiContent {
pub role: String,
pub parts: Vec<GeminiPart>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiPart {
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub inline_data: Option<GeminiInlineData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub function_call: Option<GeminiFunctionCall>,
#[serde(skip_serializing_if = "Option::is_none")]
pub function_response: Option<GeminiFunctionResponse>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thought_signature: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GeminiFunctionCall {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub name: String,
pub args: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GeminiFunctionResponse {
pub id: String,
pub name: String,
pub response: serde_json::Value,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GeminiInlineData {
pub mime_type: String,
pub data: String, }
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GeminiGenerationConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_output_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_sequences: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub candidate_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seed: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presence_penalty: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub frequency_penalty: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_logprobs: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logprobs: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_enhanced_civic_answers: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking_config: Option<GeminiThinkingConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub speech_config: Option<GeminiSpeechConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_resolution: Option<GeminiMediaResolution>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_modalities: Option<Vec<GeminiModality>>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GeminiThinkingConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub include_thoughts: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thinking_budget: Option<u32>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GeminiSpeechConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub voice_config: Option<GeminiVoiceConfig>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GeminiVoiceConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub prebuilt_voice_config: Option<GeminiPrebuiltVoiceConfig>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct GeminiPrebuiltVoiceConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub voice_name: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GeminiMediaResolution {
#[serde(rename = "MEDIA_RESOLUTION_UNSPECIFIED")]
Unspecified,
#[serde(rename = "MEDIA_RESOLUTION_LOW")]
Low,
#[serde(rename = "MEDIA_RESOLUTION_MEDIUM")]
Medium,
#[serde(rename = "MEDIA_RESOLUTION_HIGH")]
High,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GeminiModality {
ModalityUnspecified,
Text,
Image,
Audio,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiSafetySetting {
pub category: GeminiHarmCategory,
pub threshold: GeminiHarmBlockThreshold,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GeminiHarmCategory {
#[serde(rename = "HARM_CATEGORY_UNSPECIFIED")]
Unspecified,
#[serde(rename = "HARM_CATEGORY_HARASSMENT")]
Harassment,
#[serde(rename = "HARM_CATEGORY_HATE_SPEECH")]
HateSpeech,
#[serde(rename = "HARM_CATEGORY_SEXUALLY_EXPLICIT")]
SexuallyExplicit,
#[serde(rename = "HARM_CATEGORY_DANGEROUS_CONTENT")]
DangerousContent,
#[serde(rename = "HARM_CATEGORY_CIVIC_INTEGRITY")]
CivicIntegrity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GeminiHarmBlockThreshold {
HarmBlockThresholdUnspecified,
BlockLowAndAbove,
BlockMediumAndAbove,
BlockOnlyHigh,
BlockNone,
Off,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiResponse {
pub candidates: Option<Vec<GeminiCandidate>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage_metadata: Option<GeminiUsageMetadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model_version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_id: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiCandidate {
pub content: Option<GeminiContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub safety_ratings: Option<Vec<GeminiSafetyRating>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiSafetyRating {
pub category: GeminiHarmCategory,
pub probability: GeminiHarmProbability,
#[serde(skip_serializing_if = "Option::is_none")]
pub blocked: Option<bool>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GeminiHarmProbability {
HarmProbabilityUnspecified,
Negligible,
Low,
Medium,
High,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiUsageMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_token_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_content_token_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub candidates_token_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_token_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thoughts_token_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_tokens_details: Option<Vec<GeminiTokenDetail>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub candidates_tokens_details: Option<Vec<GeminiTokenDetail>>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiTokenDetail {
pub modality: GeminiModality,
pub token_count: u32,
}