Overview

Recipe configurations allow you to define parameters that can be passed to your custom recipes. This provides flexibility and reusability, enabling you to run the same recipe with different parameters without modifying the code.

Basic configuration structure

Every recipe configuration inherits from InputConfig, and it defines the parameters your recipe requires and/or optional “knobs” you want to expose for other users to tweak.
from adaptive_harmony.runtime import InputConfig

class MyConfig(InputConfig):
    # Training parameters
    learning_rate: float = 0.001
    batch_size: int = 32
    epochs: int = 10
    
    # Model parameters
    judge_model_name: AdaptiveModel
    max_length: int = 2048
    
    # Logging parameters
    log_interval: int = 100
    save_interval: int = 1000

Using your configuration

Once you’ve defined a configuration class, you can use it in your recipe by passing it as an input argument to your @recipe_main function.
@recipe_main
async def main(config: MyConfig, ctx: RecipeContext):
    # Access configuration values
    print(f"Learning Rate: {config.learning_rate}")
    print(f"Batch size: {config.batch_size}")
    
    # Your recipe logic here
    # ...

Field Types and Annotations

Once you are done developing and you upload your recipe to the recipe library in your Adaptive Deployment, you’ll want to make it as easy as possible for yourself or team mates to reuse it in the future. Adaptive automatically converts your input configurations into self-documented and type-safe widgets in the UI (enforcing types in Python SDK as well). See an example of a very simple recipe config as rendered in the UI (you will find out more in launch recipe):
Giving field types in the config will make sure to work with proper type and will render the field with the right widget in the UI. Below are all the types that you can define.

Basic Types

from pydantic import Field
from typing import Annotated

class BasicConfig(InputConfig):
    # String field with description
    string_field: Annotated[str, Field(description="A string field")] = "default value"
    
    # Integer field with validation
    integer_field: Annotated[int, Field(description="An integer field", min_value=1, max_value=10)] = 1
    
    # Float field
    float_field: Annotated[float, Field(description="A float field")] = 0.001
    
    # Boolean field
    boolean_field: Annotated[bool, Field(description="A boolean field")] = True

Collection Types

from typing import List, Set, Dict

class CollectionConfig(InputConfig):
    # List field with validation
    list_field: Annotated[List[str], Field(description="A list field", min_length=1, max_length=10)]
    
    # Set field
    set_field: Annotated[Set[str], Field(description="A set field")]
    
    # Dictionary field
    dict_field: Annotated[Dict[str, str], Field(description="A dict field")]

    # Literal enum field
    enum_field: Annotated[Literal["sgd", "adam"], Field(description="An enum field")] = "sgd"

Adaptive Model Fields

Adaptive provides special support for model fields, allowing you to pass model instances to your recipes:
from adaptive_harmony.runtime import AdaptiveModel

class ModelConfig(InputConfig):
    # Single model field
    model_field: Annotated[AdaptiveModel, Field(description="A model field")]
    
    # List of models
    model_list_field: Annotated[List[AdaptiveModel], Field(description="A list of models field")]
    
    # Set of models
    model_set_field: Annotated[Set[AdaptiveModel], Field(description="A set of models field")]

Adaptive Dataset Fields

Adaptive provides special support for model fields, allowing you to pass model instances to your recipes:
from adaptive_harmony.runtime import AdaptiveDataset

class DatasetConfig(InputConfig):
    # Single model field
    dataset_field: Annotated[AdaptiveDataset, Field(description="A dataset field")]
    
    # List of models
    dataset_list_field: Annotated[List[AdaptiveDataset], Field(description="A list of dataset field")]
    
    # Set of models
    dataset_set_field: Annotated[Set[AdaptiveDataset], Field(description="A set of dataset field")]

Nested Configurations

You can create complex configurations by nesting other config classes:
class MySubConfig(InputConfig):
    name: str
    value: int
    children: List[str]
    optimizer: Literal["sgd", "adam"]

class NestedConfig(InputConfig):
    # Single nested config
    sub_config: MySubConfig
    
    # List of nested configs
    sub_config_list: List[MySubConfig]