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: HPas the first argument, validated byvalidate_config_func_signatureinsrc/hypster/utils.py. - Use the
HPDSL (select,float,int,nest) to declare typed parameters with defaults and constraints insrc/hypster/hp.py. - Instantiate configurations via
instantiate()fromsrc/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →