use crate::base::entity::node::Node;
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "PascalCase")]
pub enum MaxLoops {
Fixed(u32),
Auto {
max_subtasks: u32,
},
}
impl<'de> Deserialize<'de> for MaxLoops {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum MaxLoopsHelper {
Integer(u32),
Enum(MaxLoopsEnum),
}
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
enum MaxLoopsEnum {
Fixed(u32),
Auto { max_subtasks: u32 },
}
match MaxLoopsHelper::deserialize(deserializer)? {
MaxLoopsHelper::Integer(n) => Ok(MaxLoops::Fixed(n)),
MaxLoopsHelper::Enum(MaxLoopsEnum::Fixed(n)) => Ok(MaxLoops::Fixed(n)),
MaxLoopsHelper::Enum(MaxLoopsEnum::Auto { max_subtasks }) => {
Ok(MaxLoops::Auto { max_subtasks })
}
}
}
}
impl Default for MaxLoops {
fn default() -> Self {
MaxLoops::Fixed(3)
}
}
impl MaxLoops {
pub fn as_u32(&self) -> u32 {
match self {
MaxLoops::Fixed(count) => *count,
MaxLoops::Auto { max_subtasks } => *max_subtasks,
}
}
pub fn is_auto(&self) -> bool {
matches!(self, MaxLoops::Auto { .. })
}
pub fn is_fixed(&self) -> bool {
matches!(self, MaxLoops::Fixed(_))
}
}
impl std::fmt::Display for MaxLoops {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MaxLoops::Fixed(count) => write!(f, "{}", count),
MaxLoops::Auto { max_subtasks } => write!(f, "Auto({})", max_subtasks),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum PaladinStatus {
Idle,
Reasoning,
Executing,
Completed,
Failed(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaladinData {
pub system_prompt: String,
pub name: String,
pub user_name: String,
pub model: String,
pub temperature: f32,
pub max_loops: MaxLoops,
pub stop_words: Vec<String>,
pub status: PaladinStatus,
pub vision_enabled: bool,
#[serde(default)]
pub autonomous_planning: bool,
#[serde(default)]
pub autonomous_prompts: bool,
#[serde(default)]
pub agent_description: String,
#[serde(default)]
pub dynamic_temperature: bool,
}
pub type Paladin = Node<PaladinData>;
impl Default for PaladinData {
fn default() -> Self {
Self {
system_prompt: String::new(),
name: "Paladin".to_string(),
user_name: "User".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::default(),
stop_words: Vec::new(),
status: PaladinStatus::Idle,
vision_enabled: false,
autonomous_planning: false,
autonomous_prompts: false,
agent_description: String::new(),
dynamic_temperature: false,
}
}
}
impl PaladinStatus {
pub fn is_terminal(&self) -> bool {
matches!(self, PaladinStatus::Completed | PaladinStatus::Failed(_))
}
pub fn is_active(&self) -> bool {
matches!(self, PaladinStatus::Reasoning | PaladinStatus::Executing)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_paladin_status_is_terminal() {
assert!(PaladinStatus::Completed.is_terminal());
assert!(PaladinStatus::Failed("error".to_string()).is_terminal());
assert!(!PaladinStatus::Idle.is_terminal());
assert!(!PaladinStatus::Reasoning.is_terminal());
assert!(!PaladinStatus::Executing.is_terminal());
}
#[test]
fn test_paladin_status_is_active() {
assert!(PaladinStatus::Reasoning.is_active());
assert!(PaladinStatus::Executing.is_active());
assert!(!PaladinStatus::Idle.is_active());
assert!(!PaladinStatus::Completed.is_active());
assert!(!PaladinStatus::Failed("error".to_string()).is_active());
}
#[test]
fn test_max_loops_fixed() {
let loops = MaxLoops::Fixed(5);
assert_eq!(loops, MaxLoops::Fixed(5));
}
#[test]
fn test_max_loops_auto() {
let loops = MaxLoops::Auto { max_subtasks: 10 };
if let MaxLoops::Auto { max_subtasks } = loops {
assert_eq!(max_subtasks, 10);
} else {
panic!("Expected MaxLoops::Auto variant");
}
}
#[test]
fn test_max_loops_default() {
let loops = MaxLoops::default();
assert_eq!(loops, MaxLoops::Fixed(3));
}
#[test]
fn test_paladin_data_default() {
let data = PaladinData::default();
assert_eq!(data.name, "Paladin");
assert_eq!(data.user_name, "User");
assert_eq!(data.model, "gpt-4");
assert_eq!(data.temperature, 0.7);
assert_eq!(data.max_loops, MaxLoops::Fixed(3));
assert_eq!(data.status, PaladinStatus::Idle);
assert!(!data.vision_enabled);
}
#[test]
fn test_paladin_data_with_auto_planning() {
let data = PaladinData {
max_loops: MaxLoops::Auto { max_subtasks: 8 },
..Default::default()
};
if let MaxLoops::Auto { max_subtasks } = data.max_loops {
assert_eq!(max_subtasks, 8);
} else {
panic!("Expected MaxLoops::Auto variant");
}
}
#[test]
fn test_vision_enabled_field() {
let mut data = PaladinData::default();
assert!(!data.vision_enabled);
data.vision_enabled = true;
assert!(data.vision_enabled);
}
}