Skip to content

Configuration#

ai_term uses a robust configuration system based on pydantic-settings.

AppConfig#

The AppConfig class manages all application settings, including LLM providers, audio settings, and appearance.

Environment Variables#

Configuration is loaded from several sources in this order: 1. System Environment Variables 2. .env file (if configured) 3. Internal env dictionary in config.json

Security Warning

NEVER commit your .env file to version control (Git). It contains sensitive API keys. Ensure it is added to your .gitignore.

Provider Schemas#

The application supports dynamic configuration for different providers (LLM, TTS, STT). The schemas are defined in PROVIDER_SCHEMAS.

ai_term.cli.config.AppConfig #

Bases: BaseSettings

Source code in src/ai_term/cli/config.py
class AppConfig(BaseSettings):
    llm: LLMConfig = Field(default_factory=LLMConfig)
    audio: AudioConfig = Field(default_factory=AudioConfig)
    database: DatabaseConfig = Field(default_factory=DatabaseConfig)
    appearance: AppearanceConfig = Field(default_factory=AppearanceConfig)

    # Environment configuration
    dotEnvFilePath: str | None = None
    env: dict[str, str] = Field(default_factory=dict)

    @classmethod
    def load(cls, config_path: Path | None = None) -> "AppConfig":
        """Load configuration from JSON file (raw values, no substitution)."""
        if config_path is None:
            config_path = Path(__file__).parents[2] / ".aiterm" / "config.json"

        config_data: dict[str, Any] = {}
        if config_path.exists():
            with open(config_path) as f:
                config_data = json.load(f)

        # Load environment (for validation/resolution later)
        _load_environment(config_path, config_data)

        # Create AppConfig with raw values (no substitution here)
        return cls(**config_data)

load(config_path=None) classmethod #

Load configuration from JSON file (raw values, no substitution).

Source code in src/ai_term/cli/config.py
@classmethod
def load(cls, config_path: Path | None = None) -> "AppConfig":
    """Load configuration from JSON file (raw values, no substitution)."""
    if config_path is None:
        config_path = Path(__file__).parents[2] / ".aiterm" / "config.json"

    config_data: dict[str, Any] = {}
    if config_path.exists():
        with open(config_path) as f:
            config_data = json.load(f)

    # Load environment (for validation/resolution later)
    _load_environment(config_path, config_data)

    # Create AppConfig with raw values (no substitution here)
    return cls(**config_data)
{
  "llm": {
    "ollama": {
      "model": { "type": "text", "label": "Model Name", "default": "llama3.1" },
      "base_url": { "type": "text", "label": "Base URL", "default": "http://localhost:11434" }
    },
    "openai": {
      "api_key": { "type": "secret", "label": "API Key (Env Var)", "placeholder": "OPENAI_API_KEY" },
      "model": { "type": "text", "label": "Model Name", "default": "gpt-4o" }
    },
    "anthropic": {
      "api_key": { "type": "secret", "label": "API Key (Env Var)", "placeholder": "ANTHROPIC_API_KEY" },
      "model": { "type": "text", "label": "Model Name", "default": "claude-3-sonnet-20240229" }
    }
  },
  "tts": {
    "coqui": {},
    "elevenlabs": {
      "api_key": { "type": "secret", "label": "API Key (Env Var)", "placeholder": "ELEVEN_API_KEY" },
      "voice_id": { "type": "text", "label": "Voice ID", "default": "21m00Tcm4TlvDq8ikWAM" }
    }
  },
  "stt": {
    "whisper": {
      "model": { "type": "text", "label": "Model Size", "default": "base" }
    }
  }
}