Publish a plugin crate¶
atune has no contrib directory and no plugin monorepo, and it is not going to grow one. A sampler, a scheduler or a storage backend is a crate, written against the same public traits the built-ins use, published under its own name, versioned on its own schedule. This page is about that packaging step: what your crate should depend on, what its manifest should declare, and what a user does to reach it.
Nothing is published yet
No version of atune has been released, so the version numbers below are the ones a manifest will carry; until the first release a plugin crate depends on a path or a git revision. Install says which routes work today.
The release-staging rehearsal builds atune_plugin_example against an extracted
atune_core archive alone, with default features off. Its evidence records the commit and source-tree hash, the consumer
lockfile, archive hashes, and the resolved feature set: proof of the staged
bytes, not a crates.io receipt.
Depend on the traits, not on the world¶
There are two crates you could point at, and the choice is not a matter of taste:
| Depend on | When | What it costs your users |
|---|---|---|
atune_core |
You need the traits and the model, which is the normal case for a sampler, a scheduler or a storage backend | Six direct dependencies, and a crate that compiles to wasm32 |
atune (the facade) |
You need something the facade owns — a feature-gated backend, or one of the heavier algorithms to build on | The facade's feature graph, resolved in your users' builds as well as yours |
The reason the first row is the default is the same reason cmaes, gp, carbs
and sqlite are facade features rather than core modules: atune_core is
wasm32-clean, and a CI gate checks it with default features off. A plugin that
depends only on the core inherits that property; one that depends on the facade
hands the decision to whatever the facade's features pull in.
Two manifest details follow from it. Set default-features = false on an
atune_core dependency and opt back into what you need, so you do not force the
operating-system surface on a consumer who is building for the browser. And put
anything OS-shaped or dependency-heavy in your crate behind a cargo feature of your
own, off by default — a clock, a filesystem, a thread pool or a linear-algebra
library is exactly the kind of thing a user should be able to decline.
If your crate also wants #[derive(Space)] for a struct-shaped search space, that
lives in atune_derive.
What the manifest should say¶
Beyond the usual name, description and license:
rust-version. atune's workspace is 1.92 and the crates are edition 2024. A plugin cannot be older than what it compiles against, and saying so turns a confusing mid-build failure into a clear refusal.- Keywords and categories. The dependency graph is the only registry there is
(see below), so discovery is search. atune itself uses
hyperparameter,optimization,tuning,hpo,search. - docs.rs metadata, if you have feature-gated items. The published crates here
set
all-features = trueand pass--cfg docsrs, so every gated item appears on docs.rs carrying a badge that names the feature it needs. A plugin with an optional sampler behind a feature wants the same treatment; without it, docs.rs simply does not show the thing you gated. - A version bound you can defend. The traits are the contract, and the shape of that contract is that new capability arrives as a defaulted method — the sampler trait carries five of them and the scheduler trait eight, and most built-ins inherit those defaults untouched. Adding a defaulted method does not break an implementer; adding a required one does. Versioning and stability is where the promise itself is stated.
What your documentation must say¶
Three claims a user cannot check from the outside, and every built-in states its own:
- Your determinism grade, from Write a sampler, in the type's own documentation, not only in a README.
- Which cargo features change behaviour, and which of them are on by default.
- For a storage backend, that it passes the conformance suite the built-in backends pass (Write a storage backend). That is the single most useful sentence such a crate can contain.
How a user reaches your crate¶
Once both your crate and its atune dependency are published, add it to an
application with cargo add; before then, use a path or git dependency as
Install explains. Pass it at the study builder — the
same line a built-in occupies, because the builder takes an Arc<dyn Sampler>,
Arc<dyn Scheduler> or Arc<dyn Storage> and does not care where the
implementation came from. There is no registry to enrol in, no plugin directory to
drop a file into, and no dynamic loading anywhere in the system.
That is a deliberate trade, and it is worth being explicit about both halves. Compiled registration means a plugin is type-checked, versioned and reproducible like any other dependency; the price is that plugins are a Rust-side extension mechanism: Python cannot select a third-party sampler (Write a sampler), and CLI storage specs cannot name a third-party backend (Write a storage backend).
Know that before promising a Python or CLI audience. Everything a Rust user does
with Tpe, they can do with yours.
Where to go next¶
| If you want to | Go to |
|---|---|
| Implement the sampler seam | Write a sampler |
| Implement the scheduler seam | Write a scheduler |
| Implement and validate a storage backend | Write a storage backend |
| Control how a trial is evaluated | Write an objective or an executor |
| Know which feature carries what | Feature reference |
| Know what atune promises about breakage | Versioning and stability |