use crate::models::*;
#[ derive( Debug, Clone ) ]
pub enum ValidationError
{
RequiredFieldMissing {
field : String,
context : String
},
InvalidFieldValue {
field : String,
value : String,
reason : String
},
ValueOutOfRange {
field : String,
value : f64,
min : Option< f64 >,
max : Option< f64 >
},
EmptyCollection {
field : String,
context : String
},
CollectionTooLarge {
field : String,
size : usize,
max : usize
},
}
impl core::fmt::Display for ValidationError
{
fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result
{
match self
{
ValidationError::RequiredFieldMissing { field, context } =>
write!( f, "Required field '{field}' is missing or empty in {context}" ),
ValidationError::InvalidFieldValue { field, value, reason } =>
write!( f, "Invalid value '{value}' for field '{field}': {reason}" ),
ValidationError::ValueOutOfRange { field, value, min, max } =>
{
let range_str = match ( min, max )
{
( Some( min ), Some( max ) ) => format!( "between {min} and {max}" ),
( Some( min ), None ) => format!( "at least {min}" ),
( None, Some( max ) ) => format!( "at most {max}" ),
( None, None ) => "within valid range".to_string(),
};
write!( f, "Value {value} for field '{field}' is not {range_str}" )
}
ValidationError::EmptyCollection { field, context } =>
write!( f, "Collection '{field}' cannot be empty in {context}" ),
ValidationError::CollectionTooLarge { field, size, max } =>
write!( f, "Collection '{field}' has {size} items, exceeding maximum of {max}" ),
}
}
}
impl core::error::Error for ValidationError
{
}
const MAX_MODELS_TO_COMPARE: usize = 10;
const MAX_BATCH_TOKEN_REQUESTS: usize = 100;
const MAX_MODEL_STATUS_REQUESTS: usize = 50;
const MAX_ALLOWED_FUNCTION_NAMES: usize = 100;
const MAX_TUNING_EXAMPLES: usize = 10000;
const MAX_CODE_EXECUTION_TIMEOUT: i32 = 300;
fn validate_model_name( model_name : &str ) -> Result< (), ValidationError >
{
if model_name.trim().is_empty()
{
return Err( ValidationError::RequiredFieldMissing {
field : "model_name".to_string(),
context : "model validation".to_string(),
} );
}
if model_name.contains( '\n' ) || model_name.contains( '\r' ) || model_name.contains( '\0' )
{
return Err( ValidationError::InvalidFieldValue {
field : "model_name".to_string(),
value : model_name.to_string(),
reason : "Model name contains invalid characters (newlines or null bytes)".to_string(),
} );
}
Ok( () )
}
fn validate_content( content : &Content ) -> Result< (), ValidationError >
{
if content.parts.is_empty()
{
return Err( ValidationError::EmptyCollection {
field : "parts".to_string(),
context : "Content".to_string(),
} );
}
for ( i, part ) in content.parts.iter().enumerate()
{
validate_part( part )
.map_err( |e| ValidationError::InvalidFieldValue {
field : format!( "parts[{}]", i ),
value : "Part".to_string(),
reason : e.to_string(),
} )?;
}
Ok( () )
}
fn validate_part( part : &Part ) -> Result< (), ValidationError >
{
let has_text = part.text.as_ref().map_or( false, |t| !t.trim().is_empty() );
let has_inline_data = part.inline_data.is_some();
let has_function_call = part.function_call.is_some();
let has_function_response = part.function_response.is_some();
let content_count = [ has_text, has_inline_data, has_function_call, has_function_response ]
.iter()
.filter( |&&x| x )
.count();
if content_count == 0
{
return Err( ValidationError::RequiredFieldMissing {
field : "content".to_string(),
context : "Part must have at least one content type (text, inline_data, function_call, or function_response)".to_string(),
} );
}
Ok( () )
}
mod tokens;
mod config;
mod tuning;
mod content;
pub use tokens::*;
pub use config::*;
pub use tuning::*;
pub use content::*;