Skip to content

Plugins and ports

Plugin dataclass

Plugin(
    namespace: str,
    version: str,
    contract: str = DEFAULT_CONTRACT,
    providers: Sequence[object] = (),
    middleware: Sequence[MiddlewareDef] = (),
    runners: Sequence[type[Any]] = (),
    adapters: Sequence[type[Any]] = (),
    profiles: Mapping[str, str] = dict[str, str](),
    templates: Mapping[str, str] = dict[str, str](),
    description: str | None = None,
)

A plugin: everything one package or module contributes (DESIGNS No. 13).

Example

plugin = Plugin( ... namespace="acme", ... version="1.2.0", ... providers=[InHousePII], ... middleware=[no_weekend_refunds], ... profiles={"baseline": "profiles/baseline.yaml"}, ... ) # doctest: +SKIP

Attributes:

Name Type Description
namespace str

Owns every name <namespace>.*. For an installed plugin it must equal the entry-point name.

version str

The plugin's own SemVer version.

contract str

The harnessweaver/v1 contract range it was built against (V5).

providers Sequence[object]

Classes decorated with @hw.provider and functions decorated with @hw.decider.

middleware Sequence[MiddlewareDef]

Objects made by @hw.middleware.

runners Sequence[type[Any]]

Classes decorated with @hw.runner.

adapters Sequence[type[Any]]

Classes decorated with @hw.adapter.

profiles Mapping[str, str]

name -> path of profile files inside the plugin's package, referenced as profile://<namespace>.<name>.

templates Mapping[str, str]

name -> path of project templates for harnessweaver init.

contributions

contributions() -> list[Contribution]

Everything this plugin contributes, with qualified names.

Raises:

Type Description
TypeError

If an item isn't decorated.

provider

provider(
    *,
    port: str,
    name: str,
    config: type[BaseModel] | None = None,
    contract: str = DEFAULT_CONTRACT,
    deterministic: bool = False,
    latency_class: str = "medium",
    stages: Iterable[str] = (),
    sample: Mapping[str, Any] | None = None,
) -> Callable[[type[T]], type[T]]

Declare a class as a provider for port (DESIGNS No. 12).

The class must be constructible from its config model alone (RULES C11). sample gives settings for the conformance suite when the config model has required fields (T5).

Raises:

Type Description
ValueError

For an unknown port or latency class, a malformed name or contract range, or a probabilistic PolicyEngine (R3).

decider

decider(
    name: str,
    *,
    deterministic: bool = True,
    contract: str = DEFAULT_CONTRACT,
) -> Callable[
    [Callable[[DecisionRequest], Awaitable[Decision]]],
    Callable[[DecisionRequest], Awaitable[Decision]],
]

Declare an async function as a routing stage (Decider port, DESIGNS No. 19).

runner

runner(
    name: str,
    *,
    reaches: Iterable[HookPoint | str],
    config: type[BaseModel] | None = None,
    contract: str = DEFAULT_CONTRACT,
    resumable: bool = False,
    sample: Mapping[str, Any] | None = None,
) -> Callable[[type[T]], type[T]]

Declare a class as a runner (DESIGNS No. 15).

reaches must be honest: loading fails if the baseline needs a hook point the runner can't fire (R4). resumable runners continue from a checkpoint after an approval; others re-run their interrupted step.

adapter

adapter(
    name: str,
    *,
    wraps: Iterable[str],
    reaches: Iterable[HookPoint | str],
    contract: str = DEFAULT_CONTRACT,
    sample: Callable[[], Any] | None = None,
) -> Callable[[type[T]], type[T]]

Declare a class as an adapter for harness.wrap() / harness.instrument().

wraps lists the fully qualified type names it accepts, such as "openai.AsyncOpenAI"; they are matched by name, so nothing is imported.

Ports

The port Protocols providers implement: part of the public extension surface (DESIGNS No. 13).

ModelProvider

Bases: Protocol

Port ModelProvider: answers a neutral ModelRequest (DESIGNS No. 16).

Providers translate to and from their vendor's API; no SDK type crosses this port (R7). Errors should be raised as ProviderError.

StreamingModelProvider

Bases: Protocol

A ModelProvider that can also stream.

stream yields StreamChunk items and then one final ModelResponse carrying the usage.

Guardrail

Bases: Protocol

Port Guardrail: checks one piece of content (DESIGNS No. 20).

PolicyEngine

Bases: Protocol

Port PolicyEngine: deterministic allow, deny or require-approval (R3).

Providers must be declared deterministic=True and are built from PolicyConfig plus their own settings. They combine rules with decide_by_rules.

Decider

Bases: Protocol

Port Decider: one routing stage (DESIGNS No. 19).

Embedder

Bases: Protocol

Port Embedder: texts to vectors, for the semantic routing stage (DESIGNS No. 19).

ContextProvider

Bases: Protocol

Port ContextProvider: retrieves items for a query (DESIGNS No. 21).

A provider must itself return only what the envelope's tenant and principal may see; the context module re-checks every item's ACL anyway and drops the rest (defense in depth).

Memory

Bases: Protocol

Port Memory: key-value entries in a namespace, with expiry (DESIGNS No. 21).

The harness picks the namespace, (tenant, principal, task), so one tenant or principal never reads another's entries. Times come from the run's clock.

ApprovalChannel

Bases: Protocol

Port ApprovalChannel: tells approvers a run is waiting for them (DESIGNS No. 18).

The request is already saved when notify is called; answers come back through harness.approve. A handler must not answer in-line.

StateStore

Bases: Protocol

Port StateStore: runs, checkpoints, idempotency records and the event log.

claim_approval async

claim_approval(
    checkpoint: Checkpoint, approval_id: str
) -> bool

Replace the run's checkpoint only if it still waits on approval_id.

Must be atomic, across processes for a store they share: of any answers racing for one request, exactly one gets True and resumes the run (ADR-0044).

claim_idempotency async

claim_idempotency(
    tenant: str, task: str, key: str, run_id: str
) -> str

Record run_id for (tenant, task, key) unless one exists; return the owner.

append_recording async

append_recording(call: RecordedCall) -> None

Keep one recorded call (state.record: true); a repeated seq replaces it.

recording async

recording(run_id: str) -> list[RecordedCall]

A run's recorded calls in seq order.

put_sample async

put_sample(sample: OnlineSample) -> None

Keep a graded online sample (evals.online); one per run.

samples async

samples(task: str | None = None) -> list[OnlineSample]

Online samples, oldest first, for one task or all.

Evaluator

Bases: Protocol

Port Evaluator: grades one eval sample (DESIGNS No. 23).

Graders only measure; they never grant anything (R3).

Sandbox

Bases: Protocol

Port Sandbox: runs untrusted code away from the host process (S4).

The code must not reach the host's files, network or environment. Errors in the code are returned in SandboxResult, not raised.

Runner

Bases: Protocol

Port Runner: executes a task. Runners don't implement controls; hooks do.

resume is the runner's own resume point from a checkpoint, or None for a fresh run.

TelemetrySetup

Bases: Protocol

Port Telemetry: sets up an OpenTelemetry SDK and exporter (R5, ADR-0010).

Used only when observability.configure_sdk is true; the core itself only uses the OpenTelemetry API.

network_allowed

network_allowed() -> bool

Whether live providers may call out. False when HARNESSWEAVER_TEST_MODE=1 (R14).