cookbook/09_specs_and_publishing.py
Rama main · réplica de lectura del repositorio original.
# Copyright (c) 2026 OpenNVR
# SPDX-License-Identifier: Apache-2.0
"""What the adapter says about itself — and how to ship it.
Demonstrates: `adapter_asyncapi`, `contract_openapi_extras`,
`error_responses`, `infer_request_body`, `CONTRACT_VERSION`, and the
`opennvr-adapter spec` command.
Every adapter publishes two machine-readable documents, both generated
from the contract types it actually returns, so neither can drift from
the implementation:
* **OpenAPI 3.1** at `/openapi.json` — the HTTP surface, with Swagger
UI at `/docs`. Point a client generator at a running adapter and it
works.
* **AsyncAPI 3.0** at `/asyncapi.json` — the `/infer/stream` protocol,
which OpenAPI cannot express.
"""
import json
from opennvr_adapter_sdk import Adapter, adapter_asyncapi
from opennvr_adapter_sdk.openapi import CONTRACT_VERSION
adapter = Adapter("demo-specs", version="2.0.0", vendor="ACME",
license="Apache-2.0", tasks=["object_detection"])
adapter.on_image()(lambda call: [])
def the_http_surface() -> dict:
"""FastAPI generates it; the SDK supplies what FastAPI cannot infer
— the response models, the `/infer` body for this adapter's own
body shape, the §7 envelope on every error status, and the bearer
scheme where the middleware enforces it."""
document = adapter.app.openapi()
assert document["info"]["x-opennvr-contract-version"] == CONTRACT_VERSION
assert document["info"]["x-opennvr-tasks"] == ["object_detection"]
return document
def the_streaming_surface() -> dict:
"""Generated from the §6 message types, so every field a session
exchanges is described. An adapter that does not stream publishes
the document with no channels rather than advertising a protocol it
will answer 501 to."""
return adapter_asyncapi("demo-specs", "2.0.0", supports_stream=False,
tasks=("object_detection",))
def write_them_for_the_docs_site() -> None:
"""Or from the command line, without running the adapter at all::
opennvr-adapter spec > openapi.json
opennvr-adapter spec --format asyncapi --yaml > asyncapi.yaml
Which is what lets them go into CI, a client generator, or a
published API reference."""
with open("openapi.json", "w") as handle:
json.dump(the_http_surface(), handle, indent=2)
# ── Publishing the adapter ─────────────────────────────────────────
#
# 1. `opennvr-adapter validate .` — green means KAI-C will accept it.
# 2. Build and push the image where operators can pull it.
# 3. Write the listing so deployments can find it:
#
# opennvr-adapter listing . --image ghcr.io/you/demo-specs:1.0.0
#
# That prints an adapters-index entry — identity, the task it
# advertises, its permissions, the image digest, and the model card.
# Open a PR adding it to `server/config/adapters_index.yml` in
# open-nvr and every deployment can install it.
#
# Your adapter stays yours: the SDK is Apache-2.0 and talks to the
# platform over HTTP, so the licence is your choice.
app = adapter.app