Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions bindings/python/src/transcribe_cpp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
Itn = Literal["default", "off", "on"]
Diarize = Literal["default", "off", "on"]
SortformerPreset = Literal["default", "very_high_latency", "high_latency", "low_latency"]
Nemotron3DiarPreset = Literal["default", "very_high_latency", "low_latency",
"very_low_latency", "ultra_low_latency"]
CommitPolicy = Literal["auto", "on_finalize", "stable_prefix"]
Feature = Literal[
"initial_prompt", "temperature_fallback", "long_form",
Expand Down Expand Up @@ -85,9 +87,12 @@
"ParakeetStreamOptions",
"ParakeetBufferedStreamOptions",
"SortformerStreamOptions",
"Nemotron3DiarRunOptions",
"Nemotron3DiarStreamOptions",
"VoxtralRealtimeStreamOptions",
"Backend",
"SortformerPreset",
"Nemotron3DiarPreset",
"KVType",
"Task",
"Timestamps",
Expand Down Expand Up @@ -870,6 +875,57 @@ def _apply(self, ext) -> None:
ext.preset = self._presets[self.preset]


class _Nemotron3DiarPresetOptions(FamilyExtension):
"""Shared preset mapping for the two Nemotron-3-Diarization slots."""

_presets = {
"default": _generated.TRANSCRIBE_NEMOTRON3_DIAR_PRESET_DEFAULT,
"very_high_latency": _generated.TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_HIGH_LATENCY,
"low_latency": _generated.TRANSCRIBE_NEMOTRON3_DIAR_PRESET_LOW_LATENCY,
"very_low_latency": _generated.TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_LOW_LATENCY,
"ultra_low_latency": _generated.TRANSCRIBE_NEMOTRON3_DIAR_PRESET_ULTRA_LOW_LATENCY,
}

def __init__(self, *, preset: Nemotron3DiarPreset | None = None):
if preset is not None and preset not in self._presets:
raise ValueError(f"unknown nemotron3_diar preset {preset!r}; "
f"expected one of {sorted(self._presets)}")
self.preset = preset

def _apply(self, ext) -> None:
if self.preset is not None:
ext.preset = self._presets[self.preset]


class Nemotron3DiarRunOptions(_Nemotron3DiarPresetOptions):
"""Nemotron-3-Diarization operating point for a whole-file run (run slot).

A diarizer: a run produces speaker segments (up to 8 speakers), no text.
``preset`` picks the model card's latency / accuracy bundle:
``"very_high_latency"`` (30.4 s, the offline point; also ``"default"``),
``"low_latency"`` (1.04 s), ``"very_low_latency"`` (0.64 s),
``"ultra_low_latency"`` (0.32 s). Smaller chunks cost more compute per
audio second."""

_slot = "run"
_kind = _generated.TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_RUN
_struct = _generated.transcribe_nemotron3_diar_run_ext
_init = "transcribe_nemotron3_diar_run_ext_init"


class Nemotron3DiarStreamOptions(_Nemotron3DiarPresetOptions):
"""Nemotron-3-Diarization operating point for push-audio streaming
(stream slot). After each feed the speaker segments cover all audio
processed so far (a turn still in progress is open-ended and may extend);
finalize equals a whole-file run at the same preset. Presets as for
:class:`Nemotron3DiarRunOptions`."""

_slot = "stream"
_kind = _generated.TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_STREAM
_struct = _generated.transcribe_nemotron3_diar_stream_ext
_init = "transcribe_nemotron3_diar_stream_ext_init"


# --- high-level handles ---------------------------------------------------


Expand Down
21 changes: 20 additions & 1 deletion bindings/python/src/transcribe_cpp/_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# Stable digest of the ABI surface below (structs, enums, macros, layout,
# prototypes). A native provider package echoes this back so the API
# package can reject an ABI-mismatched provider before dlopen.
PUBLIC_HEADER_HASH = "7df72bf9e667b8c2"
PUBLIC_HEADER_HASH = "ae25d09c2b7b325b"

# === enum constants ===
TRANSCRIBE_OK = 0
Expand Down Expand Up @@ -102,6 +102,11 @@
TRANSCRIBE_STREAM_COMMIT_AUTO = 0
TRANSCRIBE_STREAM_COMMIT_ON_FINALIZE = 1
TRANSCRIBE_STREAM_COMMIT_STABLE_PREFIX = 2
TRANSCRIBE_NEMOTRON3_DIAR_PRESET_DEFAULT = 0
TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_HIGH_LATENCY = 1
TRANSCRIBE_NEMOTRON3_DIAR_PRESET_LOW_LATENCY = 2
TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_LOW_LATENCY = 3
TRANSCRIBE_NEMOTRON3_DIAR_PRESET_ULTRA_LOW_LATENCY = 4
TRANSCRIBE_SORTFORMER_PRESET_DEFAULT = 0
TRANSCRIBE_SORTFORMER_PRESET_VERY_HIGH_LATENCY = 1
TRANSCRIBE_SORTFORMER_PRESET_HIGH_LATENCY = 2
Expand All @@ -111,6 +116,8 @@

# === macro constants (integer object-like macros) ===
TRANSCRIBE_EXT_KIND_MOONSHINE_STREAMING_STREAM = 1414746957
TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_RUN = 1380201294
TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_STREAM = 1396978510
TRANSCRIBE_EXT_KIND_PARAKEET_BUFFERED_STREAM = 1396853584
TRANSCRIBE_EXT_KIND_PARAKEET_STREAM = 1414744912
TRANSCRIBE_EXT_KIND_SORTFORMER_STREAM = 1414743635
Expand Down Expand Up @@ -150,6 +157,10 @@ class transcribe_speaker_segment(_c.Structure):
pass
class transcribe_moonshine_streaming_stream_ext(_c.Structure):
pass
class transcribe_nemotron3_diar_run_ext(_c.Structure):
pass
class transcribe_nemotron3_diar_stream_ext(_c.Structure):
pass
class transcribe_parakeet_stream_ext(_c.Structure):
pass
class transcribe_parakeet_buffered_stream_ext(_c.Structure):
Expand Down Expand Up @@ -179,6 +190,8 @@ class transcribe_whisper_chunk_trace(_c.Structure):
transcribe_token._fields_ = [("struct_size", _c.c_uint64), ("id", _c.c_int), ("p", _c.c_float), ("t0_ms", _c.c_int64), ("t1_ms", _c.c_int64), ("seg_index", _c.c_int), ("word_index", _c.c_int), ("text", _c.c_char_p)]
transcribe_speaker_segment._fields_ = [("struct_size", _c.c_uint64), ("t0_ms", _c.c_int64), ("t1_ms", _c.c_int64), ("speaker_id", _c.c_int32), ("p", _c.c_float)]
transcribe_moonshine_streaming_stream_ext._fields_ = [("ext", transcribe_ext), ("min_decode_interval_ms", _c.c_int32)]
transcribe_nemotron3_diar_run_ext._fields_ = [("ext", transcribe_ext), ("preset", _c.c_int)]
transcribe_nemotron3_diar_stream_ext._fields_ = [("ext", transcribe_ext), ("preset", _c.c_int)]
transcribe_parakeet_stream_ext._fields_ = [("ext", transcribe_ext), ("att_context_right", _c.c_int32)]
transcribe_parakeet_buffered_stream_ext._fields_ = [("ext", transcribe_ext), ("left_ms", _c.c_int32), ("chunk_ms", _c.c_int32), ("right_ms", _c.c_int32)]
transcribe_sortformer_stream_ext._fields_ = [("ext", transcribe_ext), ("preset", _c.c_int)]
Expand Down Expand Up @@ -224,6 +237,8 @@ class transcribe_whisper_chunk_trace(_c.Structure):
'transcribe_token': {'size': 48, 'align': 8, 'offsets': {'struct_size': 0, 'id': 8, 'p': 12, 't0_ms': 16, 't1_ms': 24, 'seg_index': 32, 'word_index': 36, 'text': 40}},
'transcribe_speaker_segment': {'size': 32, 'align': 8, 'offsets': {'struct_size': 0, 't0_ms': 8, 't1_ms': 16, 'speaker_id': 24, 'p': 28}},
'transcribe_moonshine_streaming_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'min_decode_interval_ms': 16}},
'transcribe_nemotron3_diar_run_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'preset': 16}},
'transcribe_nemotron3_diar_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'preset': 16}},
'transcribe_parakeet_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'att_context_right': 16}},
'transcribe_parakeet_buffered_stream_ext': {'size': 32, 'align': 8, 'offsets': {'ext': 0, 'left_ms': 16, 'chunk_ms': 20, 'right_ms': 24}},
'transcribe_sortformer_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'preset': 16}},
Expand Down Expand Up @@ -343,6 +358,10 @@ def configure(lib):
lib.transcribe_n_tokens.argtypes = [_c.c_void_p]
lib.transcribe_n_words.restype = _c.c_int
lib.transcribe_n_words.argtypes = [_c.c_void_p]
lib.transcribe_nemotron3_diar_run_ext_init.restype = None
lib.transcribe_nemotron3_diar_run_ext_init.argtypes = [_c.POINTER(transcribe_nemotron3_diar_run_ext)]
lib.transcribe_nemotron3_diar_stream_ext_init.restype = None
lib.transcribe_nemotron3_diar_stream_ext_init.argtypes = [_c.POINTER(transcribe_nemotron3_diar_stream_ext)]
lib.transcribe_open.restype = _c.c_int
lib.transcribe_open.argtypes = [_c.c_char_p, _c.POINTER(transcribe_model_load_params), _c.POINTER(transcribe_session_params), _c.POINTER(_c.c_void_p)]
lib.transcribe_parakeet_buffered_stream_ext_init.restype = None
Expand Down
18 changes: 18 additions & 0 deletions bindings/python/tests/test_family_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
t.ParakeetStreamOptions,
t.ParakeetBufferedStreamOptions,
t.SortformerStreamOptions,
t.Nemotron3DiarRunOptions,
t.Nemotron3DiarStreamOptions,
t.VoxtralRealtimeStreamOptions,
]

