use std::sync::Arc;
use async_trait::async_trait;
use super::scorer::{BinaryVerdict, Judge, judge_via_provider};
use crate::brain::provider::Provider;
pub struct LiveJudge {
provider: Arc<dyn Provider>,
model: String,
}
impl LiveJudge {
pub fn new(provider: Arc<dyn Provider>, model: impl Into<String>) -> Self {
Self {
provider,
model: model.into(),
}
}
}
#[async_trait]
impl Judge for LiveJudge {
async fn judge(&self, question: &str, artifact: &str) -> BinaryVerdict {
judge_via_provider(self.provider.as_ref(), &self.model, question, artifact).await
}
}
pub struct PanelJudge {
members: Vec<Box<dyn Judge>>,
threshold: f64,
}
impl PanelJudge {
pub fn new(members: Vec<Box<dyn Judge>>) -> Self {
Self {
members,
threshold: 0.5,
}
}
pub fn with_threshold(mut self, threshold: f64) -> Self {
self.threshold = threshold;
self
}
pub fn len(&self) -> usize {
self.members.len()
}
pub fn is_empty(&self) -> bool {
self.members.is_empty()
}
}
#[async_trait]
impl Judge for PanelJudge {
async fn judge(&self, question: &str, artifact: &str) -> BinaryVerdict {
if self.members.is_empty() {
return BinaryVerdict {
yes: false,
explanation: Some("empty judge panel".to_string()),
};
}
let mut yes = 0usize;
for member in &self.members {
if member.judge(question, artifact).await.yes {
yes += 1;
}
}
let total = self.members.len();
let fraction = yes as f64 / total as f64;
BinaryVerdict {
yes: fraction > self.threshold,
explanation: Some(format!("{yes}/{total} judges said YES")),
}
}
}
pub fn panel_from_providers(providers: &[Arc<dyn Provider>]) -> PanelJudge {
let members: Vec<Box<dyn Judge>> = providers
.iter()
.map(|p| {
let model = p.default_model().to_string();
Box::new(LiveJudge::new(p.clone(), model)) as Box<dyn Judge>
})
.collect();
PanelJudge::new(members)
}