mod private
{
use super::super::content::orphan::*;
use serde::{ Serialize, Deserialize };
#[ cfg( feature = "tools" ) ]
use serde_json::Value;
#[ cfg( feature = "tools" ) ]
#[ derive( Debug, Clone, Serialize, Deserialize, PartialEq ) ]
pub struct ToolDefinition
{
pub name : String,
pub description : String,
pub input_schema : Value,
}
#[ cfg( feature = "tools" ) ]
impl ToolDefinition
{
#[ inline ]
#[ must_use ]
pub fn new< S1 : Into< String >, S2 : Into< String > >( name : S1, description : S2, input_schema : Value ) -> Self
{
Self
{
name : name.into(),
description : description.into(),
input_schema,
}
}
#[ inline ]
#[ must_use ]
pub fn simple< S1 : Into< String >, S2 : Into< String > >( name : S1, description : S2 ) -> Self
{
let schema = serde_json::json!({
"type": "object",
"properties": {},
"required": []
});
Self::new( name, description, schema )
}
#[ inline ]
#[ must_use ]
pub fn with_properties< S1 : Into< String >, S2 : Into< String > >(
name : S1,
description : S2,
properties : &Value,
required : &[String],
) -> Self
{
let schema = serde_json::json!({
"type": "object",
"properties": properties.clone(),
"required": required
});
Self::new( name, description, schema )
}
#[ cfg( feature = "error-handling" ) ]
pub fn validate( &self ) -> crate::error::AnthropicResult< () >
{
if self.name.trim().is_empty()
{
return Err( crate::error::AnthropicError::InvalidRequest(
"tool name cannot be empty".to_string()
) );
}
if self.description.trim().is_empty()
{
return Err( crate::error::AnthropicError::InvalidRequest(
format!( "tool '{}' description cannot be empty", self.name )
) );
}
if !self.name.chars().all( | c | c.is_alphanumeric() || c == '_' || c == '-' )
{
return Err( crate::error::AnthropicError::InvalidRequest(
format!( "tool name '{}' contains invalid characters - only alphanumeric, underscore, and hyphen allowed", self.name )
) );
}
if self.name.len() > 64
{
return Err( crate::error::AnthropicError::InvalidRequest(
format!( "tool name '{}' too long - maximum 64 characters", self.name )
) );
}
if self.description.len() > 1024
{
return Err( crate::error::AnthropicError::InvalidRequest(
format!( "tool '{}' description too long - maximum 1024 characters", self.name )
) );
}
Ok( () )
}
#[ inline ]
#[ must_use ]
pub fn name( &self ) -> &str
{
&self.name
}
#[ inline ]
#[ must_use ]
pub fn description( &self ) -> &str
{
&self.description
}
#[ inline ]
#[ must_use ]
pub fn input_schema( &self ) -> &Value
{
&self.input_schema
}
}
#[ cfg( feature = "tools" ) ]
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct ToolUseContent
{
pub r#type : String,
pub id : String,
pub name : String,
pub input : Value,
}
#[ cfg( feature = "tools" ) ]
#[ derive( Debug, Clone, Serialize, Deserialize ) ]
pub struct ToolResultContent
{
pub r#type : String,
pub tool_use_id : String,
pub content : String,
#[ serde( skip_serializing_if = "Option::is_none" ) ]
pub is_error : Option< bool >,
}
#[ cfg( feature = "tools" ) ]
#[ derive( Debug, Clone, Serialize, Deserialize, PartialEq ) ]
#[ serde( tag = "type", rename_all = "lowercase" ) ]
pub enum ToolChoice
{
None,
Auto,
Any,
Tool
{
name : String,
},
}
#[ cfg( feature = "tools" ) ]
impl ToolChoice
{
#[ inline ]
#[ must_use ]
pub fn specific< S : Into< String > >( name : S ) -> Self
{
Self::Tool { name : name.into() }
}
#[ inline ]
#[ must_use ]
pub fn is_auto( &self ) -> bool
{
matches!( self, ToolChoice::Auto )
}
#[ inline ]
#[ must_use ]
pub fn is_none( &self ) -> bool
{
matches!( self, ToolChoice::None )
}
#[ inline ]
#[ must_use ]
pub fn is_any( &self ) -> bool
{
matches!( self, ToolChoice::Any )
}
#[ inline ]
#[ must_use ]
pub fn is_specific( &self ) -> bool
{
matches!( self, ToolChoice::Tool { .. } )
}
#[ inline ]
#[ must_use ]
pub fn tool_name( &self ) -> Option< &str >
{
match self
{
ToolChoice::Tool { name } => Some( name ),
_ => None,
}
}
}
#[ derive( Debug, Clone, Serialize, Deserialize, PartialEq ) ]
pub struct Message
{
pub role : Role,
pub content : Vec< Content >,
#[ serde( skip_serializing_if = "Option::is_none" ) ]
pub cache_control : Option< crate::CacheControl >,
}
#[ derive( Debug, Default ) ]
pub struct MessageBuilder
{
role : Option< Role >,
content : Vec< Content >,
}
impl MessageBuilder
{
#[ inline ]
#[ must_use ]
pub fn new() -> Self
{
Self::default()
}
#[ inline ]
#[ must_use ]
pub fn role( mut self, role : Role ) -> Self
{
self.role = Some( role );
self
}
#[ inline ]
#[ must_use ]
pub fn user( mut self ) -> Self
{
self.role = Some( Role::User );
self
}
#[ inline ]
#[ must_use ]
pub fn assistant( mut self ) -> Self
{
self.role = Some( Role::Assistant );
self
}
#[ inline ]
#[ must_use ]
pub fn text< S : Into< String > >( mut self, text : S ) -> Self
{
self.content.push( Content::new_text( text ) );
self
}
#[ cfg( feature = "vision" ) ]
#[ inline ]
#[ must_use ]
pub fn image( mut self, source : ImageSource ) -> Self
{
self.content.push( Content::image( source ) );
self
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn tool_use< S1 : Into< String >, S2 : Into< String > >( mut self, id : S1, name : S2, input : Value ) -> Self
{
self.content.push( Content::tool_use( id, name, input ) );
self
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn tool_result< S1 : Into< String >, S2 : Into< String > >( mut self, tool_use_id : S1, content : S2 ) -> Self
{
self.content.push( Content::tool_result( tool_use_id, content ) );
self
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn tool_result_error< S1 : Into< String >, S2 : Into< String > >( mut self, tool_use_id : S1, content : S2, is_error : bool ) -> Self
{
self.content.push( Content::tool_result_error( tool_use_id, content, is_error ) );
self
}
#[ inline ]
#[ must_use ]
pub fn content( mut self, content : Content ) -> Self
{
self.content.push( content );
self
}
#[ inline ]
#[ must_use ]
pub fn contents( mut self, contents : Vec< Content > ) -> Self
{
self.content.extend( contents );
self
}
#[ inline ]
#[ must_use ]
pub fn build( self ) -> Message
{
Message
{
role : self.role.expect( "Role must be set" ),
content : if self.content.is_empty() { panic!( "Content cannot be empty" ) } else { self.content },
cache_control : None,
}
}
}
impl Message
{
#[ inline ]
#[ must_use ]
pub fn builder() -> MessageBuilder
{
MessageBuilder::new()
}
#[ inline ]
#[ must_use ]
pub fn user< S : Into< String > >( text : S ) -> Self
{
Self
{
role : Role::User,
content : vec![ Content::new_text( text ) ],
cache_control : None,
}
}
#[ inline ]
#[ must_use ]
pub fn assistant< S : Into< String > >( text : S ) -> Self
{
Self
{
role : Role::Assistant,
content : vec![ Content::new_text( text ) ],
cache_control : None,
}
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn assistant_with_tool_use( tool_uses : Vec< ToolUseContent > ) -> Self
{
let content = tool_uses.into_iter().map( | tool_use |
Content::ToolUse
{
r#type : tool_use.r#type,
id : tool_use.id,
name : tool_use.name,
input : tool_use.input,
}
).collect();
Self
{
role : Role::Assistant,
content,
cache_control : None,
}
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn user_with_tool_result( tool_results : Vec< ToolResultContent > ) -> Self
{
let content = tool_results.into_iter().map( | tool_result |
Content::ToolResult
{
r#type : tool_result.r#type,
tool_use_id : tool_result.tool_use_id,
content : tool_result.content,
is_error : tool_result.is_error,
}
).collect();
Self
{
role : Role::User,
content,
cache_control : None,
}
}
#[ cfg( feature = "vision" ) ]
#[ inline ]
#[ must_use ]
pub fn user_image( image : ImageContent ) -> Self
{
let content = vec![ Content::Image
{
r#type : image.r#type,
source : image.source,
} ];
Self
{
role : Role::User,
content,
cache_control : None,
}
}
#[ cfg( feature = "vision" ) ]
#[ inline ]
#[ must_use ]
pub fn user_with_image< S : Into< String > >( text : S, image : ImageContent ) -> Self
{
let content = vec![
Content::new_text( text ),
Content::Image { r#type : image.r#type, source : image.source }
];
Self
{
role : Role::User,
content,
cache_control : None,
}
}
#[ cfg( feature = "vision" ) ]
#[ inline ]
#[ must_use ]
pub fn user_with_images< S : Into< String > >( text : S, images : Vec< ImageContent > ) -> Self
{
let mut content = vec![ Content::new_text( text ) ];
for image in images
{
content.push( Content::Image
{
r#type : image.r#type,
source : image.source,
} );
}
Self
{
role : Role::User,
content,
cache_control : None,
}
}
#[ inline ]
#[ must_use ]
pub fn first_text( &self ) -> Option< &str >
{
self.content.iter()
.find_map( | c | c.text() )
}
#[ inline ]
#[ must_use ]
pub fn all_text( &self ) -> Vec< &str >
{
self.content.iter()
.filter_map( | c | c.text() )
.collect()
}
#[ inline ]
#[ must_use ]
pub fn count_by_type( &self, content_type : &str ) -> usize
{
self.content.iter()
.filter( | c | c.r#type() == content_type )
.count()
}
#[ inline ]
#[ must_use ]
pub fn has_text( &self ) -> bool
{
self.content.iter().any( Content::is_text )
}
#[ cfg( feature = "vision" ) ]
#[ inline ]
#[ must_use ]
pub fn has_images( &self ) -> bool
{
self.content.iter().any( Content::is_image )
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn has_tool_use( &self ) -> bool
{
self.content.iter().any( Content::is_tool_use )
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn has_tool_results( &self ) -> bool
{
self.content.iter().any( Content::is_tool_result )
}
#[ cfg( feature = "tools" ) ]
#[ inline ]
#[ must_use ]
pub fn tool_use_ids( &self ) -> Vec< &str >
{
self.content.iter()
.filter_map( | c | c.tool_use_id() )
.collect()
}
}
}
crate::mod_interface!
{
exposed use Message;
exposed use MessageBuilder;
#[ cfg( feature = "tools" ) ]
exposed use ToolDefinition;
#[ cfg( feature = "tools" ) ]
exposed use ToolUseContent;
#[ cfg( feature = "tools" ) ]
exposed use ToolResultContent;
#[ cfg( feature = "tools" ) ]
exposed use ToolChoice;
}