use crate::agents::run_context::AgentRunContext;
use std::fmt::Debug;
pub trait CompletionCriteria: Send + Sync + Debug {
fn is_complete(&self, ctx: &AgentRunContext) -> bool;
fn completion_reason(&self, _ctx: &AgentRunContext) -> String {
"criteria met".into()
}
}
#[derive(Debug)]
pub struct MaxIterationsCriteria {
pub max: u32,
}
impl MaxIterationsCriteria {
pub fn new(max: u32) -> Self {
Self { max }
}
}
impl CompletionCriteria for MaxIterationsCriteria {
fn is_complete(&self, ctx: &AgentRunContext) -> bool {
ctx.iteration >= self.max
}
fn completion_reason(&self, _ctx: &AgentRunContext) -> String {
format!("reached max iterations ({})", self.max)
}
}
#[derive(Debug)]
pub struct KeywordCriteria {
pub keyword: String,
}
impl KeywordCriteria {
pub fn new(keyword: impl Into<String>) -> Self {
Self {
keyword: keyword.into(),
}
}
}
impl CompletionCriteria for KeywordCriteria {
fn is_complete(&self, ctx: &AgentRunContext) -> bool {
ctx.last_response()
.map(|r| r.contains(&self.keyword))
.unwrap_or(false)
}
fn completion_reason(&self, _ctx: &AgentRunContext) -> String {
format!("keyword '{}' found in response", self.keyword)
}
}
#[derive(Debug)]
pub struct StructuredOutputCriteria {
pub schema: Option<serde_json::Value>,
}
impl StructuredOutputCriteria {
pub fn new(schema: Option<serde_json::Value>) -> Self {
Self { schema }
}
}
impl CompletionCriteria for StructuredOutputCriteria {
fn is_complete(&self, ctx: &AgentRunContext) -> bool {
let Some(last) = ctx.last_response() else {
return false;
};
let Ok(parsed) = serde_json::from_str::<serde_json::Value>(last) else {
return false;
};
if let Some(schema) = &self.schema {
return jsonschema::is_valid(schema, &parsed);
}
true
}
fn completion_reason(&self, _ctx: &AgentRunContext) -> String {
"structured output received".into()
}
}