Skip to main content

Ctx

Struct Ctx 

Source
pub struct Ctx { /* private fields */ }
Expand description

Execution context for agents (services, config, etc.)

Implementations§

Source§

impl Ctx

Source

pub fn new() -> Self

Create a new context. Configuration is read from environment variables (see the crate-level docs for the full list).

Examples found in repository?
examples/hello_world.rs (line 22)
21fn main() {
22    let mut ctx = Ctx::new();
23    let mut agent = AddOne;
24    let (state, out) = agent.run(State { n: 1 }, &mut ctx).unwrap();
25    println!("state.n={} outcome={out:?}", state.n);
26}
More examples
Hide additional examples
examples/workflow.rs (line 33)
32fn main() {
33    let mut ctx = Ctx::new();
34    let wf = Workflow::builder("demo")
35        .register(AddOne)
36        .register(StopAtThree)
37        .start_at("add_one")
38        .then("stop")
39        .build()
40        .unwrap();
41
42    let final_state = Runner::new(wf).run(State { n: 0 }, &mut ctx).unwrap();
43    println!("final n={}", final_state.n);
44}
examples/assistant.rs (line 117)
116fn main() {
117    let mut ctx = Ctx::new();
118
119    let wf = Workflow::builder("daily-briefing")
120        .register(FetchWeather)
121        .register(FetchCalendar)
122        .register(FetchEmail)
123        .register(Summarize)
124        .start_at("fetch_weather")
125        .then("fetch_calendar")
126        .then("fetch_email")
127        .then("summarize")
128        .build()
129        .unwrap();
130
131    let mut runner = Runner::new(wf).with_tracing();
132
133    let mut iteration = 0;
134    loop {
135        iteration += 1;
136        println!("=== Briefing #{iteration} ===\n");
137
138        match runner.run(BriefingState::new(), &mut ctx) {
139            Ok(state) => {
140                println!("{}\n", state.summary);
141            }
142            Err(e) => {
143                eprintln!("Briefing failed: {e}\n");
144            }
145        }
146
147        ctx.clear_logs();
148
149        println!("(sleeping 30s before next briefing...)\n");
150        thread::sleep(Duration::from_secs(30));
151    }
152}
examples/edit_loop.rs (line 91)
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}
examples/coder.rs (line 168)
161fn main() {
162    let tmp = std::env::temp_dir().join("agent-line-coder");
163    scaffold_project(&tmp);
164
165    let lib_path = tmp.join("src/lib.rs").display().to_string();
166    let manifest = tmp.join("Cargo.toml").display().to_string();
167
168    let mut ctx = Ctx::new();
169    ctx.set("manifest_path", &manifest);
170
171    let wf = Workflow::builder("coding-agent")
172        .register(Planner)
173        .register(Coder)
174        .register(Tester)
175        .start_at("planner")
176        .then("coder")
177        .then("tester")
178        .build()
179        .unwrap();
180
181    let mut runner = Runner::new(wf);
182
183    let result = runner.run(
184        Task {
185            description: "Add a function called `reverse_string` that reverses a string \
186                          and add unit tests"
187                .into(),
188            file_path: lib_path,
189            code: String::new(),
190            test_output: String::new(),
191            attempts: 0,
192            max_attempts: 3,
193        },
194        &mut ctx,
195    );
196
197    println!("=== Result ===");
198    match result {
199        Ok(task) => {
200            println!("  Success after {} fix attempts", task.attempts);
201            println!("  Final code:\n{}", task.code);
202        }
203        Err(e) => println!("  Failed: {e}"),
204    }
205
206    println!("\n=== Log ===");
207    for entry in ctx.logs() {
208        println!("  {entry}");
209    }
210}
examples/newsletter.rs (line 156)
155fn main() {
156    let mut ctx = Ctx::new();
157
158    // could populate ctx.store with some writing rules or could read in a markdown skill and pass
159    // that into the agent.
160    // Phase 1: find topics
161    let topic_wf = Workflow::builder("find-topics")
162        .register(TopicSearcher)
163        .register(TopicPicker)
164        .start_at("topic_searcher")
165        .then("topic_picker")
166        .build()
167        .unwrap();
168
169    let mut topic_runner = Runner::new(topic_wf);
170    let topics = topic_runner
171        .run(
172            TopicState {
173                query: "bluecollar engineering newsletter".into(),
174                topics: vec![],
175                selected: vec![],
176            },
177            &mut ctx,
178        )
179        .unwrap();
180
181    println!("=== Topics ===");
182    for entry in ctx.logs() {
183        println!("  {entry}");
184    }
185    ctx.clear_logs();
186    println!();
187
188    // Phase 2: write one article per topic
189    let article_wf = Workflow::builder("write-article")
190        .register(ArticleWriter)
191        .register(ArticleValidator)
192        .register(ArticleFixer)
193        .start_at("article_writer")
194        .then("article_validator")
195        .build()
196        .unwrap();
197
198    let mut article_runner = Runner::new(article_wf);
199    let mut finished_articles: Vec<String> = Vec::new();
200
201    for (i, topic) in topics.selected.iter().enumerate() {
202        println!("=== Article {} ===", i + 1);
203
204        let result = article_runner
205            .run(
206                ArticleState {
207                    topic: topic.clone(),
208                    draft: String::new(),
209                    revision: 0,
210                },
211                &mut ctx,
212            )
213            .unwrap();
214
215        finished_articles.push(result.draft.clone());
216        println!("  Revisions: {}", result.revision);
217
218        for entry in ctx.logs() {
219            println!("  {entry}");
220        }
221        ctx.clear_logs();
222        println!();
223    }
224
225    // Phase 3: "store" the articles
226    println!("=== Stored ===");
227    for (i, article) in finished_articles.iter().enumerate() {
228        let preview: String = article.chars().take(60).collect();
229        println!("  article_{}.md: {preview}...", i + 1);
230    }
231}
Source

pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>)

Insert or overwrite a key in the KV store.

Examples found in repository?
examples/newsletter.rs (line 127)
115    fn run(&mut self, state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
116        // use store k/v to pull in validation rules and pass them to the llm
117        let response = ctx
118            .llm()
119            .system("You are a strict editor. List any errors. Say PASS if none.")
120            .user(&state.draft)
121            .send()?;
122
123        // ctx,store get rules or previous work?
124        if response.contains("PASS") {
125            Ok((state, Outcome::Done))
126        } else {
127            ctx.set("errors", &response);
128            Ok((state, Outcome::Next("article_fixer")))
129        }
130    }
More examples
Hide additional examples
examples/coder.rs (line 59)
37    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
38        state.code = tools::read_file(&state.file_path).unwrap_or_default();
39
40        let plan = ctx
41            .llm()
42            .system(
43                "You are a senior developer. Create a brief implementation plan. \
44                 List the specific changes needed. Be concise. \
45                 Do not include doc comments or doc tests.",
46            )
47            .user(format!(
48                "Task: {}\n\nFile: {}\n\nCurrent code:\n{}",
49                state.description,
50                state.file_path,
51                if state.code.is_empty() {
52                    "(new file)".to_string()
53                } else {
54                    state.code.clone()
55                }
56            ))
57            .send()?;
58
59        ctx.set("plan", &plan);
60        ctx.log(format!("planner: created plan for {}", state.file_path));
61        Ok((state, Outcome::Continue))
62    }
63}
64
65struct Coder;
66impl Agent<Task> for Coder {
67    fn name(&self) -> &'static str {
68        "coder"
69    }
70    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
71        let is_fix = !state.test_output.is_empty();
72
73        let response = if is_fix {
74            ctx.log(format!("coder: fixing (attempt {})", state.attempts));
75
76            ctx.llm()
77                .system(
78                    "You are a developer. Fix the code based on the test failures. \
79                     Return ONLY the complete fixed file contents, no explanation. \
80                     Do not include doc comments or doc tests. \
81                     Do not wrap the output in markdown code fences.",
82                )
83                .user(format!(
84                    "Test errors:\n{}\n\nCurrent code:\n{}",
85                    state.test_output, state.code
86                ))
87                .send()?
88        } else {
89            let plan = ctx.get("plan").unwrap_or("no plan found").to_string();
90            ctx.log("coder: writing initial code");
91
92            ctx.llm()
93                .system(
94                    "You are a developer. Write the code based on the plan. \
95                     Return ONLY the complete file contents, no explanation. \
96                     Do not include doc comments or doc tests. \
97                     Do not wrap the output in markdown code fences.",
98                )
99                .user(format!(
100                    "Plan:\n{plan}\n\nFile: {}\n\nCurrent code:\n{}",
101                    state.file_path, state.code
102                ))
103                .send()?
104        };
105
106        state.code = tools::strip_code_fences(&response);
107        state.test_output.clear();
108        tools::write_file(&state.file_path, &state.code)?;
109        Ok((state, Outcome::Continue))
110    }
111}
112
113struct Tester;
114impl Agent<Task> for Tester {
115    fn name(&self) -> &'static str {
116        "tester"
117    }
118    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
119        let manifest = ctx.get("manifest_path").unwrap_or("Cargo.toml").to_string();
120        let result = tools::run_cmd(&format!("cargo test --manifest-path {manifest} --lib"))?;
121
122        if result.success {
123            ctx.log("tester: all passed");
124            Ok((state, Outcome::Done))
125        } else {
126            state.test_output = result.stderr;
127            state.attempts += 1;
128            ctx.log(format!("tester: failed (attempt {})", state.attempts));
129
130            if state.attempts >= state.max_attempts {
131                Ok((
132                    state,
133                    Outcome::Fail("max fix attempts reached, tests still failing".into()),
134                ))
135            } else {
136                Ok((state, Outcome::Next("coder")))
137            }
138        }
139    }
140}
141
142// ---------------------------------------------------------------------------
143// Helpers
144// ---------------------------------------------------------------------------
145
146fn scaffold_project(dir: &std::path::Path) {
147    let src = dir.join("src");
148    std::fs::create_dir_all(&src).unwrap();
149    std::fs::write(
150        dir.join("Cargo.toml"),
151        "[package]\nname = \"scratch\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
152    )
153    .unwrap();
154    std::fs::write(src.join("lib.rs"), "").unwrap();
155}
156
157// ---------------------------------------------------------------------------
158// Main
159// ---------------------------------------------------------------------------
160
161fn main() {
162    let tmp = std::env::temp_dir().join("agent-line-coder");
163    scaffold_project(&tmp);
164
165    let lib_path = tmp.join("src/lib.rs").display().to_string();
166    let manifest = tmp.join("Cargo.toml").display().to_string();
167
168    let mut ctx = Ctx::new();
169    ctx.set("manifest_path", &manifest);
170
171    let wf = Workflow::builder("coding-agent")
172        .register(Planner)
173        .register(Coder)
174        .register(Tester)
175        .start_at("planner")
176        .then("coder")
177        .then("tester")
178        .build()
179        .unwrap();
180
181    let mut runner = Runner::new(wf);
182
183    let result = runner.run(
184        Task {
185            description: "Add a function called `reverse_string` that reverses a string \
186                          and add unit tests"
187                .into(),
188            file_path: lib_path,
189            code: String::new(),
190            test_output: String::new(),
191            attempts: 0,
192            max_attempts: 3,
193        },
194        &mut ctx,
195    );
196
197    println!("=== Result ===");
198    match result {
199        Ok(task) => {
200            println!("  Success after {} fix attempts", task.attempts);
201            println!("  Final code:\n{}", task.code);
202        }
203        Err(e) => println!("  Failed: {e}"),
204    }
205
206    println!("\n=== Log ===");
207    for entry in ctx.logs() {
208        println!("  {entry}");
209    }
210}
Source

pub fn get(&self, key: &str) -> Option<&str>

Look up a key in the KV store.

Examples found in repository?
examples/newsletter.rs (line 139)
138    fn run(&mut self, mut state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
139        let errors = ctx.get("errors").unwrap_or("no errors found").to_string();
140        let response = ctx
141            .llm()
142            .system("You are a writer. Rewrite the article fixing only the listed errors.")
143            .user(format!("Errors:\n{errors}\n\nArticle:\n{}", state.draft))
144            .send()?;
145
146        state.draft = response;
147        Ok((state, Outcome::Next("article_validator")))
148    }
More examples
Hide additional examples
examples/coder.rs (line 89)
70    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
71        let is_fix = !state.test_output.is_empty();
72
73        let response = if is_fix {
74            ctx.log(format!("coder: fixing (attempt {})", state.attempts));
75
76            ctx.llm()
77                .system(
78                    "You are a developer. Fix the code based on the test failures. \
79                     Return ONLY the complete fixed file contents, no explanation. \
80                     Do not include doc comments or doc tests. \
81                     Do not wrap the output in markdown code fences.",
82                )
83                .user(format!(
84                    "Test errors:\n{}\n\nCurrent code:\n{}",
85                    state.test_output, state.code
86                ))
87                .send()?
88        } else {
89            let plan = ctx.get("plan").unwrap_or("no plan found").to_string();
90            ctx.log("coder: writing initial code");
91
92            ctx.llm()
93                .system(
94                    "You are a developer. Write the code based on the plan. \
95                     Return ONLY the complete file contents, no explanation. \
96                     Do not include doc comments or doc tests. \
97                     Do not wrap the output in markdown code fences.",
98                )
99                .user(format!(
100                    "Plan:\n{plan}\n\nFile: {}\n\nCurrent code:\n{}",
101                    state.file_path, state.code
102                ))
103                .send()?
104        };
105
106        state.code = tools::strip_code_fences(&response);
107        state.test_output.clear();
108        tools::write_file(&state.file_path, &state.code)?;
109        Ok((state, Outcome::Continue))
110    }
111}
112
113struct Tester;
114impl Agent<Task> for Tester {
115    fn name(&self) -> &'static str {
116        "tester"
117    }
118    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
119        let manifest = ctx.get("manifest_path").unwrap_or("Cargo.toml").to_string();
120        let result = tools::run_cmd(&format!("cargo test --manifest-path {manifest} --lib"))?;
121
122        if result.success {
123            ctx.log("tester: all passed");
124            Ok((state, Outcome::Done))
125        } else {
126            state.test_output = result.stderr;
127            state.attempts += 1;
128            ctx.log(format!("tester: failed (attempt {})", state.attempts));
129
130            if state.attempts >= state.max_attempts {
131                Ok((
132                    state,
133                    Outcome::Fail("max fix attempts reached, tests still failing".into()),
134                ))
135            } else {
136                Ok((state, Outcome::Next("coder")))
137            }
138        }
139    }
Source

pub fn remove(&mut self, key: &str) -> Option<String>

Remove a key from the KV store, returning its value if it existed.

Source

pub fn log(&mut self, msg: impl Into<String>)

Append a message to the event log.

Examples found in repository?
examples/assistant.rs (line 38)
37    fn run(&mut self, mut state: BriefingState, ctx: &mut Ctx) -> StepResult<BriefingState> {
38        ctx.log("fetching weather data");
39
40        // Stub: in a real app, call a weather API via tools::http
41        state.weather = "72F and sunny, high of 78F. Light breeze from the southwest.".into();
42
43        Ok((state, Outcome::Continue))
44    }
45}
46
47struct FetchCalendar;
48impl Agent<BriefingState> for FetchCalendar {
49    fn name(&self) -> &'static str {
50        "fetch_calendar"
51    }
52    fn run(&mut self, mut state: BriefingState, ctx: &mut Ctx) -> StepResult<BriefingState> {
53        ctx.log("fetching calendar events");
54
55        state.calendar = "\
56            9:00 AM - Standup with the team\n\
57            11:00 AM - Design review\n\
58            1:00 PM - Lunch with Sarah\n\
59            3:00 PM - Sprint planning"
60            .into();
61
62        Ok((state, Outcome::Continue))
63    }
64}
65
66struct FetchEmail;
67impl Agent<BriefingState> for FetchEmail {
68    fn name(&self) -> &'static str {
69        "fetch_email"
70    }
71    fn run(&mut self, mut state: BriefingState, ctx: &mut Ctx) -> StepResult<BriefingState> {
72        ctx.log("fetching email summaries");
73
74        state.emails = "\
75            - AWS billing alert: $42.17 for March (within budget)\n\
76            - PR #187 approved by Jake, ready to merge\n\
77            - Newsletter from Rust Weekly: edition #412"
78            .into();
79
80        Ok((state, Outcome::Continue))
81    }
82}
83
84struct Summarize;
85impl Agent<BriefingState> for Summarize {
86    fn name(&self) -> &'static str {
87        "summarize"
88    }
89    fn run(&mut self, mut state: BriefingState, ctx: &mut Ctx) -> StepResult<BriefingState> {
90        ctx.log("generating daily briefing via LLM");
91
92        let prompt = format!(
93            "Weather:\n{}\n\nCalendar:\n{}\n\nEmails:\n{}",
94            state.weather, state.calendar, state.emails
95        );
96
97        let response = ctx
98            .llm()
99            .system(
100                "You are a personal assistant. Produce a concise daily briefing \
101                 from the provided weather, calendar, and email data. \
102                 Keep it under 200 words. Use plain text, no markdown.",
103            )
104            .user(prompt)
105            .send()?;
106
107        state.summary = response;
108        Ok((state, Outcome::Done))
109    }
More examples
Hide additional examples
examples/edit_loop.rs (line 16)
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        // Simulate: first draft has typos, second is clean.
19        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        // Collect logs first, then clear. Reading logs() borrows &self,
66        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    }
examples/newsletter.rs (line 31)
30    fn run(&mut self, mut state: TopicState, ctx: &mut Ctx) -> StepResult<TopicState> {
31        ctx.log(format!("searching for: {}", state.query));
32
33        // Stub: pretend we did a web search
34        state.topics = vec![
35            "Rust in embedded systems".into(),
36            "Why plumbers love side projects".into(),
37            "Welding meets software: CNC pipelines".into(),
38            "HVAC technicians automating schedules".into(),
39            "Electricians using Raspberry Pi on the job".into(),
40        ];
41
42        ctx.log(format!("found {} topics", state.topics.len()));
43        Ok((state, Outcome::Continue))
44    }
45}
46
47struct TopicPicker;
48impl Agent<TopicState> for TopicPicker {
49    fn name(&self) -> &'static str {
50        "topic_picker"
51    }
52    fn run(&mut self, mut state: TopicState, ctx: &mut Ctx) -> StepResult<TopicState> {
53        let response = ctx.llm()
54            .system("You are a newsletter curator. Pick exactly 3 topics. Return one per line, nothing else.")
55            .user(format!("Choose from:\n{}", state.topics.join("\n")))
56            .send()?;
57
58        // brittle, but should pick out the 3
59        state.selected = response
60            .lines()
61            .map(|l| l.trim())
62            .map(|l| {
63                l.trim_start_matches(|c: char| c.is_numeric() || c == '.' || c == '-' || c == ' ')
64            })
65            .map(|l| l.trim().to_string())
66            .filter(|l| !l.is_empty())
67            .collect();
68
69        for topic in &state.selected {
70            ctx.log(format!("selected: {topic}"));
71        }
72
73        Ok((state, Outcome::Done))
74    }
75}
76
77// ---------------------------------------------------------------------------
78// Phase 2 agents: write, validate, fix one article
79// ---------------------------------------------------------------------------
80
81struct ArticleWriter;
82impl Agent<ArticleState> for ArticleWriter {
83    fn name(&self) -> &'static str {
84        "article_writer"
85    }
86    fn run(&mut self, mut state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
87        state.revision += 1;
88        ctx.log(format!(
89            "writing draft {} for: {}",
90            state.revision, state.topic
91        ));
92
93        // Stub: first draft is sloppy, second is clean
94        if state.revision == 1 {
95            state.draft = format!(
96                "# {}\n\nThis is a artcle about {}. It has lots of good infomation.",
97                state.topic, state.topic
98            );
99        } else {
100            state.draft = format!(
101                "# {}\n\nThis is an article about {}. It has lots of good information.",
102                state.topic, state.topic
103            );
104        }
105
106        Ok((state, Outcome::Continue))
107    }
examples/coder.rs (line 60)
37    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
38        state.code = tools::read_file(&state.file_path).unwrap_or_default();
39
40        let plan = ctx
41            .llm()
42            .system(
43                "You are a senior developer. Create a brief implementation plan. \
44                 List the specific changes needed. Be concise. \
45                 Do not include doc comments or doc tests.",
46            )
47            .user(format!(
48                "Task: {}\n\nFile: {}\n\nCurrent code:\n{}",
49                state.description,
50                state.file_path,
51                if state.code.is_empty() {
52                    "(new file)".to_string()
53                } else {
54                    state.code.clone()
55                }
56            ))
57            .send()?;
58
59        ctx.set("plan", &plan);
60        ctx.log(format!("planner: created plan for {}", state.file_path));
61        Ok((state, Outcome::Continue))
62    }
63}
64
65struct Coder;
66impl Agent<Task> for Coder {
67    fn name(&self) -> &'static str {
68        "coder"
69    }
70    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
71        let is_fix = !state.test_output.is_empty();
72
73        let response = if is_fix {
74            ctx.log(format!("coder: fixing (attempt {})", state.attempts));
75
76            ctx.llm()
77                .system(
78                    "You are a developer. Fix the code based on the test failures. \
79                     Return ONLY the complete fixed file contents, no explanation. \
80                     Do not include doc comments or doc tests. \
81                     Do not wrap the output in markdown code fences.",
82                )
83                .user(format!(
84                    "Test errors:\n{}\n\nCurrent code:\n{}",
85                    state.test_output, state.code
86                ))
87                .send()?
88        } else {
89            let plan = ctx.get("plan").unwrap_or("no plan found").to_string();
90            ctx.log("coder: writing initial code");
91
92            ctx.llm()
93                .system(
94                    "You are a developer. Write the code based on the plan. \
95                     Return ONLY the complete file contents, no explanation. \
96                     Do not include doc comments or doc tests. \
97                     Do not wrap the output in markdown code fences.",
98                )
99                .user(format!(
100                    "Plan:\n{plan}\n\nFile: {}\n\nCurrent code:\n{}",
101                    state.file_path, state.code
102                ))
103                .send()?
104        };
105
106        state.code = tools::strip_code_fences(&response);
107        state.test_output.clear();
108        tools::write_file(&state.file_path, &state.code)?;
109        Ok((state, Outcome::Continue))
110    }
111}
112
113struct Tester;
114impl Agent<Task> for Tester {
115    fn name(&self) -> &'static str {
116        "tester"
117    }
118    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
119        let manifest = ctx.get("manifest_path").unwrap_or("Cargo.toml").to_string();
120        let result = tools::run_cmd(&format!("cargo test --manifest-path {manifest} --lib"))?;
121
122        if result.success {
123            ctx.log("tester: all passed");
124            Ok((state, Outcome::Done))
125        } else {
126            state.test_output = result.stderr;
127            state.attempts += 1;
128            ctx.log(format!("tester: failed (attempt {})", state.attempts));
129
130            if state.attempts >= state.max_attempts {
131                Ok((
132                    state,
133                    Outcome::Fail("max fix attempts reached, tests still failing".into()),
134                ))
135            } else {
136                Ok((state, Outcome::Next("coder")))
137            }
138        }
139    }
examples/parallel.rs (line 57)
56    fn run(&mut self, mut state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
57        ctx.log(format!("researching: {}", state.topic));
58
59        // Stub: pretend we did a web search and summarized the results
60        state.research = match state.topic.as_str() {
61            t if t.contains("embedded") => {
62                "Rust's ownership model prevents memory bugs common in C firmware. \
63                 The embassy framework provides async on bare metal. \
64                 Companies like Espressif ship official Rust support for ESP32."
65                    .into()
66            }
67            t if t.contains("plumber") => {
68                "Side projects help tradespeople automate billing and scheduling. \
69                 Low-code tools like AppSheet let plumbers build apps without coding. \
70                 One plumber built a leak-detection IoT sensor with a Raspberry Pi."
71                    .into()
72            }
73            _ => "Raspberry Pi runs Node-RED for home automation wiring diagrams. \
74                 Electricians use it to monitor panel loads in real time. \
75                 The $35 price point makes it practical for small shops."
76                .into(),
77        };
78
79        Ok((state, Outcome::Continue))
80    }
81}
82
83/// Writes (or rewrites) the article draft.
84///
85/// On the first pass, the system prompt is "write from scratch." When the
86/// editor has sent it back with feedback, the system prompt becomes "rewrite
87/// incorporating this feedback" so the LLM understands it is revising, not
88/// starting over.
89struct Writer;
90impl Agent<ArticleState> for Writer {
91    fn name(&self) -> &'static str {
92        "writer"
93    }
94    fn run(&mut self, mut state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
95        state.revision += 1;
96
97        let is_rewrite = !state.feedback.is_empty();
98
99        if is_rewrite {
100            ctx.log(format!(
101                "rewriting draft {} for: {} (feedback: {})",
102                state.revision, state.topic, state.feedback
103            ));
104
105            // Stub for rewrite pass
106            // In a real app:
107            // let response = ctx.llm()
108            //     .system(&format!(
109            //         "You are a writer. Rewrite this article incorporating the editor's feedback.\n\
110            //          Guidelines: {}\n\
111            //          Feedback: {}",
112            //         state.guidelines, state.feedback
113            //     ))
114            //     .user(&state.draft)
115            //     .send()?;
116            // state.draft = response;
117
118            state.draft = format!(
119                "# {}\n\n\
120                 Ever wonder how {} is changing the trades?\n\n\
121                 {}",
122                state.topic,
123                state.topic.to_lowercase(),
124                state.research,
125            );
126            state.feedback.clear();
127        } else {
128            ctx.log(format!(
129                "writing draft {} for: {}",
130                state.revision, state.topic
131            ));
132
133            // Stub for first draft
134            // In a real app:
135            // let response = ctx.llm()
136            //     .system(&format!(
137            //         "You are a writer. Write a short article based on the research notes.\n\
138            //          Guidelines: {}",
139            //         state.guidelines
140            //     ))
141            //     .user(&format!("Topic: {}\n\nResearch:\n{}", state.topic, state.research))
142            //     .send()?;
143            // state.draft = response;
144
145            state.draft = format!(
146                "# {}\n\n\
147                 {} is a interesting topic that many people are talking about.\n\n\
148                 {}",
149                state.topic, state.topic, state.research,
150            );
151        }
152
153        Ok((state, Outcome::Continue))
154    }
155}
156
157/// Reviews the draft against the writing guidelines and the author's voice.
158/// Approves or sends it back to the writer with specific feedback.
159struct Editor;
160impl Agent<ArticleState> for Editor {
161    fn name(&self) -> &'static str {
162        "editor"
163    }
164    fn run(&mut self, mut state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
165        ctx.log(format!("reviewing rev {}: {}", state.revision, state.topic));
166
167        // Stub: check the draft against guidelines
168        // In a real app:
169        // let response = ctx.llm()
170        //     .system(&format!(
171        //         "You are an editor. Review this article against the guidelines.\n\
172        //          Guidelines: {}\n\n\
173        //          If the article passes, respond with exactly: APPROVED\n\
174        //          Otherwise list the specific changes needed.",
175        //         state.guidelines
176        //     ))
177        //     .user(&state.draft)
178        //     .send()?;
179        //
180        // if response.contains("APPROVED") { ... } else { state.feedback = response; }
181
182        // Stub logic: first draft always needs work, second passes
183        if state.revision < 2 {
184            state.feedback =
185                "opening is bland, needs a hook. 'a interesting' should be 'an interesting'".into();
186            ctx.log(format!("needs revision: {}", state.feedback));
187            Ok((state, Outcome::Next("writer")))
188        } else {
189            ctx.log(format!("approved: {}", state.topic));
190            Ok((state, Outcome::Done))
191        }
192    }
Source

