mod private
{
use crate::
{
components ::
{
responses ::{ CreateResponseRequest, ResponseInput },
common ::ModelIdsResponses,
input ::{ InputMessage, InputContentPart, InputText, InputItem },
tools ::{ FunctionTool, FunctionParameters },
},
};
#[ derive( Debug, Clone, PartialEq ) ]
pub enum ResponseRequestValidationError
{
InvalidModel(String),
InvalidInput(String),
InvalidTemperature(f32),
InvalidMaxTokens(i32),
}
impl core::fmt::Display for ResponseRequestValidationError
{
#[ inline ]
fn fmt(&self, f : &mut core::fmt::Formatter< '_ >) -> core::fmt::Result
{
match self
{
Self::InvalidModel(model) => write!(f, "Invalid model : {model}"),
Self::InvalidInput(input) => write!(f, "Invalid input : {input}"),
Self::InvalidTemperature(temp) => write!(f, "Invalid temperature {temp}: must be between 0.0 and 2.0"),
Self::InvalidMaxTokens(tokens) => write!(f, "Invalid max tokens {tokens}: must be positive"),
}
}
}
impl core::error::Error for ResponseRequestValidationError
{}
pub trait CreateResponseRequestEnhancements
{
fn with_simple_text(model : &str, text : &str) -> CreateResponseRequest;
fn with_messages(model : &str, messages : Vec< InputMessage >) -> CreateResponseRequest;
fn validate_request(&self) -> core::result::Result< (), ResponseRequestValidationError >;
}
impl CreateResponseRequestEnhancements for CreateResponseRequest
{
#[ inline ]
fn with_simple_text(model : &str, text : &str) -> CreateResponseRequest
{
CreateResponseRequest::former()
.model(ModelIdsResponses::from(model.to_string()))
.input(ResponseInput::String(text.to_string()))
.form()
}
#[ inline ]
fn with_messages(model : &str, messages : Vec< InputMessage >) -> CreateResponseRequest
{
let input_items : Vec< InputItem > = messages.into_iter()
.map(InputItem::Message)
.collect();
CreateResponseRequest::former()
.model(ModelIdsResponses::from(model.to_string()))
.input(ResponseInput::Items(input_items))
.form()
}
#[ inline ]
fn validate_request(&self) -> core::result::Result< (), ResponseRequestValidationError >
{
if self.model.value.is_empty()
{
return Err(ResponseRequestValidationError::InvalidModel("Model cannot be empty".to_string()));
}
match &self.input
{
ResponseInput::String(s) if s.is_empty() =>
{
return Err(ResponseRequestValidationError::InvalidInput("String input cannot be empty".to_string()));
},
ResponseInput::Items(items) if items.is_empty() =>
{
return Err(ResponseRequestValidationError::InvalidInput("Items input cannot be empty".to_string()));
},
_ => {} }
if let Some(temp) = self.temperature
{
if !(0.0..=2.0).contains(&temp)
{
return Err(ResponseRequestValidationError::InvalidTemperature(temp));
}
}
if let Some(max_tokens) = self.max_output_tokens
{
if max_tokens <= 0
{
return Err(ResponseRequestValidationError::InvalidMaxTokens(max_tokens));
}
}
Ok(())
}
}
pub trait InputMessageEnhancements
{
fn user_text(text : &str) -> InputMessage;
fn system_text(text : &str) -> InputMessage;
fn assistant_text(text : &str) -> InputMessage;
}
impl InputMessageEnhancements for InputMessage
{
#[ inline ]
fn user_text(text : &str) -> InputMessage
{
InputMessage::former()
.role("user".to_string())
.content(vec![InputContentPart::Text(InputText { text : text.to_string() })])
.form()
}
#[ inline ]
fn system_text(text : &str) -> InputMessage
{
InputMessage::former()
.role("system".to_string())
.content(vec![InputContentPart::Text(InputText { text : text.to_string() })])
.form()
}
#[ inline ]
fn assistant_text(text : &str) -> InputMessage
{
InputMessage::former()
.role("assistant".to_string())
.content(vec![InputContentPart::Text(InputText { text : text.to_string() })])
.form()
}
}
pub trait FunctionToolEnhancements
{
fn simple_function(name : &str, description : &str) -> FunctionTool;
fn with_object_params(name : &str, description : &str, properties : serde_json::Value) -> FunctionTool;
}
impl FunctionToolEnhancements for FunctionTool
{
#[ inline ]
fn simple_function(name : &str, description : &str) -> FunctionTool
{
let params = serde_json::json!({
"type": "object",
"properties": {},
"required": []
});
FunctionTool::former()
.name(name.to_string())
.description(description.to_string())
.parameters(FunctionParameters::new(params))
.form()
}
#[ inline ]
fn with_object_params(name : &str, description : &str, properties : serde_json::Value) -> FunctionTool
{
let params = serde_json::json!({
"type": "object",
"properties": properties,
"required": []
});
FunctionTool::former()
.name(name.to_string())
.description(description.to_string())
.parameters(FunctionParameters::new(params))
.form()
}
}
#[ cfg( test ) ]
mod tests
{
use super::*;
#[ test ]
fn test_simple_text_convenience_method()
{
let request = CreateResponseRequest::with_simple_text("gpt-4", "Tell me a story");
assert_eq!(request.model.value, "gpt-4");
assert_eq!(request.input, ResponseInput::String("Tell me a story".to_string()));
}
#[ test ]
fn test_user_message_convenience_method()
{
let message = InputMessage::user_text("Hello world");
assert_eq!(message.role, "user");
assert_eq!(message.content.len(), 1);
if let InputContentPart::Text(text_part) = &message.content[0]
{
assert_eq!(text_part.text, "Hello world");
}
else
{
panic!("Expected text content");
}
}
#[ test ]
fn test_request_validation_success()
{
let request = CreateResponseRequest::with_simple_text("gpt-4", "Valid input");
let validation_result = request.validate_request();
assert!(validation_result.is_ok());
}
#[ test ]
fn test_request_validation_invalid_temperature()
{
let mut request = CreateResponseRequest::with_simple_text("gpt-4", "Valid input");
request.temperature = Some(3.0);
let validation_result = request.validate_request();
assert!(validation_result.is_err());
if let Err(ResponseRequestValidationError::InvalidTemperature(temp)) = validation_result
{
assert!((temp - 3.0).abs() < f32::EPSILON);
}
else
{
panic!("Expected InvalidTemperature error");
}
}
#[ test ]
fn test_simple_function_tool()
{
let tool = FunctionTool::simple_function("test_func", "A test function");
assert_eq!(tool.name, "test_func");
assert_eq!(tool.description, Some("A test function".to_string()));
assert_eq!(tool.parameters.0["type"], "object");
}
#[ test ]
fn test_messages_convenience_method()
{
let messages = vec![
InputMessage::user_text("Hello"),
InputMessage::assistant_text("Hi there"),
InputMessage::system_text("You are a helpful assistant"),
];
let request = CreateResponseRequest::with_messages("gpt-4", messages);
assert_eq!(request.model.value, "gpt-4");
if let ResponseInput::Items(items) = request.input
{
assert_eq!(items.len(), 3);
let InputItem::Message(msg) = &items[0];
assert_eq!(msg.role, "user");
}
else
{
panic!("Expected Items input");
}
}
}
}
crate ::mod_interface!
{
orphan use ResponseRequestValidationError;
orphan use CreateResponseRequestEnhancements;
orphan use InputMessageEnhancements;
orphan use FunctionToolEnhancements;
}