Customize Corpus backends and readiness policies#
Optional packages are not a single yes/no capability. A package may be installed while its model or system asset is missing, a policy may select a backend that has not run yet, and a successful fallback may be deliberately reported as degraded.
This example shows the public pieces that keep those states separate:
component_capabilitiesfor side-effect-free readiness inspection,AudioReader.plan_asr_backendsfor exact preflight selection,BackendPolicyfor ordered/offline/strict selection policy,ASRBackendfor a user-provided speech recognizer,DocumentReader.backend_reportsfor structured runtime evidence.
The executable path uses a tiny synthetic local backend. It performs no network access, model download, or audio decoding.
# Authors: The scikit-plots developers
# SPDX-License-Identifier: BSD-3-Clause
from __future__ import annotations
import tempfile
from pathlib import Path
from scikitplot.corpus import (
ASRBackend,
ASRRequest,
AudioReader,
BackendPolicy,
component_capabilities,
)
Inspect capability readiness without loading models#
installed and ready deliberately mean different things. Whisper
packages may be discoverable while model readiness is unknown until first
use; the readiness probe must not download a model merely to make a green
status claim.
snapshot = component_capabilities(
("asr:faster-whisper", "asr:openai-whisper")
)
for name, report in snapshot.items():
print(
name,
{
key: report[key]
for key in ("installed", "assets_ready", "ready", "selected", "active")
},
)
asr:faster-whisper {'installed': True, 'assets_ready': None, 'ready': None, 'selected': False, 'active': False}
asr:openai-whisper {'installed': True, 'assets_ready': None, 'ready': None, 'selected': False, 'active': False}
Provide a user-side ASR backend#
Custom backends receive one immutable ASRRequest, keeping the
extension signature stable as future request metadata is added.
def local_demo_asr(request: ASRRequest):
"""Return one deterministic segment without touching the media bytes."""
return [
{
"text": f"synthetic transcript for {request.media_path.stem}",
"timecode_start": 0.0,
"timecode_end": 1.25,
"provider": "gallery-demo",
}
]
custom_backend = ASRBackend(
name="gallery-local-asr",
transcribe=local_demo_asr,
requires_network=False,
may_download=False,
offline_capable=True,
)
Make backend order an explicit policy#
include_unlisted=False means the two built-in Whisper implementations
are not attempted. This is useful for deterministic tests and for projects
that own their inference runtime.
policy = BackendPolicy.resilient().with_order(
"gallery-local-asr",
include_unlisted=False,
)
with tempfile.TemporaryDirectory() as tmpdir:
audio_path = Path(tmpdir) / "demo.mp3"
audio_path.write_bytes(b"")
reader = AudioReader(
audio_path,
transcribe=True,
backend_policy=policy,
asr_backends=(custom_backend,),
)
# Preflight uses the same candidate factory as execution but does not call
# ``local_demo_asr`` or any model backend.
plan = reader.plan_asr_backends()
print("preflight:", plan.to_dict())
assert plan.selected == ("gallery-local-asr",)
chunks = list(reader.get_raw_chunks())
print("chunk text:", chunks[0]["text"])
print("provider:", chunks[0]["asr_metadata"]["provider"])
print("backend report:", reader.backend_reports[-1])
print("policy skips:", reader.backend_reports[-1].get("skip_details", []))
# The runtime report is the evidence that a selected backend actually ran.
assert chunks[0]["asr_metadata"]["provider"] == "gallery-demo"
assert reader.backend_reports[-1]["status"] == "success"
assert reader.backend_reports[-1]["backend"] == "gallery-local-asr"
preflight: {'policy': 'resilient', 'declared': ['faster-whisper', 'openai-whisper', 'gallery-local-asr'], 'ordered': ['gallery-local-asr'], 'eligible': ['gallery-local-asr'], 'selected': ['gallery-local-asr'], 'skipped': [], 'selected_capabilities': [], 'skip_details': []}
chunk text: synthetic transcript for demo
provider: gallery-demo
backend report: {'status': 'success', 'backend': 'gallery-local-asr', 'attempted': ['gallery-local-asr'], 'missed': [], 'skipped': [], 'policy': 'resilient', 'selected_capabilities': [], 'active_capability': None, 'skip_details': [], 'errors': []}
policy skips: []
Policy presets express orchestration, not reader semantics#
offline forbids network and download side effects rather than equating
“may download” with “must download now”. A backend may participate when it
can enforce local-only execution; model-backed adapters can therefore use an
already-cached model without weakening the policy. Readers still decide what
constitutes a successful result and which runtime errors are safe to fall
back from.
for item in (
BackendPolicy.resilient(),
BackendPolicy.offline(),
BackendPolicy.first_available(),
BackendPolicy.strict(),
):
print(item.name, item)
resilient BackendPolicy(name='resilient', order=(), include_unlisted=True, allow_fallback=True, require_ready=False, allow_network=True, allow_download=True, on_exhausted=<ErrorPolicy.COLLECT: 'collect'>)
offline BackendPolicy(name='offline', order=(), include_unlisted=True, allow_fallback=True, require_ready=False, allow_network=False, allow_download=False, on_exhausted=<ErrorPolicy.COLLECT: 'collect'>)
first-available BackendPolicy(name='first-available', order=(), include_unlisted=True, allow_fallback=False, require_ready=False, allow_network=True, allow_download=True, on_exhausted=<ErrorPolicy.COLLECT: 'collect'>)
strict BackendPolicy(name='strict', order=(), include_unlisted=True, allow_fallback=True, require_ready=False, allow_network=True, allow_download=True, on_exhausted=<ErrorPolicy.RAISE: 'raise'>)
Total running time of the script: (0 minutes 0.009 seconds)
Related examples