Ridiculously Easy Quant Manager — config-based aliased object factory with enforced interfaces
Project description
reqm
Ridiculously Easy Quant Manager
Directory-based config management and object factory built on Hydra.
The problem
Hydra is excellent for config-driven instantiation. But using it as a general object factory requires ceremony:
# You have to do all of this just to instantiate one object
with hydra.initialize(config_path="conf"):
cfg = hydra.compose(config_name="my_model")
model = hydra.utils.instantiate(cfg.model)
This ceremony means Hydra stays in the lab. It's awkward in a notebook, verbose in a service, and doesn't belong in production call sites.
reqm gives you Hydra's power — config-driven instantiation, composable overrides, recursive object graphs — with none of the ceremony:
from reqm import QuantManager
import my_configs
QM = QuantManager(my_configs)
model = QM.build("summarizer_prod")
Same call in a notebook, a FastAPI endpoint, a test, or a batch job.
Core concept: the Quant
A Quant is the unit reqm builds and manages. It is:
- Callable — invoked directly with its inputs
- Config-driven — constructor arguments defined in YAML, no hardcoding
- Auditable — implements
dummy_inputs(), example inputs the factory uses to verify it actually runs
from reqm import Quant
from reqm.overrides_ext import override
class Summarizer(Quant):
def __init__(self, model_name: str, max_tokens: int):
self.model = load_model(model_name)
self.max_tokens = max_tokens
@override
def dummy_inputs(self) -> list[dict]:
return [{"text": "The quick brown fox jumps over the lazy dog."}]
@override
def __call__(self, text: str) -> str:
return self.model.summarize(text, max_tokens=self.max_tokens)
The dummy_inputs contract is what separates a Quant from a plain ABC. reqm can call each Quant with its own dummy inputs at build time — if it fails, it fails early and loudly, not silently in production.
Config modules and QuantManager
A config module is any importable Python package containing YAML files:
my_configs/
├── __init__.py
├── summarizer_prod.yaml
├── summarizer_fast.yaml
└── serving/
└── prod.yaml
Each YAML config declares what to build:
# @package _global_
_target_: myproject.models.Summarizer
model_name: gpt-4o
max_tokens: 512
QuantManager takes the config module and gives you a uniform API. Construct it once in the __init__.py next to your configs directory, then import QM everywhere:
# myproject/__init__.py (next to configs/)
import myproject.configs as configs
from reqm import QuantManager
QM = QuantManager(configs)
# Any call site — notebook, script, service, test
from myproject import QM
QM.list_configs() # ["serving/prod", "summarizer_fast", "summarizer_prod"]
QM.validate() # check all configs have # @package _global_
cfg = QM.get_config("summarizer_prod") # resolved OmegaConf DictConfig
obj = QM.build("summarizer_prod") # instantiated object
Configs can compose other configs via Hydra defaults lists:
# @package _global_
defaults:
- /base_model@child
- _self_
_target_: myproject.models.Ensemble
weight: 0.6
The uniform call site
The core value proposition: ONE script, swap the config name, get different experimental results. No code changes, no if/else chains, no factory functions.
Construct QM once in the __init__.py right next to your configs directory:
# myproject/__init__.py (lives next to configs/)
import myproject.configs as configs
from reqm import QuantManager
QM = QuantManager(configs)
Then every script just imports it:
import sys
from myproject import QM
model = QM.build(sys.argv[1]) # <-- only this string changes
result = model(text="Hello world")
python evaluate.py summarizer_prod
python evaluate.py summarizer_fast
python evaluate.py summarizer_experiment_v3
Runnable example
The repo includes a complete example project at examples/estimators/ that demonstrates Quant subclasses, non-Quant configurable dependencies (Filters), Hydra config composition, and multiple scripts sharing a single QM instance defined in examples/estimators/__init__.py:
# Evaluate a single estimator config
uv run python -m examples.estimators.scripts.evaluate mean_simple
# Inspect the fully resolved config YAML
uv run python -m examples.estimators.scripts.inspect_config ensemble/mean_median
# Compare multiple configs side by side
uv run python -m examples.estimators.scripts.compare mean_simple mean_outlier median_simple
# Validate all configs
uv run python -m examples.estimators.scripts.validate_configs
# Sweep all configs and rank by performance
uv run python -m examples.estimators.scripts.sweep
PyTorch integration
reqm does not depend on PyTorch, but its primary use case is config-driven model experimentation with nn.Module. The repo includes a TorchQuant bridge class that resolves the __call__ vs forward() conflict — subclass it instead of plain Quant when your model is an nn.Module:
Don't subclass TorchQuant directly for every model — create a domain base class that locks the forward signature for your use case:
from myproject.torch_quant import TorchQuant # copy from examples/
# Domain base — locks forward(self, x: Tensor) -> Tensor for all regressors
class Regressor(TorchQuant):
in_features: int
@override
@abc.abstractmethod
def forward(self, x: torch.Tensor) -> torch.Tensor: ...
@override
def dummy_inputs(self) -> list[dict]:
return [{"x": torch.randn(4, self.in_features)}]
# Concrete model — must match the Regressor signature
class LinearRegressor(Regressor):
def __init__(self, in_features: int):
super().__init__()
self.in_features = in_features
self.linear = nn.Linear(in_features, 1)
@override
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.linear(x)
TorchQuant.forward uses @allow_any_override so domain bases can narrow freely. Once the domain base locks the signature (without @allow_any_override), all concrete models must match — enforced at class definition time, not runtime.
See docs/torch_integration.md for the full explanation, and examples/torch_models/ for a runnable example:
uv run python -m examples.torch_models.scripts.evaluate linear_simple
uv run python -m examples.torch_models.scripts.evaluate mlp_small
uv run python -m examples.torch_models.scripts.audit
Why not just Hydra?
Hydra is framework-first. It expects to own your program's entry point. reqm is library-first — it has no opinion about your application structure and works wherever Python runs.
| Hydra | reqm | |
|---|---|---|
| Object instantiation | yes | yes |
| Config composition | yes | yes (via Hydra) |
Auditability (dummy_inputs) |
no | yes |
| Works in notebooks | limited | yes |
| CLI ceremony required | yes | no |
Name
reqm is also a nod to Rue Esquermoise, one of the oldest streets in Lille, France, dating to the 13th century. Its etymology traces to the Flemish eskelm — "frontier." A fitting name for a library that sits at the frontier between research and production.
Status
Core API implemented: Quant, QuantManager, config composition, and override support all work. See examples/estimators/ for a complete working project.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reqm-0.1.0.tar.gz.
File metadata
- Download URL: reqm-0.1.0.tar.gz
- Upload date:
- Size: 60.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b24d7521c39711891999a9525522200edf6c1ca042e3c2a43c309d06d7b0cd4f
|
|
| MD5 |
b7c0c317932c8fb9826d68aa11a2749d
|
|
| BLAKE2b-256 |
1f64e5c2fc5d346cc96a3f1b14b5867f3646b2c6efc743cc68d955e187a3ef4f
|
File details
Details for the file reqm-0.1.0-py3-none-any.whl.
File metadata
- Download URL: reqm-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67ac4945564e6140d8c45649ae120a510fe8e7d8e51c8de7209029f95218c352
|
|
| MD5 |
9bb63e0846112fd1d385a4226c72b790
|
|
| BLAKE2b-256 |
34196ac4eccaeb03847e1c1e8885d84f51b975f68abfa0d22e7845297ed3c82b
|