llm-agent 0.3.0

The agent library to build LLM applications that work with any LLM providers.
Documentation
use dotenvy::dotenv;
use llm_agent::{Agent, AgentRequest, InstructionParam};
use llm_sdk::{
    openai::{OpenAIModel, OpenAIModelOptions},
    Message, Part,
};
use std::{env, error::Error, sync::Arc};
use tokio::time::{sleep, Duration};

#[derive(Clone)]
struct DungeonRunContext {
    dungeon_master: String,
    party_name: String,
    current_quest: String,
    highlight_player_class: String,
    oracle_hint: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    dotenv().ok();

    let model = Arc::new(OpenAIModel::new(
        "gpt-4o",
        OpenAIModelOptions {
            api_key: env::var("OPENAI_API_KEY")
                .expect("OPENAI_API_KEY environment variable must be set"),
            ..Default::default()
        },
    ));

    let dungeon_coach = Agent::<DungeonRunContext>::builder("Torch", model)
        .add_instruction(
            "You are Torch, a supportive guide who keeps tabletop role-playing sessions moving. \
             Offer concrete options instead of long monologues.",
        )
        .add_instruction(|ctx: &DungeonRunContext| {
            Ok(format!(
                "You are helping {}, the Dungeon Master for the {}. They are running the quest \
                 \"{}\" and need a quick nudge that favors the party's {}.",
                ctx.dungeon_master, ctx.party_name, ctx.current_quest, ctx.highlight_player_class
            ))
        })
        .add_instruction(InstructionParam::AsyncFunc(Box::new(
            |ctx: &DungeonRunContext| {
                let hint = ctx.oracle_hint.clone();
                Box::pin(async move {
                    sleep(Duration::from_millis(25)).await;
                    Ok(format!(
                        "Weave in the oracle whisper: \"{hint}\" so it feels like an in-world \
                         hint."
                    ))
                })
            },
        )))
        .build();

    let context = DungeonRunContext {
        dungeon_master: "Rowan".into(),
        party_name: "Lanternbearers".into(),
        current_quest: "Echoes of the Sunken Keep".into(),
        highlight_player_class: "ranger".into(),
        oracle_hint: "the moss remembers every secret step".into(),
    };

    let response = dungeon_coach
        .run(AgentRequest {
            context,
            input: vec![llm_agent::AgentItem::Message(Message::user(vec![
                Part::text("The party is stuck at a collapsed bridge. What should happen next?"),
            ]))],
        })
        .await?;

    println!("{}", response.text());

    Ok(())
}