Expand Down Expand Up @@ -87,6 +89,22 @@ def test_sortformer_unknown_preset_rejected():
t.SortformerStreamOptions(preset="ultra_low_latency") # type: ignore[arg-type]


@pytest.mark.parametrize("cls,slot,kind", [
(t.Nemotron3DiarRunOptions, "run", _generated.TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_RUN),
(t.Nemotron3DiarStreamOptions, "stream", _generated.TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_STREAM),
])
def test_nemotron3_diar_preset_maps_to_enum_value(cls, slot, kind):
assert cls._slot == slot and cls._kind == kind
built = cls(preset="ultra_low_latency")._build()
assert built.preset == _generated.TRANSCRIBE_NEMOTRON3_DIAR_PRESET_ULTRA_LOW_LATENCY
assert cls()._build().preset == _generated.TRANSCRIBE_NEMOTRON3_DIAR_PRESET_DEFAULT


def test_nemotron3_diar_unknown_preset_rejected():
with pytest.raises(ValueError, match="preset"):
t.Nemotron3DiarRunOptions(preset="high_latency") # type: ignore[arg-type]


# --- model-gated: resolve_family validation + a real extension run ----------


Expand Down
63 changes: 61 additions & 2 deletions bindings/rust/sys/src/transcribe_sys.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// @generated by `cargo xtask bindgen` from include/transcribe/extensions.h
// DO NOT EDIT BY HAND. Regenerate: `cargo xtask bindgen`.
// Pinned to include/transcribe.abihash = 7df72bf9e667b8c2
// Pinned to include/transcribe.abihash = ae25d09c2b7b325b

