Settings

Provider settings introspection via the forge-settings crate.

Settings

forge-settings is the introspection layer for provider configuration. It answers three questions consistently across every provider:

  1. What environment variables / config fields does this provider need?
  2. Which of those are currently set?
  3. What's missing before this provider can start?

The contract

pub trait ProviderSettings: Send + Sync {
    fn provider_id(&self) -> &str;             // "anthropic", "openai", ...
    fn required_fields(&self) -> Vec<FieldSpec>;
    fn optional_fields(&self) -> Vec<FieldSpec>;
    fn current_state(&self) -> SettingsState;
    fn missing(&self) -> Vec<FieldSpec>;
}

pub struct FieldSpec {
    pub name: String,                 // "ANTHROPIC_API_KEY"
    pub kind: FieldKind,              // Secret | Url | String | ...
    pub source: FieldSource,          // EnvVar | ConfigFile | Inline
    pub description: String,
}

pub enum SettingsState {
    Ready,
    Partial { missing: Vec<String> },
    Empty,
}

Inspecting a single provider

use forge::providers::AnthropicConfig;
use forge::settings::ProviderSettings;

let config = AnthropicConfig::no_credentials();   // skip env loading
let state = config.current_state();
match state {
    SettingsState::Ready => println!("anthropic is fully configured"),
    SettingsState::Partial { missing } => {
        println!("anthropic missing: {:?}", missing);
    }
    SettingsState::Empty => println!("anthropic not configured"),
}

Inspecting every provider at once

use forge::settings::list_all_providers;

for descriptor in list_all_providers() {
    println!(
        "{:>14}  state={:?}  missing={:?}",
        descriptor.provider_id,
        descriptor.state,
        descriptor.missing,
    );
}

This is what powers the Forge website's /start page — it scans the user's environment and tells them exactly which providers are live, which are partially configured, and which haven't been touched.

Why this exists

Before forge-settings, "is this provider ready?" was a per-provider exercise in reading constructor source. With it:

  • The harness's settings panel can render a uniform UI for all providers
  • CI can fail fast with a clear "OPENAI_API_KEY missing" instead of cryptic authentication errors at first use
  • New providers get this introspection for free by implementing one trait

Adding a new provider

impl ProviderSettings for MyProviderConfig {
    fn provider_id(&self) -> &str { "myprovider" }

    fn required_fields(&self) -> Vec<FieldSpec> {
        vec![
            FieldSpec::env("MYPROVIDER_API_KEY", FieldKind::Secret,
                "API key issued by myprovider.com"),
        ]
    }

    fn optional_fields(&self) -> Vec<FieldSpec> {
        vec![
            FieldSpec::env("MYPROVIDER_BASE_URL", FieldKind::Url,
                "Override base URL (defaults to api.myprovider.com)"),
        ]
    }

    fn current_state(&self) -> SettingsState { /* check env */ }
    fn missing(&self) -> Vec<FieldSpec> { /* delta */ }
}

Next