1use agent_line::{Agent, Ctx, Outcome, RetryHint, Runner, StepResult, Workflow};
2
3#[derive(Clone, Debug)]
4struct Doc {
5 text: String,
6 revision: u32,
7}
8
9struct Writer;
10impl Agent<Doc> for Writer {
11 fn name(&self) -> &'static str {
12 "writer"
13 }
14 fn run(&mut self, mut state: Doc, ctx: &mut Ctx) -> StepResult<Doc> {
15 state.revision += 1;
16 ctx.log(format!("writer: producing revision {}", state.revision));
17
18 if state.revision == 1 {
20 state.text = "Hello wrold! This is a dcument.".to_string();
21 } else {
22 state.text = "Hello world! This is a document.".to_string();
23 }
24
25 Ok((state, Outcome::Continue))
26 }
27}
28
29struct Validator;
30impl Agent<Doc> for Validator {
31 fn name(&self) -> &'static str {
32 "validator"
33 }
34 fn run(&mut self, state: Doc, ctx: &mut Ctx) -> StepResult<Doc> {
35 let mut errors = Vec::new();
36
37 if state.text.contains("wrold") {
38 errors.push("typo: 'wrold' should be 'world'");
39 }
40 if state.text.contains("dcument") {
41 errors.push("typo: 'dcument' should be 'document'");
42 }
43
44 if errors.is_empty() {
45 ctx.log("validator: all checks passed");
46 Ok((state, Outcome::Done))
47 } else {
48 for e in &errors {
49 ctx.log(format!("validator: {e}"));
50 }
51 Ok((state, Outcome::Next("fixer")))
52 }
53 }
54}
55
56struct Fixer {
57 retried: bool,
58}
59
60impl Agent<Doc> for Fixer {
61 fn name(&self) -> &'static str {
62 "fixer"
63 }
64 fn run(&mut self, mut state: Doc, ctx: &mut Ctx) -> StepResult<Doc> {
65 let entries: Vec<String> = ctx.logs().to_vec();
67
68 for entry in &entries {
69 if entry.contains("wrold") {
70 state.text = state.text.replace("wrold", "world");
71 ctx.log("fixer: corrected 'wrold' -> 'world'");
72 }
73 if entry.contains("dcument") {
74 state.text = state.text.replace("dcument", "document");
75 ctx.log("fixer: corrected 'dcument' -> 'document'");
76 }
77 }
78
79 if !self.retried {
80 self.retried = true;
81 ctx.log("fixer: retrying to double-check fixes");
82 Ok((state, Outcome::Retry(RetryHint::new("double-checking"))))
83 } else {
84 self.retried = false;
85 Ok((state, Outcome::Next("validator")))
86 }
87 }
88}
89
90fn main() {
91 let mut ctx = Ctx::new();
92
93 let mut runner = Runner::new(
94 Workflow::builder("edit-loop")
95 .register(Writer)
96 .register(Validator)
97 .register(Fixer { retried: false })
98 .start_at("writer")
99 .then("validator")
100 .build()
101 .unwrap(),
102 );
103
104 let mut revision = 0;
105 for round in 1..=3 {
106 println!("=== Round {round} ===");
107
108 let doc = Doc {
109 text: String::new(),
110 revision,
111 };
112
113 match runner.run(doc, &mut ctx) {
114 Ok(doc) => {
115 println!(" Final text: {:?}", doc.text);
116 println!(" Revisions: {}", doc.revision);
117 revision = doc.revision;
118 }
119 Err(e) => println!(" Error: {e}"),
120 }
121
122 println!(" Log:");
123 for entry in ctx.logs() {
124 println!(" {entry}");
125 }
126 ctx.clear_logs();
127 println!();
128 }
129}