Standard ASR

standard_asr.engine

The engine-author facade: everything you need to build a compliant ASR plugin, in a single import path.

from standard_asr.engine import (
    EngineBase,
    BaseConfig,
    BaseProperties,
    DeclaredCapabilities,
    BatchCapabilities,
    FlagCap,
    LanguageCaps,
    PreparedAudio,
    RuntimeParams,
    TranscriptionResult,
)

Engine-author surface: everything you need to build a compliant ASR plugin.

This module is the single import path for engine authors. Where the top-level standard_asr namespace is curated for application developers (discover an engine, pass audio, read a result), standard_asr.engine aggregates the types an engine author implements and declares against:

Exceptions an engine raises live in standard_asr.contract.exceptions (and are also re-exported at the package top level). Compliance helpers for testing your plugin live in standard_asr.compliance.

Example
>>> from standard_asr.engine import (
...     EngineBase,
...     BaseConfig,
...     BaseProperties,
...     DeclaredCapabilities,
...     BatchCapabilities,
...     LanguageCaps,
...     FlagCap,
... )

AUTO#attributeSource

AUTO = 'auto'

AudioFormat#classSource

class AudioFormat(BaseModel)

Declared wire format for raw PCM frames fed to a streaming session.

Raises
  • ValueError

    If validation fails.

Attributes
NameTypeDescription
encodingstr

Wire encoding of the PCM frames (for example, "pcm_s16le", "mulaw"). MUST be one of the engine's wire_encodings.

sample_rateint
gt=0

Sample rate of the frames in Hz.

channels
= 1
int
gt=0

Number of interleaved channels. Defaults to 1 (mono).

BaseConfig#classSource

class BaseConfig(BaseModel, Generic[EngineNameT])

Base class for ASR engine init configuration models.

Raises
  • ValueError

    If validation fails.

Attributes
NameTypeDescription
engineEngineNameT

Discriminator equal to the entrypoint-derived engine_id.

strict
= True
bool

Global policy for unsupported standard parameters. True raises UnsupportedFeatureError; False is best_effort (ignore + diagnostic).

allow_private_urls
= False
bool

Opt-in to relax the SSRF policy so an AudioUrl may target a private/loopback/link-local address (HTTPS is still required). False by default; set True only for a trusted internal endpoint.

__pydantic_init_subclass__#class methodSource

@classmethod
def BaseConfig.__pydantic_init_subclass__(**kwargs: Any) -> None

Enforce definition-time invariants: secret annotations, flat aliases.

