Skip to main content

osp-provider-runtime

Thin, boring runtime harness for OSP providers.

This package handles RabbitMQ message plumbing so provider implementations can focus on business logic.

What it does

  • Parses a versioned request envelope.
  • Builds provider RequestContext/ProviderRequest and calls execute(...).
  • Dispatches optional read-only provider assessment RPCs without task execution.
  • Emits canonical asynchronous task updates and synchronous control-RPC replies.
  • Applies explicit ack/requeue/dead-letter decisions.
  • Emits structured logs for delivery decisions.
  • Supports explicit runtime knobs for prefetch/concurrency/retries/transport timeouts/DLQ.
  • Accepts wire-version-6 request envelopes with task identity and cleared Gate keys separated from provider action payload.
  • Provides ProviderIdentity so providers derive the same RabbitMQ queue/binding, routing prefix, and lane name from PROVIDER, PROVIDER_INSTANCE, and optional PROVIDER_INSTANCE_ID.

Provider identity

Use a provider family for permissions and API requests, then add an instance only when you need a separate runtime lane:

from osp_provider_runtime import ProviderIdentity

identity = ProviderIdentity(
    provider="nrec",
    instance="pr",
    instance_id=37,
)

identity.routing_prefix   # "nrec.pr.37"
identity.request_queue    # "provider.nrec.pr.37"
identity.request_binding  # "nrec.pr.37"

The common case stays small: ProviderIdentity("vmware", "dev") gives the vmware.dev routing prefix. Instance IDs refine routing within a lane.

Result payload conventions

Provider results should keep ProviderResult.data focused on the resolved values for the task. The runtime builds the full update payload envelope:

  • success: provider outcome flag; false still represents a completed no-op or degraded outcome
  • resolved: your ProviderResult.data (after runtime normalization)
  • provenance: optional; extracted from ProviderResult.data["provenance"]
  • produced_resources: typed provider-native references produced by the operation

Raise a ProviderError when the task should enter a failed lifecycle state. ProviderResult(success=False) does not turn a completed outcome into a task failure.

Special keys in ProviderResult.data:

  • progress_events (list): lifted into the update payload and removed from resolved to avoid duplicate TaskEvent rows.
  • provenance (dict): lifted into the top-level provenance field.

Providers may opt into live updates by accepting a keyword-only progress argument on execute. Use ProgressReporter so the same action also works without live delivery:

from osp_provider_contracts import ProducedResource, ProviderResult
from osp_provider_runtime import ProgressReporter


def execute(self, request, context, *, progress=None):
    reporter = ProgressReporter(progress)
    reporter.report("Creating virtual machine", {"stage": "openstack_create"})
    vm = create_virtual_machine(request)
    return ProviderResult(
        success=True,
        message="Virtual machine created",
        produced_resources=(ProducedResource(kind="vm", target_ref=vm.id),),
        data={"progress_events": reporter.events},
    )

With a callback, the event is published live and reporter.events stays empty. Without one, the event is returned through progress_events and the runtime expands it from the terminal update. This keeps each event on one delivery path. The callback is best-effort and transport-owned. request.action is the sole action source.

Active task cancellation

The .cancellation.rpc control binding invokes the optional provider-owned task_cancellation(task_ref) and cancel_task(task_ref) hooks on the separate control executor. Providers report current backend capability and perform the actual stop. A successful RPC only records the request; it emits no terminal task update. The execution must finish with TaskCancelled(ProviderResult(...)) after confirmed termination, retaining any surviving resource references. Providers without those hooks report unsupported cancellation.

Task logging

The runtime binds the task context once around execute() and emits one task.summary event when the delivery finishes. Provider log sinks must use JSON serialization so these fields are retained:

  • task_ref: stable provider execution UUID
  • dispatch_id: one delivery attempt
  • provider and task_type
  • step, outcome, and duration_ms
  • error.type, error.message, and error.code when applicable

Use task_step("external.operation") around meaningful external operations. It records the step in the summary trail and logs a full traceback before re-raising failures. Task code must not bind correlation fields itself.

from osp_provider_runtime import task_step

