Skip to main content

assistant/
assistant.rs

1use agent_line::{Agent, Ctx, Outcome, Runner, StepResult, Workflow};
2use std::thread;
3use std::time::Duration;
4
5// ---------------------------------------------------------------------------
6// State
7// ---------------------------------------------------------------------------
8
9#[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
28// ---------------------------------------------------------------------------
29// Agents
30// ---------------------------------------------------------------------------
31
32struct 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        // 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    }
110}
111
112// ---------------------------------------------------------------------------
113// Main
114// ---------------------------------------------------------------------------
115
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}