Plugin Guide

September 1, 2026 · View on GitHub

lm-eval resolves components — model backends, filters, metrics, aggregations — by name against internal registries. A plugin is a component that lives in your own package and gets registered into those registries at runtime, so lm-eval treats it exactly like a built-in without you forking or editing the lm-eval source tree.

What can be contributed

ComponentEntry-point groupDecoratorResolved by
Modellm_eval.models@register_model--model <name>
Filterlm_eval.filters@register_filterfilter_list in a task YAML
Metriclm_eval.metrics@register_metricmetric_list in a task YAML
Aggregationlm_eval.aggregations@register_aggregationaggregation in a task YAML

Tasks are not plugins — point --include_path at a directory of task YAMLs instead. See the New Task Guide.

1. Entry points (zero-config, recommended for published packages)

Declare the entry point in your package's pyproject.toml, mapping the name lm-eval should resolve to module:object:

[project.entry-points."lm_eval.models"]
my-backend = "your_pkg.models:MyBackendLM"

[project.entry-points."lm_eval.metrics"]
my-metric = "your_pkg.metrics:my_metric"

Once your package is pip installed it works with no extra flags:

lm-eval run --model my-backend --tasks hellaswag

Discovery is lazy: the entry point is recorded as a placeholder, and your module is only imported when the component is actually requested. A broken plugin therefore only breaks runs that ask for it, and is reported with the offending name.

Your object can also carry its @register_* decorator — importing the module fires the decorator, which upgrades the placeholder in place. The two are reconciled automatically as long as the entry-point value and the decorated object agree.

Metrics need their decorator

higher_is_better and the default aggregation are recorded as side effects of @register_metric(...), not as separate entry points. So a metric plugin's module must run the decorator, and the entry point should name the decorated function:

# my_pkg/metrics.py
from lm_eval.api.registry import register_aggregation, register_metric


@register_aggregation("my-agg")
def my_agg(items):
    return sum(items) / len(items)


@register_metric(metric="my-metric", higher_is_better=True, aggregation="my-agg")
def my_metric(items):
    return sum(items)

If a metric's aggregation is also a plugin, register it in the same module — the aggregation must already be in the registry when @register_metric runs.

2. Explicit import (for local or unpublished modules)

Point lm-eval at one or more modules to import before evaluation, so their @register_* decorators run:

lm-eval run --model my-backend --plugins my_pkg.models --tasks hellaswag

--plugins accepts a comma-separated or space-separated list, and is also settable as plugins: in a --config YAML. A module that fails to import is logged and skipped rather than aborting the run.

Precedence

Built-in components always win. Discovery skips any alias that is already registered, so a plugin declaring acc cannot shadow the core acc metric — the plugin module is not even imported. Pick a distinctive name.

Programmatic use

Entry-point plugins need no wiring at all — they are discovered on first name resolution, including from lm_eval.simple_evaluate(...).

For a module that is not an installed entry-point plugin, import it yourself before evaluating.

!!! Warning Registration mutates process-global registries and is not scoped to a single evaluation:

import lm_eval
from lm_eval.api.registry import import_plugins

import_plugins(["my_pkg.models"])   # or simply: import my_pkg.models

results = lm_eval.simple_evaluate(model="my-backend", tasks=["hellaswag"])