Definition-time guards, so a credential leak or an unclassifiable construction failure can never reach runtime:

  1. The input surface stays CLOSED, at every depth. The config itself must keep extra="forbid" (BaseConfig's default; allow stores undeclared caller data and emits it from public_dump, ignore silently drops a mistyped key), and every nested input container its schema reaches -- a submodel, a TypedDict, a pydantic dataclass -- must forbid undeclared keys too: pydantic's default for all three silently DROPS them, so a typo'd nested option reads as applied while the engine runs on the field's default.
  2. A field marked secret=True (via secret_field) must resolve to exactly ONE masking carrier (_secret_carrier): SecretStr or SecretBytes, optionally unioned with None and nothing else. A plain annotation (str | None) or a plaintext union member (SecretStr | int) would be hidden from REST/auto-UI while leaking plaintext in repr/str/ model_dump/public_dump; two carriers make raw-string wrapping ambiguous; a container of secrets (for example, list[SecretStr]) is only half-protected. 1b. The field's DEFAULT must uphold the same contract (pydantic does not validate defaults): required, None (only when the annotation admits it), or an instance of the carrier itself. A plain-string default would live on instances as raw str -- plaintext repr/model_dump and a crash in model_dump_json/public_dump. A default_factory is rejected outright (it runs at construction, unvetted by this guard).
  3. A secret marker on a field of a nested submodel (the standard encourages per-model-family submodels) is rejected outright. The secret pipeline -- the enforcement here, the whitespace-preserving validator, and public_dump's masking -- only operates on a BaseConfig's own scalar fields, so a secret nested one level down is silently unprotected and leaks plaintext through public_dump / repr / model_dump. Credentials MUST therefore be modeled as top-level scalar SecretStr fields on the config, not buried in a submodel.
  4. A non-string validation alias -- AliasPath, or an AliasChoices carrying one -- is rejected. The whole config surface is built on FLAT single-token field resolution: the env convention maps one STANDARD_ASR_<ENGINE>__<FIELD> variable to one field name (spec IC.4), and the absent-vs-invalid classifier behind ConfigurationRequiredError resolves each error loc back to a single string token. A path alias populates a field from NESTED input, which neither surface can express -- pre-guard it produced an environment-dependent compliance verdict (pure absence misclassified as a plugin defect). An AliasChoices of plain strings is fine: each choice resolves like a string alias.
  5. The flat input-key vocabulary is UNIQUE across fields: a field's alias (or AliasChoices choice) colliding with another field's name or alias would let one caller key silently populate two independent settings (populate_by_name fills both) and makes loc-token resolution ambiguous. Every key has exactly one owning field.
  6. The config's serialization surface is CLOSED: author-defined serialization hooks -- @computed_field, @model_serializer, @field_serializer (declared here or inherited), and PlainSerializer/WrapSerializer metadata on a field -- are rejected outright. They run INSIDE model_dump, that is, inside public_dump's "masked" serialization, where they can rematerialize a sibling secret in plaintext (a computed authorization property, a field_serializer reading self.api_key) under keys the by-name mask never touches -- and they break the dump-is-the-declared-input-surface contract (G1.3/G3.1: auto-UI renders model_fields; extra="forbid" rejects a computed key on reload). Derived values belong on a plain @property or in the engine, never in the dump.
  7. A field whose annotation contains a secret CARRIER (SecretStr/SecretBytes, anywhere in it) must carry the secret MARKER: an unmarked carrier is half-protected -- pydantic masks its dumps, but the whitespace-preserving validator skips it (the raw string input is silently stripped: a padded credential is rewritten with no diagnostic), and the schema never renders it as a password/write-only input.
  8. The security-owned serialization methods keep their IDENTITY: public_dump / reveal_dump (owned here) and model_dump / model_dump_json / __iter__ (owned by BaseModel) MUST NOT be overridden -- directly or through a mixin / intermediate base. Guard 5 closes the serializer SCHEMA, but an ordinary Python override of these is the same customization one DISPATCH entry over: it never enters the decorator registry or the core schema, yet it runs inside public_dump and can rematerialize a sibling secret under a key the by-name mask never touches. Resolved statically over the MRO; the runtime dumps also call the base implementations unbound (defense in depth). See _reject_security_method_override.
Parameters
NameTypeDescription
**kwargs
= {}
Any

Forwarded subclass keyword arguments.

Raises
  • TypeError

    If a secret-marked field's annotation does not resolve to exactly one carrier (SecretStr/SecretBytes, optionally with None), if its default violates the carrier contract (plain value, None on a non-optional annotation, or a default_factory), if a nested submodel reachable from any field carries a secret-marked or carrier-annotated field, if a field's annotation contains an unresolved forward reference the nested-secret scan cannot vet, if a field declares a non-string validation alias (AliasPath / AliasChoices with a non-string entry), if two fields claim the same flat input key, if the class declares or inherits an author serialization hook, if the class overrides a security-owned serialization method (public_dump / reveal_dump / model_dump / model_dump_json / __iter__), if a carrier-annotated field lacks the secret marker, if the class reopens its own input surface (extra != "forbid"), if a nested input container reachable from the schema does not forbid undeclared keys, or if a field's env value has no defined reading -- its schema reaches both a scalar and a structured shape, or the core schema cannot be introspected at all (Guard 7, _env_codecs).

model_validate_json#class methodSource

@classmethod
def BaseConfig.model_validate_json(
    json_data: str | bytes | bytearray,
    *,
    strict: bool | None = None,
    context: Any | None = None,
    **kwargs: Any,
) -> _ConfigT

Validate a JSON document against this config (python-mode delegation).

Overridden because pydantic's native JSON pipeline is incompatible with the whitespace-preserving secret wrap: the wrap replaces a raw secret string with its carrier INSTANCE (SecretStr), which JSON-mode field validation rejects outright (its secret schema accepts only the JSON string form) -- so model_validate_json failed for EVERY config document carrying a secret value. Skipping the wrap in JSON mode is not an option either: str_strip_whitespace applies in JSON mode too, so the padded credential would be silently trimmed -- the exact rewrite the wrap exists to prevent. The one path that preserves both contracts is to parse the document and validate the resulting mapping in python mode, where the wrap is defined.

Grammar parity holds because json.loads and pydantic's parser accept the same token set for the config value space (both accept NaN/Infinity tokens; field validation then treats the resulting float identically on both routes -- pinned by test). A document json.loads rejects is delegated to pydantic's own JSON parser so the canonical json_invalid error surfaces; that delegation is REQUIRED to raise -- if the parsers ever diverged and pydantic accepted such a document, its JSON field pipeline would run without the wrap (the silent trim again), so a delegation that returns is refused outright (fail closed) rather than handed back.

Parameters
NameTypeDescription
json_datastr | bytes | bytearray

The JSON document.

strict
= None
bool | None

Field strictness, applied by the python-mode validation of the parsed document.

context
= None
Any | None

Validation context, forwarded unchanged.

**kwargs
= {}
Any

Any further model_validate keyword arguments a newer pydantic accepts (for example, by_alias), forwarded unchanged.

Returns
  • _ConfigT

    The validated config.

Raises
  • ValidationError

    If the document is malformed JSON (pydantic's canonical json_invalid error), is not a JSON object (the config mapping-required error), or fails field validation.

  • ConfigError

    If pydantic's parser accepted a document json.loads rejected (a parser divergence this override refuses to validate around).

model_validate_strings#class methodSource

@classmethod
def BaseConfig.model_validate_strings(
    obj: Any,
    *,
    strict: bool | None = None,
    context: Any | None = None,
    **kwargs: Any,
) -> _ConfigT

Validate string-valued data against this config (env-grammar delegation).

Overridden for the same reason as model_validate_json: pydantic's native strings pipeline is incompatible with the whitespace-preserving secret wrap -- the wrap replaces a raw secret string with its carrier INSTANCE (SecretStr), which strings-mode field validation rejects (its secret schema accepts only the string form) -- so model_validate_strings failed for EVERY config supplying a secret value, blaming the caller's own credential field for a wrong type it never passed. Skipping the wrap is not an option either: str_strip_whitespace applies in strings mode too, so the padded credential would be silently trimmed.

The string grammar is the config surface's OWN, shared with from_env / env_overrides rather than pydantic's strings mode: a scalar field takes the raw string (python-mode lax coercion handles "4" / "true"), a STRUCTURED field (list / mapping / submodel -- the fields _ENV_CODECS marks "json") takes a JSON document, and on a JSON error the raw string is kept so construction still fails loudly. One grammar for every string-valued source (env vars, CLI key=value pairs, query params), not two subtly different ones.

Parameters
NameTypeDescription
objAny

The string-valued mapping; keys may use any of a field's flat input keys (canonical name, string alias/ validation_alias, an AliasChoices choice).

strict
= None
bool | None

Field strictness, applied by the python-mode validation of the decoded mapping. strict=True therefore rejects the string spelling of a non-string scalar ("4" for an int field) -- leave it unset for string-valued sources, exactly as from_env does.

context
= None
Any | None

Validation context, forwarded unchanged.

**kwargs
= {}
Any

Any further model_validate keyword arguments a newer pydantic accepts, forwarded unchanged.

Returns
  • _ConfigT

    The validated config.

Raises
  • ValidationError

    If obj is not a mapping (the config mapping-required error) or the decoded mapping fails field validation.

public_dump#methodSource

def BaseConfig.public_dump() -> dict[str, Any]

Return a serialization with secrets masked (the default path).

This is the masked half of the secret-serialization contract and is the serialization to use for /v1/models, persistence, and telemetry. SecretStr / SecretBytes fields are rendered as SECRET_MASK (never plaintext). As a defensive measure, any secret-marked field is masked by name, so even a value that (hypothetically) slipped through as plaintext is never emitted. The default pydantic serializers (model_dump / model_dump_json) likewise mask the secret carriers; use reveal_dump only when plaintext is genuinely required in-process.

Trusting model_dump here rests on the definition-time guards that reject the authored serializer shapes -- the three serializer decorators, Annotated serializer metadata, nested credential carriers, fields excluded from the dump (Guards 5/5c/6) -- so the dump contains the declared input fields, serialized by pydantic's own machinery. Per the trust model (AGENTS.md) this is an enumeration, not a proof: the channels only a core-schema prover could see (SerializeAsAny, a custom __get_pydantic_core_schema__) require an author actively smuggling a credential past the mask -- an adversary, out of scope.

The guards bound what the schema installs in the dump, not the CONTENTS of the values author code constructed. Config code that copies a secret out of its carrier into non-secret state -- an after-validator writing self.api_key.get_secret_value() into a declared str field, or onto display state a trusted serializer renders by builtin dispatch (a validator-returned Path SUBCLASS whose __str__ embeds the credential; pydantic's own audited ser_path calls str() on the runtime value) -- emits that plaintext here, and no serialization mechanism can prevent it: both shapes are the same author act, and the leaking value passes every type or dispatch audit (a plain str whose content nothing can classify as secret). The value-level envelope is and remains the carrier contract (IC.3): credentials live in secret-marked SecretStr/SecretBytes fields, are masked here by name, and MUST NOT be copied out of the carrier into any other field or object state. The boundary is pinned executable in test_secret_extraction_is_the_closure_boundary; moving it means updating this contract, the spec, and that test together.

Returns
  • dict[str, Any]

    A JSON-safe dict with credentials masked.

reveal_dump#methodSource

def BaseConfig.reveal_dump() -> dict[str, Any]

Return a serialization with secrets materialized as plaintext.

This is the reveal half of the secret-serialization contract: the explicit, named counterpart to public_dump. Use it only for in-process calls into an engine SDK that needs the raw credential (for example, an Authorization header). The result contains plaintext secrets and MUST NEVER be logged, persisted, sent to /v1/models, or emitted as telemetry -- those paths use public_dump.

SecretStr / SecretBytes fields are unwrapped via get_secret_value(); all other fields keep their Python values (no JSON coercion), so a credential is returned as the engine SDK expects it.

Returns
  • dict[str, Any]

    A dict with secret-marked fields materialized to plaintext.

from_env#class methodSource

@classmethod
def BaseConfig.from_env(
    engine_id: str,
    *,
    environ: Mapping[str, str] | None = None,
    **explicit: Any,
) -> _ConfigT

Construct a config, filling unset fields from the environment.

Applies the normative priority explicit > env > (required-missing error): each field that is not supplied in explicit under any of its flat input keys (canonical name, string alias/ validation_alias, or an AliasChoices choice -- the _flat_input_keys vocabulary) is filled from its STANDARD_ASR_<NORMENGINE>__<NORMFIELD> environment variable (collision detected), and the merged mapping is then passed to the constructor. Alias-awareness is what makes "explicit wins" true for aliased fields: a caller passing apiKey=... suppresses the api_key env fallback instead of colliding with it under extra="forbid". Because construction does the field coercion, SecretStr credentials are wrapped (and so masked in repr/str/public_dump) instead of being handed around as raw plaintext -- avoiding the leak footgun of passing a plaintext {field: secret} dict through application code.

Note on explicit None: "absent" means the key is not present in explicit, not "present with value None". A key passed explicitly as None is a value and wins over env (priority is "explicit wins", not "explicit-non-None wins"). A wrapper that forwards optional kwargs with None defaults therefore disables the env fallback for those fields; drop None keys before calling ({k: v for k, v in kwargs if v is not None}) if env fallback should apply.

The engine discriminator is never read from the environment; it is the entrypoint-derived identity and defaults on each engine's subclass. The strict safety policy is likewise excluded so the environment can never silently downgrade fail-loud to best_effort (see _ENV_EXCLUDED_FIELDS).

Parameters
NameTypeDescription
engine_idstr

The engine identifier used to build env var names.

environ
= None
Mapping[str, str] | None

Environment mapping (defaults to os.environ).

**explicit
= {}
Any

Explicitly supplied field values (highest priority).

Returns
  • _ConfigT

    A validated config instance.

Raises
  • ConfigError

    If two field names collide on the same env var, or if construction fails (an invalid value, or a required field missing from both explicit and the environment) -- wrapped from pydantic's ValidationError with the offending input scrubbed. ConfigError is a ValueError subclass, so existing except ValueError handlers keep working.

env_overrides#class methodSource

@classmethod
def BaseConfig.env_overrides(
    engine_id: str,
    *,
    environ: Mapping[str, str] | None = None,
) -> dict[str, Any]

Collect config overrides from environment variables.

Only fields absent from explicit config should be filled from these; the caller applies priority (explicit > env). Collisions (two fields normalizing to the same env var) are rejected.

Security note: the returned dict holds raw plaintext values, including any credential fields, because SecretStr wrapping happens only at construction. Prefer from_env, which merges and constructs in one step (so secrets are wrapped/masked); treat the dict returned here as sensitive and never log it.

Parameters
NameTypeDescription
engine_idstr

The engine identifier used to build env var names.

environ
= None
Mapping[str, str] | None

Environment mapping (defaults to os.environ).

Returns
  • dict[str, Any]

    A dict of {field_name: value} discovered in the environment.

Raises
  • ConfigError

    If two field names collide on the same env var.

BaseProperties#classSource

class BaseProperties(BaseModel)

Base class for ASR engine static properties.

Raises
  • ValueError

    If validation fails.

Attributes
NameTypeDescription
engine_idstr

Engine identifier (surface syntax checked here; PEP 503 canonicalization happens at discovery).

model_namestr

Model preset name within the engine.

protocol_versionstr

Standard ASR protocol version supported by the engine.

accepted_inputset[InputKind]

Audio shapes the engine accepts (MUST be non-empty).

native_sample_rateint
gt=0

The model's native sample rate in Hz.

accepted_sample_rateslist[int] | SampleRateRange | Literal['any']

Sample rates the engine accepts, or "any".

required_input_sample_rate
= None
int | None
gt=0

Sample rate the wire protocol hard-requires (for example, 24000 for OpenAI Realtime), if any.

max_file_size
= None
int | None
gt=0

Maximum file/payload size in bytes, if any.

max_audio_duration
= None
float | None
gt=0

Maximum audio duration in seconds, if any.

wire_encodings
= None
list[str] | None

Wire encodings supported for streaming, if any.

selectable_languages
= list()
list[str]

Languages the application may explicitly select (BCP-47 tags plus optional "auto"). Empty means the engine has no language axis.

detectable_languages
= list()
list[str]

Languages detectable in auto mode; required when "auto" is selectable.

description
= None
str | None

Optional human-readable, display-only description. MUST NOT carry machine-readable negotiation/gating data -- that belongs in Capabilities (the free-form extra metadata pocket was removed; it duplicated the blanket metadata the standard deliberately dropped).

has_language_axisbool

Whether the engine exposes a language axis.

supports_autobool

Whether automatic language detection is selectable.

accepts_any_sample_ratebool

Whether the engine accepts any input sample rate.

Renamed from self_describes_sample_rate: that name clashed with the audio-carrier sense of "self-describing sample rate" -- a property of the audio carrier (a file/stream header that states its own rate), an unrelated concept. This predicate is purely about the engine's declared I/O boundary: accepted_sample_rates == "any".

model_idstr

Return the fully qualified model identifier (engine/model).

BatchCapabilities#classSource

class BatchCapabilities(_Container)

Capability tree for the batch mode domain.

Attributes
NameTypeDescription
language
= LanguageCaps()
LanguageCaps

Language capabilities.

word_timestamps
= WordTimestampsCap()
WordTimestampsCap

Word-timestamp capability.

guidance
= GuidanceCaps()
GuidanceCaps

Guidance-family capabilities.

diarization
= DiarizationCap()
DiarizationCap

Diarization capability.

CandidateLanguagesCap#classSource

class CandidateLanguagesCap(_FlagLikeNode)

Bounded capability for candidate languages.

Attributes
NameTypeDescription
constraints
= None
CandidateLanguagesConstraints | None

Limits (for example, max) when supported.

supportedbool

Whether candidate languages are supported.

CandidateLanguagesConstraints#classSource

class CandidateLanguagesConstraints(_JsonExtraModel)

Constraints for the candidate-languages capability.

Attributes
NameTypeDescription
maxint
gt=0

Maximum number of candidate languages accepted.

ChannelResult#classSource

class ChannelResult(BaseModel)

Per-channel transcription for multi-channel audio.

Raises
  • ValueError

    If field validation fails.

Attributes
NameTypeDescription
channelint
ge=0

Channel index.

textstr

Full transcript for this channel.

segments
= None
list[Segment] | None

Optional segment-level details for this channel.

words
= None
list[Word] | None

Optional flattened word-level details for this channel.

CredentialsConfigMixin#classSource

class CredentialsConfigMixin(BaseModel)

Applicability mixin: cloud credentials and endpoint routing.

Credentials (api_key) are secret; endpoint routing fields (base_url / region / org_id) are not secret and may be logged.

Attributes
NameTypeDescription
api_key
= secret_field(description='Secret API key / token.')
SecretStr | None

Secret API key / token.

base_url
= None
str | None

Non-secret API base URL.

region
= None
str | None

Non-secret service region.

org_id
= None
str | None

Non-secret organization id.

DIARIZE#attributeSource

DIARIZE: Final[DiarizationRequest] = DiarizationRequest()

DeclaredCapabilities#classSource

class DeclaredCapabilities(_Container)

The full capability tree declared by an engine.

Mode domains are optional: omitting a domain means the mode is not supported (fail-closed). Engine-global orthogonal flags live at the top level.

Attributes
NameTypeDescription
batch
= None
BatchCapabilities | None

Batch-mode capabilities, or None if batch is unsupported.

streaming
= None
StreamingCapabilities | None

Streaming-mode capabilities, or None if unsupported.

streaming_input
= FlagCap()
FlagCap

Whether the engine accepts incremental audio. May only be supported when a streaming domain is declared.

streaming_output
= FlagCap()
FlagCap

Whether the engine returns results incrementally. May only be supported when a streaming domain is declared.

self_resamples
= FlagCap()
FlagCap

Whether the engine resamples audio internally. This is one of the behavioral facts the spec declares in Capabilities rather than Properties -- alongside the per-mode diarization.always_on and the streaming behavior flags; unlike those it is engine-global (a static behavior of the engine, not per-mode), so it lives at the top level alongside streaming_input / streaming_output.

It is purely informational: accepted_sample_rates remains authoritative for every resampling decision, so this flag has no decision power and does NOT change whether the standard resamples. It lets a client-side resampling engine (for example, faster-whisper, which declares accepted_sample_rates="any") advertise that incoming audio is downsampled inside the engine rather than by the standard. Absent ⇒ False (fail-closed).

supports#methodSource

def DeclaredCapabilities.supports(dot_path: str) -> bool

Return whether the capability at dot_path is supported.

The only standard way to query capabilities. Walks the tree segment by segment; any missing segment returns False (fail-closed). Resolving a present mode-domain or container also returns True.

Parameters
NameTypeDescription
dot_pathstr

Dotted capability path without the capabilities. prefix (for example, "batch.word_timestamps", "streaming.guidance.phrase_hints", "streaming_input").

Returns
  • bool

    True if supported, otherwise False.

node_at#methodSource

def DeclaredCapabilities.node_at(dot_path: str) -> _CapNode | None

Return the typed capability node at dot_path, or None.

Unlike supports (which returns a bool), this returns the leaf node object itself so callers can inspect its constraints / enums (for example, a WordTimestampsCap to validate a requested granularity against WordTimestampsCap.granularities). Returns None if the path is absent or does not resolve to a capability leaf node.

Parameters
NameTypeDescription
dot_pathstr

Dotted capability path without the capabilities. prefix (for example, "batch.word_timestamps").

Returns
  • _CapNode | None

    The capability leaf node, or None.

iter_supported_paths#methodSource

def DeclaredCapabilities.iter_supported_paths() -> Iterator[str]

Yield every dot-path in the tree whose node is supported.

Only the children of a supported typed node are descended into, so an unsupported feature's constraint sub-containers (which are always present, never None) do not appear. A raw x_* dict subtree is walked unconditionally (see _iter_paths) so nested explicit vendor capabilities stay discoverable. Used to verify the effective ⊆ declared invariant.

Yields
  • str

    Dot-paths of supported capability nodes and present containers.

iter_queryable_paths#methodSource

def DeclaredCapabilities.iter_queryable_paths() -> Iterator[str]

Yield the dot-path of every NODE in the tree -- supported or not.

The node set is pinned by the two-layer isomorphism: exactly the paths at which canonical_json renders a JSON object and at which supports resolves a model/dict -- capability leaves, containers, constraint submodels, and x_* extension subtrees (typed or raw-dict; model extras pass the same x_* gate as every other traversal, so a non-extension unknown key is not a node). Scalar field values (a supported bool, a mode token, a granularities list) are field internals, not nodes: neither yielded nor descended. None children (an absent mode domain, a constraints=None) are skipped.

Unlike iter_supported_paths (the supported-only view behind effective ⊆ declared), UNSUPPORTED nodes are yielded and descended, so a consumer can verify the fail-closed False answers too -- for example, an unsupported feature's constraints submodel MUST probe False. The compliance suite sweeps this set to assert a hand-written supports() agrees with the tree on every node.

Yields
  • str

    Dot-paths of every capability node, container, submodel, and

  • str

    extension subtree in the tree.

covers#methodSource

def DeclaredCapabilities.covers(other: DeclaredCapabilities) -> bool

Return whether other is a valid narrowing of this tree.

Enforces the normative effective ⊆ declared invariant: the effective set may only close declared capabilities, never widen them. This checks two things:

  • Set containment -- every supported path in other is also supported here (no feature is enabled that this tree did not declare).
  • Constraint narrowing -- where both trees support a bounded or enum/mode node, other's limits MUST be no looser than this tree's (for example, a smaller-or-equal max, a subset of granularities, a mode that is the same or a reduction). A widening (declared max=2 -> effective max=999) is rejected.
Parameters
NameTypeDescription
otherDeclaredCapabilities

A (typically narrowed, effective) capability tree.

Returns
  • bool

    True if other is a subset narrowing of this tree.

canonical_json#methodSource

def DeclaredCapabilities.canonical_json() -> dict[str, Any]

Serialize to canonical JSON with a derived supported at every node.

Cross-language clients read capabilities from this JSON. Flag and bounded nodes carry supported as a real field, but enum/mode nodes derive it from mode (a Python property, absent from model_dump). This method injects the uniform boolean at every capability node and present container so a client never has to special-case archetypes or know the "none"/"unsupported" sentinels (enum/mode nodes' supported is server-injected). The root object itself carries no supported key (it is the container of all modes, not a capability); an absent mode domain serializes as null (fail-closed).

Returns
  • dict[str, Any]

    A JSON-serializable capability tree with supported on each node.

DeviceConfigMixin#classSource

class DeviceConfigMixin(BaseModel)

Applicability mixin: compute-device selection.

Attributes
NameTypeDescription
device
= None
str | None

Compute device (for example, "cpu", "cuda", "mps").

Diagnostic#classSource

class Diagnostic(BaseModel)

A structured, non-fatal notification from the standard layer.

Diagnostics report lossy conversions, assumed parameters, best_effort degradations, and similar non-ideal paths.

Attributes
NameTypeDescription
level
= 'info'
Literal['info', 'warning']

Severity, "info" or "warning".

codestr

Stable machine-readable code (for example, "audio_conversion").

messagestr

Human-readable explanation.

param
= None
str | None

The parameter the diagnostic concerns, if any.

provided
= None
WireJsonValue

The value the application provided, if relevant.

effective
= None
WireJsonValue

The value that took effect, if relevant.

DiarizationCap#classSource

class DiarizationCap(_FlagLikeNode)

Capability for speaker diarization (requested via RuntimeParams.diarization).

always_on is a behavioral fact, in the same family as self_resamples (and the streaming behavior flags emits_partials / re_segments / word_stability): it describes what the engine does -- its architecture cannot DISABLE diarization, so speaker labels may appear even when diarization is not requested -- and grants nothing an application could request.

It is nonetheless a regular queryable flag node (a FlagCap), uniform with self_resamples: supports("<mode>.diarization.always_on") works, it appears in iter_supported_paths when supported, canonical_json injects a uniform supported boolean for it, and covers treats a declared-unsupported -> effective-supported change as a rejected widening (declaration drift) by plain set containment.

The one thing that distinguishes always_on from every other flag is a semantic inversion: for every other flag True means "you MAY request this", whereas for always_on True means "this is imposed on you" -- speaker labels may appear even when you did not ask for them (the documented exemption to request-gated diarization). That inversion is documented prose, not a difference in representation.

It is NOT placed inside constraints (constraints are machine-checkable request limits, and always_on is a behavioral fact, not a limit). It is reserved for architecturally non-disableable engines: an engine that CAN disable diarization MUST disable it when diarization is not requested (can-disable-must-disable), and MUST NOT declare always_on for adapter convenience.

Attributes
NameTypeDescription
always_on
= FlagCap()
FlagCap

Whether diarization is architecturally non-disableable (labels may appear unrequested). May only be supported when supported is True.

constraints
= DiarizationConstraints()
DiarizationConstraints

Limits when supported.

supportedbool

Whether diarization is supported.

DiarizationConstraints#classSource

class DiarizationConstraints(_JsonExtraModel)

Constraints for the diarization capability.

Attributes
NameTypeDescription
max_speakers
= None
int | None
gt=0

Optional maximum number of speakers.

DiarizationRequest#classSource

class DiarizationRequest(BaseModel)

Request marker for speaker diarization ("who said what").

Presence enables diarization: RuntimeParams(diarization=DiarizationRequest()) (or the DIARIZE convenience constant) requests speaker labels; diarization=None (the default) means not requested. There is no []-analog -- a "requested-but-empty" state is meaningless for an on/off feature, so None vs an instance is the whole state space. On the wire the marker maps three ways: "diarization": {} -> DiarizationRequest(); "diarization": null -> None; key absent -> None.

The model is a deliberately empty frozen marker in v1: tuning parameters (num_speakers / min/max hints, granularity selection) are deferred because today's engine landscape cannot honor them portably -- they graduate additively onto this model once support is broad enough. extra="forbid" keeps that evolution honest: an unknown key (for example, a client guessing num_speakers) fails loudly (a 422 on the wire) instead of being silently ignored. An import-time assert in standard_asr.runtime.gating additionally forces sub-gating to be written the moment a field is added here.

Raises
  • ValueError

    If an unknown field is supplied (extra="forbid").

DownloadConfigMixin#classSource

class DownloadConfigMixin(BaseModel)

Applicability mixin: model download / cache location.

Attributes
NameTypeDescription
download_root
= None
Path | None

Root directory for model artifacts. Priority: explicit > STANDARD_ASR_MODEL_DIR > library default > ~/.cache.

EngineBase#classSource

class EngineBase(ABC)

Abstract base implementing the standard transcribe pipeline.

Subclasses MUST set properties and declared_capabilities as class attributes, assign config in __init__ (which MUST stay pure -- no filesystem, GPU, or network access), and implement _transcribe. Streaming engines additionally override _start_transcription (the streaming template hook); the public start_transcription runs the standard gating pipeline for them.

Attributes
NameTypeDescription
propertiesBaseProperties
declared_capabilitiesDeclaredCapabilities
provider_params_type
= None
type[ProviderParams] | None
config_type
= None
type[BaseConfig[str]] | None
configBaseConfig[str]
effective_capabilitiesDeclaredCapabilities

Runtime-effective capabilities (default: the declared set).

Engines that narrow capabilities based on configuration override this; the result MUST satisfy effective ⊆ declared.

supports#methodSource

def EngineBase.supports(dot_path: str) -> bool

Return whether a capability is supported at runtime (fail-closed).

Parameters
NameTypeDescription
dot_pathstr

A capability dot-path.

Returns
  • bool

    True if supported by the effective capabilities.

prepare#methodSource

def EngineBase.prepare() -> None

Warm up the engine (download / load weights) without transcribing.

The optional, synchronous, idempotent pre-warm hook, invoked by standard-asr prepare and by production / CI pre-warming to move the lazy side effects (weight download / model load) off the first transcription to one billing-free, transcription-free call. The base implementation is a no-op: an engine with nothing to warm up inherits it unchanged and the toolchain reports a no-op rather than failing.

Engines that load weights MUST override this to materialize them (for example, call _ensure_model_loaded), and that path MUST honor the same download gate as transcription: check allow_downloads and raise DiscoveryError when downloads are disabled and weights are missing. An override MUST remain a zero-argument synchronous method -- never an async def (a coroutine function would be called but never awaited, silently reporting a false success); the compliance suite and the CLI reject a coroutine prepare.

Returns
  • None

    None.

Raises
  • DiscoveryError

    An override SHOULD raise this when downloads are disabled and the weights are not already present (the base no-op never raises).

transcribe#methodSource

def EngineBase.transcribe(
    audio: AudioInputLike,
    params: RuntimeParams | None = None,
) -> TranscriptionResult

Transcribe a complete audio input (template method).

Runs the standard pipeline, fail-fast first: validate the language config -> gate parameters (provider_params + capability gating, which needs no audio) -> resolve & validate the effective language axis -> coerce -> negotiate -> convert/resample -> call the engine -> synthesize missing segment speakers from word speakers (the pinned synthesis rule) -> attach diagnostics.

Parameter validation runs before the (potentially expensive) audio decode/resample so a swapped-engine provider_params bug or an unsupported parameter is rejected before any audio is touched (fail fast on provider_params first).

Parameters
NameTypeDescription
audioAudioInputLike

The audio to transcribe.

params
= None
RuntimeParams | None

Per-request runtime parameters.

Returns
Raises
  • ConfigError

    If the engine's default_language VALUE is malformed or not in selectable_languages -- fixable by whoever supplies the config.

  • EngineContractError

    On an engine-declaration defect -- a declared language axis with no default_language (IC.6), or a malformed declared selectable/detectable tag.

  • IncompatibleAudioInputError

    If no conversion path exists.

  • UnsafeAudioUrlError

    If an AudioUrl fails the SSRF policy (non-HTTPS, or a private/loopback/link-local target).

  • AudioProcessingError

    On an audio failure surfaced by the conversion pipeline -- a decode failure, an over-max_file_size payload, or (in strict mode) a bare array with no sample rate.

  • UnsupportedFeatureError

    In strict mode, on an unsupported parameter, a requested language not selectable by the engine, or a valid-but-unreachable candidate list (a non-detectable candidate or one over the declared max).

  • InvalidProviderParamError

    On wrong provider params.

  • ValueError

    On a malformed candidate tag or one containing auto -- a caller code bug, raised always (independent of strict/best_effort).

  • TranscriptionError

    On an engine-execution failure inside _transcribe -- including a pydantic ValidationError escaping it (an invalid result construction is an engine fault; the template wraps it here so it can never masquerade as a client-input validation error).

transcribe_async#async methodSource

async def EngineBase.transcribe_async(
    audio: AudioInputLike,
    params: RuntimeParams | None = None,
) -> TranscriptionResult

Asynchronously transcribe (default: run transcribe in a thread).

Parameters
NameTypeDescription
audioAudioInputLike

The audio to transcribe.

params
= None
RuntimeParams | None

Per-request runtime parameters.

Returns
Raises
  • EngineContractError

    If transcribe (overridden by the subclass) violated the synchronous protocol contract -- returned an awaitable (async def) or a value that is not a TranscriptionResult -- or propagated a declaration defect from transcribe (a missing IC.6 default, a malformed declared tag).

  • Exception

    The same exception set as transcribe (it runs that method): ConfigError, IncompatibleAudioInputError, UnsafeAudioUrlError, AudioProcessingError, UnsupportedFeatureError, InvalidProviderParamError, ValueError, and TranscriptionError.

ensure_stream_inputs_exclusive#static methodSource

@staticmethod
def EngineBase.ensure_stream_inputs_exclusive(
    audio_format: AudioFormat | None,
    audio: AudioInputLike | None,
) -> None

Enforce the audio_format / audio mutual-exclusion.

audio_format (incremental PCM feeding) and audio (whole-input streaming output) are mutually exclusive; passing both MUST raise. This shared guard lets every streaming engine enforce the rule with one call instead of reimplementing it; the base start_transcription invokes it before raising the unsupported-streaming error.

Parameters
NameTypeDescription
audio_formatAudioFormat | None

The wire format for incremental frames, if any.

audioAudioInputLike | None

A complete audio input for whole-input streaming, if any.

Raises
  • ValueError

    If both audio_format and audio are provided.

ensure_stream_format_supported#methodSource

def EngineBase.ensure_stream_format_supported(audio_format: AudioFormat) -> None

Validate a declared streaming wire format at session establishment.

Shared session-establishment guard for streaming engines: call it first (like ensure_stream_inputs_exclusive) when opening a audio_format=... session. It is fail-closed on the wire sample rate and the channel count unconditionally, and on the wire encoding when wire_encodings is declared.

Wire encoding: when the engine declares wire_encodings, an encoding not among them is rejected up front rather than misframed as PCM and silently mistranscribed. When wire_encodings is None ("unconstrained") the encoding cannot be validated and the check is skipped -- the engine is then trusted to accept any encoding (typically a self-managed-wire-format engine). The compliance suite emits a warning for a streaming_input engine that leaves wire_encodings unset, since that skip is where a forgotten declaration would let a non-PCM frame be misframed.

Wire sample rate: the standard's v1 implementation note is explicit that v1 does NOT resample streaming bare frames in the standard layer (unlike the batch transcribe path, which resamples). Therefore, until standard-layer streaming resampling lands, a wire sample_rate that the engine does not accept MUST be rejected here rather than forwarded as frames the engine never declared -- a loud error beats a silent mistranscription. When required_input_sample_rate is set, the wire rate MUST equal it -- even when accepted_sample_rates is "any" (that combination is constructible; the declaration-time reachability validator only checks concrete lists). Otherwise the rate is accepted when accepted_sample_rates is "any" or when it is in that concrete list.

Parameters
NameTypeDescription
audio_formatAudioFormat

The wire format the session declared.

Raises
  • UnsupportedFeatureError

    If wire_encodings is declared and the requested encoding is not among them, if the wire channels is not 1 (v1 streaming wire is mono-only), or if the wire sample rate is not reachable for the engine (fail-closed; v1 does not resample streaming wire frames).

recommended_wire_format#methodSource

def EngineBase.recommended_wire_format() -> AudioFormat | None

Return a minimal wire AudioFormat to open a streaming session.

Single source of truth for the legal bare-frame wire format the standard layer uses when it must open a streaming_input session but has no application-chosen format -- the CLI sync-bridge runner and the streaming gating probe both rely on it. They previously derived one independently and disagreed (which sample-rate source to use, and what to do with no declared wire_encodings); this unifies them. The format is built from the engine's own Properties so ensure_stream_format_supported accepts it (the compliance suite asserts that round-trip):

  • sample_rate = required_input_sample_rate when the engine hard-requires one, else native_sample_rate (the reachability invariant guarantees the native rate is accepted).
  • encoding = the first declared wire_encodings entry, else the canonical pcm_s16le (used only when wire_encodings is unconstrained, where the engine accepts any encoding).
  • channels = 1 (v1 streaming wire is mono-only).

The derivation is deliberately capability-blind (Properties only): whether a bare-frame session can be opened at all is decided by the streaming_input gate in start_transcription, not here -- so the recommendation stays a pure, class-level static fact and the compliance round-trip (format ⊆ ensure_stream_format_supported) holds for every engine, streaming or not.

Returns
  • AudioFormat | None

    A wire format the engine's session-establishment guard accepts, or

  • AudioFormat | None

    None when the engine declares no usable (positive) sample rate, so

  • AudioFormat | None

    no bare-frame streaming format can be recommended.

start_transcription#methodSource

def EngineBase.start_transcription(
    *,
    audio_format: AudioFormat | None = None,
    params: RuntimeParams | None = None,
    audio: AudioInputLike | None = None,
    deadlines: StreamDeadlines | None = None,
) -> TranscriptionSession

Open a streaming transcription session (template method).

Symmetric to transcribe: the base runs the standard streaming pipeline and delegates only the engine-specific session construction to _start_transcription. The pipeline enforces input mutual-exclusion, validates the language config, validates the wire format, gates parameters against the streaming capabilities, resolves the language axis, prepares whole-input audio through the standard audio pipeline, and attaches the resulting diagnostics to the session.

Because gating now runs here, provider_params swap-safety is enforced on the streaming path too: a swapped-engine provider_params type-mismatch always raises InvalidProviderParamError (no longer undefined behavior), and an unsupported standard parameter is rejected (strict) or dropped + diagnosed (best_effort) exactly as for batch.

The streaming input/output capability axis is checked before the hook override defense, so an engine that implements the hook but does not declare the requested session mode fails on the missing capability rather than reaching parameter or audio gating. The hook override defense still runs before parameter gating, so a batch-only engine reports "does not support streaming" rather than a confusing parameter error -- while still running the input mutual-exclusion guard first, exactly as before.

Streaming param freeze: the already-gated, frozen RuntimeParams is handed to the hook as gated_params; the engine uses that for the whole session and MUST NOT re-accept raw params mid-stream.

Parameters
NameTypeDescription
audio_format
= None
AudioFormat | None

Wire format for incremental PCM frames.

params
= None
RuntimeParams | None

Per-request runtime parameters.

audio
= None
AudioInputLike | None

A complete audio input for whole-input streaming output.

deadlines
= None
StreamDeadlines | None

Application overrides for the session's termination deadlines. Applied by this template after the engine hook constructed the session, so explicitly set fields always win over the engine's construction-time choices -- precedence: application explicit > engine choice > standard default. Unset fields are left untouched.

Returns
Raises
  • ValueError

    If both audio_format and audio are provided, or on a malformed/auto candidate-language entry (a caller code bug; always raises, independent of strict/best_effort).

  • ConfigError

    If the engine's default_language VALUE is malformed or not in selectable_languages.

  • EngineContractError

    On an engine-declaration defect -- a declared language axis with no default_language (IC.6), or a malformed declared selectable/detectable tag.

  • UnsupportedFeatureError

    When the requested streaming input/output axis is unsupported, when streaming is unsupported, when the wire format is unreachable, or, in strict mode, on an unsupported parameter or a valid-but-unreachable candidate list (non-detectable / over-max).

  • IncompatibleAudioInputError

    If no conversion path exists for a whole-input streaming audio value.

  • UnsafeAudioUrlError

    If a whole-input AudioUrl fails the SSRF policy.

  • AudioProcessingError

    On a decode / size / missing-sample-rate failure for a whole-input audio value.

  • InvalidProviderParamError

    On wrong provider_params (swap-safety).

  • TranscriptionError

    When a pydantic ValidationError escapes the engine's _start_transcription hook (an invalid model construction is an engine fault; wrapped here so it can never masquerade as a client-input validation error).

FinalityCap#classSource

class FinalityCap(_CapNode)

Streaming finality level the engine can guarantee.

Attributes
NameTypeDescription
mode
= 'final'
Literal['final', 'closed']

final (may still be revised by post-processing) or closed.

is_supportedbool

Whether a finality level is guaranteed (always True here).

FlagCap#classSource

class FlagCap(_FlagLikeNode)

A simple supported / not-supported flag.

GuidanceCaps#classSource

class GuidanceCaps(_Container)

Guidance-family capabilities for one mode.

Attributes
NameTypeDescription
prompt
= PromptCap()
PromptCap

Free-text prompt channel.

phrase_hints
= PhraseHintsCap()
PhraseHintsCap

Phrase-hint channel.

InputKind#classSource

class InputKind(str, Enum)

Closed enumeration of the audio shapes an engine can accept.

Engines declare the set of shapes they accept via Properties.accepted_input. The negotiation layer matches the variant an application provides against this set.

Attributes
NameTypeDescription
ARRAY
= 'array'

An already-decoded waveform (NumPy array).

ENCODED_BYTES
= 'encoded_bytes'

Encoded audio held in memory (for example, MP3/WAV bytes).

ENCODED_FILE
= 'encoded_file'

An encoded audio file on disk.

FETCHABLE_URL
= 'fetchable_url'

A URL the engine/cloud service fetches server-side.

STORAGE_URI
= 'storage_uri'

A provider cloud-storage URI (for example, s3://, gs://) the engine resolves with its own cloud-SDK credentials. Distinct from FETCHABLE_URL: it is not an HTTPS-fetchable public URL and never passes through the standard's SSRF validator.

LanguageCaps#classSource

class LanguageCaps(_Container)

Language capabilities for one mode.

Attributes
NameTypeDescription
runtime_override
= FlagCap()
FlagCap

Whether per-request language override is allowed.

candidate_languages
= CandidateLanguagesCap()
CandidateLanguagesCap

Candidate-language support and limits.

LanguageConfigMixin#classSource

class LanguageConfigMixin(BaseModel)

Applicability mixin: default language selection.

Attributes
NameTypeDescription
default_language
= None
str | None

Default language (BCP-47 or "auto"). Required when the engine exposes a language axis.

default_candidate_languages
= None
list[str] | None

Default candidate languages.

Mode#attributeSource

Mode = ModeName

PhraseHintsCap#classSource

class PhraseHintsCap(_FlagLikeNode)

Guidance channel: phrase-hint term boosting.

Attributes
NameTypeDescription
constraints
= PhraseHintsConstraints()
PhraseHintsConstraints

Limits when supported.

supportedbool

Whether phrase hints are supported.

PhraseHintsConstraints#classSource

class PhraseHintsConstraints(_JsonExtraModel)

Constraints for the phrase-hints guidance channel.

Attributes
NameTypeDescription
max_terms
= None
int | None
gt=0

Optional maximum number of phrase-hint terms.

max_chars_per_term
= None
int | None
gt=0

Optional maximum characters per term.

max_words_per_term
= None
int | None
gt=0

Optional maximum words per term.

PreparedAudio#classSource

class PreparedAudio

PreparedAudio(
    kind: InputKind,
    array: NDArray[np.float32] | None = None,
    sample_rate: int | None = None,
    data: bytes | None = None,
    container: str | None = None,
    path: str | None = None,
    url: str | None = None,
    storage_uri: str | None = None,
    diagnostics: list[Diagnostic] = _empty_diagnostics(),
)

Audio negotiated into exactly one engine-accepted shape.

Exactly one payload slot is populated according to kind.

Parameters
NameTypeDescription
kindInputKind

The accepted shape this payload represents.

array
= None
NDArray[np.float32] | None

Waveform (for ARRAY).

sample_rate
= None
int | None

Sample rate of array in Hz (for ARRAY).

data
= None
bytes | None

Encoded bytes (for ENCODED_BYTES).

container
= None
str | None

Optional container hint for data.

path
= None
str | None

File path (for ENCODED_FILE).

url
= None
str | None

Remote URL (for FETCHABLE_URL).

storage_uri
= None
str | None

Provider cloud-storage URI (for STORAGE_URI).

PromptCap#classSource

class PromptCap(_FlagLikeNode)

Guidance channel: free-text prompt.

Attributes
NameTypeDescription
constraints
= PromptConstraints()
PromptConstraints

Limits when supported.

supportedbool

Whether prompt guidance is supported.

PromptConstraints#classSource

class PromptConstraints(_JsonExtraModel)

Constraints for the prompt guidance channel.

Attributes
NameTypeDescription
max_tokens
= None
int | None
gt=0

Optional maximum prompt length in tokens. The standard layer has no engine tokenizer, so it enforces this bound against a conservative, script-aware approximation -- whitespace-delimited words plus one unit per space-less (CJK, kana, Hangul, Thai, and so on) codepoint -- not the engine's exact token count. Honest scope of the guarantee: the approximation never under-counts relative to that whitespace + no-space-script tokenization, but it MAY under-count an engine's subword (BPE) tokenization of long Latin words / URLs / digit runs (counted as 1 here, often 6-17 BPE tokens), so such a prompt can exceed the engine's true budget despite passing the gate. Declare max_tokens with headroom below the engine's hard limit rather than at it; the standard never exceeds the declared value.

ProviderParams#classSource

class ProviderParams(BaseModel)

Base class for an engine's typed, non-portable parameter model.

Engines publish a subclass (for example, OpenAIParams) and declare it as their expected provider_params type. Passing one engine's params model to a different engine is a validation error (swap-safe), raised as InvalidProviderParamError by the engine layer regardless of the strict / best_effort policy.

The swap-safety match is exact (type(provided) is <EngineParams>), not isinstance: every engine MUST publish a distinct terminal params type, because honoring a subclass would let one engine silently accept another's params and drop the extra fields. Inheritance is therefore not a way to declare cross-engine compatibility. This bare base is never a valid concrete params model -- declaring it as an engine's provider_params type, or passing a bare instance, is rejected (the latter at RuntimeParams construction).

ReconnectCap#classSource

class ReconnectCap(_CapNode)

Streaming reconnect capability.

Attributes
NameTypeDescription
mode
= 'unsupported'
Literal['seamless', 'lossy', 'unsupported']

seamless / lossy / unsupported.

is_supportedbool

Whether reconnect is supported.

RuntimeParams#classSource

class RuntimeParams(BaseModel)

Closed per-request parameter container.

Raises
  • ValueError

    If field validation fails.

Attributes
NameTypeDescription
language
= None
str | None

Per-request language (BCP-47 or "auto") overriding the engine default. Gated by <mode>.language.runtime_override.

candidate_languages
= None
list[str] | None

Candidate languages, meaningful only in auto mode. Gated by <mode>.language.candidate_languages.

word_timestamps
= None
WordTimestampGranularity | None

Requested word-timestamp granularity. Gated by <mode>.word_timestamps.

diarization
= None
DiarizationRequest | None

Speaker-diarization request marker; presence = enable (None = not requested). Gated by <mode>.diarization.

prompt
= None
str | None

Free-text guidance prompt. Gated by <mode>.guidance.prompt.

phrase_hints
= None
list[str] | None

Phrase-hint boost terms. Gated by <mode>.guidance.phrase_hints.

on_unsupported
= 'fail'
Literal['fail', 'degrade_to_prompt']

Guidance degradation policy. "fail" (default) keeps the fail-closed contract; "degrade_to_prompt" opts into the one-way rich->prompt fallback (a diagnostic is emitted on degrade).

provider_params
= None
ProviderParams | None

Engine-specific typed parameters, or None.

SampleRateRange#classSource

class SampleRateRange(BaseModel)

A continuous inclusive range of accepted input sample rates.

The third accepted_sample_rates variant (besides an explicit list[int] and "any"), for engines whose I/O boundary is a range rather than a discrete set -- for example, AWS Transcribe accepts any rate in [8000, 48000] (research 4). Without it such an engine must either enumerate a few points (forcing the standard to needlessly resample an in-range rate it would accept, losing quality) or declare "any" (over-declaring, so an out-of-range rate is passed through and fails vendor-side instead of being negotiated). Both betray the standard's "negotiate before the call" promise. On the wire it serializes as {"min": 8000, "max": 48000}.

Attributes
NameTypeDescription
minint
gt=0

Lowest accepted rate in Hz (inclusive, > 0).

maxint
gt=0

Highest accepted rate in Hz (inclusive, >= min).

contains#methodSource

def SampleRateRange.contains(rate: int) -> bool

Return whether rate falls within the inclusive range.

Parameters
NameTypeDescription
rateint

A sample rate in Hz.

Returns
  • bool

    True if min <= rate <= max.

Segment#classSource

class Segment(BaseModel)

Segment-level detail, shared between batch results and streaming events.

Note

start / end follow the same time frame as Word: non-negative finite float seconds with origin at the first submitted sample (t=0), end >= start (zero-duration allowed), and NaN / Inf rejected -- OR None when the engine measured no such time. None is data, not absence-of-field: the values themselves are the single source of timing truth (there is no side-channel marker), and the legal shapes are pinned by timestamp_status. An end without a start is unrepresentable (rejected at construction): no engine measures where speech stopped without knowing it started.

Ordering: within one channel, MEASURED segments are time-ordered, and the top-level TranscriptionResult.segments with a start are sorted by (start, channel, speaker) (cross-channel spans may overlap; speaker is the final tie-break for equal-(start, channel) overlapping segments, None sorting before any real label). A start=None segment has no time position: the producer keeps the list in READING order instead (list order is the reading order, and TranscriptionResult.text joins segment texts in list order), so a single unmeasured segment never scrambles -- or forces fabricated positions into -- an otherwise real timeline.

Raises
  • ValueError

    If field validation fails (incl. NaN/Inf, a negative time, end < start, or end without start).

Attributes
NameTypeDescription
startfloat | None
ge=0.0

Segment start time in seconds (origin = first submitted sample; non-negative, finite), or None when unmeasured.

endfloat | None
ge=0.0

Segment end time in seconds (non-negative, finite, >= start), or None when unmeasured. Requires start.

textstr

Segment transcript text.

words
= None
list[Word] | None

Optional word-level details for this segment.

speaker
= None
str | None

Optional speaker label (authoritative diarization shape).

channel
= None
int | None
ge=0

Optional channel index for provenance (>= 0).

avg_logprob
= None
float | None

Optional average log-probability.

no_speech_prob
= None
float | None

Optional no-speech probability.

temperature
= None
float | None

Optional decoding temperature.

compression_ratio
= None
float | None

Optional compression-ratio metric.

extra
= dict()
WireExtra

Engine-specific extra data (engine-owned; the standard reserves no keys here).

timestamp_statusLiteral['measured', 'start_only', 'unavailable']

The segment's timing shape, derived from start/end.

Derived, not stored: the nullable values are the single source of truth, so the status can never disagree with them (the previous design stored fabricated 0.0 spans guarded by a mutable side-channel marker -- two truths that could, and did, diverge).

StandardASR#classSource

class StandardASR(Protocol)

Structural protocol for a Standard ASR engine.

Any object exposing these members is a compliant engine, regardless of how it is implemented. The protocol describes the full public surface every engine exposes -- batch (transcribe / transcribe_async) and the streaming entry point (start_transcription). start_transcription is always present; streaming support itself is optional, so a batch-only engine raises UnsupportedFeatureError from it. Because the surface is complete, callers (for example, the server) can type an engine as StandardASR and call the streaming entry point without a cast.

Attributes
NameTypeDescription
propertiesBaseProperties
declared_capabilitiesDeclaredCapabilities
configBaseConfig[str]

The engine's runtime configuration.

Declared as a READ-ONLY property, not a mutable attribute: a mutable protocol member is invariant under strict typing, so a real engine annotating its own subtype (config: WhisperConfig) would not be structurally assignable to StandardASR without a cast -- defeating the protocol's own no-cast promise above. Read-only makes the member covariant (any engine's narrower config satisfies it), and matches intent: config is constructor-injected; callers never reassign it through the protocol. Implementations satisfy this with a plain (even mutable) instance attribute -- no @property required.

transcribe#methodSource

def StandardASR.transcribe(
    audio: AudioInputLike,
    params: RuntimeParams | None = None,
) -> TranscriptionResult

Transcribe a complete audio input.

Parameters
NameTypeDescription
audioAudioInputLike

The audio to transcribe (any AudioInput variant or a coercible bare value).

params
= None
RuntimeParams | None

Per-request runtime parameters.

Returns
Raises
  • ConfigError

    On an invalid language configuration VALUE (default_language malformed or not selectable) -- fixable by whoever supplies the config.

  • EngineContractError

    On an engine-declaration defect -- a declared language axis with no default_language (IC.6), or a malformed declared selectable/detectable tag.

  • IncompatibleAudioInputError

    If no conversion path exists.

  • UnsafeAudioUrlError

    If an AudioUrl fails the SSRF policy.

  • AudioProcessingError

    On a decode / size / missing-sample-rate failure in the conversion pipeline.

  • UnsupportedFeatureError

    In strict mode, on an unsupported parameter, a non-selectable language, or a valid-but-unreachable candidate list (non-detectable candidate / over-max).

  • InvalidProviderParamError

    On wrong provider_params (swap-safety).

  • ValueError

    On a malformed or "auto" candidate-language entry (a caller code bug; raises independent of strict/best_effort).

  • TranscriptionError

    On an engine-execution failure.

transcribe_async#async methodSource

async def StandardASR.transcribe_async(
    audio: AudioInputLike,
    params: RuntimeParams | None = None,
) -> TranscriptionResult

Asynchronously transcribe a complete audio input.

Parameters
NameTypeDescription
audioAudioInputLike

The audio to transcribe (any AudioInput variant or a coercible bare value).

params
= None
RuntimeParams | None

Per-request runtime parameters.

Returns
Raises

start_transcription#methodSource

def StandardASR.start_transcription(
    *,
    audio_format: AudioFormat | None = None,
    params: RuntimeParams | None = None,
    audio: AudioInputLike | None = None,
    deadlines: StreamDeadlines | None = None,
) -> TranscriptionSession

Open a streaming transcription session.

Always present on a compliant engine, but streaming itself is optional: a batch-only engine raises UnsupportedFeatureError here. Callers that need streaming should gate on supports("streaming_input") / supports("streaming_output") (or be ready to handle the unsupported-streaming error).

Parameters
NameTypeDescription
audio_format
= None
AudioFormat | None

Wire format for incremental PCM frames.

params
= None
RuntimeParams | None

Per-request runtime parameters.

audio
= None
AudioInputLike | None

A complete audio input for whole-input streaming output.

deadlines
= None
StreamDeadlines | None

Application overrides for the session's termination deadlines; explicitly set fields win over the engine's construction-time choices.

Returns
Raises
  • ValueError

    If both audio_format and audio are provided, or on a malformed/auto candidate-language entry (a caller code bug; always raises, independent of strict/best_effort).

  • ConfigError

    On an invalid language configuration VALUE (default_language malformed or not selectable).

  • EngineContractError

    On an engine-declaration defect -- a declared language axis with no default_language (IC.6), or a malformed declared selectable/detectable tag.

  • UnsupportedFeatureError

    When streaming (or the requested streaming input/output axis) is unsupported, when the wire format is unreachable, or, in strict mode, on an unsupported parameter or a valid-but-unreachable candidate list (non-detectable / over-max).

  • IncompatibleAudioInputError

    If no conversion path exists for a whole-input streaming audio value.

  • UnsafeAudioUrlError

    If a whole-input AudioUrl fails the SSRF policy.

  • AudioProcessingError

    On a decode / size / missing-sample-rate failure for a whole-input audio value.

  • InvalidProviderParamError

    On wrong provider_params (swap-safety).

  • TranscriptionError

    When a pydantic ValidationError escapes the engine's session-construction hook (an invalid model construction is an engine fault, never a request error).

supports#methodSource

def StandardASR.supports(dot_path: str) -> bool

Return whether the capability at dot_path is supported.

Parameters
NameTypeDescription
dot_pathstr

A capability dot-path.

Returns
  • bool

    True if supported.

recommended_wire_format#methodSource

def StandardASR.recommended_wire_format() -> AudioFormat | None

Return a wire AudioFormat this engine accepts for streaming.

Part of the protocol because it is the documented first step of the streaming journey (README / quickstart / streaming guide all start with it) and the toolchain's sync-bridge runner and gating probes rely on it -- a member every caller is taught to invoke MUST be part of the contract, or a structural engine (and every StandardASR-typed variable) breaks on the standard's own 80% path. The value is purely derivable from the engine's static Properties (see EngineBase.recommended_wire_format for the derivation EngineBase provides for free) -- deliberately capability-blind: whether a bare-frame session can be OPENED is the streaming_input capability gate's job inside start_transcription, so a batch-only or output-only engine still derives a format here (callers gate on supports("streaming_input") first, per the streaming guide).

Returns
  • AudioFormat | None

    A wire format the engine's session-establishment guard accepts, or

  • AudioFormat | None

    None when no bare-frame streaming format can be recommended.

StreamTimestampsCap#classSource

class StreamTimestampsCap(_CapNode)

Source of streaming timestamps.

Attributes
NameTypeDescription
mode
= 'none'
Literal['native_frame_aligned', 'post_align', 'none']

native_frame_aligned / post_align / none.

is_supportedbool

Whether streaming timestamps are provided.

StreamingCapabilities#classSource

class StreamingCapabilities(_Container)

Capability tree for the streaming mode domain.

Attributes
NameTypeDescription
language
= LanguageCaps()
LanguageCaps

Language capabilities (MAY differ from batch).

word_timestamps
= WordTimestampsCap()
WordTimestampsCap

Word-timestamp capability.

diarization
= DiarizationCap()
DiarizationCap

Diarization capability (MAY differ from batch).

guidance
= StreamingGuidanceCaps()
GuidanceCaps

Guidance-family capabilities (MAY differ from batch); the streaming variant additionally exposes mutable_mid_stream.

emits_partials
= FlagCap()
FlagCap

Whether partial events are emitted.

re_segments
= FlagCap()
FlagCap

Whether supersede events may occur.

word_stability
= FlagCap()
FlagCap

Whether a meaningful stable_until is provided.

reconnect
= ReconnectCap()
ReconnectCap

Reconnect capability mode.

finality_level
= FinalityCap()
FinalityCap

Finality level guaranteed.

timestamps
= StreamTimestampsCap()
StreamTimestampsCap

Source of streaming timestamps.

StreamingGuidanceCaps#classSource

class StreamingGuidanceCaps(GuidanceCaps)

Streaming guidance-family capabilities (adds mid-stream mutability).

Identical to GuidanceCaps plus mutable_mid_stream -- the declaration site for the "guidance may change mid-stream" flag. It lives only on the streaming guidance family because mid-stream mutability is meaningless for batch (a single shot); batch guidance keeps the plain GuidanceCaps.

A supported mutable_mid_stream means the engine MAY accept updated guidance after start_transcription (otherwise RuntimeParams is frozen for the whole session). v1 reserves the flag as the standard query path (supports("streaming.guidance.mutable_mid_stream")) and does NOT promise an update_guidance() method; default supported=False coincides with the fail-closed "session-locked" semantics, so the compliance suite requires no behavior for it. Modeled as a FlagCap (not a bare bool) so it derives a uniform supported and covers() set-containment auto-rejects a declared=false -> effective=true widening.

Attributes
NameTypeDescription
mutable_mid_stream
= FlagCap()
FlagCap

Whether guidance may be updated mid-session.

promptPromptCap

Free-text prompt channel.

phrase_hintsPhraseHintsCap

Phrase-hint channel.

TranscriptionEvent#classSource

class TranscriptionEvent(BaseModel)

A single streaming transcription event.

Attributes
NameTypeDescription
typeEventType

The event type.

segment_id
= None
str | None

Stable id of the segment this event concerns.

text
= None
str | None

The segment's complete current text (cumulative/replace).

stable_until
= None
int | None

Frozen-prefix length in codepoints (monotonic per segment while recognition is in progress; a terminal closed restatement may shrink it).

finality
= 'final'
Literal['final', 'closed']

For final events, "final" or "closed" (a closed final is the terminal restatement and may rewrite frozen text once).

words
= None
list[Word] | None

Optional word-level detail (shares the batch Word model).

speaker
= None
str | None

Segment-level speaker label, with the same inheritance rule as Segment.speaker (a non-None words[i].speaker overrides it at word level) and the same label-validity rule. A frozen segment's accepted speaker is protected across events by the lifecycle guard, not at construction.

start
= None
float | None
ge=0.0

Segment start time in seconds (origin = first session sample).

end
= None
float | None
ge=0.0

Segment end time in seconds.

audio_processed_until
= None
float | None
ge=0.0

Monotonic audio-time cursor in seconds.

old_ids
= list()
list[str]

For supersede, the retired segment ids.

new_ids
= list()
list[str]

For supersede, the replacement segment ids.

code
= None
str | None

For error, the error code.

recoverable
= None
bool | None

For error, whether the session may continue.

retriable_after
= None
float | None
ge=0.0

For error, suggested retry delay in seconds.

reconnect
= None
bool | None

For progress, whether this marks a reconnect.

gap_start
= None
float | None
ge=0.0

For a reconnect progress, the gap start time.

gap_end
= None
float | None
ge=0.0

For a reconnect progress, the gap end time.

detected_language
= None
str | None

The engine-detected language (BCP-47, never auto), validated like TranscriptionResult.detected_language; sticky per session (the last non-None value wins), and the reconnect-continuity carrier.

extra
= dict()
WireExtra

Engine-specific extra data.

stable_textstr

The frozen prefix of text (text[:stable_until]).

Guards against an invalid (negative or out-of-range) stable_until so a malformed frontier never produces a wrong or oversized prefix.

is_contentbool

Whether this event advances transcription content.

is_terminalbool

Whether this event ends the session.

partial#class methodSource

@classmethod
def TranscriptionEvent.partial(
    segment_id: str,
    text: str,
    **kw: Any,
) -> TranscriptionEvent

Build a partial event.

Parameters
NameTypeDescription
segment_idstr

The segment id.

textstr

The segment's complete current text.

**kw
= {}
Any

Additional event fields.

Returns

final#class methodSource

@classmethod
def TranscriptionEvent.final(
    segment_id: str,
    text: str,
    **kw: Any,
) -> TranscriptionEvent

Build a final event.

Parameters
NameTypeDescription
segment_idstr

The segment id.

textstr

The segment's final text.

**kw
= {}
Any

Additional event fields.

Returns

closed#class methodSource

@classmethod
def TranscriptionEvent.closed(
    segment_id: str,
    text: str,
    **kw: Any,
) -> TranscriptionEvent

Build a closed finality event (a final with finality=closed).

Parameters
NameTypeDescription
segment_idstr

The segment id.

textstr

The segment's possibly post-processed text.

**kw
= {}
Any

Additional event fields.

Returns

supersede#class methodSource

@classmethod
def TranscriptionEvent.supersede(
    old_ids: list[str],
    new_ids: list[str],
    **kw: Any,
) -> TranscriptionEvent

Build a supersede event replacing old segments with new ones.

Lineage is set-to-set: old_ids/new_ids express the cardinality of the re-segmentation (which ids retire, which appear) but not a per-old->per-new mapping. On a merge+split (many->many) a UI cannot tell which specific old segment a given new segment descends from. This is a documented v1 limitation; the spec does not require a pairwise mapping, and the frozen-prefix-preservation invariant is enforced over the concatenated prefixes, not per pair. Per-pair edit-ops/diffs are a possible future direction (additive later).

Parameters
NameTypeDescription
old_idslist[str]

The retired segment ids, in reading (time) order.

new_idslist[str]

The replacement segment ids, in reading (time) order (must be disjoint from old_ids).

**kw
= {}
Any

Additional event fields.

Returns
Raises
  • ValueError

    If old_ids is empty (a supersede MUST retire at least one segment), if old_ids and new_ids intersect, or if either list repeats a segment id.

progress#class methodSource

@classmethod
def TranscriptionEvent.progress(**kw: Any) -> TranscriptionEvent

Build a progress event (heartbeat / cursor / reconnect notice).

Parameters
NameTypeDescription
**kw
= {}
Any

Event fields (for example, audio_processed_until, reconnect).

Returns

done#class methodSource

@classmethod
def TranscriptionEvent.done(**kw: Any) -> TranscriptionEvent

Build a terminal done event.

Parameters
NameTypeDescription
**kw
= {}
Any

Additional event fields.

Returns

make_error#class methodSource

@classmethod
def TranscriptionEvent.make_error(
    code: str,
    *,
    recoverable: bool = False,
    **kw: Any,
) -> TranscriptionEvent

Build an error event.

Parameters
NameTypeDescription
codestr

The error code.

recoverable
= False
bool

Whether the session may continue.

**kw
= {}
Any

Additional event fields.

Returns

TranscriptionResult#classSource

class TranscriptionResult(BaseModel)

The constant-shape result returned by transcribe and stream reduction.

The top-level text / segments / words are always the complete, channel- and speaker-agnostic transcription. For multi-channel audio they are the time-merge of all channels (never channel-0-only), so ignoring channels is always safe and lossless.

Raises
  • ValueError

    If field validation fails (incl. NaN/Inf, a negative duration, or a malformed detected_language).

Attributes
NameTypeDescription
textstr

Full transcript (required).

detected_language
= None
str | None

Detected language as a well-formed BCP-47 tag in auto mode; None when not applicable.

language_confidence
= None
float | None
ge=0.0, le=1.0

Detection confidence in [0, 1].

duration
= None
float | None
ge=0.0

Audio duration in seconds, if known (non-negative, finite).

segments
= None
list[Segment] | None

Segments across all channels, if available. Segments WITH a start SHOULD be sorted by (start, channel, speaker) (monotonic within a channel; speaker is the final tie-break, None sorting first); a start=None segment has no time position, so the list stays in READING order instead (list order is the reading order; text joins segment texts in list order). The ordering is an engine obligation, neither enforced at construction nor checked by the compliance suite (the streaming reducer keeps arrival order whenever any retained segment lacks a start). The SRT/VTT renderers' defensive re-sort of measured cues is the only standard-layer safety net.

words
= None
list[Word] | None

Flattened word-level details, if available.

channels
= None
list[ChannelResult] | None

Per-channel results when channel separation was performed. Each channel index MUST be unique (one entry per channel), enforced at construction.

diagnostics
= lambda: cast('list[Diagnostic]', [])()
list[Diagnostic]

Conversion / best_effort / degradation diagnostics.

extra
= dict()
WireExtra

Engine-specific / experimental data (incl. provider formats).

TranscriptionSession#classSource

class TranscriptionSession(ABC)

TranscriptionSession(
    *,
    done_timeout: float | None = DEFAULT_DONE_TIMEOUT,
    max_idle: float | None = DEFAULT_MAX_IDLE,
    max_session_seconds: float | None = DEFAULT_MAX_SESSION_SECONDS,
    event_buffer_capacity: int = DEFAULT_EVENT_BUFFER_CAPACITY,
    audio_queue_maxsize: int = DEFAULT_AUDIO_QUEUE_MAXSIZE,
    audio_history_maxlen: int = DEFAULT_AUDIO_HISTORY_MAXLEN,
    strict_lifecycle: bool = False,
    max_guard_diagnostics: int = DEFAULT_MAX_GUARD_DIAGNOSTICS,
)

Async-first, full-duplex streaming session base.

Authors implement _produce (and optionally _open / _close), reading fed audio via audio_chunks and yielding TranscriptionEvent objects. The base manages input ownership, bounded backpressure, lifecycle enforcement, a bounded rolling audio buffer

  • reconnect scaffolding, termination deadlines, and result reduction.

Reconnect contract -- base vs engine:

  • The base owns a bounded rolling audio buffer (the most recent fed chunks) and exposes replay_buffer, the source replayable classification, and note_reconnect. It cannot detect a reconnect itself (it owns no network connection).
  • The engine detects the disconnect, re-establishes the connection, replays replay_buffer audio, keeps segment_id / timestamps / detected language / speaker-label mapping continuous, then calls note_reconnect. For speaker labels the safe default is mint-fresh: the engine MUST NOT reuse a pre-reconnect label for a post-reconnect cluster without identity evidence (blind clustering restarts from zero after a reconnect) -- it MUST mint fresh labels (speaker_2, speaker_3, and so on) and emit a speaker_labels_reset diagnostic via emit_diagnostic -- the fidelity-warning counterpart of content_lost, which is an error event -- because over-counting speakers is the safe direction while silently merging two people under one label is not. The base then emits the progress(reconnect=True, gap_start, gap_end) event and, iff the engine passed content_lost=True (its own determination that the reconnect + replay could not cover the gap), a trailing error(code="content_lost", recoverable=True) fidelity warning.

Initialize the session.

Parameters
NameTypeDescription
done_timeout
= DEFAULT_DONE_TIMEOUT
float | None

Seconds of total pipeline inactivity -- no event arriving AND no fed audio consumed via audio_chunks -- before synthesizing a done_timeout error. A hang backstop, not engine-liveness detection: a silently listening engine stays alive while it keeps consuming audio. After end_audio() it bounds the engine's flush-and-done window. None disables (explicit opt-out of the backstop).

max_idle
= DEFAULT_MAX_IDLE
float | None

Seconds without a content event (partial / final / supersede) before force-terminating with a stream_stalled error. NOT reset by progress heartbeats or audio consumption, so it detects an engine that consumes (or chats) without ever producing content. None (the default) disables: silence is a normal state for a live session.

max_session_seconds
= DEFAULT_MAX_SESSION_SECONDS
float | None

Absolute wall-clock cap; None disables.

event_buffer_capacity
= DEFAULT_EVENT_BUFFER_CAPACITY
int

Pending-event budget shared by every event kind -- drop-proof final / supersede / error / done slots consume it too, and a not-yet-delivered segment can hold two slots at once (its declaration partial plus its final). A new-segment partial or progress heartbeat arriving once the budget is spent overflows, which the session reports as a terminal backpressure error.

audio_queue_maxsize
= DEFAULT_AUDIO_QUEUE_MAXSIZE
int

Max pending audio chunks; bounds feed / send_audio so a slow engine exerts real backpressure.

audio_history_maxlen
= DEFAULT_AUDIO_HISTORY_MAXLEN
int

Capacity of the bounded rolling audio buffer used to replay recent audio after a reconnect.

strict_lifecycle
= False
bool

If True, raise on illegal lifecycle transitions instead of suppressing + diagnosing them.

max_guard_diagnostics
= DEFAULT_MAX_GUARD_DIAGNOSTICS
int

Cap on the bounded lifecycle-suppression diagnostics channel; further diagnostics are aggregated into a single overflow summary rather than growing without bound. Exposed alongside the other bounds so a session can size it; defaults to DEFAULT_MAX_GUARD_DIAGNOSTICS.

Raises
  • ValueError

    If a deadline is not positive (or None where allowed) or a buffer/queue bound is not positive. In particular audio_queue_maxsize=0 would mean an UNBOUNDED asyncio.Queue -- silently disabling the documented feed backpressure -- so it is rejected rather than passed through. Also if max_guard_diagnostics is not positive.

Attributes
NameTypeDescription
replayablebool

Whether the audio source can be re-read after a reconnect.

done_timeoutfloat | None

The configured pipeline-inactivity backstop in seconds.

max_idlefloat | None

The configured content-stall deadline in seconds.

max_session_secondsfloat | None

The configured absolute wall-clock cap in seconds.

audio_chunks#async methodSource

async def TranscriptionSession.audio_chunks() -> AsyncIterator[bytes]

Async-iterate fed audio chunks until the input ends.

Each yielded chunk is also retained in the bounded rolling audio buffer for possible replay after a reconnect.

Consuming from this iterator is the session's liveness anchor: every dequeue resets the done_timeout backstop, so an engine that legitimately emits no events during user silence stays alive for as long as it keeps reading audio here.

Yields
  • AsyncIterator[bytes]

    Raw audio chunks in the session's declared format.

emit_diagnostic#methodSource

def TranscriptionSession.emit_diagnostic(
    *,
    code: str,
    message: str,
    level: Literal['info', 'warning'] = 'info',
    param: str | None = None,
    provided: JsonValue | BaseModel = None,
    effective: JsonValue | BaseModel = None,
) -> None

Surface a structured diagnostic from _produce (the streaming channel).

The streaming counterpart of the batch path's result.diagnostics: call this from your _produce to report a non-fatal note -- a best-effort degradation, an assumed parameter, a lossy fallback -- through the session's own diagnostics channel, so a WS client (mid-stream diagnostics frame) or a sync caller (diagnostics()) sees WHY. For a fatal condition, emit an error event instead -- this is for non-fatal notes that must not end the stream.

Recorded through the same bounded channel as the guard's own diagnostics: past the cap entries aggregate into the overflow summary rather than growing without bound, so a chatty producer cannot exhaust memory.

Security -- this is engine-authored, client-facing output, like the transcript text itself. Every field (message / param / provided / effective) is forwarded to clients VERBATIM -- to a (possibly unauthenticated) WebSocket client as a diagnostics frame, and, for the batch counterpart result.diagnostics, in the REST response -- and is NOT redacted. The standard layer scrubs only the detail IT auto-captures (an error event's extra, which holds a scrubbed exception summary -- never a raw str(exc)); content you pass here is your contract to keep. NEVER pass a credential, API key, a URL with embedded auth, or raw exception text -- route sensitive operator detail to logging instead. (That asymmetry is deliberate: an error event's extra is auto-captured so the server drops it; this note you chose, so you own its safety.)

Parameters
NameTypeDescription
codestr

Stable, machine-readable diagnostic code (for example, "vad_fallback").

messagestr

Human-readable explanation (client-facing; no secrets).

level
= 'info'
Literal['info', 'warning']

"info" or "warning" (default "info").

param
= None
str | None

The parameter the diagnostic concerns, if any.

provided
= None
JsonValue | BaseModel

The value the application provided, if relevant (client-facing). A pydantic model is projected into its JSON form via to_json_value -- the same projection the standard's own diagnostics apply -- so passing a request submodel just works instead of raising out of a live _produce.

effective
= None
JsonValue | BaseModel

The value that took effect, if relevant (client-facing; projected like provided).

Raises
  • pydantic.ValidationError

    If provided / effective has no JSON form even after projection (for example, an arbitrary in-process object) -- an engine bug surfaced loudly at the call site, naming the field, instead of failing later in the transport. Raising from _produce terminates the session, so keep such objects in your own state (see the docs' wire-visible values section).

replay_buffer#methodSource

def TranscriptionSession.replay_buffer() -> list[bytes]

Return audio for re-feeding to a freshly re-established connection.

Engines call this on reconnect to re-send audio. For a replayable (list / tuple) source the COMPLETE source is returned -- replayability promises loss-free replay, so it must not be silently truncated to the rolling ring. For a non-replayable / live source only the bounded rolling window of recent chunks is available (older audio was evicted).

Returns
  • list[bytes]

    The audio chunks to replay, oldest first: the full source if

  • list[bytes]

    replayable, otherwise the bounded rolling window.

note_reconnect#methodSource

def TranscriptionSession.note_reconnect(
    gap_start: float | None = None,
    gap_end: float | None = None,
    *,
    content_lost: bool = False,
) -> None

Record that an internal reconnect bridged a gap (engine-driven).

The base ALWAYS emits a progress(reconnect=True, gap_start, gap_end) event in order with produced events, followed -- IMMEDIATELY -- by a trailing error(code="content_lost", recoverable=True, gap_start, gap_end) IFF the engine passes content_lost=True.

The events are flushed promptly, the moment this is called, through the drop-proof path (_CoalescingBuffer.put_forced, the same sync primitive the base uses for synthesized terminals). This matters because an engine typically calls note_reconnect and then BLOCKS while it re-establishes the connection without yielding another event; deferring the flush to the next produced event would leave the consumer staring at a timeout/silence during a slow reconnect -- the opposite of "transparent but honest". note_reconnect runs in the producer-task coroutine on the session loop, so a synchronous flush is correct and ordered (the events land after already-emitted events and before any subsequent one). The _run_producer / finally drains remain as a harmless safety net -- they simply find nothing pending.

Content loss is an explicit engine determination, not something the base infers from rolling-buffer eviction: a live ring is always evicting, so eviction is the wrong signal (it would falsely claim permanent loss on every long live session). The engine -- which alone knows whether its reconnect + replay_buffer replay actually covered the gap -- sets content_lost=True only when audio the engine had not yet processed could not be replayed and is therefore truly lost. This mirrors the existing contract where segment_id / timestamps / detected language / speaker-label mapping continuity across the reconnect is likewise the engine's responsibility (the base never rewrites them). For speaker labels the engine MUST NOT reuse a pre-reconnect label without identity evidence; the safe default is to mint fresh labels and emit a speaker_labels_reset diagnostic via emit_diagnostic (the fidelity-warning counterpart of the content_lost error event).

Parameters
NameTypeDescription
gap_start
= None
float | None

Start time (seconds) of the lossy gap, if known.

gap_end
= None
float | None

End time (seconds) of the lossy gap, if known.

content_lost
= False
bool

True if the reconnect could not cover the gap and unreplayable audio was permanently lost; queues a non-terminal content_lost fidelity warning after the progress.

feed#methodSource

def TranscriptionSession.feed(
    source: Iterable[bytes] | AsyncIterable[bytes] | bytes | bytearray,
) -> None

Feed audio from a managed source (mutually exclusive with manual).

A bare bytes / bytearray is treated as a single audio chunk (not an iterable of chunks -- iterating it would yield int byte values). A non-async, re-iterable collection (list / tuple, or a wrapped bytes-like) is classified as replayable for reconnect purposes; an async source or a one-shot generator/iterator is non-replayable. Any AsyncIterable (the Pythonic __aiter__ protocol, not only the stricter AsyncIterator) is accepted and normalized via aiter.

Parameters
NameTypeDescription
sourceIterable[bytes] | AsyncIterable[bytes] | bytes | bytearray

A sync or async iterable of byte chunks (Iterable / AsyncIterable of bytes), or a single bytes / bytearray chunk.

Raises
  • TypeError

    If source is a str. A str satisfies Iterable[str] and would be silently consumed one character at a time (or fail deep inside an engine as a confusing engine_error); passing a file path here is a common slip. A whole audio file goes to start_transcription(audio=...) and incremental input is raw PCM byte chunks.

  • InvalidSessionUseError

    If manual input was already used (mixing) or feed was already called once -- a usage error against a still-live session, NOT a lifecycle close.

  • TypeError

    If a subclass rebound a reserved base attribute.

send_audio#async methodSource

async def TranscriptionSession.send_audio(chunk: bytes) -> None

Manually send one audio chunk (mutually exclusive with feed).

Manual sources are always treated as non-replayable (live input).

Parameters
NameTypeDescription
chunkbytes

The audio chunk.

Raises
  • InvalidSessionUseError

    If feed was already used (mixing input modes is a usage error against a still-live session).

  • StreamClosedError

    If the input was ended (end_audio()) or the session already delivered a terminal event -- a genuine lifecycle close: the audio queue has no consumer anymore, so raising beats blocking forever on a dead queue.

  • TypeError

    If a subclass rebound a reserved base attribute.

end_audio#async methodSource

async def TranscriptionSession.end_audio() -> None

Mark the end of manual audio input (idempotent in manual mode).

Claims manual ownership if this is the first input call, so a later feed is correctly rejected as mixing.

Raises
  • InvalidSessionUseError

    If feed was used (mixing input modes is a usage error against a still-live session).

  • TypeError

    If a subclass rebound a reserved base attribute.

__aenter__#async methodSource

async def TranscriptionSession.__aenter__() -> TranscriptionSession

Open resources and start the producer.

Anchors the max_session_seconds wall-clock origin here, at session establishment, so the absolute cap measures from when the session opened -- not from the first __anext__ -- and a re-entered iterator cannot reset it.

Returns
Raises
  • TypeError

    If a subclass rebound a reserved base attribute.

__aexit__#async methodSource

async def TranscriptionSession.__aexit__(*exc: object) -> None

Tear down the producer, feed task, and engine resources.

Cancels the producer and feed tasks and AWAITS them (so no coroutine is still touching engine state when _close runs), then closes.

__aiter__#methodSource

def TranscriptionSession.__aiter__() -> AsyncIterator[TranscriptionEvent]

Return the event async iterator (single-consumer).

A session has exactly ONE event stream and exactly ONE consumer: the events live in a shared bounded buffer, and a second concurrent iterator would race the first for _CoalescingBuffer.get, silently splitting the stream between them (each iterator would see an arbitrary subset, so neither's result would match the stream -- breaking the stream == result invariant). Re-iterating would also reset the per-iteration deadline anchors. Both are programming errors, so the second call fails loudly instead of returning a competing iterator.

Returns
Raises
  • InvalidSessionUseError

    If the session is already being iterated -- a usage error against a still-live session, not a lifecycle close.

  • TypeError

    If a subclass rebound a reserved base attribute.

diagnostics#methodSource

def TranscriptionSession.diagnostics() -> list[Diagnostic]

Return the diagnostics accumulated for this session so far.

The lifecycle-suppression channel is bounded: a misbehaving engine that trips a clamp on (nearly) every event cannot grow this list without limit over a long session. Once DEFAULT_MAX_GUARD_DIAGNOSTICS entries accumulate, further suppressions are aggregated into a single trailing diagnostics_truncated summary (per-code counts) instead of being retained individually -- so the overflow is reported honestly, never silently dropped.

Returns
  • list[Diagnostic]

    The standard-layer parameter-gating / language diagnostics attached

  • list[Diagnostic]

    at session establishment, followed by the runtime's

  • list[Diagnostic]

    lifecycle-suppression diagnostics (suppressed illegal transitions or

  • list[Diagnostic]

    clamped stable_until values), capped as described above.

result#methodSource

def TranscriptionSession.result() -> TranscriptionResult

Reduce the session so far into a transcription result.

Returns

Word#classSource

class Word(BaseModel)

Word-level detail, shared between batch results and streaming events.

Note

Time is measured in float seconds with the origin at the first submitted sample (audio time t=0), the same origin as the streaming cursor. start / end are therefore non-negative finite floats and end >= start (a zero-duration span is allowed). NaN / Inf are rejected (allow_inf_nan=False). Engines convert ms / protobuf-duration / ticks into this frame; a negative or inverted span is an engine bug, so the model refuses to represent one rather than let it surface as a silent wrong timestamp downstream.

Raises
  • ValueError

    If field validation fails (incl. NaN/Inf, a negative time, or end < start).

Attributes
NameTypeDescription
startfloat
ge=0.0

Word start time in seconds (origin = first submitted sample; non-negative, finite).

endfloat
ge=0.0

Word end time in seconds (non-negative, finite, >= start).

textstr

Word text.

probability
= None
float | None
ge=0.0, le=1.0

Optional confidence in [0, 1].

logprob
= None
float | None

Optional log-probability (kept separate from probability).

speaker
= None
str | None

Optional speaker label.

channel
= None
int | None
ge=0

Optional channel index for provenance (>= 0).

extra
= dict()
WireExtra

Engine-specific extra data.

WordTimestampGranularity#classSource

class WordTimestampGranularity(str, Enum)

Granularity for requested word timestamps.

The member values are the single source of truth shared with the declaration-side capability vocabulary WordTimestampGranularityName (a Literal). A module-level assertion (below) and a drift test bind the two sets so an additive change to one cannot silently desync the other.

Attributes
NameTypeDescription
WORD
= 'word'

Word-level timestamps.

SEGMENT
= 'segment'

Segment-level timestamps.

CHAR
= 'char'

Character-level timestamps (reserved, additive).

WordTimestampGranularityName#attributeSource

WordTimestampGranularityName = Literal['word', 'segment', 'char']

WordTimestampsCap#classSource

class WordTimestampsCap(_FlagLikeNode)

Capability for word-level timestamps.

A supported word-timestamp capability MUST enumerate at least one granularity: an engine that declares supported=True but lists no granularities is ambiguous -- gating could not tell whether a requested granularity is offered, and silently honoring an unlisted one is the cardinal sin. Requiring explicit enumeration makes the "supported but unenumerated" state unrepresentable, so gating always validates against a real set. When supported=False the list is ignored (it defaults to empty and carries no meaning).

Attributes
NameTypeDescription
granularities
= lambda: cast('list[WordTimestampGranularityName]', [])()
list[WordTimestampGranularityName]

Supported granularities (word/segment/char); MUST be non-empty when supported is True.

supportedbool

Whether word timestamps are supported.

allow_downloads#functionSource

def allow_downloads(env_var: str = 'STANDARD_ASR_ALLOW_DOWNLOAD') -> bool

Return whether model downloads are allowed at runtime.

Implements the download-policy contract: 1/true/yes enable downloads, 0/false/no disable them, an unset variable defaults to enabled, and any other value (including an empty string, for example, a VAR= line in docker-compose, or a typo like on) disables them fail-closed -- an unrecognized value must never silently enable downloads.

Because the engine that later raises DiscoveryError cannot see that the toggle held an unrecognized value (it only sees this boolean), an unrecognized non-disable value is logged here on every read so the operator can trace a surprising "downloads disabled" back to the real cause -- the explicit, logged explanation the philosophy requires instead of a silent degrade.

Note the empty-string asymmetry with _env_override (used by the cache helpers) is deliberate: for a path override an empty value is meaningless and treated as unset, whereas for this safety toggle an empty value is an unrecognized value and must fail closed to disabled.

Parameters
NameTypeDescription
env_var
= 'STANDARD_ASR_ALLOW_DOWNLOAD'
str

Environment variable name that controls download policy.

Returns
  • bool

    True when downloads are allowed, otherwise False.

effective_candidate_languages#functionSource

def effective_candidate_languages(
    effective_lang: str | None,
    request_candidates: list[str] | None,
    default_candidates: list[str] | None,
    *,
    candidate_supported: bool,
    detectable_languages: Collection[str],
    max_count: int | None,
    strict: bool,
    mode: ModeName | None = None,
) -> tuple[list[str] | None, list[Diagnostic]]

Resolve the candidate languages in effect for a request.

Parameters
NameTypeDescription
effective_langstr | None

The resolved effective language.

request_candidateslist[str] | None

Per-request candidate languages, if any.

default_candidateslist[str] | None

The engine's default candidate languages.

candidate_supportedbool

Whether candidate languages are supported.

detectable_languagesCollection[str]

Languages detectable in auto mode. Tags are canonicalized here (idempotent on already-canonical input; the engine base passes its pre-canonicalized, ConfigError-checked set).

max_countint | None

Maximum candidate count, if constrained.

strictbool

Whether to raise (vs truncate/drop + diagnostic) on violations.

mode
= None
ModeName | None

The mode being resolved ("batch" / "streaming"; the signature enforces the closed set), or None when unknown (direct callers outside the engine pipeline); carried on the strict-mode UnsupportedFeatureError so the rejection reads like every other strict gate rejection.

Returns
  • list[str] | None

    A (candidates, diagnostics) pair; candidates is None when not

  • list[Diagnostic]

    applicable. A candidate_languages_ignored diagnostic is emitted only

  • tuple[list[str] | None, list[Diagnostic]]

    when a candidate list was actually provided (per request or per the

  • tuple[list[str] | None, list[Diagnostic]]

    engine default) but the engine/mode does not support candidate

  • tuple[list[str] | None, list[Diagnostic]]

    languages -- never when no list was provided at all (there is nothing to

  • tuple[list[str] | None, list[Diagnostic]]

    ignore), so an auto engine without candidate support stays

  • tuple[list[str] | None, list[Diagnostic]]

    diagnostic-free on ordinary requests.

Raises
  • ValueError

    Independent of strict, if a candidate is a malformed BCP-47 tag or the reserved "auto" token -- once per-item validation is reached: per spec §LANG R3 the unsupported-capability short-circuit (step 3) runs FIRST, so when the engine/mode does not support candidate languages the provided list is ignored with a diagnostic and its items are never validated here. Direct callers relying on unconditional malformed-item rejection get it from RuntimeParams construction, which validates every candidate before any resolution runs (the engine pipeline is always covered by that). Also raised if a detectable_languages entry is empty/whitespace (engine paths pre-validate this into a ConfigError; the bare error is the direct-call contract). These are caller code bugs, never policy.

  • UnsupportedFeatureError

    In strict mode, on a valid-but-unreachable candidate list -- a candidate not in detectable_languages or a list over max_count. This is the standard strict-gate rejection type (spec, Runtime Parameters R2), so every transport maps it to the same client-error verdict as any other strict rejection (the server's 422) instead of an internal-error 500.

effective_language#functionSource

def effective_language(
    request_language: str | None,
    default_language: str | None,
    *,
    has_language_axis: bool,
    runtime_override_supported: bool,
) -> str | None

Resolve the language in effect for a request.

Parameters
NameTypeDescription
request_languagestr | None

The per-request language, if any.

default_languagestr | None

The engine's default language.

has_language_axisbool

Whether the engine exposes a language axis.

runtime_override_supportedbool

Whether per-request override is supported.

Returns
  • str | None

    The effective language tag / "auto", or None if the engine has

  • str | None

    no language axis.

ensure_wire_format_supported#functionSource

def ensure_wire_format_supported(
    properties: BaseProperties,
    audio_format: AudioFormat,
) -> None

Validate a streaming wire format against an engine's declared Properties.

The standard's session-establishment format rule as a PURE function of (Properties, AudioFormat) -- the single owner shared by EngineBase.ensure_stream_format_supported (the template's establishment guard) and the compliance suite's check_recommended_wire_format round-trip. Compliance validating through this function instead of the EngineBase method keeps the check honest for structural (non-EngineBase) engines: the guard method is NOT a StandardASR protocol member, so calling it on a structural engine raised AttributeError and mis-reported a fully compliant engine as self-inconsistent.

See EngineBase.ensure_stream_format_supported for the full normative semantics (fail-closed on sample rate and channels; fail-closed on encoding only when wire_encodings is declared).

Parameters
NameTypeDescription
propertiesBaseProperties

The engine's declared static Properties.

audio_formatAudioFormat

The wire format the session declared.

Raises
  • UnsupportedFeatureError

    If wire_encodings is declared and the requested encoding is not among them, if the wire channels is not 1 (v1 streaming wire is mono-only), or if the wire sample rate is not reachable for the engine (fail-closed; v1 does not resample streaming wire frames).

env_var_name#functionSource

def env_var_name(engine_id: str, field_name: str) -> str

Return the environment variable name for an engine config field.

The engine and field segments are joined by a double underscore (STANDARD_ASR_<ENGINE>__<FIELD>) so the boundary between them is unambiguous for the realistic name space. With a single-underscore separator the engine/field split was not recoverable -- env_var_name("openai", "api_key") and env_var_name("openai-api", "key") both produced STANDARD_ASR_OPENAI_API_KEY, so two different engines could silently read each other's credentials. Because _normalize_segment collapses each non-alphanumeric run to a single _, an interior single _ (folded from - / .) can never be mistaken for the __ boundary. This relies on engine_id being entrypoint-derived and PEP 503-normalized (no leading/trailing separator) and field_name being a Python identifier: a pathological engine_id ending in a separator combined with a field_name starting with one is out of that space. Same-class collisions (two fields of one config normalizing alike) are still caught by BaseConfig.env_overrides.

Parameters
NameTypeDescription
engine_idstr

The engine identifier.

field_namestr

The standard config field name.

Returns
  • str

    The fully qualified environment variable name.

granularity_offers_all#functionSource

def granularity_offers_all(granularities: Sequence[str]) -> bool

Return whether a declared granularities list means "unbounded (all)".

An empty enumeration on a bounded node is the "engine did not enumerate" / unbounded case, not "offers nothing" (on a bounded archetype an empty enumeration list does not constrain). The only consumer is the capability-narrowing comparison _node_narrows / DeclaredCapabilities.covers, and there only on the raw dict / x_* path: a typed WordTimestampsCap cannot reach this case because its validator makes supported=True with an empty granularities unrepresentable, so "empty == all" survives solely for untyped JSON-sourced nodes.

This is deliberately NOT shared with runtime parameter gating: runtime.gating._gate_granularity does not call this function and takes the opposite stance (a supported WordTimestampsCap always enumerates its granularities, so a requested value MUST be one of them -- no "empty => honor anything"). The two modules agree because that typed-validator invariant closes the empty case, not because they share this helper.

Parameters
NameTypeDescription
granularitiesSequence[str]

The declared granularity list (possibly empty).

Returns
  • bool

    True if the list is empty (unbounded -- every granularity offered).

nearest_accepted_sample_rate#functionSource

def nearest_accepted_sample_rate(accepted: AcceptedSampleRates, source: int) -> int

Return the accepted rate closest to source (resample target choice).

Only meaningful when source is NOT already accepted (the caller checks that first). For a list, the nearest member preferring not to upsample (the anti-upsampling spirit). For a range, source clamped into [min, max] -- the closest reachable in-range rate, which never upsamples when source is above the range. "any" accepts everything, so it can never reach here with an unaccepted rate.

Parameters
NameTypeDescription
acceptedAcceptedSampleRates

The declared accepted sample rates (a list or range).

sourceint

The input waveform's current sample rate in Hz.

Returns
  • int

    An accepted target sample rate in Hz.

Raises
  • ValueError

    If accepted is "any" (no finite target to choose; an "any" engine accepts source directly and never resamples).

normalize_bcp47#functionSource

def normalize_bcp47(tag: str) -> str

Normalize a BCP 47 language tag into a consistent, canonical form.

Trims/replaces separators, then applies BCP-47 canonical casing (language lowercase, script Titlecase, region UPPERCASE) so values echoed back to applications -- for example, detected_language and diagnostic provided/effective fields -- read canonically (zh-Hans, not zh-hans). Per RFC 5646 §2.1.1 the script/region conventions apply only before the first singleton (a 1-char subtag); every subtag after it -- extension and private-use subtags -- stays lowercase (zh-Hans-u-co-pinyin, never u-CO). Membership comparisons are unaffected: both the declared set and the request are canonicalized through this same function, so matching stays case-insensitive in effect.

Parameters
NameTypeDescription
tagstr

Input language tag.

Returns
  • str

    The canonicalized tag (separators normalized to -, canonical casing).

Raises
  • ValueError

    If tag is empty or only whitespace.

resolve_download_root#functionSource

def resolve_download_root(
    explicit: Path | None = None,
    *,
    has_library_default: bool = False,
) -> Path | None

Resolve an engine's model download root per the standard precedence.

Implements the normative four-level chain engines MUST follow when picking where model artifacts land: explicit config (the engine's download_root field) > the STANDARD_ASR_MODEL_DIR environment override > the underlying ASR library's own default cache (when it has one; expressed as a None passthrough, see below) > the shared Standard ASR cache directory (resolve_cache_dir, ~/.cache/standard-asr or the platform equivalent). The environment tier inherits resolve_cache_dir's reading of the variable: a whitespace-only value is unset and a relative value resolves against the current working directory at call time.

The library tier is a passthrough: ASR libraries typically express "use my own default cache" as an unset download path (for example, faster-whisper's WhisperModel(download_root=None) resolves via the HuggingFace hub cache), so when the chain lands on that tier this returns None and the engine forwards it unchanged. Substituting a concrete directory here would delete the spec's third tier: every unconfigured install's models would relocate away from the library's existing cache, breaking offline loads of already-downloaded models and silently re-downloading them.

Parameters
NameTypeDescription
explicit
= None
Path | None

Explicitly configured download root (highest priority); a leading ~ is expanded.

has_library_default
= False
bool

Whether the underlying ASR library has its own default model cache (the spec's third tier). When True and neither an explicit value nor the environment override is present, None is returned for the engine to forward; an engine that knows the library's concrete cache path may substitute it for the None.

Returns
  • Path | None

    The resolved download root, or None when the underlying ASR library's own

  • Path | None

    default cache applies (only possible with has_library_default).

sample_rate_accepted#functionSource

def sample_rate_accepted(accepted: AcceptedSampleRates, rate: int) -> bool

Return whether rate is accepted by an accepted_sample_rates value.

The single membership predicate for all three variants, so every call site (reachability checks, batch resampling decision, streaming session validation) agrees on what "accepted" means.

Parameters
NameTypeDescription
acceptedAcceptedSampleRates

The declared accepted sample rates.

rateint

A candidate sample rate in Hz.

Returns
  • bool

    True if accepted admits rate: always for "any",

  • bool

    rate in accepted for a list, min <= rate <= max for a range.

secret_field#functionSource

def secret_field(default: Any = None, *, description: str = '') -> Any

Build a Field for a write-only credential rendered as a password.

Use together with a SecretStr or SecretBytes annotation (exactly one carrier, optionally unioned with None). The json_schema_extra marks the field secret so auto-UI renders a password / write-only input and REST exposes it POST-only. BaseConfig enforces the carrier annotation AND the default's shape at class-definition time, masks the value in BaseConfig.public_dump, and preserves the secret's exact contents (no whitespace stripping) so a paste error is never silently swallowed.

Parameters
NameTypeDescription
default
= None
Any

Field default. None for an optional credential (the annotation must union None), ... (Ellipsis) for a required one, or a carrier instance (for example, SecretStr("preset")) -- a plain string is rejected at class definition (defaults are not validated, so it would leak plaintext).

description
= ''
str

Field description.

Returns
  • Any

    A configured pydantic Field.

to_json_value#functionSource

def to_json_value(value: object) -> JsonValue

Project a Python value into the wire value space.

Every wire-visible slot -- Diagnostic.provided / effective, every extra mapping -- is declared JsonValue, because the Python objects and the JSON documents are meant to be the same protocol seen twice (G5.2). Declaring them Any admitted values with no JSON representation at all, which then failed during the wire projection -- after an endpoint had already committed to a response.

Two things stand between an ordinary value and that declaration, and this helper is where both are handled:

  • a structured value (a pydantic submodel such as a DiarizationRequest) has a JSON form but is not itself JSON, so it is dumped;
  • a typed container (list[str], dict[str, int]) IS JSON data, but a type checker does not accept it where list[JsonValue] is expected, because list is invariant. That is a static-analysis artifact, not a real mismatch, so it is absorbed here once instead of forcing a cast at every call site.

Runtime validation is unaffected: the model still validates what it is given, so a value that is genuinely not JSON is rejected loudly at construction, naming the field.

Parameters
NameTypeDescription
valueobject

The value to hand to a wire-visible slot.

Returns
  • JsonValue

    The value's JSON projection.

On this page

standard_asr.engineAUTOAudioFormatBaseConfig__pydantic_init_subclass__model_validate_jsonmodel_validate_stringspublic_dumpreveal_dumpfrom_envenv_overridesBasePropertiesBatchCapabilitiesCandidateLanguagesCapCandidateLanguagesConstraintsChannelResultCredentialsConfigMixinDIARIZEDeclaredCapabilitiessupportsnode_atiter_supported_pathsiter_queryable_pathscoverscanonical_jsonDeviceConfigMixinDiagnosticDiarizationCapDiarizationConstraintsDiarizationRequestDownloadConfigMixinEngineBasesupportspreparetranscribetranscribe_asyncensure_stream_inputs_exclusiveensure_stream_format_supportedrecommended_wire_formatstart_transcriptionFinalityCapFlagCapGuidanceCapsInputKindLanguageCapsLanguageConfigMixinModePhraseHintsCapPhraseHintsConstraintsPreparedAudioPromptCapPromptConstraintsProviderParamsReconnectCapRuntimeParamsSampleRateRangecontainsSegmentStandardASRtranscribetranscribe_asyncstart_transcriptionsupportsrecommended_wire_formatStreamTimestampsCapStreamingCapabilitiesStreamingGuidanceCapsTranscriptionEventpartialfinalclosedsupersedeprogressdonemake_errorTranscriptionResultTranscriptionSessionaudio_chunksemit_diagnosticreplay_buffernote_reconnectfeedsend_audioend_audio__aenter____aexit____aiter__diagnosticsresultWordWordTimestampGranularityWordTimestampGranularityNameWordTimestampsCapallow_downloadseffective_candidate_languageseffective_languageensure_wire_format_supportedenv_var_namegranularity_offers_allnearest_accepted_sample_ratenormalize_bcp47resolve_download_rootsample_rate_acceptedsecret_fieldto_json_value