use deepstrike_core::context::renderer::RenderedContext;
use deepstrike_core::harness::eval::{
build_eval_messages as core_build_eval_messages, parse_verdict as core_parse_verdict,
verdict_output_schema as core_verdict_output_schema, EvalResult,
};
use deepstrike_core::types::message::{Content, Message, Role};
use futures::StreamExt;
use crate::providers::{LLMProvider, StreamEvent};
use crate::Result;
pub use deepstrike_core::harness::eval::Criterion;
pub type Verdict = EvalResult;
pub fn build_eval_messages(goal: &str, criteria: &[Criterion], result: &str) -> Vec<Message> {
core_build_eval_messages(goal, criteria, result, 1, false)
}
pub fn parse_verdict(text: &str) -> Verdict {
core_parse_verdict(text)
}
pub fn verdict_output_schema() -> serde_json::Value {
core_verdict_output_schema(false)
}
pub async fn judge(
provider: &dyn LLMProvider,
goal: &str,
criteria: &[Criterion],
result: &str,
) -> Result<Verdict> {
let msgs = build_eval_messages(goal, criteria, result);
let system_text: String = msgs
.iter()
.filter(|m| m.role == Role::System)
.filter_map(|m| match &m.content {
Content::Text(s) => Some(s.clone()),
_ => None,
})
.collect::<Vec<_>>()
.join("\n\n");
let turns: Vec<Message> = msgs.into_iter().filter(|m| m.role != Role::System).collect();
let ctx = RenderedContext {
system_text,
system_stable: String::new(),
system_knowledge: String::new(),
state_turn: None,
turns,
frozen_prefix_len: None,
};
let mut text = String::new();
let mut stream = provider.stream(&ctx, &[], None, None).await?;
while let Some(evt) = stream.next().await {
if let Ok(StreamEvent::TextDelta { delta }) = evt {
text.push_str(&delta);
}
}
if text.is_empty() {
return Err(crate::Error::Other("judge: provider produced no text".to_string()));
}
Ok(parse_verdict(&text))
}