use serde::{Deserialize, Serialize};
use super::{BattalionConfig, BattalionError};
use crate::platform::container::paladin::Paladin;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Formation {
pub paladins: Vec<Paladin>,
pub config: BattalionConfig,
pub shared_context: Option<String>,
}
impl Formation {
pub fn new(paladins: Vec<Paladin>, config: BattalionConfig) -> Result<Self, BattalionError> {
let formation = Self {
paladins,
config,
shared_context: None,
};
formation.validate()?;
Ok(formation)
}
pub fn with_shared_context(mut self, context: String) -> Self {
self.shared_context = Some(context);
self
}
fn validate(&self) -> Result<(), BattalionError> {
if self.paladins.len() < 2 {
return Err(BattalionError::ValidationError(format!(
"Formation requires at least 2 Paladins, got {}",
self.paladins.len()
)));
}
Ok(())
}
pub fn paladin_count(&self) -> usize {
self.paladins.len()
}
pub fn paladins(&self) -> &[Paladin] {
&self.paladins
}
pub fn config(&self) -> &BattalionConfig {
&self.config
}
pub fn shared_context(&self) -> Option<&str> {
self.shared_context.as_deref()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::base::entity::node::Node;
use crate::platform::container::paladin::{MaxLoops, PaladinData, PaladinStatus};
fn create_test_paladin(name: &str) -> Paladin {
let data = PaladinData {
system_prompt: format!("You are {}", name),
name: name.to_string(),
user_name: "TestUser".to_string(),
model: "gpt-4".to_string(),
temperature: 0.7,
max_loops: MaxLoops::Fixed(3),
stop_words: vec![],
status: PaladinStatus::Idle,
vision_enabled: false,
..Default::default()
};
Node::new(data, Some(name.to_string()))
}
#[test]
fn test_formation_creation_valid() {
let p1 = create_test_paladin("P1");
let p2 = create_test_paladin("P2");
let config = BattalionConfig::new("test");
let result = Formation::new(vec![p1, p2], config);
assert!(result.is_ok());
}
#[test]
fn test_formation_validation_minimum_paladins() {
let p1 = create_test_paladin("P1");
let config = BattalionConfig::new("test");
let result = Formation::new(vec![p1], config);
assert!(result.is_err());
}
#[test]
fn test_formation_paladin_count() {
let paladins: Vec<_> = (0..5)
.map(|i| create_test_paladin(&format!("P{}", i)))
.collect();
let config = BattalionConfig::new("test");
let formation = Formation::new(paladins, config).unwrap();
assert_eq!(formation.paladin_count(), 5);
}
#[test]
fn test_formation_with_shared_context() {
let p1 = create_test_paladin("P1");
let p2 = create_test_paladin("P2");
let config = BattalionConfig::new("test");
let formation = Formation::new(vec![p1, p2], config)
.unwrap()
.with_shared_context("Test context".to_string());
assert_eq!(formation.shared_context(), Some("Test context"));
}
#[test]
fn test_formation_accessors() {
let p1 = create_test_paladin("P1");
let p2 = create_test_paladin("P2");
let config = BattalionConfig::new("test");
let formation = Formation::new(vec![p1, p2], config).unwrap();
assert_eq!(formation.paladins().len(), 2);
assert_eq!(formation.config().name, "test");
assert_eq!(formation.shared_context(), None);
}
}