How to Create Conditional Configurations in Hypster: A Complete Guide

To create conditional configurations in Hypster that depend on other parameter values, use standard Python conditionals (if, elif, else) inside your configuration function after defining a controlling parameter with hp.select().

Hypster treats configuration functions as ordinary Python code, enabling dynamic branching based on parameter values. The gilad-rubin/hypster library provides a flexible API through the HP class that resolves values at runtime, allowing you to build complex configuration spaces where subsequent parameters depend on earlier choices.

How Conditional Logic Works in Configuration Functions

Unlike rigid configuration DSLs, Hypster executes your configuration function as native Python. The hp: HP argument provides methods like hp.select(), hp.int(), and hp.nest(), but the control flow remains pure Python.

When you call hp.select(), the method returns the actual value (either the default or an override) immediately. This allows you to use the result in standard conditional statements. The selector implementation resides in HP._handle_select_single() at src/hypster/hp.py#L190-L207.

Step-by-Step Implementation

Define the Controlling Parameter

First, establish the parameter that determines your configuration branch. Use hp.select() to define discrete options:

def config(hp: HP):
    model_type = hp.select(["gpt", "claude"], name="model_type", default="gpt")
    # model_type now holds the string "gpt" or "claude"

The HP class validates the value against allowed options and checks for duplicate parameter names using the internal called_params set.

Branch Using Python Conditionals

With the controlling value captured, use standard Python conditionals to define branch-specific parameters:

    if model_type == "gpt":
        top_p = hp.float(0.9, name="top_p", min=0.0, max=1.0)
        return {"model": "gpt", "top_p": top_p}
    else:
        temperature = hp.float(0.7, name="temperature", min=0.0, max=1.0)
        return {"model": "claude", "temperature": temperature}

For conditional sub-configurations, use hp.nest(). This method creates a fresh HP instance for the child configuration, filters overrides belonging to that branch, and maintains duplicate-parameter detection across the entire call graph. The nesting logic is implemented at src/hypster/hp.py#L335-L389.

Handle Nested Overrides and Validation

When using hp.nest(), the system automatically resolves dotted keys (e.g., "model.param") and nested dictionaries. According to the test suite in tests/test_nested_dict_override_precedence.py, nested dictionary overrides take precedence over dotted keys, ensuring predictable configuration merging.

The HP class also utilizes unflatten_dict from src/hypster/utils.py to handle complex nested override structures.

Practical Code Examples

Simple Conditional Parameter Selection

This example demonstrates conditional parameter definition based on model family selection:

from hypster import HP, instantiate

def config(hp: HP):
    # Choose the model family

    model_type = hp.select(["gpt", "claude"], name="model_type", default="gpt")

    # Conditional hyper-parameters

    if model_type == "gpt":
        top_p = hp.float(0.9, name="top_p", min=0.0, max=1.0)
        return {"model": "gpt", "top_p": top_p}
    else:
        temperature = hp.float(0.7, name="temperature", min=0.0, max=1.0)
        return {"model": "claude", "temperature": temperature}

# Default run – uses the "gpt" branch

print(instantiate(config))

# → {'model': 'gpt', 'top_p': 0.9}

# Override to the "claude" branch

print(instantiate(config, values={"model_type": "claude", "temperature": 0.5}))

# → {'model': 'claude', 'temperature': 0.5}

Conditional Nesting with Sub-Configurations

For complex scenarios, conditionally nest entire configuration functions:

from typing import Dict, Any
from hypster import HP, instantiate

def model_a(hp: HP) -> Dict[str, Any]:
    return {"type": "A", "param": hp.int(1, name="param")}

def model_b(hp: HP) -> Dict[str, Any]:
    return {"type": "B", "param": hp.float(2.0, name="param")}

def config(hp: HP) -> Dict[str, Any]:
    # Controlling selector

    model_type = hp.select(["a", "b"], name="model_type", default="a")

    # Conditional nesting

    if model_type == "a":
        model = hp.nest(model_a, name="model")
    else:
        model = hp.nest(model_b, name="model")

    return {"model": model}

# Default – selects model_a

print(instantiate(config))

# → {'model': {'type': 'A', 'param': 1}}

# Force model_b and override its nested parameter

print(instantiate(config, values={"model_type": "b", "model.param": 3.5}))

# → {'model': {'type': 'B', 'param': 3.5}}

The test_conditional_nesting test case in tests/test_nesting.py validates this pattern.

Nested Dictionary Override Precedence

When overriding nested configurations, understand the precedence rules:

def child(hp: HP):
    return {"x": hp.int(10, name="x"), "y": hp.int(20, name="y")}

def parent(hp: HP):
    return hp.nest(child, name="child")

# Both dotted and nested dict overrides – nested dict wins

result = instantiate(
    parent,
    values={
        "child.x": 100,          # dotted

        "child": {"x": 200},     # nested dict

    },
)
print(result)   # → {'x': 200, 'y': 20}

Summary

  • Use plain Python: Hypster configuration functions execute as standard Python, supporting if, elif, else, and loops for conditional logic.
  • Capture values first: Call hp.select() to get the actual value before conditional branches, enabling dynamic parameter definition.
  • Nest conditionally: Use hp.nest() within conditional blocks to load sub-configurations based on runtime values, with automatic override filtering.
  • Override handling: The system supports both dotted notation ("model.param") and nested dictionaries, with nested dicts taking precedence.
  • Validation across branches: The HP class tracks called_params to prevent duplicate definitions even across mutually exclusive conditional branches.

Frequently Asked Questions

Can I use loops to generate conditional parameters in Hypster?

Yes, because Hypster executes configuration functions as ordinary Python, you can use for and while loops to dynamically generate parameters based on other values. For example, you could iterate over a list returned by hp.select() to create multiple related parameters. The HP API simply provides methods to register hyperparameters within this standard Python execution context.

How does Hypster handle overrides for parameters in unselected branches?

Parameters defined inside branches not taken during execution are never evaluated, so no overrides are required for them. When you call instantiate(), Hypster only processes HP method calls within the active execution path. This lazy evaluation means you only need to provide overrides for parameters that are actually accessed, reducing configuration complexity.

What happens if different branches define parameters with the same name?

Hypster prevents this through the called_params tracking mechanism. If two branches both attempt to define a parameter with identical names (e.g., "param"), the HP class raises an error because the set tracks all called parameters across the entire function execution, regardless of branching. This ensures namespace consistency in your configuration space.

Can I nest multiple levels of conditional configurations?

Absolutely. You can chain hp.nest() calls within conditional blocks to arbitrary depths. Each nested call creates a fresh HP instance that inherits the parent's override context but filters for its specific namespace. As implemented in HP.nest() at lines 335-389, the method properly handles dotted keys like "parent.child.param" and maintains the called_params set across the entire nesting hierarchy to prevent duplicates at any level.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →