How to Structure Configurations for Large Language Models (LLMs) with Hypster

Hypster structures LLM configurations as Python functions using an HP instance for typed parameters, enabling validation, nesting, and dot-notation overrides through the instantiate() API.

Hypster is a lightweight, open-source configuration library designed to make LLM pipeline management declarative and type-safe. Instead of juggling YAML files or dictionaries, you define configurations as standard Python functions using the HP domain-specific language (DSL). This approach allows you to structure configurations for Large Language Models (LLMs) with Hypster using native Python syntax while gaining runtime validation and modular composition.

Core Concepts for LLM Configuration

The HP DSL for Typed Parameters

The HP class in src/hypster/hp.py provides a tiny DSL for declaring typed configuration parameters. Inside your configuration function, you access methods like hp.select(), hp.float(), hp.int(), and hp.bool() to define constraints such as allowed options, min/max bounds, and default values.

When instantiate() executes your configuration function, the HP object resolves raw input values against these declarations, falling back to defaults when overrides are not provided.

Validation and Instantiation

Hypster validates configuration functions through validate_config_func_signature in src/hypster/utils.py, which ensures the function accepts hp: HP as its first parameter. During instantiation, the instantiate() function in src/hypster/core.py creates an HP instance, executes the configuration function to collect resolved values, and then invokes _handle_unknown_parameters to warn or raise errors if the caller supplied keys that were never consumed by any hp.* call.

Building a Simple LLM Configuration

Start by defining a basic LLM configuration function:

from hypster import HP, instantiate

def llm_cfg(hp: HP):
    # Choose a model name from a small whitelist

    model_name = hp.select(
        ["gpt-4o-mini", "gpt-4o"],
        name="model_name"
    )
    # Temperature must be between 0.0 and 1.0

    temperature = hp.float(0.0, name="temperature", min=0.0, max=1.0)
    # Maximum tokens allowed by the provider

    max_tokens = hp.int(256, name="max_tokens", max=2048)

    return {
        "model_name": model_name,
        "temperature": temperature,
        "max_tokens": max_tokens,
    }

Instantiate with defaults or overrides:


# Instantiate the default configuration

default_cfg = instantiate(llm_cfg)

# Override a couple of values for a "creative" run

creative_cfg = instantiate(
    llm_cfg,
    values={"model_name": "gpt-4o", "temperature": 1.0}
)

print(default_cfg)

# {'model_name': 'gpt-4o-mini', 'temperature': 0.0, 'max_tokens': 256}

print(creative_cfg)

# {'model_name': 'gpt-4o', 'temperature': 1.0, 'max_tokens': 256}

Hierarchical Configurations with Nesting

For complex LLM pipelines, use hp.nest() to compose configurations:

from hypster import HP, instantiate

# Re-use the LLM config defined above

def qa_pipeline_cfg(hp: HP):
    llm = hp.nest(llm_cfg, name="llm")   # nested config

    # QA-specific parameters

    max_context = hp.int(1500, name="max_context", min=500, max=3000)

    return {
        "llm": llm,                     # expose the nested dict

        "max_context": max_context,
    }

Override nested values using dot notation:

pipeline_cfg = instantiate(
    qa_pipeline_cfg,
    values={
        "llm.model_name": "gpt-4o",
        "llm.temperature": 0.7,
        "max_context": 2000,
    },
)

print(pipeline_cfg["llm"]["model_name"])   # gpt-4o

print(pipeline_cfg["max_context"])         # 2000

The nest implementation in src/hypster/hp.py handles prefix reservation, value extraction, and propagation of called-parameter tracking to prevent duplicate or conflicting names across the hierarchy.

Loading External Configurations

Save and load configurations from disk for modularity:


# Save the basic LLM config to a file

llm_cfg.save("configs/llm.py")

def rag_cfg(hp: HP):
    # Load and nest the saved LLM config

    llm = hp.nest("configs/llm.py", name="llm")
    # Retrieval-specific parameters

    top_k = hp.int(10, name="top_k", min=1, max=100)

    return {"llm": llm, "top_k": top_k}
rag = instantiate(
    rag_cfg,
    values={"llm.model_name": "gpt-4o-mini", "top_k": 5}
)

Summary

  • Structure LLM configurations as Python functions accepting hp: HP as the first argument, validated by validate_config_func_signature in src/hypster/utils.py.
  • Use the HP DSL (select, float, int, nest) to declare typed parameters with defaults and constraints in src/hypster/hp.py.
  • Instantiate configurations via instantiate() from src/hypster/core.py, which validates signatures, resolves values, and detects unknown parameters via _handle_unknown_parameters.
  • Compose hierarchical LLM pipelines using hp.nest() to embed sub-configurations while maintaining a single flat namespace addressable via dot notation (e.g., "llm.temperature").
  • Save and load configuration modules to disk for reusable, version-controlled LLM components.

Frequently Asked Questions

What is the HP class in Hypster?

The HP class is the core domain-specific language (DSL) for declaring typed configuration parameters. Defined in src/hypster/hp.py, it provides methods like select(), float(), int(), and nest() that allow you to specify defaults, validation bounds, and allowed options. When instantiate() executes your configuration function, the HP object resolves raw input values against these declarations, falling back to defaults when overrides are not provided.

How does Hypster validate LLM configuration functions?

Hypster validates configuration functions through validate_config_func_signature in src/hypster/utils.py, which ensures the function accepts hp: HP as its first parameter. During instantiation, the instantiate() function in src/hypster/core.py creates an HP instance, executes the configuration function to collect resolved values, and then invokes _handle_unknown_parameters to warn or raise errors if the caller supplied keys that were never consumed by any hp.* call. This guarantees that LLM configurations are strictly typed and free of orphaned parameters.

Can I nest multiple LLM configurations within a single pipeline?

Yes, Hypster supports hierarchical composition through the hp.nest() method. You can embed one configuration function (or a saved module path) inside another, allowing you to build complex LLM pipelines—such as combining a retriever, LLM, and tokenizer—while maintaining a single flat namespace. The nested call receives a filtered view of the parent’s values dictionary, enabling dot-notation overrides like "llm.temperature" or "retriever.top_k". The implementation in src/hypster/hp.py ensures that parameter names remain unique across the hierarchy by sharing a called_params set between parent and child configurations.

How do I save and reuse LLM configurations across projects?

You can persist any configuration function to disk using the .save() method, which writes the function to a Python file. Later, you can reference this file path directly in hp.nest() to load and embed the saved configuration into a new pipeline. This approach enables version-controlled, reusable LLM components that can be shared across projects without copying code. For example, saving a standard llm_cfg to "configs/llm.py" allows multiple downstream configurations (RAG, chatbots, agents) to import and override it consistently using dot-notation keys like "llm.model_name".

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 →