Environment-Aware Configuration¶
DynaBot supports environment-aware configuration for deploying the same bot configuration across different environments (development, staging, production). This separates portable bot behavior from environment-specific infrastructure bindings.
Overview¶
The Problem¶
Traditional bot configurations contain environment-specific details:
# PROBLEMATIC: This config is not portable
llm:
provider: ollama
model: qwen3:8b
base_url: http://localhost:11434 # Only works locally
conversation_storage:
backend: sqlite
path: ~/.local/share/myapp/conversations.db # Local path
When stored in a shared registry or database, these configs fail in production because:
- Localhost URLs don't exist in production
- Local file paths don't exist in containers
- Different environments need different backends
The Solution¶
Use logical resource references ($resource) to separate behavior from infrastructure:
# PORTABLE: This config works in any environment
bot:
llm:
$resource: default # Logical name
type: llm_providers # Resource type
temperature: 0.7 # Behavioral setting
conversation_storage:
$resource: conversations
type: databases
The logical names are resolved at instantiation time against environment-specific bindings.
Quick Start¶
1. Create Environment Config Files¶
# config/environments/development.yaml
name: development
resources:
llm_providers:
default:
provider: ollama
model: qwen3:8b
base_url: http://localhost:11434
databases:
conversations:
backend: memory
# config/environments/production.yaml
name: production
resources:
llm_providers:
default:
provider: openai
model: gpt-4
api_key: ${OPENAI_API_KEY}
databases:
conversations:
backend: postgres
connection_string: ${DATABASE_URL}
2. Create Portable Bot Config¶
# config/bots/assistant.yaml
name: assistant
bot:
llm:
$resource: default
type: llm_providers
temperature: 0.7
conversation_storage:
$resource: conversations
type: databases
system_prompt: |
You are a helpful assistant.
3. Resolve Resources in Code¶
from dataknobs_config import EnvironmentConfig
from dataknobs_bots.config import BotResourceResolver
# Auto-detect environment from DATAKNOBS_ENVIRONMENT
env = EnvironmentConfig.load()
# Create resolver with all DynaBot factories
resolver = BotResourceResolver(env)
# Get initialized resources
llm = await resolver.get_llm("default")
db = await resolver.get_database("conversations")
Environment Detection¶
The environment is determined in this order:
- Explicit:
DATAKNOBS_ENVIRONMENT=production - Cloud indicators: AWS Lambda, ECS, Kubernetes, GCP Cloud Run, Azure Functions
- Default:
development
# Set environment explicitly
export DATAKNOBS_ENVIRONMENT=production
# Or auto-detect based on cloud environment
# AWS Lambda: AWS_EXECUTION_ENV
# Kubernetes: KUBERNETES_SERVICE_HOST
# Cloud Run: K_SERVICE
# Azure Functions: FUNCTIONS_WORKER_RUNTIME
Resource Reference Syntax¶
Basic Syntax¶
llm:
$resource: default # Logical resource name
type: llm_providers # Resource type
$requires: [function_calling] # Required capabilities (optional)
temperature: 0.7 # Merged into resolved config
Supported Resource Types¶
| Type | Description |
|---|---|
llm_providers |
LLM providers (OpenAI, Anthropic, Ollama) |
databases |
Database backends (memory, sqlite, postgres) |
vector_stores |
Vector store backends (memory, FAISS, pgvector) |
embedding_providers |
Embedding providers |
Capability Requirements ($requires)¶
Bot configs can declare required capabilities using $requires:
If the resolved resource declares capabilities metadata, the system validates that all requirements are met. Missing capabilities raise a ConfigError at resolution time.
Requirements are also inferred from bot structure — for example, a bot with reasoning.strategy: react and tools automatically requires function_calling. Explicit $requires is additive.
Capability Metadata on Resources¶
Environment configs can declare what capabilities each resource provides:
resources:
llm_providers:
default:
provider: ollama
model: qwen3:8b
capabilities: [chat, function_calling, streaming]
fast:
provider: ollama
model: gemma3:4b
capabilities: [chat, streaming]
The capabilities field is stripped during resolution — it's validation metadata, not passed to the provider constructor.
Config Merging¶
Additional fields in a resource reference (except $resource, type, and $requires) are merged with the resolved config:
# In bot config
llm:
$resource: default
type: llm_providers
temperature: 0.9 # Overrides environment default
# If environment defines temperature: 0.7
# Resolved config will have temperature: 0.9
BotResourceResolver¶
High-level resolver that automatically initializes resources.
Basic Usage¶
from dataknobs_config import EnvironmentConfig
from dataknobs_bots.config import BotResourceResolver
env = EnvironmentConfig.load()
resolver = BotResourceResolver(env)
# Get initialized LLM (calls initialize() automatically)
llm = await resolver.get_llm("default")
# Get connected database (calls connect() automatically)
db = await resolver.get_database("conversations")
# Get initialized vector store
vs = await resolver.get_vector_store("knowledge")
# Get initialized embedding provider
embedder = await resolver.get_embedding_provider("default")
Config Overrides¶
# Override temperature for this resolution
llm = await resolver.get_llm("default", temperature=0.9)
# Get fresh instance (skip cache)
llm = await resolver.get_llm("default", use_cache=False)
Cache Management¶
# Clear all cached resources
resolver.clear_cache()
# Clear only LLM providers
resolver.clear_cache("llm_providers")
Low-Level Resolution¶
For more control, use create_bot_resolver:
from dataknobs_config import EnvironmentConfig
from dataknobs_bots.config import create_bot_resolver
env = EnvironmentConfig.load("production")
resolver = create_bot_resolver(env)
# Resolve without auto-initialization
llm = resolver.resolve("llm_providers", "default")
await llm.initialize() # Manual initialization
# Check registered factories
resolver.has_factory("llm_providers") # True
resolver.get_registered_types() # ['llm_providers', 'databases', ...]
Custom Factory Registration¶
from dataknobs_config import ConfigBindingResolver, EnvironmentConfig
from dataknobs_bots.config import (
register_llm_factory,
register_database_factory,
)
env = EnvironmentConfig.load()
# Create resolver without defaults
resolver = create_bot_resolver(env, register_defaults=False)
# Register only what you need
register_llm_factory(resolver)
register_database_factory(resolver)
Environment Config Format¶
Full Example¶
# config/environments/production.yaml
name: production
description: Production environment
settings:
log_level: INFO
enable_metrics: true
resources:
llm_providers:
default:
provider: openai
model: gpt-4
api_key: ${OPENAI_API_KEY}
temperature: 0.7
max_tokens: 2000
capabilities: [chat, function_calling, streaming]
fast:
provider: openai
model: gpt-3.5-turbo
api_key: ${OPENAI_API_KEY}
capabilities: [chat, streaming]
databases:
default:
backend: postgres
connection_string: ${DATABASE_URL}
conversations:
backend: postgres
connection_string: ${DATABASE_URL}
pool_size: 20
vector_stores:
default:
backend: pgvector
connection_string: ${DATABASE_URL}
dimensions: 1536
knowledge:
backend: pgvector
connection_string: ${DATABASE_URL}
dimensions: 1536
table: knowledge_vectors
embedding_providers:
default:
provider: openai
model: text-embedding-3-small
api_key: ${OPENAI_API_KEY}
Best Practices¶
1. Store Portable Configs¶
Only store configs with $resource references in databases and registries.
2. Use Late Binding¶
Resolve environment variables at instantiation time, not load time.
3. Define All Environments¶
Create config files for development, staging, and production.
4. Use Consistent Names¶
Use the same logical names across all environment configs.
5. Keep Behavior Separate¶
Put behavioral settings (temperature, max_tokens) in bot configs, infrastructure settings in environment configs.
Integration with EnvironmentAwareConfig¶
For full config resolution including $resource references:
from dataknobs_config import EnvironmentAwareConfig
# Load app config with environment bindings
config = EnvironmentAwareConfig.load_app(
"assistant",
app_dir="config/bots",
env_dir="config/environments"
)
# Resolve for building (late binding)
resolved = config.resolve_for_build("bot")
# Get portable config for storage
portable = config.get_portable_config()
Next Steps¶
- Migration Guide - Migrate existing configs
- Configuration Reference - All configuration options
- Examples - Working examples