pub mod agent_trace_metrics;
pub mod builtin_scorer;
pub mod deterministic;
pub mod llm_judge;
pub mod normalized;
pub mod trace;
pub mod trace_eval_metrics;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScorerInput {
pub output: Value,
pub expected: Option<Value>,
pub input: Option<Value>,
pub trace: Option<Vec<TraceEvent>>,
}
impl ScorerInput {
pub fn new(output: Value) -> Self {
Self {
output,
expected: None,
input: None,
trace: None,
}
}
pub fn with_expected(mut self, expected: Value) -> Self {
self.expected = Some(expected);
self
}
pub fn with_input(mut self, input: Value) -> Self {
self.input = Some(input);
self
}
pub fn with_trace(mut self, trace: Vec<TraceEvent>) -> Self {
self.trace = Some(trace);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScorerResult {
pub score: f64,
pub passed: Option<bool>,
pub label: Option<String>,
pub explanation: Option<String>,
pub metadata: Option<Value>,
}
impl ScorerResult {
pub fn pass() -> Self {
Self {
score: 1.0,
passed: Some(true),
label: Some("pass".into()),
explanation: None,
metadata: None,
}
}
pub fn fail() -> Self {
Self {
score: 0.0,
passed: Some(false),
label: Some("fail".into()),
explanation: None,
metadata: None,
}
}
pub fn with_score(score: f64) -> Self {
Self {
score: score.clamp(0.0, 1.0),
passed: Some(score >= 0.5),
label: None,
explanation: None,
metadata: None,
}
}
pub fn passed(mut self, passed: bool) -> Self {
self.passed = Some(passed);
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn explanation(mut self, explanation: impl Into<String>) -> Self {
self.explanation = Some(explanation.into());
self
}
pub fn metadata(mut self, metadata: Value) -> Self {
self.metadata = Some(metadata);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceEvent {
pub event_type: String,
pub event_id: String,
pub correlation_id: String,
pub parent_correlation_id: Option<String>,
pub timestamp_ns: i64,
pub data: Value,
pub name: Option<String>,
}
impl TraceEvent {
pub fn new(event_type: impl Into<String>, event_id: impl Into<String>) -> Self {
Self {
event_type: event_type.into(),
event_id: event_id.into(),
correlation_id: String::new(),
parent_correlation_id: None,
timestamp_ns: 0,
data: Value::Object(Default::default()),
name: None,
}
}
pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
self.correlation_id = id.into();
self
}
pub fn parent_correlation_id(mut self, id: impl Into<String>) -> Self {
self.parent_correlation_id = Some(id.into());
self
}
pub fn timestamp_ns(mut self, ts: i64) -> Self {
self.timestamp_ns = ts;
self
}
pub fn data(mut self, data: Value) -> Self {
self.data = data;
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
}
pub use deterministic::{
contains, exact_match, json_schema, json_valid, levenshtein, numeric_range, regex_match,
ContainsConfig, ExactMatchConfig, JsonSchemaConfig, LevenshteinConfig, NumericRangeConfig,
RegexConfig,
};
pub use llm_judge::{llm_judge, LlmJudgeConfig};
pub use normalized::{
NormalizedHandoff, NormalizedLlmCall, NormalizedSession, NormalizedSessionSummary,
NormalizedSpan, NormalizedToolCall, NormalizedTraceError, RedactionPolicySnapshot,
TraceArtifactManifest, NORMALIZED_SESSION_SCHEMA, NORMALIZED_SPAN_SCHEMA,
TRACE_ARTIFACT_MANIFEST_SCHEMA, TRACE_EVAL_CONTEXT_SCHEMA,
};
pub use trace::{trace_score, TraceAssertion};
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_scorer_input_builder() {
let input = ScorerInput::new(json!("hello"))
.with_expected(json!("hello"))
.with_input(json!({"query": "test"}));
assert_eq!(input.output, json!("hello"));
assert_eq!(input.expected, Some(json!("hello")));
assert_eq!(input.input, Some(json!({"query": "test"})));
assert!(input.trace.is_none());
}
#[test]
fn test_scorer_result_builder() {
let result = ScorerResult::with_score(0.8)
.passed(true)
.label("good")
.explanation("High similarity");
assert_eq!(result.score, 0.8);
assert_eq!(result.passed, Some(true));
assert_eq!(result.label, Some("good".into()));
assert_eq!(result.explanation, Some("High similarity".into()));
}
#[test]
fn test_trace_event_builder() {
let event = TraceEvent::new("lm.completed", "event-1")
.correlation_id("corr-1")
.timestamp_ns(1000000)
.data(json!({"total_tokens": 500}))
.name("chat");
assert_eq!(event.event_type, "lm.completed");
assert_eq!(event.event_id, "event-1");
assert_eq!(event.correlation_id, "corr-1");
assert_eq!(event.timestamp_ns, 1000000);
assert_eq!(event.name, Some("chat".into()));
}
}
pub use agnt5_eval_scorers::structured_assertions;