Hierarchical configuration management for Amplifier applications
amplifier-config provides pure configuration mechanism with three-scope resolution (user, project, local), deep merge semantics, and YAML file I/O. Apps inject path conventions; the library handles resolution logic.
- Quick Start - Get started in 5 minutes
- API Reference - Complete API documentation
- User Guide - Application examples and patterns
- Technical Specification - Resolution algorithm and contracts
# From PyPI (when published)
uv pip install amplifier-config
# From git (development)
uv pip install git+https://github.com/microsoft/amplifier-config@main
# For local development
cd amplifier-config
uv pip install -e .
# Or using uv sync for development with dependencies
uv sync --devfrom amplifier_config import ConfigManager, ConfigPaths, Scope
from pathlib import Path
# Define paths for your application (CLI example)
paths = ConfigPaths(
user=Path.home() / ".amplifier" / "settings.yaml",
project=Path(".amplifier/settings.yaml"),
local=Path(".amplifier/settings.local.yaml"),
)
# Create configuration manager
config = ConfigManager(paths=paths)
# Get active profile (LOCAL > PROJECT > USER > None)
active_profile = config.get_active_profile()
# Set profile at local scope
config.set_active_profile("dev", scope=Scope.LOCAL)
# Get merged settings across all scopes
all_settings = config.get_merged_settings()
# Manage module source overrides
config.add_source_override(
module_id="provider-anthropic",
source="git+https://github.com/user/custom-provider@main",
scope=Scope.PROJECT
)Configuration resolves in precedence order (highest wins):
┌─────────────────────────────────────────────────────────┐
│ LOCAL SCOPE (highest precedence) │
│ .amplifier/settings.local.yaml │
│ Developer-specific overrides (gitignored) │
├─────────────────────────────────────────────────────────┤
│ PROJECT SCOPE (middle precedence) │
│ .amplifier/settings.yaml │
│ Team-shared settings (committed to git) │
├─────────────────────────────────────────────────────────┤
│ USER SCOPE (lowest precedence) │
│ ~/.amplifier/settings.yaml │
│ User-global defaults (applies to all projects) │
└─────────────────────────────────────────────────────────┘
Resolution: Check LOCAL → PROJECT → USER → None
Merging: USER < PROJECT < LOCAL (deep recursive merge)
→ See Resolution Algorithm Specification for complete technical details.
Overlays merge recursively with overlay values taking precedence. This preserves unmodified values from lower scopes.
→ See Deep Merge Algorithm Specification for complete algorithm and examples.
The library does NOT hardcode paths like .amplifier/ or ~/.amplifier/. Apps inject their path conventions; the library provides the resolution mechanism.
→ See Path Injection Contract for complete specification and examples.
from amplifier_config import Scope
class Scope(Enum):
"""Configuration scope levels."""
USER = "user" # User-global settings
PROJECT = "project" # Project-shared settings
LOCAL = "local" # Developer-local overridesfrom amplifier_config import ConfigPaths
from pathlib import Path
@dataclass(frozen=True)
class ConfigPaths:
"""Injectable path configuration.
Apps provide these paths based on their conventions.
Library uses paths without knowing their semantic meanings.
"""
user: Path # Where user-global settings live
project: Path # Where project-shared settings live
local: Path # Where local overrides liveExample: CLI paths
cli_paths = ConfigPaths(
user=Path.home() / ".amplifier" / "settings.yaml",
project=Path(".amplifier/settings.yaml"),
local=Path(".amplifier/settings.local.yaml"),
)→ See Application Examples for Web, Desktop, and Testing path conventions.
from amplifier_config import ConfigManager, ConfigPaths, Scope
class ConfigManager:
"""Configuration management mechanism.
Provides hierarchical resolution, deep merge, and file I/O.
Does not make decisions about paths, display, or storage backend.
"""
def __init__(self, paths: ConfigPaths):
"""Initialize with injectable paths.
Args:
paths: ConfigPaths defining where settings files live
"""# Get active profile
active = config.get_active_profile()
# Returns: str | None (None if no profile active at any scope)
# Set active profile
config.set_active_profile("dev", scope=Scope.LOCAL)
# Clear active profile
config.clear_active_profile(scope=Scope.LOCAL)
# Get project default profile
default = config.get_project_default()
# Returns: str | None
# Set project default
config.set_project_default("base")
# Clear project default
config.clear_project_default()# Get merged settings
settings = config.get_merged_settings()
# Returns: dict[str, Any] - Deep merge of USER < PROJECT < LOCAL
# Get module source overrides
sources = config.get_module_sources()
# Returns: dict[str, str] mapping module_id -> source_uri# Add source override
config.add_source_override(
module_id="provider-anthropic",
source="git+https://github.com/user/custom-provider@main",
scope=Scope.PROJECT
)
# Remove source override
removed = config.remove_source_override(
module_id="provider-anthropic",
scope=Scope.PROJECT
)
# Returns: True if removed, False if not found# Update arbitrary settings at a scope
config.update_settings(
updates={"custom": {"feature": "enabled"}},
scope=Scope.PROJECT
)
# Deep merges updates into PROJECT scope settings
# Get path for a scope
path = config.scope_to_path(Scope.LOCAL)
# Returns: Path object for the LOCAL scope→ See Advanced Patterns for multi-scope batch operations and more.
from amplifier_config import deep_merge
result = deep_merge(
base={"a": {"b": 1, "c": 2}},
overlay={"a": {"b": 999, "d": 3}}
)
# Returns: {"a": {"b": 999, "c": 2, "d": 3}}
# Overlay values replace base; unmodified base values preservedParameters:
base(dict): Base dictionaryoverlay(dict): Overlay dictionary (values take precedence)
Returns: New dictionary with merged values (base and overlay unchanged)
→ See Deep Merge Algorithm Specification for complete specification.
# settings.yaml (standard format)
# Profile management
profile:
active: dev # Active profile name (set via set_active_profile)
default: base # Project default profile (PROJECT scope only)
# Module source overrides
sources:
provider-anthropic: git+https://github.com/user/custom-provider@main
tool-filesystem: file:///home/dev/custom-tool
# Custom app-specific sections (library passes through via get_merged_settings)
custom:
anything: apps can store custom configuration
library: does not interpret these sections→ See Settings File Schema for complete schema specification.
from amplifier_config import ConfigError, ConfigFileError, ConfigValidationError
try:
config.set_active_profile("dev", scope=Scope.LOCAL)
except ConfigFileError as e:
# Raised when file I/O fails (write errors, permission denied, etc.)
print(f"File error: {e}")
except ConfigValidationError as e:
# Raised when configuration data is invalid
print(f"Validation error: {e}")
except ConfigError as e:
# Base exception for all config errors
print(f"Config error: {e}")Exception hierarchy:
ConfigError- Base exception for all configuration errorsConfigFileError- File I/O errors (extends ConfigError)ConfigValidationError- Validation errors (extends ConfigError)
Graceful degradation: Missing files return None or empty dict (not an exception).
→ See Error Handling Specification for complete error handling contracts.
amplifier-config provides configuration mechanism, not policy:
- How to resolve scopes (USER < PROJECT < LOCAL)
- How to merge configurations (deep recursive merge)
- How to read/write files (YAML parsing)
Applications provide configuration policy:
- Where settings files live (path conventions)
- What settings mean (interpretation)
- When to reload (caching strategy)
This separation enables the library to work across diverse application types without modification.
→ See Design Philosophy and Rationale for complete design rationale.
# Install development dependencies
uv pip install -e ".[dev]"
# Or using uv sync
uv sync --dev
# Run tests
pytest
# Run with coverage
pytest --cov=amplifier_config --cov-report=html→ See Testing Strategy for complete testing specification.
Runtime:
- Python >=3.11 (stdlib: pathlib, dataclasses, typing)
- pyyaml >=6.0 (optional, for YAML file support)
Development:
- pytest >=8.0
- pytest-cov
→ See Dependencies Specification for rationale and philosophy.
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.