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 (v0.1)
- Parses a versioned request envelope.
- Builds provider
RequestContext/ProviderRequestand callsexecute(...). - 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 contract_v1 request envelopes.
- Provides
ProviderIdentityso providers derive the same RabbitMQ queue/binding, routing prefix, and signing source fromPROVIDER,PROVIDER_INSTANCE, and optionalPROVIDER_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.#"
identity.signing_provider # "nrec_pr"
The common case stays small: ProviderIdentity("vmware", "dev") gives the
vmware.dev routing prefix and vmware_dev signing source. Signing deliberately
uses only provider + instance, so all nrec.pr.<id> runtimes share the
nrec_pr signing scope without a separate override knob.
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;falsestill represents a completed no-op or degraded outcomeresolved: yourProviderResult.data(after runtime normalization)provenance: optional; extracted fromProviderResult.data["provenance"]dry_run: derived from the request payload
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 fromresolvedto avoid duplicate TaskEvent rows.provenance(dict): lifted into the top-levelprovenancefield.
Providers may opt into live updates by accepting a keyword-only progress
argument on execute. Call it with a short user-facing message and optional
payload while work is running:
def execute(self, action, request, context, *, progress=None):
if progress:
progress("Creating virtual machine", {"stage": "openstack_create"})
return create_virtual_machine(request)
The callback is best-effort and transport-owned. Providers without the argument keep the original three-argument contract unchanged.
Avoid embedding envelope-shaped keys (requested, resolved,
request_input, request_defaults) inside ProviderResult.data. The runtime
will drop them to keep the stored result compact and predictable.
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(default1)concurrency(default1)max_attempts(default5)idempotency_cache_max_entries(default1024)dead_letter_exchange(optional)dead_letter_routing_key(optional)heartbeat_seconds(default60)blocked_connection_timeout_seconds(default30)updates_signing_enabled(defaultFalse)signing_kid(required when signing enabled)signing_secret(UTF-8 shared secret; set this or_b64)signing_secret_b64(base64-encoded shared secret; preferred for random bytes)
Update Emission
Use TaskReporter for provider task lifecycle updates. The runtime keeps
transport details compatible with orchestrator consumers.
Docs:
docs/runtime-contract.mddocs/provider-updates.mddocs/migration-task-reporter.mddocs/runtime-upgrade-checklist.mddocs/release-notes-task-reporter.md
Tag and push:
git tag v0.2.0
git push origin v0.2.0
Release files for osp-provider-runtime 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| osp_provider_runtime-0.3.1.tar.gz | 95.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| osp_provider_runtime-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 138.7 kB
Release files / osp_provider_runtime-0.3.1.tar.gz
| Download URL | osp_provider_runtime-0.3.1.tar.gz |
|---|---|
| Size | 95.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
22baec61da1c1925e431d34b71ba1f5d84134c770ecbe31b9e5111a36e53bb9e
|
|
BLAKE2b-256 checksum How to use checksums |
4478c064f3a30971de9b99643414e189be79b32b6e3e77bf3dc9800ddf20672e
|
| 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.3.1-py3-none-any.whl
| Download URL | osp_provider_runtime-0.3.1-py3-none-any.whl |
|---|---|
| Size | 43.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0fbd0d667e7c3ea85bb1dc70b51c7795e00f43cf62202e0c0a29289589515a31
|
|
BLAKE2b-256 checksum How to use checksums |
738e6dd5a9dd241c7482815c054ceb58b1bd6ad9de093730023d5ca363b6b7ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|