Skip to main content
Once is fine. Twice is suspicious. Three times means you need to make your hyperparameters data.
Configuration is a layered dictionary backed by a retained config store. You read it like nested data, but HeavenBase resolves defaults, scope layers, version history, and interpolation for you.

1. Why Configuration Exists

Without a config manager, every wrapper that eventually calls a model, backend, prompt, cache, benchmark, or file helper has to accept and pass through the same knobs:
That shape does not scale. A higher-level application quickly becomes a chain of functions that carry parameters they do not own. Changing one default means checking every wrapper. Adding one new backend or LLM option bloats signatures across unrelated code. HeavenBase uses CM_HVNB to move those knobs into configuration data:
Now application code can read the active defaults without turning every function into a pass-through config carrier. The function can still preserve important parameters that it needs to keep for other reasons, while the unimportant ones can be removed from the signature:
The legacy file-based shape also had a hard ceiling. A few local/global/default files work for one developer, but they become confusing when one package hosts multiple apps, environments, users, or experiments. HeavenBase keeps config layers in a database-backed store instead, so each scope can keep its own retained history and override only the values it owns.

2. Core Idea: Scoped Layers and App Overrides

In a nutshell, the HeavenBase configuration manager is using a database backend to store a scoped KV dictionary. A scope is a named layer of overrides. Broad scopes hold shared defaults; narrower scopes override only what differs. For example, heavenbase.workspace.docs-demo resolves through:
  1. heavenbase
  2. heavenbase.workspace
  3. heavenbase.workspace.docs-demo
If the docs demo overrides only heavenbase.query.near.default_top_k, every other config value falls back to the broader scopes and finally to the package default template.
This is the application override pattern: set global infrastructure defaults once, then create child scopes for apps, environments, workspaces, tenants, or tests.
Enter the scope before constructing objects that resolve config during __init__. If an LLM, workspace, backend, or helper snapshots config at construction time, changing the scope afterward will not rewrite the already-built object.
Scope is the only thing application code needs to carry. The actual model names, provider defaults, backend paths, serialization defaults, and prompt language can stay in config data instead of being threaded through every wrapper.

3. Read Active Defaults

Start with get(...) when you need one value:
Use load() when you want a mutable copy of the current merged config:
load() returns a copy. Mutating that dictionary does not write back to the config store.

4. Use Scopes as Context

Use scoped(...) as a context manager when a block of code should read scoped values:
The same helper also works as a decorator:
Dynamic scopes can be provided by a callable when the active application context determines the scope name.

5. Reuse a Snapshot

For high-volume read paths, keep a snapshot and reuse it:
A snapshot is immutable. Config edits do not mutate snapshots you already hold. Ask for a fresh snapshot or call CM_HVNB.refresh() when a diagnostic path needs to bypass cached state immediately.

6. Edit a Scoped Layer

Config writes are persisted immediately. Use a scoped layer for demos and tests so you do not surprise the base configuration layer:
List edits use path syntax:
  • items[0] replaces an item.
  • items[] appends.
  • items[2+] inserts before index 2.
unset(...) writes a tombstone so a child scope can hide a value inherited from its parent scope. Dicts can use __HB_OVERWRITE__ to replace a parent dict instead of recursively merging it.
remove(...), compact(..., reset=True), and setup(reset=True) change retained config data. Use them only on scopes you intend to clean up.

7. Inspect Stored Layers

Use layer(...) when you need the raw layer stored in one scope before parent merge and interpolation:
Each scope keeps retained versions. The default heavenbase.config.versioning.keep_last_k is 10, so ordinary edits keep recent history without growing forever.

8. Use Resources and Package Paths

resource(...) resolves files under the package resources/ directory when that package can be located. load_default() returns the in-memory default config template used to initialize scopes.
CM_HVNB.pj(...) understands HeavenBase package aliases such as %/ for package-local data and &/ for package resources. For ordinary caller-provided paths, use pj(...) from the File System utilities instead. See File System for more details.

9. Understand Interpolation

OmegaConf interpolation resolves when a snapshot is compiled:
  • ${env:VAR} is allowed by default.
  • ${oc.env:VAR,default} works through OmegaConf.
  • ${cmd:...} is disabled by default and requires an explicit policy override.
This is why provider defaults can read environment variables without making every call site handle environment parsing by hand.

Further Exploration

Related resources:
  • Overview - the rest of the Utilities Toolbox.
  • File System - path and file helpers used around config artifacts.
  • CLI Reference - hb config commands over the same config manager.