pub fn logs(&self) -> &[String]

Return all log messages in order.

Examples found in repository?
examples/edit_loop.rs (line 66)
64    fn run(&mut self, mut state: Doc, ctx: &mut Ctx) -> StepResult<Doc> {
65        // Collect logs first, then clear. Reading logs() borrows &self,
66        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}
More examples
Hide additional examples
examples/coder.rs (line 207)
161fn main() {
162    let tmp = std::env::temp_dir().join("agent-line-coder");
163    scaffold_project(&tmp);
164
165    let lib_path = tmp.join("src/lib.rs").display().to_string();
166    let manifest = tmp.join("Cargo.toml").display().to_string();
167
168    let mut ctx = Ctx::new();
169    ctx.set("manifest_path", &manifest);
170
171    let wf = Workflow::builder("coding-agent")
172        .register(Planner)
173        .register(Coder)
174        .register(Tester)
175        .start_at("planner")
176        .then("coder")
177        .then("tester")
178        .build()
179        .unwrap();
180
181    let mut runner = Runner::new(wf);
182
183    let result = runner.run(
184        Task {
185            description: "Add a function called `reverse_string` that reverses a string \
186                          and add unit tests"
187                .into(),
188            file_path: lib_path,
189            code: String::new(),
190            test_output: String::new(),
191            attempts: 0,
192            max_attempts: 3,
193        },
194        &mut ctx,
195    );
196
197    println!("=== Result ===");
198    match result {
199        Ok(task) => {
200            println!("  Success after {} fix attempts", task.attempts);
201            println!("  Final code:\n{}", task.code);
202        }
203        Err(e) => println!("  Failed: {e}"),
204    }
205
206    println!("\n=== Log ===");
207    for entry in ctx.logs() {
208        println!("  {entry}");
209    }
210}
examples/newsletter.rs (line 182)
155fn main() {
156    let mut ctx = Ctx::new();
157
158    // could populate ctx.store with some writing rules or could read in a markdown skill and pass
159    // that into the agent.
160    // Phase 1: find topics
161    let topic_wf = Workflow::builder("find-topics")
162        .register(TopicSearcher)
163        .register(TopicPicker)
164        .start_at("topic_searcher")
165        .then("topic_picker")
166        .build()
167        .unwrap();
168
169    let mut topic_runner = Runner::new(topic_wf);
170    let topics = topic_runner
171        .run(
172            TopicState {
173                query: "bluecollar engineering newsletter".into(),
174                topics: vec![],
175                selected: vec![],
176            },
177            &mut ctx,
178        )
179        .unwrap();
180
181    println!("=== Topics ===");
182    for entry in ctx.logs() {
183        println!("  {entry}");
184    }
185    ctx.clear_logs();
186    println!();
187
188    // Phase 2: write one article per topic
189    let article_wf = Workflow::builder("write-article")
190        .register(ArticleWriter)
191        .register(ArticleValidator)
192        .register(ArticleFixer)
193        .start_at("article_writer")
194        .then("article_validator")
195        .build()
196        .unwrap();
197
198    let mut article_runner = Runner::new(article_wf);
199    let mut finished_articles: Vec<String> = Vec::new();
200
201    for (i, topic) in topics.selected.iter().enumerate() {
202        println!("=== Article {} ===", i + 1);
203
204        let result = article_runner
205            .run(
206                ArticleState {
207                    topic: topic.clone(),
208                    draft: String::new(),
209                    revision: 0,
210                },
211                &mut ctx,
212            )
213            .unwrap();
214
215        finished_articles.push(result.draft.clone());
216        println!("  Revisions: {}", result.revision);
217
218        for entry in ctx.logs() {
219            println!("  {entry}");
220        }
221        ctx.clear_logs();
222        println!();
223    }
224
225    // Phase 3: "store" the articles
226    println!("=== Stored ===");
227    for (i, article) in finished_articles.iter().enumerate() {
228        let preview: String = article.chars().take(60).collect();
229        println!("  article_{}.md: {preview}...", i + 1);
230    }
231}
examples/parallel.rs (line 242)
199fn main() {
200    let topics = vec![
201        "Rust in embedded systems".to_string(),
202        "Why plumbers love side projects".to_string(),
203        "Electricians using Raspberry Pi on the job".to_string(),
204    ];
205
206    // Shared guidelines for all writers -- the author's voice and style rules
207    let guidelines = "\
208        Write in first person. \
209        Do not use emdashes. \
210        Add a touch of humor. \
211        Keep it under 300 words."
212        .to_string();
213
214    println!("=== Fan-out: {} threads ===\n", topics.len());
215
216    // Fan-out: spawn one thread per topic
217    let handles: Vec<_> = topics
218        .into_iter()
219        .enumerate()
220        .map(|(i, topic)| {
221            let guidelines = guidelines.clone();
222
223            thread::spawn(move || {
224                // Each thread gets its own Ctx and Runner -- no shared mutable state
225                let mut ctx = Ctx::new();
226
227                let wf = Workflow::builder("write-article")
228                    .register(Researcher)
229                    .register(Writer)
230                    .register(Editor)
231                    .start_at("researcher")
232                    .then("writer")
233                    .then("editor")
234                    .build()
235                    .unwrap();
236
237                let mut runner = Runner::new(wf).with_max_retries(5);
238
239                let result = runner.run(ArticleState::new(topic.clone(), guidelines), &mut ctx);
240
241                // Print the log from this thread's pipeline
242                for entry in ctx.logs() {
243                    println!("  [thread {}] {}", i, entry);
244                }
245
246                result
247            })
248        })
249        .collect();
250
251    // Fan-in: join all threads, collect results
252    let mut finished = Vec::new();
253    for handle in handles {
254        match handle.join().unwrap() {
255            Ok(state) => finished.push(state),
256            Err(e) => eprintln!("thread failed: {e}"),
257        }
258    }
259
260    // Show results
261    println!("\n=== Fan-in: {} articles ===\n", finished.len());
262    for (i, article) in finished.iter().enumerate() {
263        let preview: String = article.draft.chars().take(72).collect();
264        println!(
265            "  {}. {} (rev {})\n     {preview}...\n",
266            i + 1,
267            article.topic,
268            article.revision
269        );
270    }
271}
Source

