standard_asr.contract.results
The constant-shape transcription result and its components.
Standard ASR transcription result models (constant schema).
The result schema is constant: capabilities and parameters decide whether
optional fields are populated, never the return type's shape (spec, section
"Transcription Result"). The same Segment / Word submodels are
shared between batch results and streaming events.
Null rules (disambiguation):
- A field is
None-> the data was not requested / not applicable. - A field is
[]-> it was requested but is empty (for example, silence). - Whether a feature is supported is answered by capabilities, never by a
field being
None.
ChannelResult#classSource
class ChannelResult(BaseModel)Per-channel transcription for multi-channel audio.
ValueErrorIf field validation fails.
DIAG_SEGMENT_TIMESTAMPS_UNAVAILABLE#attributeSource
DIAG_SEGMENT_TIMESTAMPS_UNAVAILABLE = 'segment_timestamps_unavailable'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.
| Name | Type | Description |
|---|---|---|
level= 'info' | Literal['info', 'warning'] | Severity, |
code | str | Stable machine-readable code (for example, |
message | str | 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. |
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.
ValueErrorIf field validation fails (incl. NaN/Inf, a negative time,
end < start, orendwithoutstart).
| Name | Type | Description |
|---|---|---|
start | float | Nonege=0.0 | Segment start time in seconds (origin = first submitted sample;
non-negative, finite), or |
end | float | Nonege=0.0 | Segment end time in seconds (non-negative, finite, |
text | str | 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 | Nonege=0 | Optional channel index for provenance ( |
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_status | Literal['measured', 'start_only', 'unavailable'] | The segment's timing shape, derived from 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 |
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.
ValueErrorIf field validation fails (incl. NaN/Inf, a negative
duration, or a malformeddetected_language).
| Name | Type | Description |
|---|---|---|
text | str | Full transcript (required). |
detected_language= None | str | None | Detected language as a well-formed BCP-47 tag in
|
language_confidence= None | float | Nonege=0.0, le=1.0 | Detection confidence in |
duration= None | float | Nonege=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
|
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
|
diagnostics= lambda: cast('list[Diagnostic]', [])() | list[Diagnostic] | Conversion / best_effort / degradation diagnostics. |
extra= dict() | WireExtra | Engine-specific / experimental data (incl. provider formats). |
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.
ValueErrorIf field validation fails (incl. NaN/Inf, a negative time, or
end < start).
| Name | Type | Description |
|---|---|---|
start | floatge=0.0 | Word start time in seconds (origin = first submitted sample; non-negative, finite). |
end | floatge=0.0 | Word end time in seconds (non-negative, finite, |
text | str | Word text. |
probability= None | float | Nonege=0.0, le=1.0 | Optional confidence in |
logprob= None | float | None | Optional log-probability (kept separate from |
speaker= None | str | None | Optional speaker label. |
channel= None | int | Nonege=0 | Optional channel index for provenance ( |
extra= dict() | WireExtra | Engine-specific extra data. |
synthesize_segment_speaker#functionSource
def synthesize_segment_speaker(words: Sequence[Word] | None) -> str | NoneDerive a segment-level speaker label from its words (the pinned synthesis rule).
THE single synthesis rule of the standard layer, used when an engine
populates Word.speaker but leaves the authoritative Segment.speaker
None:
- Majority by word count -- the label carried by the most words wins.
- Tie -> the speaker of the earliest (lowest-index) word among the tied labels.
- Words with
speaker=Nonedo not vote. - No speaker-bearing words (or
wordsNone/empty) ->None.
This function is deliberately the ONLY implementation -- both the batch
post-processing (EngineBase.transcribe) and the streaming reducer
(StreamReducer) call it, never a private
copy. Portability demands it: the same engine and audio MUST yield the same
Segment.speaker whether the app took the batch or the streaming path;
two drifting copies of the rule would silently break that promise.
| Name | Type | Description |
|---|---|---|
words | Sequence[Word] | None | The segment's word-level details, or |
str | NoneThe synthesized speaker label, or
Nonewhen no word carries one.
to_json_value#functionSource
def to_json_value(value: object) -> JsonValueProject 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 wherelist[JsonValue]is expected, becauselistis invariant. That is a static-analysis artifact, not a real mismatch, so it is absorbed here once instead of forcing acastat 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.
| Name | Type | Description |
|---|---|---|
value | object | The value to hand to a wire-visible slot. |
JsonValueThe value's JSON projection.
validate_speaker_label#functionSource
def validate_speaker_label(value: str | None) -> str | NoneValidate a speaker label at construction (shared by every speaker field).
One rule for Segment, Word, and
TranscriptionEvent: a label
must be non-empty, not whitespace-only, and carry no leading/trailing
whitespace. "" would be an undefined third state between None
(no attribution) and a real label; edge whitespace ("A " vs "A")
silently breaks within-result label consistency -- two strings = two
speakers -- so both are rejected, never normalized (normalizing would
hide an engine bug behind a silently rewritten value; the same stance as
phrase_hints). None (no attribution) passes through.
| Name | Type | Description |
|---|---|---|
value | str | None | The candidate speaker label, or |
str | NoneThe validated value unchanged.
ValueErrorIf
valueis empty, whitespace-only, or has leading or trailing whitespace.