use async_trait::async_trait;
use klieo_core::agent::{Agent, AgentContext};
use klieo_core::error::Error as CoreError;
use klieo_core::llm::ToolDef;
use klieo_core::test_utils::fake_context;
use klieo_flows::{
AgentFlow, Flow, FlowError, LoopFlow, LoopVerdict, ParallelFlow, SequentialFlow,
};
use serde_json::Value;
use std::sync::Arc;
struct TaggingAgent {
tag: String,
}
#[async_trait]
impl Agent for TaggingAgent {
type Input = Value;
type Output = Value;
type Error = CoreError;
fn name(&self) -> &str {
&self.tag
}
fn system_prompt(&self) -> &str {
""
}
fn tools(&self) -> &[ToolDef] {
&[]
}
async fn run(&self, _ctx: AgentContext, input: Value) -> Result<Value, CoreError> {
let mut v = input;
if let Value::Object(ref mut m) = v {
m.insert("touched_by".into(), Value::String(self.tag.clone()));
}
Ok(v)
}
}
struct EchoFlow {
name: String,
}
#[async_trait]
impl Flow for EchoFlow {
fn name(&self) -> &str {
&self.name
}
async fn run(&self, _ctx: AgentContext, input: Value) -> Result<Value, FlowError> {
Ok(input)
}
}
struct AddOneFlow;
#[async_trait]
impl Flow for AddOneFlow {
fn name(&self) -> &str {
"add_one"
}
async fn run(&self, _ctx: AgentContext, input: Value) -> Result<Value, FlowError> {
let n = input
.get("left")
.and_then(|v| v.get("n"))
.and_then(|v| v.as_u64())
.or_else(|| input.get("n").and_then(|v| v.as_u64()))
.unwrap_or(0);
Ok(serde_json::json!({ "n": n + 1 }))
}
}
fn ctx() -> AgentContext {
fake_context("integration")
}
#[tokio::test]
async fn end_to_end_sequential_parallel_loop_agent() {
let parallel = Arc::new(
ParallelFlow::new("split")
.branch("left", Arc::new(EchoFlow { name: "l".into() }))
.branch("right", Arc::new(EchoFlow { name: "r".into() })),
);
let loop_step = Arc::new(
LoopFlow::new("inc_until_one", Arc::new(AddOneFlow))
.until(|v| {
let n = v.get("n").and_then(|v| v.as_u64()).unwrap_or(0);
if n >= 1 {
LoopVerdict::Done
} else {
LoopVerdict::Continue
}
})
.with_max_iters(4),
);
let agent_step = Arc::new(AgentFlow::new(TaggingAgent {
tag: "tagger".into(),
}));
let pipe = SequentialFlow::new("pipe")
.step(parallel)
.step(loop_step)
.step(agent_step);
let out = pipe.run(ctx(), serde_json::json!({"n": 0})).await.unwrap();
assert_eq!(out, serde_json::json!({"n": 1, "touched_by": "tagger"}));
}