/// The public-ABI digest these bindings were generated against
/// (sha256/16 over the normalized FFI surface). The load-time version
/// gate and the CI drift check both anchor on this value.
pub const PUBLIC_HEADER_HASH: &str = "7df72bf9e667b8c2";
pub const PUBLIC_HEADER_HASH: &str = "ae25d09c2b7b325b";

/* automatically generated by rust-bindgen 0.72.1 */

pub const TRANSCRIBE_EXT_KIND_MOONSHINE_STREAMING_STREAM: u32 = 1414746957;
pub const TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_RUN: u32 = 1380201294;
pub const TRANSCRIBE_EXT_KIND_NEMOTRON3_DIAR_STREAM: u32 = 1396978510;
pub const TRANSCRIBE_EXT_KIND_PARAKEET_STREAM: u32 = 1414744912;
pub const TRANSCRIBE_EXT_KIND_PARAKEET_BUFFERED_STREAM: u32 = 1396853584;
pub const TRANSCRIBE_EXT_KIND_SORTFORMER_STREAM: u32 = 1414743635;
Expand Down Expand Up @@ -1115,6 +1117,63 @@ unsafe extern "C" {
ext: *mut transcribe_moonshine_streaming_stream_ext,
);
}
impl transcribe_nemotron3_diar_preset {
pub const TRANSCRIBE_NEMOTRON3_DIAR_PRESET_DEFAULT: transcribe_nemotron3_diar_preset =
transcribe_nemotron3_diar_preset(0);
pub const TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_HIGH_LATENCY: transcribe_nemotron3_diar_preset =
transcribe_nemotron3_diar_preset(1);
pub const TRANSCRIBE_NEMOTRON3_DIAR_PRESET_LOW_LATENCY: transcribe_nemotron3_diar_preset =
transcribe_nemotron3_diar_preset(2);
pub const TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_LOW_LATENCY: transcribe_nemotron3_diar_preset =
transcribe_nemotron3_diar_preset(3);
pub const TRANSCRIBE_NEMOTRON3_DIAR_PRESET_ULTRA_LOW_LATENCY: transcribe_nemotron3_diar_preset =
transcribe_nemotron3_diar_preset(4);
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct transcribe_nemotron3_diar_preset(pub ::std::os::raw::c_uint);
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct transcribe_nemotron3_diar_run_ext {
pub ext: transcribe_ext,
pub preset: transcribe_nemotron3_diar_preset,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
["Size of transcribe_nemotron3_diar_run_ext"]
[::std::mem::size_of::<transcribe_nemotron3_diar_run_ext>() - 24usize];
["Alignment of transcribe_nemotron3_diar_run_ext"]
[::std::mem::align_of::<transcribe_nemotron3_diar_run_ext>() - 8usize];
["Offset of field: transcribe_nemotron3_diar_run_ext::ext"]
[::std::mem::offset_of!(transcribe_nemotron3_diar_run_ext, ext) - 0usize];
["Offset of field: transcribe_nemotron3_diar_run_ext::preset"]
[::std::mem::offset_of!(transcribe_nemotron3_diar_run_ext, preset) - 16usize];
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct transcribe_nemotron3_diar_stream_ext {
pub ext: transcribe_ext,
pub preset: transcribe_nemotron3_diar_preset,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
["Size of transcribe_nemotron3_diar_stream_ext"]
[::std::mem::size_of::<transcribe_nemotron3_diar_stream_ext>() - 24usize];
["Alignment of transcribe_nemotron3_diar_stream_ext"]
[::std::mem::align_of::<transcribe_nemotron3_diar_stream_ext>() - 8usize];
["Offset of field: transcribe_nemotron3_diar_stream_ext::ext"]
[::std::mem::offset_of!(transcribe_nemotron3_diar_stream_ext, ext) - 0usize];
["Offset of field: transcribe_nemotron3_diar_stream_ext::preset"]
[::std::mem::offset_of!(transcribe_nemotron3_diar_stream_ext, preset) - 16usize];
};
unsafe extern "C" {
pub fn transcribe_nemotron3_diar_run_ext_init(ext: *mut transcribe_nemotron3_diar_run_ext);
}
unsafe extern "C" {
pub fn transcribe_nemotron3_diar_stream_ext_init(
ext: *mut transcribe_nemotron3_diar_stream_ext,
);
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct transcribe_parakeet_stream_ext {
Expand Down
70 changes: 70 additions & 0 deletions bindings/rust/transcribe-cpp/src/family.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,58 @@ pub struct SortformerStreamOptions {
pub preset: Option<SortformerPreset>,
}

/// Nemotron-3-Diarization operating point (model-card latency / accuracy
/// bundle). `Default` keeps the GGUF-shipped configuration (= `VeryHighLatency`).
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum Nemotron3DiarPreset {
#[default]
Default,
/// 30.4 s (chunk 340 + lookahead 40 frames); the offline point.
VeryHighLatency,
/// 1.04 s (chunk 9 + lookahead 4).
LowLatency,
/// 0.64 s (chunk 6 + lookahead 2).
VeryLowLatency,
/// 0.32 s (chunk 3 + lookahead 1). Most compute per audio second.
UltraLowLatency,
}

impl Nemotron3DiarPreset {
fn to_sys(self) -> sys::transcribe_nemotron3_diar_preset {
use sys::transcribe_nemotron3_diar_preset as P;
match self {
Nemotron3DiarPreset::Default => P::TRANSCRIBE_NEMOTRON3_DIAR_PRESET_DEFAULT,
Nemotron3DiarPreset::VeryHighLatency => {
P::TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_HIGH_LATENCY
}
Nemotron3DiarPreset::LowLatency => P::TRANSCRIBE_NEMOTRON3_DIAR_PRESET_LOW_LATENCY,
Nemotron3DiarPreset::VeryLowLatency => {
P::TRANSCRIBE_NEMOTRON3_DIAR_PRESET_VERY_LOW_LATENCY
}
Nemotron3DiarPreset::UltraLowLatency => {
P::TRANSCRIBE_NEMOTRON3_DIAR_PRESET_ULTRA_LOW_LATENCY
}
}
}
}

/// Nemotron-3-Diarization knobs, used on both slots: as
/// [`RunExtension::Nemotron3Diar`] for a whole-file run and as
/// [`StreamExtension::Nemotron3Diar`] for push-audio streaming (segments cover
/// all audio processed so far; finalize equals a whole-file run). A run
/// produces speaker segments (up to 8 speakers), no text.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Nemotron3DiarOptions {
pub preset: Option<Nemotron3DiarPreset>,
}

/// A family extension for the run slot (offline `run`/`run_batch`).
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum RunExtension {
Whisper(WhisperRunOptions),
Sortformer(SortformerStreamOptions),
Nemotron3Diar(Nemotron3DiarOptions),
}

/// A family extension for the stream slot.
Expand All @@ -119,6 +165,7 @@ pub enum StreamExtension {
ParakeetBuffered(ParakeetBufferedStreamOptions),
MoonshineStreaming(MoonshineStreamingOptions),
VoxtralRealtime(VoxtralRealtimeStreamOptions),
Nemotron3Diar(Nemotron3DiarOptions),
}

/// Owns a materialized run-slot C extension struct (and any strings it points
Expand All @@ -131,6 +178,7 @@ pub(crate) enum RunExtRaw {
_prompt: Option<CString>,
},
Sortformer(Box<sys::transcribe_sortformer_stream_ext>),
Nemotron3Diar(Box<sys::transcribe_nemotron3_diar_run_ext>),
}

impl RunExtRaw {
Expand All @@ -143,6 +191,10 @@ impl RunExtRaw {
RunExtRaw::Sortformer(e) => {
(&**e) as *const sys::transcribe_sortformer_stream_ext as *const sys::transcribe_ext
}
RunExtRaw::Nemotron3Diar(e) => {
(&**e) as *const sys::transcribe_nemotron3_diar_run_ext
as *const sys::transcribe_ext
}
}
}
}
Expand Down Expand Up @@ -182,6 +234,12 @@ impl RunExtension {
set(&mut ext.preset, o.preset.map(SortformerPreset::to_sys));
Ok(RunExtRaw::Sortformer(Box::new(ext)))
}
RunExtension::Nemotron3Diar(o) => {
let mut ext: sys::transcribe_nemotron3_diar_run_ext = unsafe { std::mem::zeroed() };
unsafe { sys::transcribe_nemotron3_diar_run_ext_init(&mut ext) };
set(&mut ext.preset, o.preset.map(Nemotron3DiarPreset::to_sys));
Ok(RunExtRaw::Nemotron3Diar(Box::new(ext)))
}
}
}
}
Expand All @@ -192,6 +250,7 @@ pub(crate) enum StreamExtRaw {
ParakeetBuffered(Box<sys::transcribe_parakeet_buffered_stream_ext>),
MoonshineStreaming(Box<sys::transcribe_moonshine_streaming_stream_ext>),
VoxtralRealtime(Box<sys::transcribe_voxtral_realtime_stream_ext>),
Nemotron3Diar(Box<sys::transcribe_nemotron3_diar_stream_ext>),
}

impl StreamExtRaw {
Expand All @@ -212,6 +271,10 @@ impl StreamExtRaw {
(&**e) as *const sys::transcribe_voxtral_realtime_stream_ext
as *const sys::transcribe_ext
}
StreamExtRaw::Nemotron3Diar(e) => {
(&**e) as *const sys::transcribe_nemotron3_diar_stream_ext
as *const sys::transcribe_ext
}
}
}
}
Expand Down Expand Up @@ -249,6 +312,13 @@ impl StreamExtension {
set(&mut e.min_decode_interval_ms, o.min_decode_interval_ms);
StreamExtRaw::VoxtralRealtime(Box::new(e))
}
StreamExtension::Nemotron3Diar(o) => {
let mut e: sys::transcribe_nemotron3_diar_stream_ext =
unsafe { std::mem::zeroed() };
unsafe { sys::transcribe_nemotron3_diar_stream_ext_init(&mut e) };
set(&mut e.preset, o.preset.map(Nemotron3DiarPreset::to_sys));
StreamExtRaw::Nemotron3Diar(Box::new(e))
}
}
}
}
Expand Down
Loading