AgentConfig Trait

The AgentConfig trait defines the core configuration interface for agents built with agent-air. Every agent must implement this trait to specify its configuration file location, default system prompt, logging prefix, and display name.

Trait Definition

pub trait AgentConfig {
    /// Path to the configuration file
    fn config_path(&self) -> &str;

    /// The default system prompt for this agent
    fn default_system_prompt(&self) -> &str;

    /// The log file prefix for this agent
    fn log_prefix(&self) -> &str;

    /// Agent name for display and logging
    fn name(&self) -> &str;
}

The trait requires four methods, each returning a string slice. All methods take &self, allowing implementations to store configuration values in struct fields if needed.

Method Reference

config_path

fn config_path(&self) -> &str

Returns the path to the agent’s configuration file. Paths starting with ~/ are expanded to the user’s home directory.

Example values:

  • "~/.myagent/config.yaml" - hidden directory in home
  • "~/.config/myagent/config.yaml" - XDG-style location

The configuration file uses YAML format and defines LLM provider settings. If the file does not exist or is empty, the framework falls back to environment variables.

default_system_prompt

fn default_system_prompt(&self) -> &str

Returns the default system prompt sent to the LLM at the start of each session. This prompt establishes the agent’s persona and behavior guidelines.

The system prompt is applied to all configured providers and defines how the agent responds to user input.

log_prefix

fn log_prefix(&self) -> &str

Returns a prefix used for log file naming. The framework creates log files using this prefix to distinguish logs from different agents.

Example: A log prefix of "myagent" produces log files like myagent_2024-01-15.log.

name

fn name(&self) -> &str

Returns a human-readable name for the agent. This name appears in:

  • Startup log messages
  • Error messages
  • TUI title bar (when using the TUI)

Basic Implementation

The simplest implementation uses a unit struct with constant values:

use agent_air::agent::AgentConfig;

struct MyAgentConfig;

impl AgentConfig for MyAgentConfig {
    fn config_path(&self) -> &str {
        "~/.myagent/config.yaml"
    }

    fn default_system_prompt(&self) -> &str {
        "You are a helpful coding assistant. Answer questions clearly and concisely."
    }

    fn log_prefix(&self) -> &str {
        "myagent"
    }

    fn name(&self) -> &str {
        "MyAgent"
    }
}

Configurable Implementation

For agents that need runtime configuration, store values in struct fields:

use agent_air::agent::AgentConfig;

struct FlexibleConfig {
    config_path: String,
    system_prompt: String,
    log_prefix: String,
    name: String,
}

impl FlexibleConfig {
    pub fn new(name: &str) -> Self {
        let slug = name.to_lowercase().replace(' ', "_");
        Self {
            config_path: format!("~/.{}/config.yaml", slug),
            system_prompt: format!("You are {}. Be helpful and precise.", name),
            log_prefix: slug.clone(),
            name: name.to_string(),
        }
    }
}

impl AgentConfig for FlexibleConfig {
    fn config_path(&self) -> &str {
        &self.config_path
    }

    fn default_system_prompt(&self) -> &str {
        &self.system_prompt
    }

    fn log_prefix(&self) -> &str {
        &self.log_prefix
    }

    fn name(&self) -> &str {
        &self.name
    }
}

Using AgentConfig with AgentAir

Pass your configuration to AgentAir::new() to initialize the agent:

use agent_air::agent::{AgentConfig, AgentAir};

struct MyConfig;

impl AgentConfig for MyConfig {
    fn config_path(&self) -> &str { "~/.myagent/config.yaml" }
    fn default_system_prompt(&self) -> &str { "You are a helpful assistant." }
    fn log_prefix(&self) -> &str { "myagent" }
    fn name(&self) -> &str { "MyAgent" }
}

fn main() -> std::io::Result<()> {
    let config = MyConfig;
    let agent = AgentAir::new(&config)?;

    // Continue with agent setup...
    Ok(())
}

Configuration Loading

When AgentAir::new() is called, the framework loads LLM provider configuration in this order:

  1. YAML config file: Attempts to load from the path returned by config_path()
  2. Environment variables: Falls back to API key environment variables if the config file is missing or empty

See Provider Configuration for supported providers and environment variables.

Config File Format

The configuration file uses YAML format:

providers:
  - provider: anthropic
    api_key: sk-ant-...
    model: claude-sonnet-4-20250514

  - provider: groq
    api_key: gsk_...
    model: llama-3.3-70b-versatile

default_provider: anthropic

The system prompt from default_system_prompt() is applied to all providers. See Provider Configuration for the complete list of supported providers.

Error Handling

Configuration errors are represented by ConfigError:

ErrorDescription
NoHomeDirectoryThe user’s home directory could not be determined
ReadErrorThe config file exists but could not be read
ParseErrorThe config file contains invalid YAML
UnknownProviderA provider name is not recognized

Startup Behavior

Missing configuration does not prevent startup. If no providers are configured, the agent initializes but cannot make LLM requests. A warning is logged directing users to set environment variables or create a config file.

Checking Configuration

To verify configuration before starting:

use agent_air::agent::{AgentConfig, AgentAir, load_config};

fn main() -> std::io::Result<()> {
    let config = MyConfig;

    let registry = load_config(&config);
    if registry.is_empty() {
        eprintln!("Error: No LLM providers configured.");
        eprintln!("Set an API key environment variable or create {}", config.config_path());
        std::process::exit(1);
    }

    let agent = AgentAir::new(&config)?;
    Ok(())
}

SimpleConfig

For quick setup, use the built-in SimpleConfig struct:

use agent_air::agent::SimpleConfig;

let config = SimpleConfig::new(
    "MyAgent",
    "~/.myagent/config.yaml",
    "You are a helpful assistant."
);

This provides the same functionality as a custom implementation without defining a new struct.