mod private
{
use super::super::{ Client, CreateMessageRequest, CreateMessageResponse };
use crate::error::{ AnthropicError, AnthropicResult };
use std::time::Instant;
#[ derive( Debug, Clone ) ]
pub struct ModelComparisonResult
{
pub model_name : String,
pub response : CreateMessageResponse,
pub response_time_ms : u64,
pub success : bool,
pub error_message : Option< String >,
pub input_tokens : Option< u32 >,
pub output_tokens : Option< u32 >,
}
#[ derive( Debug, Clone ) ]
pub struct ComparisonResults
{
pub results : Vec< ModelComparisonResult >,
pub total_time_ms : u64,
pub fastest_model : Option< String >,
pub slowest_model : Option< String >,
}
impl ComparisonResults
{
#[ must_use ]
pub fn success_rate( &self ) -> f64
{
if self.results.is_empty()
{
return 0.0;
}
let successful = self.results.iter().filter( | r | r.success ).count();
( successful as f64 ) / ( self.results.len() as f64 )
}
#[ must_use ]
pub fn average_response_time_ms( &self ) -> Option< u64 >
{
let successful_times : Vec< u64 > = self.results
.iter()
.filter( | r | r.success )
.map( | r | r.response_time_ms )
.collect();
if successful_times.is_empty()
{
None
}
else
{
Some( successful_times.iter().sum::< u64 >() / successful_times.len() as u64 )
}
}
#[ must_use ]
pub fn total_input_tokens( &self ) -> u32
{
self.results
.iter()
.filter_map( | r | r.input_tokens )
.sum()
}
#[ must_use ]
pub fn total_output_tokens( &self ) -> u32
{
self.results
.iter()
.filter_map( | r | r.output_tokens )
.sum()
}
}
#[ derive( Debug ) ]
pub struct ModelComparator< 'a >
{
client : &'a Client,
}
impl< 'a > ModelComparator< 'a >
{
#[ must_use ]
pub fn new( client : &'a Client ) -> Self
{
Self { client }
}
pub async fn compare_models
(
&self,
model_names : &[ impl AsRef< str > ],
base_request : &CreateMessageRequest,
) -> AnthropicResult< ComparisonResults >
{
if model_names.is_empty()
{
return Err( AnthropicError::InvalidArgument( "At least one model required".to_string() ) );
}
let start = Instant::now();
let mut results = Vec::new();
for model_name in model_names
{
let model_str = model_name.as_ref();
let mut request = base_request.clone();
request.model = model_str.to_string();
let request_start = Instant::now();
match self.client.create_message( request ).await
{
Ok( response ) => {
#[ allow( clippy::cast_possible_truncation ) ]
let elapsed = request_start.elapsed().as_millis().min( u128::from( u64::MAX ) ) as u64;
results.push( ModelComparisonResult
{
model_name : model_str.to_string(),
input_tokens : Some( response.usage.input_tokens ),
output_tokens : Some( response.usage.output_tokens ),
response,
response_time_ms : elapsed,
success : true,
error_message : None,
} );
},
Err( err ) => {
#[ allow( clippy::cast_possible_truncation ) ]
let elapsed = request_start.elapsed().as_millis().min( u128::from( u64::MAX ) ) as u64;
let empty_response = CreateMessageResponse
{
id : String::new(),
r#type : String::new(),
role : String::new(),
content : vec![],
model : model_str.to_string(),
stop_reason : None,
stop_sequence : None,
usage : crate::client::types::Usage
{
input_tokens : 0,
output_tokens : 0,
cache_creation_input_tokens : None,
cache_read_input_tokens : None,
},
};
results.push( ModelComparisonResult
{
model_name : model_str.to_string(),
response : empty_response,
response_time_ms : elapsed,
success : false,
error_message : Some( err.to_string() ),
input_tokens : None,
output_tokens : None,
} );
}
}
}
#[ allow( clippy::cast_possible_truncation ) ]
let total_time_ms = start.elapsed().as_millis().min( u128::from( u64::MAX ) ) as u64;
let fastest_model = results
.iter()
.filter( | r | r.success )
.min_by_key( | r | r.response_time_ms )
.map( | r | r.model_name.clone() );
let slowest_model = results
.iter()
.filter( | r | r.success )
.max_by_key( | r | r.response_time_ms )
.map( | r | r.model_name.clone() );
Ok( ComparisonResults
{
results,
total_time_ms,
fastest_model,
slowest_model,
} )
}
pub async fn compare_models_parallel
(
&self,
model_names : &[ impl AsRef< str > ],
base_request : &CreateMessageRequest,
) -> AnthropicResult< ComparisonResults >
{
if model_names.is_empty()
{
return Err( AnthropicError::InvalidArgument( "At least one model required".to_string() ) );
}
let start = Instant::now();
let futures : Vec< _ > = model_names
.iter()
.map( | model_name | async move {
let model_str = model_name.as_ref();
let mut request = base_request.clone();
request.model = model_str.to_string();
let request_start = Instant::now();
match self.client.create_message( request ).await
{
Ok( response ) => {
#[ allow( clippy::cast_possible_truncation ) ]
let elapsed = request_start.elapsed().as_millis().min( u128::from( u64::MAX ) ) as u64;
ModelComparisonResult
{
model_name : model_str.to_string(),
input_tokens : Some( response.usage.input_tokens ),
output_tokens : Some( response.usage.output_tokens ),
response,
response_time_ms : elapsed,
success : true,
error_message : None,
}
},
Err( err ) => {
#[ allow( clippy::cast_possible_truncation ) ]
let elapsed = request_start.elapsed().as_millis().min( u128::from( u64::MAX ) ) as u64;
let empty_response = CreateMessageResponse
{
id : String::new(),
r#type : String::new(),
role : String::new(),
content : vec![],
model : model_str.to_string(),
stop_reason : None,
stop_sequence : None,
usage : crate::client::types::Usage
{
input_tokens : 0,
output_tokens : 0,
cache_creation_input_tokens : None,
cache_read_input_tokens : None,
},
};
ModelComparisonResult
{
model_name : model_str.to_string(),
response : empty_response,
response_time_ms : elapsed,
success : false,
error_message : Some( err.to_string() ),
input_tokens : None,
output_tokens : None,
}
}
}
} )
.collect();
let results = futures::future::join_all( futures ).await;
#[ allow( clippy::cast_possible_truncation ) ]
let total_time_ms = start.elapsed().as_millis().min( u128::from( u64::MAX ) ) as u64;
let fastest_model = results
.iter()
.filter( | r | r.success )
.min_by_key( | r | r.response_time_ms )
.map( | r | r.model_name.clone() );
let slowest_model = results
.iter()
.filter( | r | r.success )
.max_by_key( | r | r.response_time_ms )
.map( | r | r.model_name.clone() );
Ok( ComparisonResults
{
results,
total_time_ms,
fastest_model,
slowest_model,
} )
}
}
impl Client
{
#[ must_use ]
#[ inline ]
pub fn comparator( &self ) -> ModelComparator< '_ >
{
ModelComparator::new( self )
}
}
}
crate::mod_interface!
{
orphan use
{
ModelComparisonResult,
ComparisonResults,
ModelComparator,
};
}