Skip to content
GitHubDiscord

YAML Configuration

Lexigram uses a hierarchical configuration system that merges user-defined YAML files, environment variables, and code defaults into a single, immutable configuration object.

The primary configuration file is application.yaml, typically located in the project root.

application.yaml
app_name: "order-service"
debug: false
env: "production"
database:
url: "${DB_URL:sqlite:///dev.db}" # Environment interpolation with default
pool_size: 10
cache:
backend: "redis"
from lexigram import LexigramConfig
# Auto-discovers application.yaml in the project root
config = LexigramConfig.from_yaml()
# Or from a specific path
config = LexigramConfig.from_yaml("path/to/application.yaml")

Lexigram supports dynamic values in your YAML files via the ${VAR:default} syntax.

  • ${PORT}: Resolves to the PORT environment variable. Fails if unset.
  • ${PORT:8080}: Resolves to PORT or defaults to 8080.
  • ${DB_URL:${LOCAL_DB_URL}}: Supports nested interpolation.
application.yaml
database:
url: "${DATABASE_URL:sqlite:///./dev.db}"
password: "${DB_PASSWORD}"

You can override base settings for different environments (Development, Staging, Production) using profile-specific files.

  • Base: application.yaml
  • Production Overrides: application.production.yaml

To activate a profile, set the LEX_PROFILE environment variable:

Terminal window
LEX_PROFILE=production lexigram run
  1. application.yaml (base)
  2. application.{profile}.yaml (profile overlay)
  3. Environment variables (highest priority)

When resolving a configuration key, Lexigram follows this strict order (highest priority wins):

  1. Environment Variables: LEX_WEB__PORT=9000 overrides everything
  2. Profile YAML: Values from application.{profile}.yaml
  3. Base YAML: Values from application.yaml
  4. Code Defaults: Values defined in Config class constructors

Configuration keys are mapped to environment variables by converting to uppercase and using double underscores for nesting:

web.port → LEX_WEB__PORT
database.url → LEX_DATABASE__URL
ai.llm.provider → LEX_AI__LLM__PROVIDER

LexigramConfig has typed fields for top-level settings:

@dataclass
class LexigramConfig(BaseConfig):
app_name: str = "lexigram-app"
debug: bool = False
env: Environment = Environment.DEVELOPMENT
logging: LoggingConfig = Field(default_factory=LoggingConfig)
modules: list[str] = Field(default_factory=list)

For dynamic sections (plugins, extension packages), use get_section():

from lexigram import LexigramConfig
config = LexigramConfig.from_yaml()
# Typed access to known sections
config.app_name # "my-app"
config.debug # False
config.env # Environment.DEVELOPMENT
# Dynamic section access (for plugin/extension sections)
db_config = config.get_section("database", DatabaseConfig)
# Dotted path traversal
rag_config = config.get_section("ai.rag", RagConfig)
# Check existence
config.has_section("web") # True

application.development.yaml
debug: true
logging:
level: DEBUG
format: text
database:
url: "sqlite:///./dev.db"
application.production.yaml
debug: false
logging:
level: WARNING
format: json
database:
url: "${DATABASE_URL}"
cache:
backend: redis