1use agent_line::{Agent, Ctx, Outcome, Runner, StepResult, Workflow};
2use std::thread;
3use std::time::Duration;
4
5#[derive(Clone, Debug)]
10struct BriefingState {
11 weather: String,
12 calendar: String,
13 emails: String,
14 summary: String,
15}
16
17impl BriefingState {
18 fn new() -> Self {
19 Self {
20 weather: String::new(),
21 calendar: String::new(),
22 emails: String::new(),
23 summary: String::new(),
24 }
25 }
26}
27
28struct FetchWeather;
33impl Agent<BriefingState> for FetchWeather {
34 fn name(&self) -> &'static str {
35 "fetch_weather"
36 }
37 fn run(&mut self, mut state: BriefingState, ctx: &mut Ctx) -> StepResult<BriefingState> {
38 ctx.log("fetching weather data");
39
40 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 }
110}
111
112fn 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}