Plugins¶
OpenTernary can be extended with four kinds of plugins. Each is an installed Python package that registers an object under a versioned entry point group.
| Kind | Entry point group | Selected by | Reserved names |
|---|---|---|---|
| backend | openternary.backends.v1 |
--backend NAME / quantization.backend |
ternary, torchao |
| pass | openternary.passes.v1 |
quantization.passes: [{name: NAME}] |
clip, noop |
| exporter | openternary.exporters.v1 |
export --format NAME |
safetensors, ternary-packed, torchao, gguf |
| evaluator | openternary.evaluators.v1 |
evaluate --evaluator NAME |
diffusion |
Rules¶
- Listing never runs plugin code.
openternary pluginsandbackends listread entry-point metadata only. - A plugin is imported only when selected by name, and then it must have
api_version = 1. Otherwise:plugin API version mismatch(exit code3). - Duplicates and reserved names are conflicts. Two packages registering the same name, or a plugin using a
built-in name, show up as
conflictand cannot be selected. - Plugins are not sandboxed. They run in the same process with your user's permissions. Install only plugins you trust.
- Results stay honest. Backend output is wrapped in the same manifest as built-in output, and exporter output is written and published atomically by OpenTernary.
$ openternary plugins
Kind Name Version status
────────────────────────────────────────
passes shrink 0.1.0 · discovered
Interfaces (API version 1)¶
| Kind | The registered object must provide |
|---|---|
| backend | a class with api_version = 1 and spec: BackendSpec (whose name matches the entry point), validate(config), and convert(source, destination, config) returning a ConvertReport-compatible result. It is instantiated with no arguments. |
| pass | an object with api_version = 1, plan(options, backend) -> dict returning {"stage": "pre_quantization", "runtime_state": None}, and apply(tensor, options) -> tensor, which must keep shape, dtype and device. Passes currently apply to the ternary backend only. |
| exporter | an object with api_version = 1, validate(source_format) (raise to refuse), and export(source, staging_directory), which writes into the staging folder. Manifest and atomic publication are handled for you. |
| evaluator | an object with api_version = 1, validate(raw_profile) -> profile, and evaluate(config, model, profile, run) -> dict (the report, saved as evaluation.json). Evaluators version their own quality contract. |
BackendSpec lives in openternary.backends:
from openternary.backends import BackendSpec
spec = BackendSpec(
name="mybackend",
distribution="my-openternary-backend", # the installed distribution, for version reporting
schemes=("myscheme",),
weight_dtypes=("ternary",),
formats=("safetensors",),
implementation="my_backend:MyBackend",
)
Example: a pass plugin¶
This plugin multiplies every weight by a constant before quantization. It is only an illustration: AbsMean rescales anyway, so it does not change the codes. It is a complete, working package.
pyproject.toml
[project]
name = "openternary-shrink"
version = "0.1.0"
requires-python = ">=3.11"
[project.entry-points."openternary.passes.v1"]
shrink = "openternary_shrink:SHRINK"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
src/openternary_shrink/__init__.py
"""An example OpenTernary pass: multiply every weight by a constant before quantizing."""
import math
class Shrink:
api_version = 1 # the plugin API this object implements
def plan(self, options, backend):
factor = options.get("factor")
if set(options) != {"factor"} or not isinstance(factor, (int, float)) or not math.isfinite(factor) or factor <= 0:
raise ValueError("shrink requires only a finite factor > 0")
# Tensor passes must be stateless transformations applied before quantization.
return {"stage": "pre_quantization", "runtime_state": None}
def apply(self, tensor, options):
return tensor * options["factor"] # same shape, dtype and device
SHRINK = Shrink()
Install it into the same environment as OpenTernary and use it:
openternary plan tiny-llama --config shrink.yaml # "Passes shrink"
openternary quantize tiny-llama --config shrink.yaml --device cpu --output runs/shrink
Compatibility¶
Version 1 never guesses: unknown formats are not converted, and old manifests are not silently upgraded. An
incompatible change to a plugin interface will use a new entry point group (….v2) and a new api_version, so
existing plugins keep working or fail clearly.