use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeminiConfig {
pub model: String,
pub generation_config: Option<GeminiGenerationConfig>,
pub safety_settings: Option<Vec<SafetySetting>>,
pub tools: Option<Vec<Tool>>,
pub tool_config: Option<ToolConfig>,
pub system_instruction: Option<SystemInstruction>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
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<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub candidate_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_output_tokens: Option<i32>,
#[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 response_schema: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_logprobs: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logprobs: Option<i32>,
#[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 seed: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafetySetting {
pub category: SafetyCategory,
pub threshold: SafetyThreshold,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SafetyCategory {
#[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, Serialize, Deserialize)]
pub enum SafetyThreshold {
#[serde(rename = "BLOCK_NONE")]
BlockNone,
#[serde(rename = "BLOCK_ONLY_HIGH")]
BlockOnlyHigh,
#[serde(rename = "BLOCK_MEDIUM_AND_ABOVE")]
BlockMediumAndAbove,
#[serde(rename = "BLOCK_LOW_AND_ABOVE")]
BlockLowAndAbove,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tool {
#[serde(skip_serializing_if = "Option::is_none")]
pub function_declarations: Option<Vec<FunctionDeclaration>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code_execution: Option<CodeExecution>,
#[serde(skip_serializing_if = "Option::is_none")]
pub google_search_retrieval: Option<GoogleSearchRetrieval>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionDeclaration {
pub name: String,
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<Value>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeExecution {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GoogleSearchRetrieval {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolConfig {
pub function_calling_config: FunctionCallingConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCallingConfig {
pub mode: FunctionCallingMode,
#[serde(skip_serializing_if = "Option::is_none")]
pub allowed_function_names: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FunctionCallingMode {
#[serde(rename = "AUTO")]
Auto,
#[serde(rename = "ANY")]
Any,
#[serde(rename = "NONE")]
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemInstruction {
pub parts: Vec<Part>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Part {
Text { text: String },
InlineData { inline_data: InlineData },
FileData { file_data: FileData },
FunctionCall { function_call: FunctionCall },
FunctionResponse { function_response: FunctionResponse },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InlineData {
pub mime_type: String,
pub data: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileData {
pub mime_type: String,
pub file_uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionCall {
pub name: String,
pub args: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionResponse {
pub name: String,
pub response: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Content {
pub role: String,
pub parts: Vec<Part>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Candidate {
pub content: Content,
#[serde(skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<FinishReason>,
#[serde(skip_serializing_if = "Option::is_none")]
pub safety_ratings: Option<Vec<SafetyRating>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub citation_metadata: Option<CitationMetadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub token_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub groundingattributions: Option<Vec<GroundingAttribution>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logprobs_result: Option<LogprobsResult>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FinishReason {
#[serde(rename = "FINISH_REASON_UNSPECIFIED")]
Unspecified,
#[serde(rename = "STOP")]
Stop,
#[serde(rename = "MAX_TOKENS")]
MaxTokens,
#[serde(rename = "SAFETY")]
Safety,
#[serde(rename = "RECITATION")]
Recitation,
#[serde(rename = "OTHER")]
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafetyRating {
pub category: SafetyCategory,
pub probability: SafetyProbability,
#[serde(skip_serializing_if = "Option::is_none")]
pub blocked: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SafetyProbability {
#[serde(rename = "NEGLIGIBLE")]
Negligible,
#[serde(rename = "LOW")]
Low,
#[serde(rename = "MEDIUM")]
Medium,
#[serde(rename = "HIGH")]
High,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CitationMetadata {
pub citation_sources: Vec<CitationSource>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CitationSource {
#[serde(skip_serializing_if = "Option::is_none")]
pub start_index: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_index: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub license: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroundingAttribution {
pub content: Content,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogprobsResult {
pub top_candidates: Vec<TopCandidate>,
pub chosen_candidates: Vec<Candidate>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TopCandidate {
pub token: String,
pub log_probability: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsageMetadata {
pub prompt_token_count: i32,
pub candidates_token_count: i32,
pub total_token_count: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_content_token_count: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerateContentResponse {
pub candidates: Vec<Candidate>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_feedback: Option<PromptFeedback>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage_metadata: Option<UsageMetadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PromptFeedback {
#[serde(skip_serializing_if = "Option::is_none")]
pub block_reason: Option<BlockReason>,
#[serde(skip_serializing_if = "Option::is_none")]
pub safety_ratings: Option<Vec<SafetyRating>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BlockReason {
#[serde(rename = "BLOCKED_REASON_UNSPECIFIED")]
Unspecified,
#[serde(rename = "SAFETY")]
Safety,
#[serde(rename = "OTHER")]
Other,
}
pub struct GeminiCostCalculator;
impl GeminiCostCalculator {
pub fn calculate_cost(model: &str, input_tokens: usize, output_tokens: usize) -> f64 {
let (input_rate, output_rate) = match model {
"gemini-1.5-pro" | "gemini-1.5-pro-001" | "gemini-1.5-pro-002" => (3.50, 10.50),
"gemini-1.5-flash" | "gemini-1.5-flash-001" | "gemini-1.5-flash-002" => (0.075, 0.30),
"gemini-2.0-flash-thinking-exp" => (0.0, 0.0), "gemini-ultra" | "gemini-ultra-1.0" => (10.0, 30.0),
"gemini-pro" => (0.50, 1.50),
"gemini-pro-vision" => (0.50, 1.50),
_ => (0.075, 0.30), };
let input_cost = (input_tokens as f64 / 1_000_000.0) * input_rate;
let output_cost = (output_tokens as f64 / 1_000_000.0) * output_rate;
input_cost + output_cost
}
}