Skip to content

External Provider Packages

External adapter packages can register WorldForge providers without modifying the WorldForge repository. WorldForge discovers them at construction time through the worldforge.providers Python entry-point group.

Entry-point declaration

Add an entry to your package's pyproject.toml:

[project.entry-points."worldforge.providers"]
my-policy = "my_pkg.adapters:make_my_policy_provider"

The entry-point name (my-policy) is the provider name WorldForge will surface in providers(), doctor(), and benchmark/evaluation reports. The value (my_pkg.adapters:make_my_policy_provider) is a fully qualified callable that returns a :class:~worldforge.providers.base.BaseProvider.

Factory contract

The referenced callable takes one optional keyword argument and returns a BaseProvider:

from worldforge.providers import BaseProvider, ProviderProfileSpec
from worldforge import ProviderCapabilities


def make_my_policy_provider(*, event_handler=None) -> BaseProvider:
    return MyPolicyProvider(event_handler=event_handler)

Three rules:

  1. The provider's name attribute must equal the entry-point name. WorldForge raises WorldForgeError from ProviderCatalogEntry.create() when they disagree, which prevents accidental name collisions in user-facing output.
  2. The factory must return a BaseProvider subclass. Returning anything else is rejected with a typed error when the entry is invoked.
  3. The provider follows the same env-gated auto-registration rules as in-repo providers: configured() must return True for the provider to be auto-registered. Hosts can still register an unconfigured provider explicitly via forge.register_provider(...).

Contract CLI

Run the provider contract CLI before opening an issue or PR for an external adapter:

uv run worldforge provider contract my-policy --format json
uv run worldforge provider contract --factory my_pkg.adapters:make_my_policy_provider --format markdown

The command accepts either a registered provider name or a direct module:factory path. It emits safe-to-attach JSON or Markdown evidence with provider metadata, passed checks, skipped host-owned checks, failures, next steps, and validation commands.

WorldForge does not call non-local provider capabilities by default. A configured remote, robotics, or optional-runtime provider records those capability checks as skipped until the host reruns with --live on a prepared machine. Use --score-info, --score-candidates, and --policy-info when a score or policy adapter needs provider-native fixture payloads.

Failure behaviour

Discovery never crashes the host. Each entry-point that cannot be wrapped is recorded with a typed reason:

Cause Example reason
Module import fails (missing optional dependency) missing dependency: No module named 'torch'
Loaded value is not callable entry point did not resolve to a callable
Name collides with an in-repo provider duplicate name (in-repo provider already registered)
Two entry points share a name duplicate name (already discovered earlier in this group)
Loader raises any other exception load failed: <message>
Factory raises at construction time factory raised: <message>

The full report lives on WorldForge.entry_point_discovery():

from worldforge import WorldForge

forge = WorldForge()
report = forge.entry_point_discovery()
print(report.discovered_count, "external providers discovered")
for skip in report.skipped:
    print(f"skipped {skip.name}: {skip.reason}")

Checkout-safe package demo

Run the demo workflow when you want a complete package-shape example without publishing or installing anything globally:

uv run python scripts/demo_showcases.py run external-provider-package --workspace-dir .worldforge/demo-showcases --overwrite

It writes a temp package under the demo workspace with a pyproject.toml, provider factory, and package-local test. The preserved external-provider-discovery.json shows successful discovery, explicit disabled-discovery behavior, duplicate in-repo provider-name handling, and a missing optional dependency skip reason. The output is safe to attach, but it is package-shape evidence only; it is not PyPI publishing, a live provider call, or promotion evidence.

Disabling discovery

Two switches turn discovery off:

  1. Constructor flag: WorldForge(discover_entry_points=False) skips discovery entirely.
  2. Environment variable: setting WORLDFORGE_DISABLE_ENTRY_POINTS to a non-empty value has the same effect for all forges in the process. Hosted environments use this to keep CI runs deterministic when third-party packages are installed for unrelated reasons.

When discovery is disabled, forge.entry_point_discovery().enabled is False and no external providers are registered.

Stability

This surface is provisional. The entry-point group name and discovery report shape are stable for the current release, but EntryPointSkip.reason strings are human-readable diagnostics rather than a machine-parseable contract. Treat them like log messages.

Symbol Purpose
worldforge.ENTRY_POINT_GROUP The entry-point group name (worldforge.providers).
worldforge.ENTRY_POINT_DISABLE_ENV_VAR Name of the disable env var.
worldforge.discover_entry_point_providers Run discovery without instantiating a forge.
worldforge.EntryPointDiscoveryReport Frozen dataclass with the run summary.
worldforge.EntryPointSkip One skip record (name, value, reason).
WorldForge.entry_point_discovery() The report captured at construction time.

Validation

uv run pytest tests/test_provider_entry_points.py tests/test_provider_catalog.py
uv run pytest tests/test_provider_contracts.py
uv run mkdocs build --strict