Skip to main content

openai_protocol/
messages.rs

1//! Anthropic Messages API protocol definitions
2//!
3//! This module provides Rust types for the Anthropic Messages API.
4//! See: https://docs.anthropic.com/en/api/messages
5
6use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use validator::Validate;
11
12use crate::validated::Normalizable;
13
14// ============================================================================
15// Request Types
16// ============================================================================
17
18/// Request to create a message using the Anthropic Messages API.
19///
20/// This is the main request type for `/v1/messages` endpoint.
21#[serde_with::skip_serializing_none]
22#[derive(Debug, Clone, Serialize, Deserialize, Validate, schemars::JsonSchema)]
23#[validate(schema(function = "validate_message_request"))]
24pub struct CreateMessageRequest {
25    /// The model that will complete your prompt.
26    #[validate(length(min = 1, message = "model field is required and cannot be empty"))]
27    pub model: String,
28
29    /// Input messages for the conversation.
30    #[validate(length(min = 1, message = "messages array is required and cannot be empty"))]
31    pub messages: Vec<InputMessage>,
32
33    /// The maximum number of tokens to generate before stopping.
34    #[validate(range(min = 1, message = "max_tokens must be greater than 0"))]
35    pub max_tokens: u32,
36
37    /// An object describing metadata about the request.
38    pub metadata: Option<Metadata>,
39
40    /// Service tier for the request (auto or standard_only).
41    pub service_tier: Option<ServiceTier>,
42
43    /// Custom text sequences that will cause the model to stop generating.
44    pub stop_sequences: Option<Vec<String>>,
45
46    /// Whether to incrementally stream the response using server-sent events.
47    pub stream: Option<bool>,
48
49    /// System prompt for providing context and instructions.
50    pub system: Option<SystemContent>,
51
52    /// Amount of randomness injected into the response (0.0 to 1.0).
53    pub temperature: Option<f64>,
54
55    /// Configuration for extended thinking.
56    pub thinking: Option<ThinkingConfig>,
57
58    /// How the model should use the provided tools.
59    pub tool_choice: Option<ToolChoice>,
60
61    /// Definitions of tools that the model may use.
62    pub tools: Option<Vec<Tool>>,
63
64    /// Only sample from the top K options for each subsequent token.
65    pub top_k: Option<u32>,
66
67    /// Use nucleus sampling.
68    pub top_p: Option<f64>,
69
70    // Beta features
71    /// Container configuration for code execution (beta).
72    pub container: Option<ContainerConfig>,
73
74    /// MCP servers to be utilized in this request (beta).
75    pub mcp_servers: Option<Vec<McpServerConfig>>,
76}
77
78impl Normalizable for CreateMessageRequest {
79    // Use default no-op implementation
80}
81
82impl CreateMessageRequest {
83    /// Check if the request is for streaming
84    pub fn is_stream(&self) -> bool {
85        self.stream.unwrap_or(false)
86    }
87
88    /// Get the model name
89    pub fn get_model(&self) -> &str {
90        &self.model
91    }
92
93    /// Check if the request contains any `mcp_toolset` tool entries.
94    pub fn has_mcp_toolset(&self) -> bool {
95        self.tools
96            .as_ref()
97            .is_some_and(|tools| tools.iter().any(|t| matches!(t, Tool::McpToolset(_))))
98    }
99
100    /// Return MCP server configs if present and non-empty.
101    pub fn mcp_server_configs(&self) -> Option<&[McpServerConfig]> {
102        self.mcp_servers
103            .as_deref()
104            .filter(|servers| !servers.is_empty())
105    }
106}
107
108impl Tool {
109    fn matches_tool_choice_name(&self, name: &str) -> bool {
110        match self {
111            Self::Custom(tool) => tool.name == name,
112            Self::ToolSearch(tool) => tool.name == name,
113            Self::Bash(tool) => tool.name == name,
114            Self::TextEditor(tool) => tool.name == name,
115            Self::WebSearch(tool) => tool.name == name,
116            Self::McpToolset(toolset) => {
117                let default_enabled = toolset
118                    .default_config
119                    .as_ref()
120                    .and_then(|config| config.enabled)
121                    .unwrap_or(true);
122
123                toolset
124                    .configs
125                    .as_ref()
126                    .and_then(|configs| configs.get(name))
127                    .and_then(|config| config.enabled)
128                    .unwrap_or(default_enabled)
129            }
130        }
131    }
132}
133/// Validate cross-field constraints for Messages API requests.
134fn validate_message_request(req: &CreateMessageRequest) -> Result<(), validator::ValidationError> {
135    if req.has_mcp_toolset() && req.mcp_server_configs().is_none() {
136        let mut e = validator::ValidationError::new("mcp_servers_required");
137        e.message = Some("mcp_servers is required when mcp_toolset tools are present".into());
138        return Err(e);
139    }
140
141    let Some(tool_choice) = &req.tool_choice else {
142        return Ok(());
143    };
144
145    let has_tools = req.tools.as_ref().is_some_and(|tools| !tools.is_empty());
146    let requires_tools = !matches!(tool_choice, ToolChoice::None);
147
148    if requires_tools && !has_tools {
149        let mut e = validator::ValidationError::new("tool_choice_requires_tools");
150        e.message = Some(
151            "Invalid value for 'tool_choice': 'tool_choice' is only allowed when 'tools' are specified."
152                .into(),
153        );
154        return Err(e);
155    }
156
157    if let ToolChoice::Tool { name, .. } = tool_choice {
158        let tool_exists = req
159            .tools
160            .as_ref()
161            .is_some_and(|tools| tools.iter().any(|tool| tool.matches_tool_choice_name(name)));
162
163        if !tool_exists {
164            let mut e = validator::ValidationError::new("tool_choice_tool_not_found");
165            e.message = Some(
166                format!("Invalid value for 'tool_choice': tool '{name}' not found in 'tools'.")
167                    .into(),
168            );
169            return Err(e);
170        }
171    }
172
173    Ok(())
174}
175
176/// Request metadata
177#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
178pub struct Metadata {
179    /// An external identifier for the user who is associated with the request.
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub user_id: Option<String>,
182}
183
184/// Service tier options
185#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
186#[serde(rename_all = "snake_case")]
187pub enum ServiceTier {
188    Auto,
189    StandardOnly,
190}
191
192/// System content can be a string or an array of text blocks
193#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
194#[serde(untagged)]
195pub enum SystemContent {
196    String(String),
197    Blocks(Vec<TextBlock>),
198}
199
200/// A single input message in a conversation
201#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
202pub struct InputMessage {
203    /// The role of the message sender (user or assistant)
204    pub role: Role,
205
206    /// The content of the message
207    pub content: InputContent,
208}
209
210/// Role of a message sender
211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
212#[serde(rename_all = "lowercase")]
213pub enum Role {
214    User,
215    Assistant,
216}
217
218/// Input content can be a string or an array of content blocks
219#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
220#[serde(untagged)]
221pub enum InputContent {
222    String(String),
223    Blocks(Vec<InputContentBlock>),
224}
225
226// ============================================================================
227// Input Content Blocks
228// ============================================================================
229
230/// Input content block types
231#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
232#[serde(tag = "type", rename_all = "snake_case")]
233pub enum InputContentBlock {
234    /// Text content
235    Text(TextBlock),
236    /// Image content
237    Image(ImageBlock),
238    /// Document content
239    Document(DocumentBlock),
240    /// Tool use block (for assistant messages)
241    ToolUse(ToolUseBlock),
242    /// Tool result block (for user messages)
243    ToolResult(ToolResultBlock),
244    /// Thinking block
245    Thinking(ThinkingBlock),
246    /// Redacted thinking block
247    RedactedThinking(RedactedThinkingBlock),
248    /// Server tool use block
249    ServerToolUse(ServerToolUseBlock),
250    /// Search result block
251    SearchResult(SearchResultBlock),
252    /// Web search tool result block
253    WebSearchToolResult(WebSearchToolResultBlock),
254    /// Tool search tool result block
255    ToolSearchToolResult(ToolSearchToolResultBlock),
256    /// Tool reference block
257    ToolReference(ToolReferenceBlock),
258}
259
260/// Text content block
261#[serde_with::skip_serializing_none]
262#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
263pub struct TextBlock {
264    /// The text content
265    pub text: String,
266
267    /// Cache control for this block
268    pub cache_control: Option<CacheControl>,
269
270    /// Citations for this text block
271    pub citations: Option<Vec<Citation>>,
272}
273
274/// Image content block
275#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
276pub struct ImageBlock {
277    /// The image source
278    pub source: ImageSource,
279
280    /// Cache control for this block
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub cache_control: Option<CacheControl>,
283}
284
285/// Image source (base64 or URL)
286#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
287#[serde(tag = "type", rename_all = "snake_case")]
288pub enum ImageSource {
289    Base64 { media_type: String, data: String },
290    Url { url: String },
291}
292
293/// Document content block
294#[serde_with::skip_serializing_none]
295#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
296pub struct DocumentBlock {
297    /// The document source
298    pub source: DocumentSource,
299
300    /// Cache control for this block
301    pub cache_control: Option<CacheControl>,
302
303    /// Optional title for the document
304    pub title: Option<String>,
305
306    /// Optional context for the document
307    pub context: Option<String>,
308
309    /// Citations configuration
310    pub citations: Option<CitationsConfig>,
311}
312
313/// Document source (base64, text, or URL)
314#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
315#[serde(tag = "type", rename_all = "snake_case")]
316pub enum DocumentSource {
317    Base64 { media_type: String, data: String },
318    Text { data: String },
319    Url { url: String },
320    Content { content: Vec<InputContentBlock> },
321}
322
323/// Tool use block (in assistant messages)
324#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
325pub struct ToolUseBlock {
326    /// Unique identifier for this tool use
327    pub id: String,
328
329    /// Name of the tool being used
330    pub name: String,
331
332    /// Input arguments for the tool
333    pub input: Value,
334
335    /// Cache control for this block
336    #[serde(skip_serializing_if = "Option::is_none")]
337    pub cache_control: Option<CacheControl>,
338}
339
340/// Tool result block (in user messages)
341#[serde_with::skip_serializing_none]
342#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
343pub struct ToolResultBlock {
344    /// The ID of the tool use this is a result for
345    pub tool_use_id: String,
346
347    /// The result content (string or blocks)
348    pub content: Option<ToolResultContent>,
349
350    /// Whether this result indicates an error
351    pub is_error: Option<bool>,
352
353    /// Cache control for this block
354    pub cache_control: Option<CacheControl>,
355}
356
357/// Tool result content (string or blocks)
358#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
359#[serde(untagged)]
360pub enum ToolResultContent {
361    String(String),
362    Blocks(Vec<ToolResultContentBlock>),
363}
364
365/// Content blocks allowed in tool results
366#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
367#[serde(tag = "type", rename_all = "snake_case")]
368pub enum ToolResultContentBlock {
369    Text(TextBlock),
370    Image(ImageBlock),
371    Document(DocumentBlock),
372    SearchResult(SearchResultBlock),
373}
374
375/// Thinking block
376#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
377pub struct ThinkingBlock {
378    /// The thinking content
379    pub thinking: String,
380
381    /// Signature for the thinking block
382    pub signature: String,
383}
384
385/// Redacted thinking block
386#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
387pub struct RedactedThinkingBlock {
388    /// The encrypted/redacted data
389    pub data: String,
390}
391
392/// Server tool use block
393#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
394pub struct ServerToolUseBlock {
395    /// Unique identifier for this tool use
396    pub id: String,
397
398    /// Name of the server tool
399    pub name: String,
400
401    /// Input arguments for the tool
402    pub input: Value,
403
404    /// Cache control for this block
405    #[serde(skip_serializing_if = "Option::is_none")]
406    pub cache_control: Option<CacheControl>,
407}
408
409/// Search result block
410#[serde_with::skip_serializing_none]
411#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
412pub struct SearchResultBlock {
413    /// Source URL or identifier
414    pub source: String,
415
416    /// Title of the search result
417    pub title: String,
418
419    /// Content of the search result
420    pub content: Vec<TextBlock>,
421
422    /// Cache control for this block
423    pub cache_control: Option<CacheControl>,
424
425    /// Citations configuration
426    pub citations: Option<CitationsConfig>,
427}
428
429/// Web search tool result block
430#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
431pub struct WebSearchToolResultBlock {
432    /// The tool use ID this result is for
433    pub tool_use_id: String,
434
435    /// The search results or error
436    pub content: WebSearchToolResultContent,
437
438    /// Cache control for this block
439    #[serde(skip_serializing_if = "Option::is_none")]
440    pub cache_control: Option<CacheControl>,
441}
442
443/// Web search tool result content
444#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
445#[serde(untagged)]
446pub enum WebSearchToolResultContent {
447    Results(Vec<WebSearchResultBlock>),
448    Error(WebSearchToolResultError),
449}
450
451/// Web search result block
452#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
453pub struct WebSearchResultBlock {
454    /// Title of the search result
455    pub title: String,
456
457    /// URL of the search result
458    pub url: String,
459
460    /// Encrypted content
461    pub encrypted_content: String,
462
463    /// Page age (if available)
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub page_age: Option<String>,
466}
467
468/// Web search tool result error
469#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
470pub struct WebSearchToolResultError {
471    #[serde(rename = "type")]
472    pub error_type: String,
473    pub error_code: WebSearchToolResultErrorCode,
474}
475
476/// Web search tool result error codes
477#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
478#[serde(rename_all = "snake_case")]
479pub enum WebSearchToolResultErrorCode {
480    InvalidToolInput,
481    Unavailable,
482    MaxUsesExceeded,
483    TooManyRequests,
484    QueryTooLong,
485}
486
487/// Cache control configuration
488#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
489#[serde(tag = "type", rename_all = "snake_case")]
490pub enum CacheControl {
491    Ephemeral,
492}
493
494/// Citations configuration
495#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
496pub struct CitationsConfig {
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub enabled: Option<bool>,
499}
500
501/// Citation types
502#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
503#[serde(tag = "type", rename_all = "snake_case")]
504#[expect(
505    clippy::enum_variant_names,
506    reason = "variant names match the OpenAI API citation type discriminators (char_location, page_location, etc.)"
507)]
508pub enum Citation {
509    CharLocation(CharLocationCitation),
510    PageLocation(PageLocationCitation),
511    ContentBlockLocation(ContentBlockLocationCitation),
512    WebSearchResultLocation(WebSearchResultLocationCitation),
513    SearchResultLocation(SearchResultLocationCitation),
514}
515
516/// Character location citation
517#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
518pub struct CharLocationCitation {
519    pub cited_text: String,
520    pub document_index: u32,
521    pub document_title: Option<String>,
522    pub start_char_index: u32,
523    pub end_char_index: u32,
524    #[serde(skip_serializing_if = "Option::is_none")]
525    pub file_id: Option<String>,
526}
527
528/// Page location citation
529#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
530pub struct PageLocationCitation {
531    pub cited_text: String,
532    pub document_index: u32,
533    pub document_title: Option<String>,
534    pub start_page_number: u32,
535    pub end_page_number: u32,
536}
537
538/// Content block location citation
539#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
540pub struct ContentBlockLocationCitation {
541    pub cited_text: String,
542    pub document_index: u32,
543    pub document_title: Option<String>,
544    pub start_block_index: u32,
545    pub end_block_index: u32,
546}
547
548/// Web search result location citation
549#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
550pub struct WebSearchResultLocationCitation {
551    pub cited_text: String,
552    pub url: String,
553    pub title: Option<String>,
554    pub encrypted_index: String,
555}
556
557/// Search result location citation
558#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
559pub struct SearchResultLocationCitation {
560    pub cited_text: String,
561    pub search_result_index: u32,
562    pub source: String,
563    pub title: Option<String>,
564    pub start_block_index: u32,
565    pub end_block_index: u32,
566}
567
568// ============================================================================
569// Tool Definitions
570// ============================================================================
571
572/// Tool definition
573#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
574#[serde(untagged)]
575#[expect(
576    clippy::enum_variant_names,
577    reason = "ToolSearch matches Anthropic API naming"
578)]
579#[schemars(rename = "MessagesTool")]
580pub enum Tool {
581    /// MCP toolset definition
582    McpToolset(McpToolset),
583    /// Custom tool definition (must come before ToolSearch: CustomTool requires
584    /// `input_schema` which acts as a discriminator — ToolSearchTool JSON lacks
585    /// it and falls through, while CustomTool JSON with "type" would incorrectly
586    /// match ToolSearchTool's less-restrictive shape if tried first)
587    Custom(CustomTool),
588    /// Tool search tool
589    ToolSearch(ToolSearchTool),
590    /// Bash tool (computer use)
591    Bash(BashTool),
592    /// Text editor tool (computer use)
593    TextEditor(TextEditorTool),
594    /// Web search tool
595    WebSearch(WebSearchTool),
596}
597
598/// Custom tool definition
599#[serde_with::skip_serializing_none]
600#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
601pub struct CustomTool {
602    /// Name of the tool
603    pub name: String,
604
605    /// Optional type (defaults to "custom")
606    #[serde(rename = "type")]
607    pub tool_type: Option<String>,
608
609    /// Description of what this tool does
610    pub description: Option<String>,
611
612    /// JSON schema for the tool's input
613    pub input_schema: InputSchema,
614
615    /// Whether to defer loading this tool
616    pub defer_loading: Option<bool>,
617
618    /// Cache control for this tool
619    pub cache_control: Option<CacheControl>,
620}
621
622/// JSON Schema for tool input
623#[serde_with::skip_serializing_none]
624#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
625pub struct InputSchema {
626    #[serde(rename = "type")]
627    pub schema_type: String,
628
629    pub properties: Option<HashMap<String, Value>>,
630
631    pub required: Option<Vec<String>>,
632
633    /// Additional properties can be stored here
634    #[serde(flatten)]
635    pub additional: HashMap<String, Value>,
636}
637
638/// Bash tool for computer use
639#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
640pub struct BashTool {
641    #[serde(rename = "type")]
642    pub tool_type: String, // "bash_20250124"
643
644    pub name: String, // "bash"
645
646    #[serde(skip_serializing_if = "Option::is_none")]
647    pub cache_control: Option<CacheControl>,
648}
649
650/// Text editor tool for computer use
651#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
652pub struct TextEditorTool {
653    #[serde(rename = "type")]
654    pub tool_type: String, // "text_editor_20250124", etc.
655
656    pub name: String, // "str_replace_editor"
657
658    #[serde(skip_serializing_if = "Option::is_none")]
659    pub cache_control: Option<CacheControl>,
660}
661
662/// Web search tool
663#[serde_with::skip_serializing_none]
664#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
665pub struct WebSearchTool {
666    #[serde(rename = "type")]
667    pub tool_type: String, // "web_search_20250305"
668
669    pub name: String, // "web_search"
670
671    pub allowed_domains: Option<Vec<String>>,
672
673    pub blocked_domains: Option<Vec<String>>,
674
675    pub max_uses: Option<u32>,
676
677    pub user_location: Option<UserLocation>,
678
679    pub cache_control: Option<CacheControl>,
680}
681
682/// User location for web search
683#[serde_with::skip_serializing_none]
684#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
685pub struct UserLocation {
686    #[serde(rename = "type")]
687    pub location_type: String, // "approximate"
688
689    pub city: Option<String>,
690
691    pub region: Option<String>,
692
693    pub country: Option<String>,
694
695    pub timezone: Option<String>,
696}
697
698// ============================================================================
699// Tool Choice
700// ============================================================================
701
702/// How the model should use the provided tools
703#[serde_with::skip_serializing_none]
704#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
705#[serde(tag = "type", rename_all = "snake_case")]
706#[schemars(rename = "MessagesToolChoice")]
707pub enum ToolChoice {
708    /// The model will automatically decide whether to use tools
709    Auto {
710        disable_parallel_tool_use: Option<bool>,
711    },
712    /// The model will use any available tools
713    Any {
714        disable_parallel_tool_use: Option<bool>,
715    },
716    /// The model will use the specified tool
717    Tool {
718        name: String,
719        disable_parallel_tool_use: Option<bool>,
720    },
721    /// The model will not use tools
722    None,
723}
724
725// ============================================================================
726// Thinking Configuration
727// ============================================================================
728
729/// Configuration for extended thinking
730#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
731#[serde(tag = "type", rename_all = "snake_case")]
732pub enum ThinkingConfig {
733    /// Enable extended thinking
734    Enabled {
735        /// Budget in tokens for thinking (minimum 1024)
736        budget_tokens: u32,
737    },
738    /// Disable extended thinking
739    Disabled,
740}
741
742// ============================================================================
743// Response Types
744// ============================================================================
745
746/// Response message from the API
747#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
748pub struct Message {
749    /// Unique object identifier
750    pub id: String,
751
752    /// Object type (always "message")
753    #[serde(rename = "type")]
754    pub message_type: String,
755
756    /// Conversational role (always "assistant")
757    pub role: String,
758
759    /// Content generated by the model
760    pub content: Vec<ContentBlock>,
761
762    /// The model that generated the message
763    pub model: String,
764
765    /// The reason the model stopped generating
766    pub stop_reason: Option<StopReason>,
767
768    /// Which custom stop sequence was generated (if any)
769    pub stop_sequence: Option<String>,
770
771    /// Billing and rate-limit usage
772    pub usage: Usage,
773}
774
775/// Output content block types
776#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
777#[serde(tag = "type", rename_all = "snake_case")]
778pub enum ContentBlock {
779    /// Text content
780    Text {
781        text: String,
782        #[serde(skip_serializing_if = "Option::is_none")]
783        citations: Option<Vec<Citation>>,
784    },
785    /// Tool use by the model
786    ToolUse {
787        id: String,
788        name: String,
789        input: Value,
790    },
791    /// Thinking content
792    Thinking { thinking: String, signature: String },
793    /// Redacted thinking content
794    RedactedThinking { data: String },
795    /// Server tool use
796    ServerToolUse {
797        id: String,
798        name: String,
799        input: Value,
800    },
801    /// Web search tool result
802    WebSearchToolResult {
803        tool_use_id: String,
804        content: WebSearchToolResultContent,
805    },
806    /// Tool search tool result
807    ToolSearchToolResult {
808        tool_use_id: String,
809        content: ToolSearchResultContent,
810    },
811    /// Tool reference (returned by tool search)
812    ToolReference {
813        tool_name: String,
814        #[serde(skip_serializing_if = "Option::is_none")]
815        description: Option<String>,
816    },
817    /// MCP tool use (beta) - model requesting tool execution via MCP
818    McpToolUse {
819        id: String,
820        name: String,
821        server_name: String,
822        input: Value,
823    },
824    /// MCP tool result (beta) - result from MCP tool execution
825    McpToolResult {
826        tool_use_id: String,
827        content: Option<ToolResultContent>,
828        is_error: Option<bool>,
829    },
830}
831
832/// Stop reasons
833#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema)]
834#[serde(rename_all = "snake_case")]
835pub enum StopReason {
836    /// The model reached a natural stopping point
837    EndTurn,
838    /// We exceeded the requested max_tokens
839    MaxTokens,
840    /// One of the custom stop_sequences was generated
841    StopSequence,
842    /// The model invoked one or more tools
843    ToolUse,
844    /// We paused a long-running turn
845    PauseTurn,
846    /// Streaming classifiers intervened
847    Refusal,
848}
849
850/// Billing and rate-limit usage
851#[serde_with::skip_serializing_none]
852#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
853#[schemars(rename = "MessagesUsage")]
854pub struct Usage {
855    /// The number of input tokens used
856    pub input_tokens: u32,
857
858    /// The number of output tokens used
859    pub output_tokens: u32,
860
861    /// The number of input tokens used to create the cache entry
862    pub cache_creation_input_tokens: Option<u32>,
863
864    /// The number of input tokens read from the cache
865    pub cache_read_input_tokens: Option<u32>,
866
867    /// Breakdown of cached tokens by TTL
868    pub cache_creation: Option<CacheCreation>,
869
870    /// Server tool usage information
871    pub server_tool_use: Option<ServerToolUsage>,
872
873    /// Service tier used for the request
874    pub service_tier: Option<String>,
875}
876
877/// Cache creation breakdown
878#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
879pub struct CacheCreation {
880    #[serde(flatten)]
881    pub tokens_by_ttl: HashMap<String, u32>,
882}
883
884/// Server tool usage information
885#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
886pub struct ServerToolUsage {
887    pub web_search_requests: u32,
888}
889
890// ============================================================================
891// Streaming Event Types
892// ============================================================================
893
894/// Server-sent event wrapper
895#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
896#[serde(tag = "type", rename_all = "snake_case")]
897pub enum MessageStreamEvent {
898    /// Start of a new message
899    MessageStart { message: Message },
900    /// Update to a message
901    MessageDelta {
902        delta: MessageDelta,
903        usage: MessageDeltaUsage,
904    },
905    /// End of a message
906    MessageStop,
907    /// Start of a content block
908    ContentBlockStart {
909        index: u32,
910        content_block: ContentBlock,
911    },
912    /// Update to a content block
913    ContentBlockDelta {
914        index: u32,
915        delta: ContentBlockDelta,
916    },
917    /// End of a content block
918    ContentBlockStop { index: u32 },
919    /// Ping event (for keep-alive)
920    Ping,
921    /// Error event
922    Error { error: ErrorResponse },
923}
924
925/// Message delta for streaming updates
926#[serde_with::skip_serializing_none]
927#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
928pub struct MessageDelta {
929    pub stop_reason: Option<StopReason>,
930
931    pub stop_sequence: Option<String>,
932}
933
934/// Usage delta for streaming updates
935#[serde_with::skip_serializing_none]
936#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
937pub struct MessageDeltaUsage {
938    pub output_tokens: u32,
939
940    pub input_tokens: Option<u32>,
941
942    pub cache_creation_input_tokens: Option<u32>,
943
944    pub cache_read_input_tokens: Option<u32>,
945
946    pub server_tool_use: Option<ServerToolUsage>,
947}
948
949/// Content block delta for streaming updates
950#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
951#[serde(tag = "type", rename_all = "snake_case")]
952#[expect(
953    clippy::enum_variant_names,
954    reason = "variant names match the OpenAI/Anthropic streaming delta type discriminators (text_delta, input_json_delta, etc.)"
955)]
956pub enum ContentBlockDelta {
957    /// Text delta
958    TextDelta { text: String },
959    /// JSON input delta (for tool use)
960    InputJsonDelta { partial_json: String },
961    /// Thinking delta
962    ThinkingDelta { thinking: String },
963    /// Signature delta
964    SignatureDelta { signature: String },
965    /// Citations delta
966    CitationsDelta { citation: Citation },
967}
968
969// ============================================================================
970// Error Types
971// ============================================================================
972
973/// Error response
974#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
975#[schemars(rename = "MessagesErrorResponse")]
976pub struct ErrorResponse {
977    #[serde(rename = "type")]
978    pub error_type: String,
979
980    pub message: String,
981}
982
983/// API error types
984#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
985#[serde(tag = "type", rename_all = "snake_case")]
986#[expect(
987    clippy::enum_variant_names,
988    reason = "variant names match the OpenAI API error type discriminators (invalid_request_error, authentication_error, etc.)"
989)]
990pub enum ApiError {
991    InvalidRequestError { message: String },
992    AuthenticationError { message: String },
993    BillingError { message: String },
994    PermissionError { message: String },
995    NotFoundError { message: String },
996    RateLimitError { message: String },
997    TimeoutError { message: String },
998    ApiError { message: String },
999    OverloadedError { message: String },
1000}
1001
1002// ============================================================================
1003// Count Tokens Types
1004// ============================================================================
1005
1006/// Request to count tokens in a message
1007#[serde_with::skip_serializing_none]
1008#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1009pub struct CountMessageTokensRequest {
1010    /// The model to use for token counting
1011    pub model: String,
1012
1013    /// Input messages
1014    pub messages: Vec<InputMessage>,
1015
1016    /// System prompt
1017    pub system: Option<SystemContent>,
1018
1019    /// Thinking configuration
1020    pub thinking: Option<ThinkingConfig>,
1021
1022    /// Tool choice
1023    pub tool_choice: Option<ToolChoice>,
1024
1025    /// Tool definitions
1026    pub tools: Option<Vec<Tool>>,
1027}
1028
1029/// Response from token counting
1030#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1031pub struct CountMessageTokensResponse {
1032    pub input_tokens: u32,
1033}
1034
1035// ============================================================================
1036// Model Info Types
1037// ============================================================================
1038
1039/// Model information
1040#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1041pub struct ModelInfo {
1042    /// Object type (always "model")
1043    #[serde(rename = "type")]
1044    pub model_type: String,
1045
1046    /// Model ID
1047    pub id: String,
1048
1049    /// Display name
1050    pub display_name: String,
1051
1052    /// When the model was created
1053    pub created_at: String,
1054}
1055
1056/// List of models response
1057#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1058pub struct ListModelsResponse {
1059    pub data: Vec<ModelInfo>,
1060    pub has_more: bool,
1061    pub first_id: Option<String>,
1062    pub last_id: Option<String>,
1063}
1064
1065// ============================================================================
1066// Beta Features - Container & MCP Configuration
1067// ============================================================================
1068
1069/// Container configuration for code execution (beta)
1070#[serde_with::skip_serializing_none]
1071#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1072pub struct ContainerConfig {
1073    /// Container ID for reuse across requests
1074    pub id: Option<String>,
1075
1076    /// Skills to be loaded in the container
1077    pub skills: Option<Vec<String>>,
1078}
1079
1080/// MCP server configuration (beta)
1081#[serde_with::skip_serializing_none]
1082#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1083pub struct McpServerConfig {
1084    /// Server type (always "url")
1085    #[serde(rename = "type", default = "McpServerConfig::default_type")]
1086    pub server_type: String,
1087
1088    /// Name of the MCP server
1089    pub name: String,
1090
1091    /// MCP server URL
1092    pub url: String,
1093
1094    /// Authorization token (if required)
1095    pub authorization_token: Option<String>,
1096
1097    /// Tool configuration for this server
1098    pub tool_configuration: Option<McpToolConfiguration>,
1099}
1100
1101impl McpServerConfig {
1102    fn default_type() -> String {
1103        "url".to_string()
1104    }
1105}
1106
1107/// MCP tool configuration
1108#[serde_with::skip_serializing_none]
1109#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1110pub struct McpToolConfiguration {
1111    /// Whether to allow all tools
1112    pub enabled: Option<bool>,
1113
1114    /// Allowed tool names
1115    pub allowed_tools: Option<Vec<String>>,
1116}
1117
1118// ============================================================================
1119// Beta Features - MCP Tool Types
1120// ============================================================================
1121
1122/// MCP tool use block (beta) - for assistant messages
1123#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1124pub struct McpToolUseBlock {
1125    /// Unique identifier for this tool use
1126    pub id: String,
1127
1128    /// Name of the tool being used
1129    pub name: String,
1130
1131    /// Name of the MCP server
1132    pub server_name: String,
1133
1134    /// Input arguments for the tool
1135    pub input: Value,
1136
1137    /// Cache control for this block
1138    #[serde(skip_serializing_if = "Option::is_none")]
1139    pub cache_control: Option<CacheControl>,
1140}
1141
1142/// MCP tool result block (beta) - for user messages
1143#[serde_with::skip_serializing_none]
1144#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1145pub struct McpToolResultBlock {
1146    /// The ID of the tool use this is a result for
1147    pub tool_use_id: String,
1148
1149    /// The result content
1150    pub content: Option<ToolResultContent>,
1151
1152    /// Whether this result indicates an error
1153    pub is_error: Option<bool>,
1154
1155    /// Cache control for this block
1156    pub cache_control: Option<CacheControl>,
1157}
1158
1159/// MCP toolset definition (beta)
1160#[serde_with::skip_serializing_none]
1161#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1162pub struct McpToolset {
1163    #[serde(rename = "type")]
1164    pub toolset_type: String, // "mcp_toolset"
1165
1166    /// Name of the MCP server to configure tools for
1167    pub mcp_server_name: String,
1168
1169    /// Default configuration applied to all tools from this server
1170    pub default_config: Option<McpToolDefaultConfig>,
1171
1172    /// Configuration overrides for specific tools
1173    pub configs: Option<HashMap<String, McpToolConfig>>,
1174
1175    /// Cache control for this toolset
1176    pub cache_control: Option<CacheControl>,
1177}
1178
1179/// Default configuration for MCP tools
1180#[serde_with::skip_serializing_none]
1181#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1182pub struct McpToolDefaultConfig {
1183    /// Whether tools are enabled
1184    pub enabled: Option<bool>,
1185
1186    /// Whether to defer loading
1187    pub defer_loading: Option<bool>,
1188}
1189
1190/// Per-tool MCP configuration
1191#[serde_with::skip_serializing_none]
1192#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1193pub struct McpToolConfig {
1194    /// Whether this tool is enabled
1195    pub enabled: Option<bool>,
1196
1197    /// Whether to defer loading
1198    pub defer_loading: Option<bool>,
1199}
1200
1201// ============================================================================
1202// Beta Features - Code Execution Types
1203// ============================================================================
1204
1205/// Code execution tool (beta)
1206#[serde_with::skip_serializing_none]
1207#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1208pub struct CodeExecutionTool {
1209    #[serde(rename = "type")]
1210    pub tool_type: String, // "code_execution_20250522" or "code_execution_20250825"
1211
1212    pub name: String, // "code_execution"
1213
1214    /// Allowed callers for this tool
1215    pub allowed_callers: Option<Vec<String>>,
1216
1217    /// Whether to defer loading
1218    pub defer_loading: Option<bool>,
1219
1220    /// Whether to use strict mode
1221    pub strict: Option<bool>,
1222
1223    /// Cache control for this tool
1224    pub cache_control: Option<CacheControl>,
1225}
1226
1227/// Code execution result block (beta)
1228#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1229pub struct CodeExecutionResultBlock {
1230    /// Stdout output
1231    pub stdout: String,
1232
1233    /// Stderr output
1234    pub stderr: String,
1235
1236    /// Return code
1237    pub return_code: i32,
1238
1239    /// Output files
1240    pub content: Vec<CodeExecutionOutputBlock>,
1241}
1242
1243/// Code execution output file reference
1244#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1245pub struct CodeExecutionOutputBlock {
1246    #[serde(rename = "type")]
1247    pub block_type: String, // "code_execution_output"
1248
1249    /// File ID
1250    pub file_id: String,
1251}
1252
1253/// Code execution tool result block (beta)
1254#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1255pub struct CodeExecutionToolResultBlock {
1256    /// The ID of the tool use this is a result for
1257    pub tool_use_id: String,
1258
1259    /// The result content (success or error)
1260    pub content: CodeExecutionToolResultContent,
1261
1262    /// Cache control for this block
1263    #[serde(skip_serializing_if = "Option::is_none")]
1264    pub cache_control: Option<CacheControl>,
1265}
1266
1267/// Code execution tool result content
1268#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1269#[serde(untagged)]
1270pub enum CodeExecutionToolResultContent {
1271    Success(CodeExecutionResultBlock),
1272    Error(CodeExecutionToolResultError),
1273}
1274
1275/// Code execution tool result error
1276#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1277pub struct CodeExecutionToolResultError {
1278    #[serde(rename = "type")]
1279    pub error_type: String, // "code_execution_tool_result_error"
1280
1281    pub error_code: CodeExecutionToolResultErrorCode,
1282}
1283
1284/// Code execution error codes
1285#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1286#[serde(rename_all = "snake_case")]
1287pub enum CodeExecutionToolResultErrorCode {
1288    Unavailable,
1289    CodeExecutionExceededTimeout,
1290    ContainerExpired,
1291    InvalidToolInput,
1292}
1293
1294/// Bash code execution result block (beta)
1295#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1296pub struct BashCodeExecutionResultBlock {
1297    /// Stdout output
1298    pub stdout: String,
1299
1300    /// Stderr output
1301    pub stderr: String,
1302
1303    /// Return code
1304    pub return_code: i32,
1305
1306    /// Output files
1307    pub content: Vec<BashCodeExecutionOutputBlock>,
1308}
1309
1310/// Bash code execution output file reference
1311#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1312pub struct BashCodeExecutionOutputBlock {
1313    #[serde(rename = "type")]
1314    pub block_type: String, // "bash_code_execution_output"
1315
1316    /// File ID
1317    pub file_id: String,
1318}
1319
1320/// Bash code execution tool result block (beta)
1321#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1322pub struct BashCodeExecutionToolResultBlock {
1323    /// The ID of the tool use this is a result for
1324    pub tool_use_id: String,
1325
1326    /// The result content (success or error)
1327    pub content: BashCodeExecutionToolResultContent,
1328
1329    /// Cache control for this block
1330    #[serde(skip_serializing_if = "Option::is_none")]
1331    pub cache_control: Option<CacheControl>,
1332}
1333
1334/// Bash code execution tool result content
1335#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1336#[serde(untagged)]
1337pub enum BashCodeExecutionToolResultContent {
1338    Success(BashCodeExecutionResultBlock),
1339    Error(BashCodeExecutionToolResultError),
1340}
1341
1342/// Bash code execution tool result error
1343#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1344pub struct BashCodeExecutionToolResultError {
1345    #[serde(rename = "type")]
1346    pub error_type: String, // "bash_code_execution_tool_result_error"
1347
1348    pub error_code: BashCodeExecutionToolResultErrorCode,
1349}
1350
1351/// Bash code execution error codes
1352#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1353#[serde(rename_all = "snake_case")]
1354pub enum BashCodeExecutionToolResultErrorCode {
1355    Unavailable,
1356    CodeExecutionExceededTimeout,
1357    ContainerExpired,
1358    InvalidToolInput,
1359}
1360
1361/// Text editor code execution tool result block (beta)
1362#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1363pub struct TextEditorCodeExecutionToolResultBlock {
1364    /// The ID of the tool use this is a result for
1365    pub tool_use_id: String,
1366
1367    /// The result content
1368    pub content: TextEditorCodeExecutionToolResultContent,
1369
1370    /// Cache control for this block
1371    #[serde(skip_serializing_if = "Option::is_none")]
1372    pub cache_control: Option<CacheControl>,
1373}
1374
1375/// Text editor code execution result content
1376#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1377#[serde(untagged)]
1378pub enum TextEditorCodeExecutionToolResultContent {
1379    CreateResult(TextEditorCodeExecutionCreateResultBlock),
1380    StrReplaceResult(TextEditorCodeExecutionStrReplaceResultBlock),
1381    ViewResult(TextEditorCodeExecutionViewResultBlock),
1382    Error(TextEditorCodeExecutionToolResultError),
1383}
1384
1385/// Text editor create result block
1386#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1387pub struct TextEditorCodeExecutionCreateResultBlock {
1388    #[serde(rename = "type")]
1389    pub block_type: String, // "text_editor_code_execution_create_result"
1390}
1391
1392/// Text editor str_replace result block
1393#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1394pub struct TextEditorCodeExecutionStrReplaceResultBlock {
1395    #[serde(rename = "type")]
1396    pub block_type: String, // "text_editor_code_execution_str_replace_result"
1397
1398    /// Snippet of content around the replacement
1399    #[serde(skip_serializing_if = "Option::is_none")]
1400    pub snippet: Option<String>,
1401}
1402
1403/// Text editor view result block
1404#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1405pub struct TextEditorCodeExecutionViewResultBlock {
1406    #[serde(rename = "type")]
1407    pub block_type: String, // "text_editor_code_execution_view_result"
1408
1409    /// Content of the viewed file
1410    pub content: String,
1411}
1412
1413/// Text editor code execution tool result error
1414#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1415pub struct TextEditorCodeExecutionToolResultError {
1416    #[serde(rename = "type")]
1417    pub error_type: String,
1418
1419    pub error_code: TextEditorCodeExecutionToolResultErrorCode,
1420}
1421
1422/// Text editor code execution error codes
1423#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1424#[serde(rename_all = "snake_case")]
1425pub enum TextEditorCodeExecutionToolResultErrorCode {
1426    Unavailable,
1427    InvalidToolInput,
1428    FileNotFound,
1429    ContainerExpired,
1430}
1431
1432// ============================================================================
1433// Beta Features - Web Fetch Types
1434// ============================================================================
1435
1436/// Web fetch tool (beta)
1437#[serde_with::skip_serializing_none]
1438#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1439pub struct WebFetchTool {
1440    #[serde(rename = "type")]
1441    pub tool_type: String, // "web_fetch_20250305" or similar
1442
1443    pub name: String, // "web_fetch"
1444
1445    /// Allowed callers for this tool
1446    pub allowed_callers: Option<Vec<String>>,
1447
1448    /// Maximum number of uses
1449    pub max_uses: Option<u32>,
1450
1451    /// Cache control for this tool
1452    pub cache_control: Option<CacheControl>,
1453}
1454
1455/// Web fetch result block (beta)
1456#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1457pub struct WebFetchResultBlock {
1458    #[serde(rename = "type")]
1459    pub block_type: String, // "web_fetch_result"
1460
1461    /// The URL that was fetched
1462    pub url: String,
1463
1464    /// The document content
1465    pub content: DocumentBlock,
1466
1467    /// When the content was retrieved
1468    #[serde(skip_serializing_if = "Option::is_none")]
1469    pub retrieved_at: Option<String>,
1470}
1471
1472/// Web fetch tool result block (beta)
1473#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1474pub struct WebFetchToolResultBlock {
1475    /// The ID of the tool use this is a result for
1476    pub tool_use_id: String,
1477
1478    /// The result content (success or error)
1479    pub content: WebFetchToolResultContent,
1480
1481    /// Cache control for this block
1482    #[serde(skip_serializing_if = "Option::is_none")]
1483    pub cache_control: Option<CacheControl>,
1484}
1485
1486/// Web fetch tool result content
1487#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1488#[serde(untagged)]
1489pub enum WebFetchToolResultContent {
1490    Success(WebFetchResultBlock),
1491    Error(WebFetchToolResultError),
1492}
1493
1494/// Web fetch tool result error
1495#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1496pub struct WebFetchToolResultError {
1497    #[serde(rename = "type")]
1498    pub error_type: String, // "web_fetch_tool_result_error"
1499
1500    pub error_code: WebFetchToolResultErrorCode,
1501}
1502
1503/// Web fetch error codes
1504#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1505#[serde(rename_all = "snake_case")]
1506pub enum WebFetchToolResultErrorCode {
1507    InvalidToolInput,
1508    Unavailable,
1509    MaxUsesExceeded,
1510    TooManyRequests,
1511    UrlNotAllowed,
1512    FetchFailed,
1513    ContentTooLarge,
1514}
1515
1516// ============================================================================
1517// Beta Features - Tool Search Types
1518// ============================================================================
1519
1520/// Tool search tool (beta)
1521#[serde_with::skip_serializing_none]
1522#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1523pub struct ToolSearchTool {
1524    #[serde(rename = "type")]
1525    pub tool_type: String, // "tool_search_tool_regex" or "tool_search_tool_bm25"
1526
1527    pub name: String,
1528
1529    /// Allowed callers for this tool
1530    pub allowed_callers: Option<Vec<String>>,
1531
1532    /// Cache control for this tool
1533    pub cache_control: Option<CacheControl>,
1534}
1535
1536/// Tool reference block (beta) - returned by tool search
1537#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1538pub struct ToolReferenceBlock {
1539    #[serde(rename = "type")]
1540    pub block_type: String, // "tool_reference"
1541
1542    /// Tool name
1543    pub tool_name: String,
1544
1545    /// Tool description
1546    #[serde(skip_serializing_if = "Option::is_none")]
1547    pub description: Option<String>,
1548}
1549
1550/// Tool search result content — wraps tool references returned by tool search
1551#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1552pub struct ToolSearchResultContent {
1553    #[serde(rename = "type")]
1554    pub block_type: String, // "tool_search_tool_search_result"
1555
1556    /// Tool references found by the search
1557    pub tool_references: Vec<ToolReferenceBlock>,
1558}
1559
1560/// Tool search tool result block (beta)
1561#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1562pub struct ToolSearchToolResultBlock {
1563    /// The ID of the tool use this is a result for
1564    pub tool_use_id: String,
1565
1566    /// The search results
1567    pub content: ToolSearchResultContent,
1568
1569    /// Cache control for this block
1570    #[serde(skip_serializing_if = "Option::is_none")]
1571    pub cache_control: Option<CacheControl>,
1572}
1573
1574// ============================================================================
1575// Beta Features - Container Upload Types
1576// ============================================================================
1577
1578/// Container upload block (beta)
1579#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1580pub struct ContainerUploadBlock {
1581    #[serde(rename = "type")]
1582    pub block_type: String, // "container_upload"
1583
1584    /// File ID
1585    pub file_id: String,
1586
1587    /// File name
1588    pub file_name: String,
1589
1590    /// File path in container
1591    #[serde(skip_serializing_if = "Option::is_none")]
1592    pub file_path: Option<String>,
1593}
1594
1595// ============================================================================
1596// Beta Features - Memory Tool Types
1597// ============================================================================
1598
1599/// Memory tool (beta)
1600#[serde_with::skip_serializing_none]
1601#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1602pub struct MemoryTool {
1603    #[serde(rename = "type")]
1604    pub tool_type: String, // "memory_20250818"
1605
1606    pub name: String, // "memory"
1607
1608    /// Allowed callers for this tool
1609    pub allowed_callers: Option<Vec<String>>,
1610
1611    /// Whether to defer loading
1612    pub defer_loading: Option<bool>,
1613
1614    /// Whether to use strict mode
1615    pub strict: Option<bool>,
1616
1617    /// Input examples
1618    pub input_examples: Option<Vec<Value>>,
1619
1620    /// Cache control for this tool
1621    pub cache_control: Option<CacheControl>,
1622}
1623
1624// ============================================================================
1625// Beta Features - Computer Use Tool Types
1626// ============================================================================
1627
1628/// Computer use tool (beta)
1629#[serde_with::skip_serializing_none]
1630#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1631pub struct ComputerUseTool {
1632    #[serde(rename = "type")]
1633    pub tool_type: String, // "computer_20241022" or "computer_20250124"
1634
1635    pub name: String, // "computer"
1636
1637    /// Display width
1638    pub display_width_px: u32,
1639
1640    /// Display height
1641    pub display_height_px: u32,
1642
1643    /// Display number (optional)
1644    pub display_number: Option<u32>,
1645
1646    /// Allowed callers for this tool
1647    pub allowed_callers: Option<Vec<String>>,
1648
1649    /// Cache control for this tool
1650    pub cache_control: Option<CacheControl>,
1651}
1652
1653// ============================================================================
1654// Beta Features - Extended Input Content Block Enum
1655// ============================================================================
1656
1657/// Beta input content block types (extends InputContentBlock)
1658#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1659#[serde(tag = "type", rename_all = "snake_case")]
1660pub enum BetaInputContentBlock {
1661    // Standard types
1662    Text(TextBlock),
1663    Image(ImageBlock),
1664    Document(DocumentBlock),
1665    ToolUse(ToolUseBlock),
1666    ToolResult(ToolResultBlock),
1667    Thinking(ThinkingBlock),
1668    RedactedThinking(RedactedThinkingBlock),
1669    ServerToolUse(ServerToolUseBlock),
1670    SearchResult(SearchResultBlock),
1671    WebSearchToolResult(WebSearchToolResultBlock),
1672
1673    // Beta MCP types
1674    McpToolUse(McpToolUseBlock),
1675    McpToolResult(McpToolResultBlock),
1676
1677    // Beta code execution types
1678    CodeExecutionToolResult(CodeExecutionToolResultBlock),
1679    BashCodeExecutionToolResult(BashCodeExecutionToolResultBlock),
1680    TextEditorCodeExecutionToolResult(TextEditorCodeExecutionToolResultBlock),
1681
1682    // Beta web fetch types
1683    WebFetchToolResult(WebFetchToolResultBlock),
1684
1685    // Beta tool search types
1686    ToolSearchToolResult(ToolSearchToolResultBlock),
1687    ToolReference(ToolReferenceBlock),
1688
1689    // Beta container types
1690    ContainerUpload(ContainerUploadBlock),
1691}
1692
1693/// Beta output content block types (extends ContentBlock)
1694#[serde_with::skip_serializing_none]
1695#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1696#[serde(tag = "type", rename_all = "snake_case")]
1697pub enum BetaContentBlock {
1698    // Standard types
1699    Text {
1700        text: String,
1701        citations: Option<Vec<Citation>>,
1702    },
1703    ToolUse {
1704        id: String,
1705        name: String,
1706        input: Value,
1707    },
1708    Thinking {
1709        thinking: String,
1710        signature: String,
1711    },
1712    RedactedThinking {
1713        data: String,
1714    },
1715    ServerToolUse {
1716        id: String,
1717        name: String,
1718        input: Value,
1719    },
1720    WebSearchToolResult {
1721        tool_use_id: String,
1722        content: WebSearchToolResultContent,
1723    },
1724
1725    // Beta MCP types
1726    McpToolUse {
1727        id: String,
1728        name: String,
1729        server_name: String,
1730        input: Value,
1731    },
1732    McpToolResult {
1733        tool_use_id: String,
1734        content: Option<ToolResultContent>,
1735        is_error: Option<bool>,
1736    },
1737
1738    // Beta code execution types
1739    CodeExecutionToolResult {
1740        tool_use_id: String,
1741        content: CodeExecutionToolResultContent,
1742    },
1743    BashCodeExecutionToolResult {
1744        tool_use_id: String,
1745        content: BashCodeExecutionToolResultContent,
1746    },
1747    TextEditorCodeExecutionToolResult {
1748        tool_use_id: String,
1749        content: TextEditorCodeExecutionToolResultContent,
1750    },
1751
1752    // Beta web fetch types
1753    WebFetchToolResult {
1754        tool_use_id: String,
1755        content: WebFetchToolResultContent,
1756    },
1757
1758    // Beta tool search types
1759    ToolSearchToolResult {
1760        tool_use_id: String,
1761        content: ToolSearchResultContent,
1762    },
1763    ToolReference {
1764        tool_name: String,
1765        description: Option<String>,
1766    },
1767
1768    // Beta container types
1769    ContainerUpload {
1770        file_id: String,
1771        file_name: String,
1772        file_path: Option<String>,
1773    },
1774}
1775
1776/// Beta tool definition (extends Tool)
1777#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1778#[serde(untagged)]
1779pub enum BetaTool {
1780    // Standard tools
1781    Custom(CustomTool),
1782    Bash(BashTool),
1783    TextEditor(TextEditorTool),
1784    WebSearch(WebSearchTool),
1785
1786    // Beta tools
1787    CodeExecution(CodeExecutionTool),
1788    McpToolset(McpToolset),
1789    WebFetch(WebFetchTool),
1790    ToolSearch(ToolSearchTool),
1791    Memory(MemoryTool),
1792    ComputerUse(ComputerUseTool),
1793}
1794
1795/// Server tool names for beta features
1796#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1797#[serde(rename_all = "snake_case")]
1798pub enum BetaServerToolName {
1799    WebSearch,
1800    WebFetch,
1801    CodeExecution,
1802    BashCodeExecution,
1803    TextEditorCodeExecution,
1804    ToolSearchToolRegex,
1805    ToolSearchToolBm25,
1806}
1807
1808/// Server tool caller types (beta)
1809#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
1810#[serde(tag = "type", rename_all = "snake_case")]
1811pub enum ServerToolCaller {
1812    /// Direct caller (the model itself)
1813    Direct,
1814    /// Code execution caller
1815    #[serde(rename = "code_execution_20250825")]
1816    CodeExecution20250825,
1817}
1818
1819#[cfg(test)]
1820mod tests {
1821    use serde_json;
1822
1823    use super::*;
1824
1825    fn base_request() -> CreateMessageRequest {
1826        CreateMessageRequest {
1827            model: "claude-test".to_string(),
1828            messages: vec![InputMessage {
1829                role: Role::User,
1830                content: InputContent::String("hello".to_string()),
1831            }],
1832            max_tokens: 16,
1833            metadata: None,
1834            service_tier: None,
1835            stop_sequences: None,
1836            stream: None,
1837            system: None,
1838            temperature: None,
1839            thinking: None,
1840            tool_choice: None,
1841            tools: None,
1842            top_k: None,
1843            top_p: None,
1844            container: None,
1845            mcp_servers: None,
1846        }
1847    }
1848
1849    fn custom_tool(name: &str) -> Tool {
1850        Tool::Custom(CustomTool {
1851            name: name.to_string(),
1852            tool_type: None,
1853            description: Some("test tool".to_string()),
1854            input_schema: InputSchema {
1855                schema_type: "object".to_string(),
1856                properties: None,
1857                required: None,
1858                additional: HashMap::new(),
1859            },
1860            defer_loading: None,
1861            cache_control: None,
1862        })
1863    }
1864
1865    fn mcp_toolset(configs: Option<HashMap<String, McpToolConfig>>) -> Tool {
1866        Tool::McpToolset(McpToolset {
1867            toolset_type: "mcp_toolset".to_string(),
1868            mcp_server_name: "brave".to_string(),
1869            default_config: None,
1870            configs,
1871            cache_control: None,
1872        })
1873    }
1874
1875    fn mcp_server_config() -> McpServerConfig {
1876        McpServerConfig {
1877            server_type: "url".to_string(),
1878            name: "brave".to_string(),
1879            url: "https://example.com/mcp".to_string(),
1880            authorization_token: None,
1881            tool_configuration: None,
1882        }
1883    }
1884    #[test]
1885    fn test_tool_mcp_toolset_defer_loading_deserialization() {
1886        let json = r#"{
1887            "type": "mcp_toolset",
1888            "mcp_server_name": "brave",
1889            "default_config": {"defer_loading": true}
1890        }"#;
1891
1892        let tool: Tool = serde_json::from_str(json).expect("Failed to deserialize McpToolset Tool");
1893        match tool {
1894            Tool::McpToolset(ts) => {
1895                assert_eq!(ts.mcp_server_name, "brave");
1896                let default_config = ts.default_config.expect("default_config should be Some");
1897                assert_eq!(default_config.defer_loading, Some(true));
1898            }
1899            other => panic!(
1900                "Expected McpToolset, got {:?}",
1901                std::mem::discriminant(&other)
1902            ),
1903        }
1904    }
1905
1906    #[test]
1907    fn test_tool_search_tool_deserialization() {
1908        let json = r#"{
1909            "type": "tool_search_tool_regex_20251119",
1910            "name": "tool_search_tool_regex"
1911        }"#;
1912
1913        let tool: Tool = serde_json::from_str(json).expect("Failed to deserialize ToolSearch Tool");
1914        match tool {
1915            Tool::ToolSearch(ts) => {
1916                assert_eq!(ts.name, "tool_search_tool_regex");
1917                assert_eq!(ts.tool_type, "tool_search_tool_regex_20251119");
1918            }
1919            other => panic!(
1920                "Expected ToolSearch, got {:?}",
1921                std::mem::discriminant(&other)
1922            ),
1923        }
1924    }
1925
1926    #[test]
1927    fn test_content_block_tool_search_tool_result_deserialization() {
1928        let json = r#"{
1929            "type": "tool_search_tool_result",
1930            "tool_use_id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
1931            "content": {
1932                "type": "tool_search_tool_search_result",
1933                "tool_references": [
1934                    {"type": "tool_reference", "tool_name": "get_weather"}
1935                ]
1936            }
1937        }"#;
1938
1939        let block: ContentBlock = serde_json::from_str(json)
1940            .expect("Failed to deserialize tool_search_tool_result ContentBlock");
1941        match block {
1942            ContentBlock::ToolSearchToolResult {
1943                tool_use_id,
1944                content,
1945            } => {
1946                assert_eq!(tool_use_id, "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2");
1947                assert_eq!(content.tool_references.len(), 1);
1948                assert_eq!(content.tool_references[0].tool_name, "get_weather");
1949            }
1950            _ => panic!("Expected ToolSearchToolResult variant"),
1951        }
1952    }
1953
1954    #[test]
1955    fn test_content_block_server_tool_use_deserialization() {
1956        let json = r#"{
1957            "type": "server_tool_use",
1958            "id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
1959            "name": "tool_search_tool_regex",
1960            "input": {"query": "weather"}
1961        }"#;
1962
1963        let block: ContentBlock =
1964            serde_json::from_str(json).expect("Failed to deserialize server_tool_use ContentBlock");
1965        match block {
1966            ContentBlock::ServerToolUse { id, name, input: _ } => {
1967                assert_eq!(id, "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2");
1968                assert_eq!(name, "tool_search_tool_regex");
1969            }
1970            _ => panic!("Expected ServerToolUse variant"),
1971        }
1972    }
1973
1974    #[test]
1975    fn test_content_block_tool_reference_deserialization() {
1976        let json = r#"{
1977            "type": "tool_reference",
1978            "tool_name": "get_weather",
1979            "description": "Get the weather for a location"
1980        }"#;
1981
1982        let block: ContentBlock =
1983            serde_json::from_str(json).expect("Failed to deserialize tool_reference ContentBlock");
1984        match block {
1985            ContentBlock::ToolReference {
1986                tool_name,
1987                description,
1988            } => {
1989                assert_eq!(tool_name, "get_weather");
1990                assert_eq!(description.unwrap(), "Get the weather for a location");
1991            }
1992            _ => panic!("Expected ToolReference variant"),
1993        }
1994    }
1995
1996    #[test]
1997    fn test_tool_choice_auto_requires_tools() {
1998        let mut request = base_request();
1999        request.tool_choice = Some(ToolChoice::Auto {
2000            disable_parallel_tool_use: None,
2001        });
2002
2003        assert!(request.validate().is_err());
2004    }
2005
2006    #[test]
2007    fn test_tool_choice_any_requires_tools() {
2008        let mut request = base_request();
2009        request.tool_choice = Some(ToolChoice::Any {
2010            disable_parallel_tool_use: None,
2011        });
2012
2013        assert!(request.validate().is_err());
2014    }
2015
2016    #[test]
2017    fn test_tool_choice_auto_with_tools_is_valid() {
2018        let mut request = base_request();
2019        request.tool_choice = Some(ToolChoice::Auto {
2020            disable_parallel_tool_use: None,
2021        });
2022        request.tools = Some(vec![custom_tool("get_weather")]);
2023
2024        assert!(request.validate().is_ok());
2025    }
2026
2027    #[test]
2028    fn test_tool_choice_any_with_tools_is_valid() {
2029        let mut request = base_request();
2030        request.tool_choice = Some(ToolChoice::Any {
2031            disable_parallel_tool_use: None,
2032        });
2033        request.tools = Some(vec![custom_tool("get_weather")]);
2034
2035        assert!(request.validate().is_ok());
2036    }
2037
2038    #[test]
2039    fn test_tool_choice_specific_tool_requires_tools() {
2040        let mut request = base_request();
2041        request.tool_choice = Some(ToolChoice::Tool {
2042            name: "get_weather".to_string(),
2043            disable_parallel_tool_use: None,
2044        });
2045
2046        assert!(request.validate().is_err());
2047    }
2048
2049    #[test]
2050    fn test_tool_choice_specific_tool_must_exist() {
2051        let mut request = base_request();
2052        request.tool_choice = Some(ToolChoice::Tool {
2053            name: "get_weather".to_string(),
2054            disable_parallel_tool_use: None,
2055        });
2056        request.tools = Some(vec![custom_tool("search_web")]);
2057
2058        assert!(request.validate().is_err());
2059    }
2060
2061    #[test]
2062    fn test_tool_choice_none_without_tools_is_valid() {
2063        let mut request = base_request();
2064        request.tool_choice = Some(ToolChoice::None);
2065
2066        assert!(request.validate().is_ok());
2067    }
2068
2069    #[test]
2070    fn test_tool_choice_specific_tool_is_valid_when_declared() {
2071        let mut request = base_request();
2072        request.tool_choice = Some(ToolChoice::Tool {
2073            name: "get_weather".to_string(),
2074            disable_parallel_tool_use: None,
2075        });
2076        request.tools = Some(vec![custom_tool("get_weather")]);
2077
2078        assert!(request.validate().is_ok());
2079    }
2080
2081    #[test]
2082    fn test_tool_choice_specific_tool_is_valid_with_mcp_toolset() {
2083        let mut request = base_request();
2084        request.tool_choice = Some(ToolChoice::Tool {
2085            name: "get_weather".to_string(),
2086            disable_parallel_tool_use: None,
2087        });
2088        request.tools = Some(vec![mcp_toolset(None)]);
2089        request.mcp_servers = Some(vec![mcp_server_config()]);
2090
2091        assert!(request.validate().is_ok());
2092    }
2093
2094    #[test]
2095    fn test_tool_choice_specific_tool_uses_mcp_toolset_default_when_override_missing() {
2096        let mut request = base_request();
2097        request.tool_choice = Some(ToolChoice::Tool {
2098            name: "get_weather".to_string(),
2099            disable_parallel_tool_use: None,
2100        });
2101        request.tools = Some(vec![mcp_toolset(Some(HashMap::from([(
2102            "search_web".to_string(),
2103            McpToolConfig {
2104                enabled: Some(false),
2105                defer_loading: None,
2106            },
2107        )])))]);
2108        request.mcp_servers = Some(vec![mcp_server_config()]);
2109
2110        assert!(request.validate().is_ok());
2111    }
2112
2113    #[test]
2114    fn test_tool_choice_specific_tool_must_be_enabled_in_mcp_toolset_configs() {
2115        let mut request = base_request();
2116        request.tool_choice = Some(ToolChoice::Tool {
2117            name: "get_weather".to_string(),
2118            disable_parallel_tool_use: None,
2119        });
2120        request.tools = Some(vec![mcp_toolset(Some(HashMap::from([(
2121            "get_weather".to_string(),
2122            McpToolConfig {
2123                enabled: Some(false),
2124                defer_loading: None,
2125            },
2126        )])))]);
2127        request.mcp_servers = Some(vec![mcp_server_config()]);
2128
2129        assert!(request.validate().is_err());
2130    }
2131
2132    #[test]
2133    fn test_full_message_with_tool_search_flow_deserialization() {
2134        // Simulates the full response from Anthropic API with tool search flow
2135        let json = r#"{
2136            "id": "msg_01TEST",
2137            "type": "message",
2138            "role": "assistant",
2139            "model": "claude-sonnet-4-5-20250929",
2140            "content": [
2141                {
2142                    "type": "server_tool_use",
2143                    "id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
2144                    "name": "tool_search_tool_regex",
2145                    "input": {"query": "weather"}
2146                },
2147                {
2148                    "type": "tool_search_tool_result",
2149                    "tool_use_id": "srvtoolu_015dw5iXvktXLmqwpyzo4Dp2",
2150                    "content": {
2151                        "type": "tool_search_tool_search_result",
2152                        "tool_references": [
2153                            {"type": "tool_reference", "tool_name": "get_weather"}
2154                        ]
2155                    }
2156                },
2157                {
2158                    "type": "tool_use",
2159                    "id": "toolu_01ABC",
2160                    "name": "get_weather",
2161                    "input": {"location": "San Francisco"}
2162                }
2163            ],
2164            "stop_reason": "tool_use",
2165            "stop_sequence": null,
2166            "usage": {
2167                "input_tokens": 100,
2168                "output_tokens": 50
2169            }
2170        }"#;
2171
2172        let msg: Message = serde_json::from_str(json)
2173            .expect("Failed to deserialize Message with tool search flow");
2174        assert_eq!(msg.content.len(), 3);
2175        assert!(matches!(msg.content[0], ContentBlock::ServerToolUse { .. }));
2176        assert!(matches!(
2177            msg.content[1],
2178            ContentBlock::ToolSearchToolResult { .. }
2179        ));
2180        assert!(matches!(msg.content[2], ContentBlock::ToolUse { .. }));
2181    }
2182}