deepstrike_sdk/runtime/
eval.rs1use deepstrike_core::context::renderer::RenderedContext;
9use deepstrike_core::harness::eval::{
10 build_eval_messages as core_build_eval_messages, parse_verdict as core_parse_verdict,
11 verdict_output_schema as core_verdict_output_schema, EvalResult,
12};
13use deepstrike_core::types::message::{Content, Message, Role};
14use futures::StreamExt;
15
16use crate::providers::{LLMProvider, StreamEvent};
17use crate::Result;
18
19pub use deepstrike_core::harness::eval::Criterion;
21
22pub type Verdict = EvalResult;
24
25pub fn build_eval_messages(goal: &str, criteria: &[Criterion], result: &str) -> Vec<Message> {
27 core_build_eval_messages(goal, criteria, result, 1, false)
28}
29
30pub fn parse_verdict(text: &str) -> Verdict {
32 core_parse_verdict(text)
33}
34
35pub fn verdict_output_schema() -> serde_json::Value {
37 core_verdict_output_schema(false)
38}
39
40pub async fn judge(
43 provider: &dyn LLMProvider,
44 goal: &str,
45 criteria: &[Criterion],
46 result: &str,
47) -> Result<Verdict> {
48 let msgs = build_eval_messages(goal, criteria, result);
49 let system_text: String = msgs
50 .iter()
51 .filter(|m| m.role == Role::System)
52 .filter_map(|m| match &m.content {
53 Content::Text(s) => Some(s.clone()),
54 _ => None,
55 })
56 .collect::<Vec<_>>()
57 .join("\n\n");
58 let turns: Vec<Message> = msgs.into_iter().filter(|m| m.role != Role::System).collect();
59 let ctx = RenderedContext {
60 system_text,
61 system_stable: String::new(),
62 system_knowledge: String::new(),
63 state_turn: None,
64 turns,
65 frozen_prefix_len: None,
66 };
67
68 let mut text = String::new();
69 let mut stream = provider.stream(&ctx, &[], None, None).await?;
70 while let Some(evt) = stream.next().await {
71 if let Ok(StreamEvent::TextDelta { delta }) = evt {
72 text.push_str(&delta);
73 }
74 }
75 if text.is_empty() {
76 return Err(crate::Error::Other("judge: provider produced no text".to_string()));
77 }
78 Ok(parse_verdict(&text))
79}