pub struct Ctx { /* private fields */ }Expand description
Execution context for agents (services, config, etc.)
Implementations§
Source§impl Ctx
impl Ctx
Sourcepub fn new() -> Self
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?
More 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}Additional examples can be found in:
Sourcepub fn set(&mut self, key: impl Into<String>, value: impl Into<String>)
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
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}Sourcepub fn get(&self, key: &str) -> Option<&str>
pub fn get(&self, key: &str) -> Option<&str>
Look up a key in the KV store.
Examples found in repository?
More 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 }Sourcepub fn remove(&mut self, key: &str) -> Option<String>
pub fn remove(&mut self, key: &str) -> Option<String>
Remove a key from the KV store, returning its value if it existed.
Sourcepub fn log(&mut self, msg: impl Into<String>)
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
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 }Sourcepub fn logs(&self) -> &[String]
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
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}Sourcepub fn clear_logs(&mut self)
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
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}Sourcepub fn llm(&self) -> LlmRequestBuilder
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
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§
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more