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 lane name 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.provider_lane # "nrec_pr"
The common case stays small: ProviderIdentity("vmware", "dev") gives the
vmware.dev routing prefix and vmware_dev lane name. Instance IDs refine
routing within a lane without changing its attribution name.
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. Use ProgressReporter so the same action also works
without live delivery:
from osp_provider_contracts import ProviderResult
from osp_provider_runtime import ProgressReporter
def execute(self, action, 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",
external_id=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. Providers
without the argument keep the original three-argument contract unchanged.
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_id: cross-service join keydispatch_id: one delivery attemptproviderandtask_typestep,outcome, andduration_mserror.type,error.message, anderror.codewhen 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(...)
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)
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.13
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.13.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| osp_provider_runtime-0.3.13-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.2 MB
Release files / osp_provider_runtime-0.3.13.tar.gz
| Download URL | osp_provider_runtime-0.3.13.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
87e40041c786d82e110bcf2004a452699773a1c9a99607d8b92892dd63604f58
|
|
BLAKE2b-256 checksum How to use checksums |
c9a14ee7eea43affee8383c86d26861f885fad3769dbbd469c0d61f03cbd2132
|
| 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.13-py3-none-any.whl
| Download URL | osp_provider_runtime-0.3.13-py3-none-any.whl |
|---|---|
| Size | 43.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
318cd5902199d6de8e1f9f382a8a99082a13a545514a0e26633ecb57e3eb99e5
|
|
BLAKE2b-256 checksum How to use checksums |
4dd1d82830704460c2871cc44a2243b3a3a07db6799b06e0256bfa1eeeb47457
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|