use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::KitError;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ModelSelector {
value: String,
}
impl ModelSelector {
pub fn new(value: impl Into<String>) -> Result<Self, KitError> {
let value = value.into();
if value.is_empty() || value.trim() != value {
return Err(KitError::invalid_input(
"model selector must be nonempty and unpadded",
));
}
Ok(Self { value })
}
pub fn as_str(&self) -> &str {
&self.value
}
}
impl std::fmt::Display for ModelSelector {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.value)
}
}
impl Serialize for ModelSelector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.value)
}
}
impl<'de> Deserialize<'de> for ModelSelector {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Self::new(value).map_err(serde::de::Error::custom)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct PersonalitySelector {
value: String,
}
impl PersonalitySelector {
pub fn new(value: impl Into<String>) -> Result<Self, KitError> {
let value = value.into();
if value.is_empty() || value.trim() != value {
return Err(KitError::invalid_input(
"personality selector must be nonempty and unpadded",
));
}
Ok(Self { value })
}
pub fn as_str(&self) -> &str {
&self.value
}
}
impl std::fmt::Display for PersonalitySelector {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.value)
}
}
impl Serialize for PersonalitySelector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.value)
}
}
impl<'de> Deserialize<'de> for PersonalitySelector {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Self::new(value).map_err(serde::de::Error::custom)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ReasoningEffort {
value: String,
}
impl ReasoningEffort {
pub fn new(value: impl Into<String>) -> Result<Self, KitError> {
let value = value.into();
if value.is_empty() || value.trim() != value {
return Err(KitError::invalid_input(
"reasoning effort must be nonempty and unpadded",
));
}
Ok(Self { value })
}
pub fn low() -> Self {
Self {
value: "low".to_owned(),
}
}
pub fn medium() -> Self {
Self {
value: "medium".to_owned(),
}
}
pub fn high() -> Self {
Self {
value: "high".to_owned(),
}
}
pub fn as_str(&self) -> &str {
&self.value
}
}
impl std::fmt::Display for ReasoningEffort {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.value)
}
}
impl Serialize for ReasoningEffort {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.value)
}
}
impl<'de> Deserialize<'de> for ReasoningEffort {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
Self::new(value).map_err(serde::de::Error::custom)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ModelInfo {
selector: ModelSelector,
display_name: Option<String>,
reasoning_efforts: Vec<ReasoningEffort>,
personalities: Vec<PersonalitySelector>,
is_default: bool,
}
impl ModelInfo {
pub fn new(
selector: ModelSelector,
display_name: Option<String>,
reasoning_efforts: Vec<ReasoningEffort>,
is_default: bool,
) -> Result<Self, KitError> {
if display_name
.as_deref()
.is_some_and(|name| name.is_empty() || name.trim() != name)
{
return Err(KitError::invalid_input(
"model display name must be nonempty and unpadded when present",
));
}
Ok(Self {
selector,
display_name,
reasoning_efforts,
personalities: Vec::new(),
is_default,
})
}
#[must_use]
pub fn with_personalities(mut self, personalities: Vec<PersonalitySelector>) -> Self {
self.personalities = personalities;
self
}
pub fn selector(&self) -> &ModelSelector {
&self.selector
}
pub fn display_name(&self) -> Option<&str> {
self.display_name.as_deref()
}
pub fn reasoning_efforts(&self) -> &[ReasoningEffort] {
&self.reasoning_efforts
}
pub fn personalities(&self) -> &[PersonalitySelector] {
&self.personalities
}
pub fn is_default(&self) -> bool {
self.is_default
}
}