Skip to main content

reqm

Ridiculously Easy Quant Manager

Directory-based config management and object factory built on Hydra.

PyPI CI

Install

pip install reqm

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

Publishing

To release a new version to PyPI:

  1. Bump the version in pyproject.toml
  2. Build and publish:
uv build
uv publish --token pypi-YOUR_TOKEN

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.

Metadata

Release files for reqm 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for reqm 0.1.1
File Size Uploaded
reqm-0.1.1.tar.gz 61.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for reqm 0.1.1
File Interpreter ABI Platform
reqm-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 74.4 kB

Release files / reqm-0.1.1.tar.gz

Download URL reqm-0.1.1.tar.gz
Size 61.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a6c86742299ec031c044c0fa6bdb2ea09671bffed24f38bec2dae2f090fa3e27
BLAKE2b-256 checksum
How to use checksums
02dd1deff9574936c7d55ca2a31c438f105e71bc03e5d93d9d6939a5404a63cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / reqm-0.1.1-py3-none-any.whl

Download URL reqm-0.1.1-py3-none-any.whl
Size 12.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
56b4fc7896951a28ca4c241a25a27fbc85d7b13eec618a05cf24a06e411b7455
BLAKE2b-256 checksum
How to use checksums
9f3add33b3de269047469a23e4d987a01d0b865405138d83924759fce8740622
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page