LightRL
A lightweight multi-armed bandit library for Python. Zero heavy dependencies. Built for agents.
Why LightRL
Most RL packages (Vowpal Wabbit, RLlib, MABWiser) are built for data science pipelines — they require heavy dependencies, custom serialization, and framework buy-in. LightRL is built for a different use case: operational decisions in software systems and AI agents.
Think of LightRL as functools.lru_cache for decision-making. You don't reach for Redis when you need to memoize a function. You don't reach for Vowpal Wabbit when you need an agent to learn which API endpoint is fastest.
The case for lightweight bandits
| LightRL | Vowpal Wabbit | MABWiser | RLlib | |
|---|---|---|---|---|
| Dependencies | tqdm |
C++ runtime | sklearn, numpy, scipy | Ray, torch |
| Install size | ~50KB | ~50MB | ~200MB+ | ~1GB+ |
| Core code | ~300 lines | ~100k lines | ~3k lines | ~500k lines |
| Persistence | bandit.save("state.json") |
Custom model files | Pickle | Ray checkpoints |
| Time to integrate | Minutes | Hours | Hours | Days |
| Agent-native API | BanditRouter |
No | No | No |
When to use LightRL
LightRL is the right choice when:
- You need a decision, not a research paper. Which endpoint is fastest? What batch size avoids rate limits? Which prompt template works best? These don't need gradient descent.
- You're building an agent. LLMs burn tokens and latency "reasoning" about operational choices. A bandit answers in microseconds with better accuracy after 50 observations.
- Dependencies matter. Lambda functions, edge devices, minimal containers, CI pipelines — anywhere scipy is too heavy.
- You want to understand the code. The entire library is auditable in 10 minutes. No hidden complexity.
When NOT to use LightRL
- You need industrial-scale contextual bandits processing billions of events (use Vowpal Wabbit)
- You need full reinforcement learning with environments and policies (use RLlib)
- You need Bayesian optimization with Gaussian processes (use BoTorch)
Installation
pip install lightrl
Or with uv:
uv pip install lightrl
Quick Start
Simple bandit
from lightrl import EpsilonGreedyBandit
bandit = EpsilonGreedyBandit(arms=["model_a", "model_b", "model_c"], epsilon=0.1)
for _ in range(1000):
arm = bandit.select_arm()
reward = get_reward(bandit.arms[arm]) # your reward function
bandit.update(arm, reward)
bandit.report()
Agent router with persistence
from lightrl import BanditRouter, ThompsonBandit
router = BanditRouter()
router.register("model", ThompsonBandit(arms=["haiku", "sonnet", "opus"]))
# agent loop
model_idx = router.select("model")
model = router._bandits["model"].arms[model_idx]
# ... agent does work, gets quality score ...
router.update("model", model_idx, reward=quality_score)
# persist across restarts
router.save("agent_state.json")
router = BanditRouter.load("agent_state.json")
Contextual decisions
from lightrl import LinUCBBandit
bandit = LinUCBBandit(n_arms=3, n_features=4, alpha=1.0)
context = [task_complexity, input_length, is_code, urgency]
arm = bandit.select_arm(context)
# ... execute with chosen arm ...
bandit.update(arm, context, reward)
Features
Bandit Strategies
| Strategy | Best for |
|---|---|
EpsilonGreedyBandit |
Simple explore/exploit with fixed exploration rate |
EpsilonFirstBandit |
Pure exploration phase followed by exploitation |
EpsilonDecreasingBandit |
Exploration that decays over time |
UCB1Bandit |
Upper confidence bound — principled exploration |
ThompsonBandit |
Bayesian posterior sampling — best general-purpose |
GreedyBanditWithHistory |
Sliding window for non-stationary environments |
LinUCBBandit |
Context-dependent decisions with linear features |
Cross-cutting features
- Warm start — initialize with prior beliefs:
EpsilonGreedyBandit(arms=[...], priors=[0.3, 0.7, 0.9]) - EMA decay — make any bandit adaptive to change:
EpsilonGreedyBandit(arms=[...], ema_alpha=0.1) - Persistence — JSON save/load on all bandits and the router
- BanditRouter — manage multiple named decision points with one object
Runners
two_state_time_dependent_process— alive/waiting state machine for rate-limited systems
Examples
See examples/ for 17 runnable scripts:
| Example | Demonstrates |
|---|---|
| Classics | |
ab_testing.py |
A/B test across landing page variants |
ad_serving.py |
Ad placement optimization with explore-first |
resource_allocation.py |
Dynamic worker pool sizing |
hyperparameter_search.py |
Bandit-based learning rate search |
network_routing.py |
Endpoint selection with sliding window |
minimal_example.py |
Two-state process with failure simulation |
| Agent & LLM | |
prompt_template_selection.py |
Per-task-type prompt template optimization |
agent_router.py |
Multi-decision agent loop with save/load |
contextual_model_routing.py |
LinUCB routes tasks to LLM models by features |
llm_cost_optimizer.py |
Quality-vs-cost optimization across haiku/sonnet/opus |
agent_fleet_roi.py |
Dispatch the right AI agent per task category |
| Infrastructure | |
retry_backoff.py |
Learning optimal retry wait times |
db_query_routing.py |
Query execution strategy per table size |
dynamic_pricing.py |
Price point optimization with seasonal shifts |
| Techniques | |
warm_start.py |
Prior beliefs vs naive cold start |
ema_nonstationary.py |
EMA decay vs cumulative average on regime change |
persistence.py |
JSON save/load across process restarts |
Development
uv venv --python 3.13
source .venv/bin/activate
uv pip install -e ".[dev]"
pre-commit install && pre-commit install --hook-type commit-msg
Run tests:
pytest -v tests/
tox
Read more about Multi-armed bandits.
Metadata
Release files for lightrl 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| lightrl-1.0.0.tar.gz | 24.7 kB | Without provenance | Details for lightrl-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lightrl-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for lightrl-1.0.0-py3-none-any.whl |
Total release size: 34.9 kB
Release files / lightrl-1.0.0.tar.gz
| Download URL | lightrl-1.0.0.tar.gz |
|---|---|
| Size | 24.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2e211043497e1570639e1eeceac5499e20d1286c026055d454ed38ed758d2091
|
|
BLAKE2b-256 checksum How to use checksums |
12b629397d6b611e76b9962d9d85433801d501fced84818a3124f9d7964dd4f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / lightrl-1.0.0-py3-none-any.whl
| Download URL | lightrl-1.0.0-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bc59a3c5454a5eda3d47c865305eb00d7e60c6253f6dad6105fee79be9cc1ac2
|
|
BLAKE2b-256 checksum How to use checksums |
6f7ea8fba7c97ff1291d23205a07c51ddea2ef19c89d43d9dac02d8d49360030
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Security for lightrl 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.