pub fn clear_logs(&mut self)

Clear the event log, leaving the KV store intact.

Examples found in repository?
examples/assistant.rs (line 147)
116fn main() {
117    let mut ctx = Ctx::new();
118
119    let wf = Workflow::builder("daily-briefing")
120        .register(FetchWeather)
121        .register(FetchCalendar)
122        .register(FetchEmail)
123        .register(Summarize)
124        .start_at("fetch_weather")
125        .then("fetch_calendar")
126        .then("fetch_email")
127        .then("summarize")
128        .build()
129        .unwrap();
130
131    let mut runner = Runner::new(wf).with_tracing();
132
133    let mut iteration = 0;
134    loop {
135        iteration += 1;
136        println!("=== Briefing #{iteration} ===\n");
137
138        match runner.run(BriefingState::new(), &mut ctx) {
139            Ok(state) => {
140                println!("{}\n", state.summary);
141            }
142            Err(e) => {
143                eprintln!("Briefing failed: {e}\n");
144            }
145        }
146
147        ctx.clear_logs();
148
149        println!("(sleeping 30s before next briefing...)\n");
150        thread::sleep(Duration::from_secs(30));
151    }
152}
More examples
Hide additional examples
examples/edit_loop.rs (line 126)
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}
examples/newsletter.rs (line 185)
155fn main() {
156    let mut ctx = Ctx::new();
157
158    // could populate ctx.store with some writing rules or could read in a markdown skill and pass
159    // that into the agent.
160    // Phase 1: find topics
161    let topic_wf = Workflow::builder("find-topics")
162        .register(TopicSearcher)
163        .register(TopicPicker)
164        .start_at("topic_searcher")
165        .then("topic_picker")
166        .build()
167        .unwrap();
168
169    let mut topic_runner = Runner::new(topic_wf);
170    let topics = topic_runner
171        .run(
172            TopicState {
173                query: "bluecollar engineering newsletter".into(),
174                topics: vec![],
175                selected: vec![],
176            },
177            &mut ctx,
178        )
179        .unwrap();
180
181    println!("=== Topics ===");
182    for entry in ctx.logs() {
183        println!("  {entry}");
184    }
185    ctx.clear_logs();
186    println!();
187
188    // Phase 2: write one article per topic
189    let article_wf = Workflow::builder("write-article")
190        .register(ArticleWriter)
191        .register(ArticleValidator)
192        .register(ArticleFixer)
193        .start_at("article_writer")
194        .then("article_validator")
195        .build()
196        .unwrap();
197
198    let mut article_runner = Runner::new(article_wf);
199    let mut finished_articles: Vec<String> = Vec::new();
200
201    for (i, topic) in topics.selected.iter().enumerate() {
202        println!("=== Article {} ===", i + 1);
203
204        let result = article_runner
205            .run(
206                ArticleState {
207                    topic: topic.clone(),
208                    draft: String::new(),
209                    revision: 0,
210                },
211                &mut ctx,
212            )
213            .unwrap();
214
215        finished_articles.push(result.draft.clone());
216        println!("  Revisions: {}", result.revision);
217
218        for entry in ctx.logs() {
219            println!("  {entry}");
220        }
221        ctx.clear_logs();
222        println!();
223    }
224
225    // Phase 3: "store" the articles
226    println!("=== Stored ===");
227    for (i, article) in finished_articles.iter().enumerate() {
228        let preview: String = article.chars().take(60).collect();
229        println!("  article_{}.md: {preview}...", i + 1);
230    }
231}
Source

