// Claude 数据模型 // Claude 协议相关数据模型 use serde::{Deserialize, Serialize}; /// Claude API 请求 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClaudeRequest { pub model: String, pub messages: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub system: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tools: Option>, #[serde(default)] pub stream: bool, #[serde(skip_serializing_if = "Option::is_none")] pub max_tokens: Option, #[serde(skip_serializing_if = "Option::is_none")] pub temperature: Option, #[serde(skip_serializing_if = "Option::is_none")] pub top_p: Option, #[serde(skip_serializing_if = "Option::is_none")] pub top_k: Option, #[serde(skip_serializing_if = "Option::is_none")] pub thinking: Option, #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, } /// Thinking 配置 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThinkingConfig { #[serde(rename = "type")] pub type_: String, // "enabled" #[serde(skip_serializing_if = "Option::is_none")] pub budget_tokens: Option, } /// System Prompt #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum SystemPrompt { String(String), Array(Vec), } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SystemBlock { #[serde(rename = "type")] pub block_type: String, pub text: String, } /// Message #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Message { pub role: String, pub content: MessageContent, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum MessageContent { String(String), Array(Vec), } /// Content Block (Claude) #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum ContentBlock { #[serde(rename = "text")] Text { text: String }, #[serde(rename = "thinking")] Thinking { thinking: String, #[serde(skip_serializing_if = "Option::is_none")] signature: Option, #[serde(skip_serializing_if = "Option::is_none")] cache_control: Option, }, #[serde(rename = "image")] Image { source: ImageSource }, #[serde(rename = "tool_use")] ToolUse { id: String, name: String, input: serde_json::Value, #[serde(skip_serializing_if = "Option::is_none")] signature: Option, #[serde(skip_serializing_if = "Option::is_none")] cache_control: Option, }, #[serde(rename = "tool_result")] ToolResult { tool_use_id: String, content: serde_json::Value, // Changed from String to Value to support Array of Blocks #[serde(skip_serializing_if = "Option::is_none")] is_error: Option, }, #[serde(rename = "server_tool_use")] ServerToolUse { id: String, name: String, input: serde_json::Value, }, #[serde(rename = "web_search_tool_result")] WebSearchToolResult { tool_use_id: String, content: serde_json::Value, }, #[serde(rename = "redacted_thinking")] RedactedThinking { data: String }, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ImageSource { #[serde(rename = "type")] pub source_type: String, pub media_type: String, pub data: String, } /// Tool - supports both client tools (with input_schema) and server tools (like web_search) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Tool { /// Tool type - for server tools like "web_search_20250305" #[serde(rename = "type")] #[serde(skip_serializing_if = "Option::is_none")] pub type_: Option, /// Tool name - "web_search" for server tools, custom name for client tools #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, /// Input schema - required for client tools, absent for server tools #[serde(skip_serializing_if = "Option::is_none")] pub input_schema: Option, } impl Tool { /// Check if this is the web_search server tool pub fn is_web_search(&self) -> bool { // Check by type (preferred for server tools) if let Some(ref t) = self.type_ { if t.starts_with("web_search") { return true; } } // Check by name (fallback) if let Some(ref n) = self.name { if n == "web_search" { return true; } } false } /// Get the effective tool name pub fn get_name(&self) -> String { self.name.clone().unwrap_or_else(|| { // For server tools, derive name from type if let Some(ref t) = self.type_ { if t.starts_with("web_search") { return "web_search".to_string(); } } "unknown".to_string() }) } } /// Metadata #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Metadata { #[serde(skip_serializing_if = "Option::is_none")] pub user_id: Option, } /// Claude API 响应 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ClaudeResponse { pub id: String, #[serde(rename = "type")] pub type_: String, pub role: String, pub model: String, pub content: Vec, pub stop_reason: String, #[serde(skip_serializing_if = "Option::is_none")] pub stop_sequence: Option, pub usage: Usage, } /// Usage #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Usage { pub input_tokens: u32, pub output_tokens: u32, #[serde(skip_serializing_if = "Option::is_none")] pub server_tool_use: Option, } // ========== Gemini 数据模型 ========== /// Gemini Content #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GeminiContent { pub role: String, pub parts: Vec, } /// Gemini Part #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GeminiPart { #[serde(skip_serializing_if = "Option::is_none")] pub text: Option, #[serde(skip_serializing_if = "Option::is_none")] pub thought: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "thoughtSignature")] pub thought_signature: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "functionCall")] pub function_call: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "functionResponse")] pub function_response: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "inlineData")] pub inline_data: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FunctionCall { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub args: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FunctionResponse { pub name: String, pub response: serde_json::Value, #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InlineData { #[serde(rename = "mimeType")] pub mime_type: String, pub data: String, } /// Gemini 完整响应 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GeminiResponse { #[serde(skip_serializing_if = "Option::is_none")] pub candidates: Option>, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "usageMetadata")] pub usage_metadata: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "modelVersion")] pub model_version: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "responseId")] pub response_id: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Candidate { #[serde(skip_serializing_if = "Option::is_none")] pub content: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "finishReason")] pub finish_reason: Option, #[serde(skip_serializing_if = "Option::is_none")] pub index: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "groundingMetadata")] pub grounding_metadata: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UsageMetadata { #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "promptTokenCount")] pub prompt_token_count: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "candidatesTokenCount")] pub candidates_token_count: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "totalTokenCount")] pub total_token_count: Option, } // ========== Grounding Metadata (for googleSearch results) ========== /// Gemini Grounding Metadata - contains search results from googleSearch tool #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GroundingMetadata { #[serde(rename = "webSearchQueries")] #[serde(skip_serializing_if = "Option::is_none")] pub web_search_queries: Option>, #[serde(rename = "groundingChunks")] #[serde(skip_serializing_if = "Option::is_none")] pub grounding_chunks: Option>, #[serde(rename = "groundingSupports")] #[serde(skip_serializing_if = "Option::is_none")] pub grounding_supports: Option>, #[serde(rename = "searchEntryPoint")] #[serde(skip_serializing_if = "Option::is_none")] pub search_entry_point: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GroundingChunk { #[serde(skip_serializing_if = "Option::is_none")] pub web: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WebSource { #[serde(skip_serializing_if = "Option::is_none")] pub uri: Option, #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GroundingSupport { #[serde(skip_serializing_if = "Option::is_none")] pub segment: Option, #[serde(rename = "groundingChunkIndices")] #[serde(skip_serializing_if = "Option::is_none")] pub grounding_chunk_indices: Option>, #[serde(rename = "confidenceScores")] #[serde(skip_serializing_if = "Option::is_none")] pub confidence_scores: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TextSegment { #[serde(rename = "startIndex")] #[serde(skip_serializing_if = "Option::is_none")] pub start_index: Option, #[serde(rename = "endIndex")] #[serde(skip_serializing_if = "Option::is_none")] pub end_index: Option, #[serde(skip_serializing_if = "Option::is_none")] pub text: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SearchEntryPoint { #[serde(rename = "renderedContent")] #[serde(skip_serializing_if = "Option::is_none")] pub rendered_content: Option, }