with task_step("openstack.create"):
    server = openstack.create_server(...)

Provider data is preserved inside result.resolved; its keys cannot override the shared result fields. Runtime extracts the existing progress_events and provenance metadata into their dedicated result fields.

The orchestrator already owns the original task request. Provider updates do not echo it; HTTP and CLI read models compose request and result when needed.

What it does not do

  • No provider framework.
  • No plugin system.
  • No workflow orchestration.

Install

pip install osp-provider-runtime

Development

env -u VIRTUAL_ENV uv sync --extra dev
hatch shell
hatch run dev:check
hatch run dev:build
hatch run dev:verify

Runtime Knobs

Set these via RuntimeConfig in your provider runtime_app.py:

  • prefetch_count (default 1)
  • concurrency (default 1)
  • max_attempts (default 5)
  • idempotency_cache_max_entries (default 1024)
  • snapshot_cache_ttl_seconds (default 3600)
  • dead_letter_exchange (optional)
  • dead_letter_routing_key (optional)
  • heartbeat_seconds (default 60)
  • blocked_connection_timeout_seconds (default 30)

Update Emission

Use TaskReporter for provider task lifecycle updates. The runtime keeps transport details compatible with orchestrator consumers.

Docs:

  • docs/runtime-contract.md
  • docs/provider-updates.md
  • docs/migration-task-reporter.md
  • docs/runtime-upgrade-checklist.md
  • docs/release-notes-task-reporter.md

Tag and push:

git tag v0.2.0
git push origin v0.2.0

Execution build evidence

The runtime adds payload.execution to live and terminal task updates. Each actual invocation gets a fresh attempt_id; cached duplicate deliveries replay the original update and identity. dispatch_id links the invocation to its orchestrator dispatch. Approval resumes and broker retries can run another build.

Deployment supplies OSP_IMAGE_DIGEST (registry manifest digest), OSP_GIT_COMMIT (the image's OCI revision label), and optional OSP_RELEASE_VERSION (a production release tag). Missing values are omitted, not inferred from moving tags or a package version. The progress callback captures the attempt evidence before it crosses worker-thread boundaries.

Providers with external data wrap execution in data_snapshot(path) and use pinned_data_dir() in their data path resolver. The deployment publisher writes .osp-revision into a retained immutable directory and switches its current symlink atomically. The runtime records that revision only when such a snapshot is used. Plain local working copies remain unversioned. This identifies code and provider data, not secrets, live infrastructure, inventory, or promotion state.

RuntimeConfig.control_concurrency sizes the independent control-RPC worker pool and its broker prefetch (default 1). Providers expose this as CONTROL_CONCURRENCY; 4 allows overlapping reads without increasing task execution concurrency. Delivery logs include worker wait, handler and finalization timings, correlated with the orchestrator by rpc_id.

Metadata

Release files for osp-provider-runtime 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for osp-provider-runtime 0.9.0
File Size Uploaded Provenance
osp_provider_runtime-0.9.0.tar.gz 71.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for osp-provider-runtime 0.9.0
File Interpreter ABI Platform
osp_provider_runtime-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 120.6 kB

Release files / osp_provider_runtime-0.9.0.tar.gz

Download URL osp_provider_runtime-0.9.0.tar.gz
Size 71.0 kB
Tags Source
SHA-256 checksum
How to use checksums
33032ab5bcb67d7058c466747cac013dde97b740b430b3bf8f6c6fe7f8e21806
BLAKE2b-256 checksum
How to use checksums
40ba5e811a2daa7d0e0c30e5746375216435abb1da30a44d34127532b46ae07f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.5

Release files / osp_provider_runtime-0.9.0-py3-none-any.whl

Download URL osp_provider_runtime-0.9.0-py3-none-any.whl
Size 49.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9a82ecbc3b1d9268e70ab07e9951e5b4a0e7cec116480944060e3257b64050a2
BLAKE2b-256 checksum
How to use checksums
2409ef2f4d885346f4cc3a65be753952075cf9da0be270224acbfd580b046d02
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.5

Security for osp-provider-runtime 0.9.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page