pub fn clear(&mut self)

Clear both the KV store and the event log.

Source

pub fn llm(&self) -> LlmRequestBuilder

Start building an LLM chat request.

Examples found in repository?
examples/assistant.rs (line 98)
89    fn run(&mut self, mut state: BriefingState, ctx: &mut Ctx) -> StepResult<BriefingState> {
90        ctx.log("generating daily briefing via LLM");
91
92        let prompt = format!(
93            "Weather:\n{}\n\nCalendar:\n{}\n\nEmails:\n{}",
94            state.weather, state.calendar, state.emails
95        );
96
97        let response = ctx
98            .llm()
99            .system(
100                "You are a personal assistant. Produce a concise daily briefing \
101                 from the provided weather, calendar, and email data. \
102                 Keep it under 200 words. Use plain text, no markdown.",
103            )
104            .user(prompt)
105            .send()?;
106
107        state.summary = response;
108        Ok((state, Outcome::Done))
109    }
More examples
Hide additional examples
examples/newsletter.rs (line 53)
52    fn run(&mut self, mut state: TopicState, ctx: &mut Ctx) -> StepResult<TopicState> {
53        let response = ctx.llm()
54            .system("You are a newsletter curator. Pick exactly 3 topics. Return one per line, nothing else.")
55            .user(format!("Choose from:\n{}", state.topics.join("\n")))
56            .send()?;
57
58        // brittle, but should pick out the 3
59        state.selected = response
60            .lines()
61            .map(|l| l.trim())
62            .map(|l| {
63                l.trim_start_matches(|c: char| c.is_numeric() || c == '.' || c == '-' || c == ' ')
64            })
65            .map(|l| l.trim().to_string())
66            .filter(|l| !l.is_empty())
67            .collect();
68
69        for topic in &state.selected {
70            ctx.log(format!("selected: {topic}"));
71        }
72
73        Ok((state, Outcome::Done))
74    }
75}
76
77// ---------------------------------------------------------------------------
78// Phase 2 agents: write, validate, fix one article
79// ---------------------------------------------------------------------------
80
81struct ArticleWriter;
82impl Agent<ArticleState> for ArticleWriter {
83    fn name(&self) -> &'static str {
84        "article_writer"
85    }
86    fn run(&mut self, mut state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
87        state.revision += 1;
88        ctx.log(format!(
89            "writing draft {} for: {}",
90            state.revision, state.topic
91        ));
92
93        // Stub: first draft is sloppy, second is clean
94        if state.revision == 1 {
95            state.draft = format!(
96                "# {}\n\nThis is a artcle about {}. It has lots of good infomation.",
97                state.topic, state.topic
98            );
99        } else {
100            state.draft = format!(
101                "# {}\n\nThis is an article about {}. It has lots of good information.",
102                state.topic, state.topic
103            );
104        }
105
106        Ok((state, Outcome::Continue))
107    }
108}
109
110struct ArticleValidator;
111impl Agent<ArticleState> for ArticleValidator {
112    fn name(&self) -> &'static str {
113        "article_validator"
114    }
115    fn run(&mut self, state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
116        // use store k/v to pull in validation rules and pass them to the llm
117        let response = ctx
118            .llm()
119            .system("You are a strict editor. List any errors. Say PASS if none.")
120            .user(&state.draft)
121            .send()?;
122
123        // ctx,store get rules or previous work?
124        if response.contains("PASS") {
125            Ok((state, Outcome::Done))
126        } else {
127            ctx.set("errors", &response);
128            Ok((state, Outcome::Next("article_fixer")))
129        }
130    }
131}
132
133struct ArticleFixer;
134impl Agent<ArticleState> for ArticleFixer {
135    fn name(&self) -> &'static str {
136        "article_fixer"
137    }
138    fn run(&mut self, mut state: ArticleState, ctx: &mut Ctx) -> StepResult<ArticleState> {
139        let errors = ctx.get("errors").unwrap_or("no errors found").to_string();
140        let response = ctx
141            .llm()
142            .system("You are a writer. Rewrite the article fixing only the listed errors.")
143            .user(format!("Errors:\n{errors}\n\nArticle:\n{}", state.draft))
144            .send()?;
145
146        state.draft = response;
147        Ok((state, Outcome::Next("article_validator")))
148    }
examples/coder.rs (line 41)
37    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
38        state.code = tools::read_file(&state.file_path).unwrap_or_default();
39
40        let plan = ctx
41            .llm()
42            .system(
43                "You are a senior developer. Create a brief implementation plan. \
44                 List the specific changes needed. Be concise. \
45                 Do not include doc comments or doc tests.",
46            )
47            .user(format!(
48                "Task: {}\n\nFile: {}\n\nCurrent code:\n{}",
49                state.description,
50                state.file_path,
51                if state.code.is_empty() {
52                    "(new file)".to_string()
53                } else {
54                    state.code.clone()
55                }
56            ))
57            .send()?;
58
59        ctx.set("plan", &plan);
60        ctx.log(format!("planner: created plan for {}", state.file_path));
61        Ok((state, Outcome::Continue))
62    }
63}
64
65struct Coder;
66impl Agent<Task> for Coder {
67    fn name(&self) -> &'static str {
68        "coder"
69    }
70    fn run(&mut self, mut state: Task, ctx: &mut Ctx) -> StepResult<Task> {
71        let is_fix = !state.test_output.is_empty();
72
73        let response = if is_fix {
74            ctx.log(format!("coder: fixing (attempt {})", state.attempts));
75
76            ctx.llm()
77                .system(
78                    "You are a developer. Fix the code based on the test failures. \
79                     Return ONLY the complete fixed file contents, no explanation. \
80                     Do not include doc comments or doc tests. \
81                     Do not wrap the output in markdown code fences.",
82                )
83                .user(format!(
84                    "Test errors:\n{}\n\nCurrent code:\n{}",
85                    state.test_output, state.code
86                ))
87                .send()?
88        } else {
89            let plan = ctx.get("plan").unwrap_or("no plan found").to_string();
90            ctx.log("coder: writing initial code");
91
92            ctx.llm()
93                .system(
94                    "You are a developer. Write the code based on the plan. \
95                     Return ONLY the complete file contents, no explanation. \
96                     Do not include doc comments or doc tests. \
97                     Do not wrap the output in markdown code fences.",
98                )
99                .user(format!(
100                    "Plan:\n{plan}\n\nFile: {}\n\nCurrent code:\n{}",
101                    state.file_path, state.code
102                ))
103                .send()?
104        };
105
106        state.code = tools::strip_code_fences(&response);
107        state.test_output.clear();
108        tools::write_file(&state.file_path, &state.code)?;
109        Ok((state, Outcome::Continue))
110    }

Trait Implementations§

Source§

impl Default for Ctx

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Ctx

§

impl RefUnwindSafe for Ctx

§

impl Send for Ctx

§

impl Sync for Ctx

§

impl Unpin for Ctx

§

impl UnsafeUnpin for Ctx

§

impl UnwindSafe for Ctx

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.