transcribe-cpp 0.1.3__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/.gitignore +4 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/PKG-INFO +19 -7
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/README.md +15 -3
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/pyproject.toml +3 -3
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/src/transcribe_cpp/__init__.py +152 -36
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/src/transcribe_cpp/_generated.py +58 -20
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/src/transcribe_cpp/_library.py +2 -2
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_backends.py +16 -7
- transcribe_cpp-0.2.0/tests/test_device_select.py +38 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_family_ext.py +15 -1
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_provider_discovery.py +1 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/uv.lock +3 -3
- transcribe_cpp-0.1.3/tests/test_device_select.py +0 -55
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/LICENSE +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/_generate/README.md +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/_generate/check_version_sync.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/_generate/generate.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/examples/stream_wav.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/examples/transcribe_wav.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/src/transcribe_cpp/_abi.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/src/transcribe_cpp/errors.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/src/transcribe_cpp/py.typed +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/conftest.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_abi.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_errors.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_example.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_lifetime.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_pcm.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_streaming.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_transcribe.py +0 -0
- {transcribe_cpp-0.1.3 → transcribe_cpp-0.2.0}/tests/test_utf8_paths.py +0 -0
|
@@ -59,6 +59,10 @@ __pycache__/
|
|
|
59
59
|
|
|
60
60
|
# WER evaluation data + generated working reports.
|
|
61
61
|
/samples/wer/
|
|
62
|
+
# Diarization eval corpora (AMI audio + fetched RTTMs); regenerable via
|
|
63
|
+
# scripts/diar/ingest_ami.py + fetch_ami_forced_alignment.py. The committed
|
|
64
|
+
# oracle case lives at samples/sortformer-2spk-mix.wav (outside samples/diar/).
|
|
65
|
+
/samples/diar/
|
|
62
66
|
/reports/*
|
|
63
67
|
|
|
64
68
|
# Reviewable porting evidence (intake, porting log) is intentionally committed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: transcribe-cpp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python bindings for transcribe.cpp
|
|
5
5
|
Project-URL: Homepage, https://github.com/handy-computer/transcribe.cpp
|
|
6
6
|
Project-URL: Repository, https://github.com/handy-computer/transcribe.cpp
|
|
@@ -21,9 +21,9 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
21
21
|
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
22
22
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
23
|
Requires-Python: >=3.9
|
|
24
|
-
Requires-Dist: transcribe-cpp-native==0.
|
|
24
|
+
Requires-Dist: transcribe-cpp-native==0.2.0.*
|
|
25
25
|
Provides-Extra: cu12
|
|
26
|
-
Requires-Dist: transcribe-cpp-native-cu12==0.
|
|
26
|
+
Requires-Dist: transcribe-cpp-native-cu12==0.2.0.*; extra == 'cu12'
|
|
27
27
|
Provides-Extra: test
|
|
28
28
|
Requires-Dist: numpy; extra == 'test'
|
|
29
29
|
Requires-Dist: pytest>=7; extra == 'test'
|
|
@@ -37,6 +37,10 @@ a C/C++ speech-to-text library built on ggml.
|
|
|
37
37
|
> **Status: in development.** Until wheels are published, use a locally built
|
|
38
38
|
> `libtranscribe` through repo auto-discovery or `TRANSCRIBE_LIBRARY`.
|
|
39
39
|
|
|
40
|
+
Upgrading from 0.1? See the
|
|
41
|
+
[0.2 migration guide](https://github.com/handy-computer/transcribe.cpp/blob/main/docs/migrating-to-0.2.md),
|
|
42
|
+
including the replacement of `gpu_device=` with exact device objects.
|
|
43
|
+
|
|
40
44
|
```python
|
|
41
45
|
import transcribe_cpp
|
|
42
46
|
|
|
@@ -75,9 +79,17 @@ Long transcriptions can be cancelled from another thread with
|
|
|
75
79
|
|
|
76
80
|
## Backends
|
|
77
81
|
|
|
78
|
-
`Model(backend=...)`
|
|
79
|
-
available). `transcribe_cpp.backends()`
|
|
80
|
-
`
|
|
82
|
+
`Model(backend=...)` applies a backend policy (`"auto"` uses the best
|
|
83
|
+
available). `transcribe_cpp.backends()` returns process-local device objects;
|
|
84
|
+
pass one as `Model(device=device)` for exact selection with no fallback. Persist
|
|
85
|
+
a device's `device_id`, not its runtime handle or index. `backend_available(kind)`
|
|
86
|
+
checks whether a backend policy can currently be satisfied.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
device = next(d for d in transcribe_cpp.backends() if d.device_type == "cpu")
|
|
90
|
+
with transcribe_cpp.Model("model.gguf", device=device) as model:
|
|
91
|
+
print(model.device)
|
|
92
|
+
```
|
|
81
93
|
|
|
82
94
|
| Variable | Effect |
|
|
83
95
|
|---|---|
|
|
@@ -6,6 +6,10 @@ a C/C++ speech-to-text library built on ggml.
|
|
|
6
6
|
> **Status: in development.** Until wheels are published, use a locally built
|
|
7
7
|
> `libtranscribe` through repo auto-discovery or `TRANSCRIBE_LIBRARY`.
|
|
8
8
|
|
|
9
|
+
Upgrading from 0.1? See the
|
|
10
|
+
[0.2 migration guide](https://github.com/handy-computer/transcribe.cpp/blob/main/docs/migrating-to-0.2.md),
|
|
11
|
+
including the replacement of `gpu_device=` with exact device objects.
|
|
12
|
+
|
|
9
13
|
```python
|
|
10
14
|
import transcribe_cpp
|
|
11
15
|
|
|
@@ -44,9 +48,17 @@ Long transcriptions can be cancelled from another thread with
|
|
|
44
48
|
|
|
45
49
|
## Backends
|
|
46
50
|
|
|
47
|
-
`Model(backend=...)`
|
|
48
|
-
available). `transcribe_cpp.backends()`
|
|
49
|
-
`
|
|
51
|
+
`Model(backend=...)` applies a backend policy (`"auto"` uses the best
|
|
52
|
+
available). `transcribe_cpp.backends()` returns process-local device objects;
|
|
53
|
+
pass one as `Model(device=device)` for exact selection with no fallback. Persist
|
|
54
|
+
a device's `device_id`, not its runtime handle or index. `backend_available(kind)`
|
|
55
|
+
checks whether a backend policy can currently be satisfied.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
device = next(d for d in transcribe_cpp.backends() if d.device_type == "cpu")
|
|
59
|
+
with transcribe_cpp.Model("model.gguf", device=device) as model:
|
|
60
|
+
print(model.device)
|
|
61
|
+
```
|
|
50
62
|
|
|
51
63
|
| Variable | Effect |
|
|
52
64
|
|---|---|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "transcribe-cpp"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Python bindings for transcribe.cpp"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# 3.8 is EOL (2024-10); 3.9 is the floor. The binding is ctypes-only, so there
|
|
@@ -34,13 +34,13 @@ classifiers = [
|
|
|
34
34
|
# packaging fix still resolves); the import-time version/header-hash check in
|
|
35
35
|
# _library.py is the runtime backstop. check_version_sync.py gates this pin
|
|
36
36
|
# against include/transcribe.h.
|
|
37
|
-
dependencies = ["transcribe-cpp-native==0.
|
|
37
|
+
dependencies = ["transcribe-cpp-native==0.2.0.*"]
|
|
38
38
|
|
|
39
39
|
[project.optional-dependencies]
|
|
40
40
|
# Opt-in accelerator providers — ADDITIVE: they install alongside the default
|
|
41
41
|
# provider and the best one wins at runtime. Same base-version pin contract
|
|
42
42
|
# as the hard dependency (gated by check_version_sync.py).
|
|
43
|
-
cu12 = ["transcribe-cpp-native-cu12==0.
|
|
43
|
+
cu12 = ["transcribe-cpp-native-cu12==0.2.0.*"]
|
|
44
44
|
# Test-only deps. Run with: uv run --extra test pytest (from bindings/python).
|
|
45
45
|
# numpy is here so the numpy PCM-input tests run in every lane instead of
|
|
46
46
|
# silently skipping wherever numpy happens to be absent.
|
|
@@ -24,7 +24,7 @@ import ctypes
|
|
|
24
24
|
import os
|
|
25
25
|
import threading
|
|
26
26
|
import weakref
|
|
27
|
-
from dataclasses import dataclass
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
28
|
from typing import Literal, Optional, Sequence, Union
|
|
29
29
|
|
|
30
30
|
from . import _abi, _generated
|
|
@@ -46,17 +46,19 @@ from .errors import (
|
|
|
46
46
|
raise_for_status,
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
__version__ = "0.
|
|
49
|
+
__version__ = "0.2.0"
|
|
50
50
|
|
|
51
51
|
# String-enum types, exported so callers (and type checkers) can name them.
|
|
52
|
-
Backend = Literal["auto", "cpu", "metal", "vulkan", "cpu_accel", "cuda"]
|
|
52
|
+
Backend = Literal["auto", "cpu", "metal", "vulkan", "cpu_accel", "cuda", "rocm"]
|
|
53
53
|
KVType = Literal["auto", "f32", "f16"]
|
|
54
54
|
Task = Literal["transcribe", "translate"]
|
|
55
55
|
Timestamps = Literal["none", "auto", "segment", "word", "token"]
|
|
56
|
+
Diarize = Literal["default", "off", "on"]
|
|
57
|
+
SortformerPreset = Literal["default", "very_high_latency", "high_latency", "low_latency"]
|
|
56
58
|
CommitPolicy = Literal["auto", "on_finalize", "stable_prefix"]
|
|
57
59
|
Feature = Literal[
|
|
58
60
|
"initial_prompt", "temperature_fallback", "long_form",
|
|
59
|
-
"cancellation", "pnc", "itn",
|
|
61
|
+
"cancellation", "pnc", "itn", "diarization",
|
|
60
62
|
]
|
|
61
63
|
|
|
62
64
|
__all__ = [
|
|
@@ -66,6 +68,7 @@ __all__ = [
|
|
|
66
68
|
"Session",
|
|
67
69
|
"Result",
|
|
68
70
|
"Segment",
|
|
71
|
+
"SpeakerSegment",
|
|
69
72
|
"Word",
|
|
70
73
|
"Token",
|
|
71
74
|
"Capabilities",
|
|
@@ -79,11 +82,14 @@ __all__ = [
|
|
|
79
82
|
"MoonshineStreamingOptions",
|
|
80
83
|
"ParakeetStreamOptions",
|
|
81
84
|
"ParakeetBufferedStreamOptions",
|
|
85
|
+
"SortformerStreamOptions",
|
|
82
86
|
"VoxtralRealtimeStreamOptions",
|
|
83
87
|
"Backend",
|
|
88
|
+
"SortformerPreset",
|
|
84
89
|
"KVType",
|
|
85
90
|
"Task",
|
|
86
91
|
"Timestamps",
|
|
92
|
+
"Diarize",
|
|
87
93
|
"CommitPolicy",
|
|
88
94
|
"Feature",
|
|
89
95
|
"TranscribeError",
|
|
@@ -165,6 +171,7 @@ _RunParams = _generated.transcribe_run_params
|
|
|
165
171
|
_Capabilities = _generated.transcribe_capabilities
|
|
166
172
|
_Timings = _generated.transcribe_timings
|
|
167
173
|
_Segment = _generated.transcribe_segment
|
|
174
|
+
_SpeakerSegment = _generated.transcribe_speaker_segment
|
|
168
175
|
_Word = _generated.transcribe_word
|
|
169
176
|
_Token = _generated.transcribe_token
|
|
170
177
|
_StreamParams = _generated.transcribe_stream_params
|
|
@@ -180,6 +187,7 @@ _BACKENDS = {
|
|
|
180
187
|
"vulkan": _generated.TRANSCRIBE_BACKEND_VULKAN,
|
|
181
188
|
"cpu_accel": _generated.TRANSCRIBE_BACKEND_CPU_ACCEL,
|
|
182
189
|
"cuda": _generated.TRANSCRIBE_BACKEND_CUDA,
|
|
190
|
+
"rocm": _generated.TRANSCRIBE_BACKEND_ROCM,
|
|
183
191
|
}
|
|
184
192
|
_KV_TYPES = {
|
|
185
193
|
"auto": _generated.TRANSCRIBE_KV_TYPE_AUTO,
|
|
@@ -198,6 +206,11 @@ _TIMESTAMPS = {
|
|
|
198
206
|
"token": _generated.TRANSCRIBE_TIMESTAMPS_TOKEN,
|
|
199
207
|
}
|
|
200
208
|
_TIMESTAMP_NAMES = {v: k for k, v in _TIMESTAMPS.items()}
|
|
209
|
+
_DIARIZE = {
|
|
210
|
+
"default": _generated.TRANSCRIBE_DIARIZE_MODE_DEFAULT,
|
|
211
|
+
"off": _generated.TRANSCRIBE_DIARIZE_MODE_OFF,
|
|
212
|
+
"on": _generated.TRANSCRIBE_DIARIZE_MODE_ON,
|
|
213
|
+
}
|
|
201
214
|
_COMMIT_POLICIES = {
|
|
202
215
|
"auto": _generated.TRANSCRIBE_STREAM_COMMIT_AUTO,
|
|
203
216
|
"on_finalize": _generated.TRANSCRIBE_STREAM_COMMIT_ON_FINALIZE,
|
|
@@ -220,6 +233,7 @@ _FEATURES = {
|
|
|
220
233
|
"cancellation": _generated.TRANSCRIBE_FEATURE_CANCELLATION,
|
|
221
234
|
"pnc": _generated.TRANSCRIBE_FEATURE_PNC,
|
|
222
235
|
"itn": _generated.TRANSCRIBE_FEATURE_ITN,
|
|
236
|
+
"diarization": _generated.TRANSCRIBE_FEATURE_DIARIZATION,
|
|
223
237
|
}
|
|
224
238
|
|
|
225
239
|
|
|
@@ -252,13 +266,17 @@ _DEVICE_TYPE_NAMES = {
|
|
|
252
266
|
}
|
|
253
267
|
|
|
254
268
|
|
|
255
|
-
@dataclass(frozen=True)
|
|
269
|
+
@dataclass(frozen=True, eq=False)
|
|
256
270
|
class BackendDevice:
|
|
257
|
-
"""One registered compute device (owned copies of the C strings).
|
|
271
|
+
"""One registered compute device (owned copies of the C strings).
|
|
272
|
+
|
|
273
|
+
Equality compares opaque native identity; display index and live memory
|
|
274
|
+
snapshots do not affect whether two values name the same device.
|
|
275
|
+
"""
|
|
258
276
|
|
|
259
277
|
name: str
|
|
260
278
|
description: str
|
|
261
|
-
kind: str # "cpu" | "accel" | "metal" | "vulkan" | "cuda" | "sycl" | "gpu" | "unknown"
|
|
279
|
+
kind: str # "cpu" | "accel" | "metal" | "vulkan" | "cuda" | "rocm" | "sycl" | "gpu" | "unknown"
|
|
262
280
|
# Vendor-agnostic class: "cpu" | "gpu" | "igpu" | "accel", or "unknown" for a
|
|
263
281
|
# value reported by a runtime newer than this binding (tell such devices
|
|
264
282
|
# apart by device_id / name, not by this axis).
|
|
@@ -269,18 +287,26 @@ class BackendDevice:
|
|
|
269
287
|
# (via backends() or Model.device) to refresh; backend-defined and not
|
|
270
288
|
# comparable across device kinds.
|
|
271
289
|
memory_free: int
|
|
272
|
-
# Registry index
|
|
273
|
-
#
|
|
274
|
-
# probed before integrated).
|
|
275
|
-
# None when the device came from Model.device, since the underlying
|
|
276
|
-
# transcribe_model_get_device() does not expose an index; correlate such a
|
|
277
|
-
# device back to backends() by device_id / name instead. The index is
|
|
278
|
-
# order-dependent and not stable across driver updates or hosts.
|
|
290
|
+
# Registry index for display. Exact model selection uses the BackendDevice
|
|
291
|
+
# itself; indices are process-local and not stable across driver updates.
|
|
279
292
|
index: Optional[int] = None
|
|
293
|
+
# Opaque process-local native device handle. Applications persist device_id,
|
|
294
|
+
# never this value.
|
|
295
|
+
_handle: Optional[int] = field(default=None, repr=False, compare=False)
|
|
296
|
+
|
|
297
|
+
def __eq__(self, other: object) -> bool:
|
|
298
|
+
if self is other:
|
|
299
|
+
return True
|
|
300
|
+
if not isinstance(other, BackendDevice):
|
|
301
|
+
return NotImplemented
|
|
302
|
+
return self._handle is not None and self._handle == other._handle
|
|
303
|
+
|
|
304
|
+
def __hash__(self) -> int:
|
|
305
|
+
return hash(self._handle) if self._handle is not None else object.__hash__(self)
|
|
280
306
|
|
|
281
307
|
|
|
282
|
-
def _backend_device_from_raw(dev, index: Optional[int] = None) -> BackendDevice:
|
|
283
|
-
"""Build a BackendDevice from a library-filled
|
|
308
|
+
def _backend_device_from_raw(dev, handle: Optional[int], index: Optional[int] = None) -> BackendDevice:
|
|
309
|
+
"""Build a BackendDevice from a library-filled transcribe_device_info."""
|
|
284
310
|
return BackendDevice(
|
|
285
311
|
name=_decode(dev.name),
|
|
286
312
|
description=_decode(dev.description),
|
|
@@ -290,6 +316,7 @@ def _backend_device_from_raw(dev, index: Optional[int] = None) -> BackendDevice:
|
|
|
290
316
|
memory_total=int(dev.memory_total),
|
|
291
317
|
memory_free=int(dev.memory_free),
|
|
292
318
|
index=index,
|
|
319
|
+
_handle=handle,
|
|
293
320
|
)
|
|
294
321
|
|
|
295
322
|
|
|
@@ -301,12 +328,15 @@ def backends() -> list[BackendDevice]:
|
|
|
301
328
|
Each device's ``memory_free`` is live as of the call; call again to poll
|
|
302
329
|
a device's available memory over time."""
|
|
303
330
|
devices = []
|
|
304
|
-
for i in range(_lib.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
331
|
+
for i in range(_lib.transcribe_device_count()):
|
|
332
|
+
handle = _lib.transcribe_device_get(i)
|
|
333
|
+
if not handle:
|
|
334
|
+
continue
|
|
335
|
+
dev = _generated.transcribe_device_info()
|
|
336
|
+
_lib.transcribe_device_info_init(_byref(dev))
|
|
337
|
+
_check(_lib.transcribe_device_get_info(handle, _byref(dev)),
|
|
308
338
|
f"reading backend device {i}")
|
|
309
|
-
devices.append(_backend_device_from_raw(dev, index=i))
|
|
339
|
+
devices.append(_backend_device_from_raw(dev, int(handle), index=i))
|
|
310
340
|
return devices
|
|
311
341
|
|
|
312
342
|
|
|
@@ -434,6 +464,18 @@ class Segment:
|
|
|
434
464
|
n_words: int
|
|
435
465
|
first_token: int
|
|
436
466
|
n_tokens: int
|
|
467
|
+
speaker_id: int
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
@dataclass(frozen=True)
|
|
471
|
+
class SpeakerSegment:
|
|
472
|
+
"""One diarized turn. Times are zero when the model attributes text but
|
|
473
|
+
does not provide speaker timing; ``p`` is NaN when unavailable."""
|
|
474
|
+
|
|
475
|
+
t0_ms: int
|
|
476
|
+
t1_ms: int
|
|
477
|
+
speaker_id: int
|
|
478
|
+
p: float
|
|
437
479
|
|
|
438
480
|
|
|
439
481
|
@dataclass(frozen=True)
|
|
@@ -499,9 +541,14 @@ class Result:
|
|
|
499
541
|
returned, so it stays valid after later runs."""
|
|
500
542
|
|
|
501
543
|
text: str
|
|
544
|
+
#: The model's decoded output before family post-processing (diarization
|
|
545
|
+
#: markers, timestamp/special tokens, tag filtering, whitespace trims).
|
|
546
|
+
#: Equal to ``text`` modulo whitespace for families that emit clean text.
|
|
547
|
+
raw_text: str
|
|
502
548
|
language: str
|
|
503
549
|
timestamp_kind: str
|
|
504
550
|
segments: tuple[Segment, ...]
|
|
551
|
+
speaker_segments: tuple[SpeakerSegment, ...]
|
|
505
552
|
words: tuple[Word, ...]
|
|
506
553
|
tokens: tuple[Token, ...]
|
|
507
554
|
timings: Timings
|
|
@@ -548,6 +595,14 @@ def _segment_from(s) -> Segment:
|
|
|
548
595
|
text=_decode(s.text), t0_ms=s.t0_ms, t1_ms=s.t1_ms,
|
|
549
596
|
first_word=s.first_word, n_words=s.n_words,
|
|
550
597
|
first_token=s.first_token, n_tokens=s.n_tokens,
|
|
598
|
+
speaker_id=s.speaker_id,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _speaker_segment_from(s) -> SpeakerSegment:
|
|
603
|
+
return SpeakerSegment(
|
|
604
|
+
t0_ms=s.t0_ms, t1_ms=s.t1_ms,
|
|
605
|
+
speaker_id=s.speaker_id, p=s.p,
|
|
551
606
|
)
|
|
552
607
|
|
|
553
608
|
|
|
@@ -583,7 +638,7 @@ def _stream_update_from(u) -> StreamUpdate:
|
|
|
583
638
|
|
|
584
639
|
|
|
585
640
|
def _build_run_params(task, language, target_language, timestamps,
|
|
586
|
-
keep_special_tags, spec_k_drafts):
|
|
641
|
+
keep_special_tags, spec_k_drafts, diarize="default"):
|
|
587
642
|
if not isinstance(spec_k_drafts, int) or spec_k_drafts < -1:
|
|
588
643
|
raise InvalidArgument(
|
|
589
644
|
f"spec_k_drafts must be -1 (family default), 0 (disabled), or a "
|
|
@@ -593,6 +648,7 @@ def _build_run_params(task, language, target_language, timestamps,
|
|
|
593
648
|
_lib.transcribe_run_params_init(_byref(params))
|
|
594
649
|
params.task = _enum(_TASKS, task, "task")
|
|
595
650
|
params.timestamps = _enum(_TIMESTAMPS, timestamps, "timestamps")
|
|
651
|
+
params.diarize = _enum(_DIARIZE, diarize, "diarize")
|
|
596
652
|
params.language = language.encode("utf-8") if language else None
|
|
597
653
|
params.target_language = target_language.encode("utf-8") if target_language else None
|
|
598
654
|
params.keep_special_tags = keep_special_tags
|
|
@@ -763,6 +819,40 @@ class VoxtralRealtimeStreamOptions(FamilyExtension):
|
|
|
763
819
|
ext.min_decode_interval_ms = self.min_decode_interval_ms
|
|
764
820
|
|
|
765
821
|
|
|
822
|
+
class SortformerStreamOptions(FamilyExtension):
|
|
823
|
+
"""Sortformer streaming operating-point options (run slot).
|
|
824
|
+
|
|
825
|
+
Sortformer is a diarizer: a run produces speaker segments, no text.
|
|
826
|
+
``preset`` selects the latency / accuracy trade-off from the model's
|
|
827
|
+
published menu; ``"default"`` keeps the GGUF-shipped checkpoint
|
|
828
|
+
configuration. ``"very_high_latency"`` (~30 s lookahead) is the
|
|
829
|
+
offline-file operating point; ``"low_latency"`` (~1 s) is the
|
|
830
|
+
real-time point and costs substantially more compute per audio
|
|
831
|
+
second."""
|
|
832
|
+
|
|
833
|
+
_slot = "run"
|
|
834
|
+
_kind = _generated.TRANSCRIBE_EXT_KIND_SORTFORMER_STREAM
|
|
835
|
+
_struct = _generated.transcribe_sortformer_stream_ext
|
|
836
|
+
_init = "transcribe_sortformer_stream_ext_init"
|
|
837
|
+
|
|
838
|
+
_presets = {
|
|
839
|
+
"default": _generated.TRANSCRIBE_SORTFORMER_PRESET_DEFAULT,
|
|
840
|
+
"very_high_latency": _generated.TRANSCRIBE_SORTFORMER_PRESET_VERY_HIGH_LATENCY,
|
|
841
|
+
"high_latency": _generated.TRANSCRIBE_SORTFORMER_PRESET_HIGH_LATENCY,
|
|
842
|
+
"low_latency": _generated.TRANSCRIBE_SORTFORMER_PRESET_LOW_LATENCY,
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
def __init__(self, *, preset: SortformerPreset | None = None):
|
|
846
|
+
if preset is not None and preset not in self._presets:
|
|
847
|
+
raise ValueError(f"unknown sortformer preset {preset!r}; "
|
|
848
|
+
f"expected one of {sorted(self._presets)}")
|
|
849
|
+
self.preset = preset
|
|
850
|
+
|
|
851
|
+
def _apply(self, ext) -> None:
|
|
852
|
+
if self.preset is not None:
|
|
853
|
+
ext.preset = self._presets[self.preset]
|
|
854
|
+
|
|
855
|
+
|
|
766
856
|
# --- high-level handles ---------------------------------------------------
|
|
767
857
|
|
|
768
858
|
|
|
@@ -783,7 +873,7 @@ class Model:
|
|
|
783
873
|
"""
|
|
784
874
|
|
|
785
875
|
def __init__(self, path: str | os.PathLike, *,
|
|
786
|
-
backend: Backend = "auto",
|
|
876
|
+
backend: Backend = "auto", device: BackendDevice | None = None):
|
|
787
877
|
# Live sessions, tracked weakly: close() must free them before the
|
|
788
878
|
# model, because transcribe_model_free is only valid once every
|
|
789
879
|
# derived session is gone (use-after-free otherwise). Created before
|
|
@@ -796,7 +886,10 @@ class Model:
|
|
|
796
886
|
params = _ModelLoadParams()
|
|
797
887
|
_lib.transcribe_model_load_params_init(_byref(params))
|
|
798
888
|
params.backend = _enum(_BACKENDS, backend, backend_source)
|
|
799
|
-
|
|
889
|
+
if device is not None:
|
|
890
|
+
if not isinstance(device, BackendDevice) or device._handle is None:
|
|
891
|
+
raise TypeError("device must be a BackendDevice returned by backends()")
|
|
892
|
+
params.device = device._handle
|
|
800
893
|
|
|
801
894
|
handle = ctypes.c_void_p()
|
|
802
895
|
status = _lib.transcribe_model_load_file(
|
|
@@ -831,11 +924,14 @@ class Model:
|
|
|
831
924
|
live snapshot, so read this again to poll how much device memory is
|
|
832
925
|
left after the model loaded. Raises if the model has no resolved
|
|
833
926
|
compute device."""
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
927
|
+
handle = _lib.transcribe_model_device(self._h)
|
|
928
|
+
if not handle:
|
|
929
|
+
raise BackendError("model has no resolved compute device")
|
|
930
|
+
dev = _generated.transcribe_device_info()
|
|
931
|
+
_lib.transcribe_device_info_init(_byref(dev))
|
|
932
|
+
_check(_lib.transcribe_device_get_info(handle, _byref(dev)),
|
|
933
|
+
"device_get_info")
|
|
934
|
+
return _backend_device_from_raw(dev, int(handle))
|
|
839
935
|
|
|
840
936
|
@property
|
|
841
937
|
def capabilities(self) -> Capabilities:
|
|
@@ -983,6 +1079,7 @@ class Session:
|
|
|
983
1079
|
language: str | None = None,
|
|
984
1080
|
target_language: str | None = None,
|
|
985
1081
|
timestamps: Timestamps = "auto",
|
|
1082
|
+
diarize: Diarize = "default",
|
|
986
1083
|
keep_special_tags: bool = False,
|
|
987
1084
|
spec_k_drafts: int = -1,
|
|
988
1085
|
family: FamilyExtension | None = None) -> Result:
|
|
@@ -1000,7 +1097,7 @@ class Session:
|
|
|
1000
1097
|
self._cancel.clear()
|
|
1001
1098
|
array, n_samples = _pcm_to_carray(pcm)
|
|
1002
1099
|
params = _build_run_params(task, language, target_language, timestamps,
|
|
1003
|
-
keep_special_tags, spec_k_drafts)
|
|
1100
|
+
keep_special_tags, spec_k_drafts, diarize)
|
|
1004
1101
|
ext = self._resolve_family(family, "run") if family is not None else None
|
|
1005
1102
|
if ext is not None:
|
|
1006
1103
|
params.family = ctypes.cast(
|
|
@@ -1019,6 +1116,7 @@ class Session:
|
|
|
1019
1116
|
language: str | None = None,
|
|
1020
1117
|
target_language: str | None = None,
|
|
1021
1118
|
timestamps: Timestamps = "auto",
|
|
1119
|
+
diarize: Diarize = "default",
|
|
1022
1120
|
keep_special_tags: bool = False,
|
|
1023
1121
|
spec_k_drafts: int = -1,
|
|
1024
1122
|
family: FamilyExtension | None = None,
|
|
@@ -1055,7 +1153,7 @@ class Session:
|
|
|
1055
1153
|
counts[k] = n
|
|
1056
1154
|
|
|
1057
1155
|
params = _build_run_params(task, language, target_language, timestamps,
|
|
1058
|
-
keep_special_tags, spec_k_drafts)
|
|
1156
|
+
keep_special_tags, spec_k_drafts, diarize)
|
|
1059
1157
|
ext = self._resolve_family(family, "run") if family is not None else None
|
|
1060
1158
|
if ext is not None:
|
|
1061
1159
|
params.family = ctypes.cast(
|
|
@@ -1106,6 +1204,7 @@ class Session:
|
|
|
1106
1204
|
|
|
1107
1205
|
def stream(self, *, task: Task = "transcribe", language: str | None = None,
|
|
1108
1206
|
target_language: str | None = None, timestamps: Timestamps = "none",
|
|
1207
|
+
diarize: Diarize = "default",
|
|
1109
1208
|
keep_special_tags: bool = False, commit_policy: CommitPolicy = "auto",
|
|
1110
1209
|
stable_prefix_agreement_n: int = 0,
|
|
1111
1210
|
family: FamilyExtension | None = None) -> Stream:
|
|
@@ -1120,7 +1219,7 @@ class Session:
|
|
|
1120
1219
|
# spec_k_drafts is an offline-decode knob; streaming always uses the
|
|
1121
1220
|
# family default (-1).
|
|
1122
1221
|
run_params = _build_run_params(task, language, target_language, timestamps,
|
|
1123
|
-
keep_special_tags, -1)
|
|
1222
|
+
keep_special_tags, -1, diarize)
|
|
1124
1223
|
sp = _StreamParams()
|
|
1125
1224
|
_lib.transcribe_stream_params_init(_byref(sp))
|
|
1126
1225
|
sp.commit_policy = _enum(_COMMIT_POLICIES, commit_policy, "commit_policy")
|
|
@@ -1152,7 +1251,10 @@ class Session:
|
|
|
1152
1251
|
get_word = lambda j, out: _lib.transcribe_get_word(h, j, out)
|
|
1153
1252
|
n_tok = lambda: _lib.transcribe_n_tokens(h)
|
|
1154
1253
|
get_tok = lambda j, out: _lib.transcribe_get_token(h, j, out)
|
|
1254
|
+
n_speaker = lambda: _lib.transcribe_n_speaker_segments(h)
|
|
1255
|
+
get_speaker = lambda j, out: _lib.transcribe_get_speaker_segment(h, j, out)
|
|
1155
1256
|
full_text = _lib.transcribe_full_text(h)
|
|
1257
|
+
raw_text = _lib.transcribe_raw_text(h)
|
|
1156
1258
|
language = _lib.transcribe_detected_language(h)
|
|
1157
1259
|
kind = _lib.transcribe_returned_timestamp_kind(h)
|
|
1158
1260
|
get_tim = lambda out: _lib.transcribe_get_timings(h, out)
|
|
@@ -1163,7 +1265,10 @@ class Session:
|
|
|
1163
1265
|
get_word = lambda j, out: _lib.transcribe_batch_get_word(h, utt, j, out)
|
|
1164
1266
|
n_tok = lambda: _lib.transcribe_batch_n_tokens(h, utt)
|
|
1165
1267
|
get_tok = lambda j, out: _lib.transcribe_batch_get_token(h, utt, j, out)
|
|
1268
|
+
n_speaker = lambda: _lib.transcribe_batch_n_speaker_segments(h, utt)
|
|
1269
|
+
get_speaker = lambda j, out: _lib.transcribe_batch_get_speaker_segment(h, utt, j, out)
|
|
1166
1270
|
full_text = _lib.transcribe_batch_full_text(h, utt)
|
|
1271
|
+
raw_text = _lib.transcribe_batch_raw_text(h, utt)
|
|
1167
1272
|
language = _lib.transcribe_batch_detected_language(h, utt)
|
|
1168
1273
|
kind = _lib.transcribe_batch_returned_timestamp_kind(h, utt)
|
|
1169
1274
|
get_tim = lambda out: _lib.transcribe_batch_get_timings(h, utt, out)
|
|
@@ -1189,15 +1294,24 @@ class Session:
|
|
|
1189
1294
|
get_tok(j, _byref(tok))
|
|
1190
1295
|
tokens.append(_token_from(tok))
|
|
1191
1296
|
|
|
1297
|
+
speaker_segments = []
|
|
1298
|
+
for j in range(n_speaker()):
|
|
1299
|
+
speaker = _SpeakerSegment()
|
|
1300
|
+
_lib.transcribe_speaker_segment_init(_byref(speaker))
|
|
1301
|
+
get_speaker(j, _byref(speaker))
|
|
1302
|
+
speaker_segments.append(_speaker_segment_from(speaker))
|
|
1303
|
+
|
|
1192
1304
|
tm = _Timings()
|
|
1193
1305
|
_lib.transcribe_timings_init(_byref(tm))
|
|
1194
1306
|
get_tim(_byref(tm))
|
|
1195
1307
|
|
|
1196
1308
|
return Result(
|
|
1197
1309
|
text=_decode(full_text),
|
|
1310
|
+
raw_text=_decode(raw_text),
|
|
1198
1311
|
language=_decode(language),
|
|
1199
1312
|
timestamp_kind=_TIMESTAMP_NAMES.get(kind, "unknown"),
|
|
1200
1313
|
segments=tuple(segments),
|
|
1314
|
+
speaker_segments=tuple(speaker_segments),
|
|
1201
1315
|
words=tuple(words),
|
|
1202
1316
|
tokens=tuple(tokens),
|
|
1203
1317
|
timings=_timings_from(tm),
|
|
@@ -1339,7 +1453,7 @@ def transcribe(
|
|
|
1339
1453
|
pcm: PCMLike,
|
|
1340
1454
|
*,
|
|
1341
1455
|
backend: Backend = "auto",
|
|
1342
|
-
|
|
1456
|
+
device: BackendDevice | None = None,
|
|
1343
1457
|
n_threads: int = 0,
|
|
1344
1458
|
kv_type: KVType = "auto",
|
|
1345
1459
|
n_ctx: int = 0,
|
|
@@ -1347,6 +1461,7 @@ def transcribe(
|
|
|
1347
1461
|
language: str | None = None,
|
|
1348
1462
|
target_language: str | None = None,
|
|
1349
1463
|
timestamps: Timestamps = "auto",
|
|
1464
|
+
diarize: Diarize = "default",
|
|
1350
1465
|
keep_special_tags: bool = False,
|
|
1351
1466
|
spec_k_drafts: int = -1,
|
|
1352
1467
|
family: FamilyExtension | None = None,
|
|
@@ -1356,19 +1471,20 @@ def transcribe(
|
|
|
1356
1471
|
*model* may be a path (loaded and freed within this call) or an existing
|
|
1357
1472
|
Model (reused and left open). Loading a model is not free, so to transcribe
|
|
1358
1473
|
many clips keep a Model and call ``model.session().run(...)`` yourself; this
|
|
1359
|
-
helper is for the one-shot case. ``backend`` / ``
|
|
1474
|
+
helper is for the one-shot case. ``backend`` / ``device`` apply only when
|
|
1360
1475
|
*model* is a path — they are ignored when an already-loaded Model is passed.
|
|
1361
1476
|
``family`` / ``spec_k_drafts`` pass through to :meth:`Session.run`.
|
|
1362
1477
|
"""
|
|
1363
1478
|
session_opts = dict(n_threads=n_threads, kv_type=kv_type, n_ctx=n_ctx)
|
|
1364
1479
|
run_opts = dict(task=task, language=language, target_language=target_language,
|
|
1365
|
-
timestamps=timestamps,
|
|
1480
|
+
timestamps=timestamps, diarize=diarize,
|
|
1481
|
+
keep_special_tags=keep_special_tags,
|
|
1366
1482
|
spec_k_drafts=spec_k_drafts, family=family)
|
|
1367
1483
|
|
|
1368
1484
|
if isinstance(model, Model):
|
|
1369
1485
|
with model.session(**session_opts) as session:
|
|
1370
1486
|
return session.run(pcm, **run_opts)
|
|
1371
1487
|
|
|
1372
|
-
with Model(model, backend=backend,
|
|
1488
|
+
with Model(model, backend=backend, device=device) as owned:
|
|
1373
1489
|
with owned.session(**session_opts) as session:
|
|
1374
1490
|
return session.run(pcm, **run_opts)
|
|
@@ -13,7 +13,7 @@ import ctypes as _c
|
|
|
13
13
|
# Stable digest of the ABI surface below (structs, enums, macros, layout,
|
|
14
14
|
# prototypes). A native provider package echoes this back so the API
|
|
15
15
|
# package can reject an ABI-mismatched provider before dlopen.
|
|
16
|
-
PUBLIC_HEADER_HASH = "
|
|
16
|
+
PUBLIC_HEADER_HASH = "7df72bf9e667b8c2"
|
|
17
17
|
|
|
18
18
|
# === enum constants ===
|
|
19
19
|
TRANSCRIBE_OK = 0
|
|
@@ -48,7 +48,8 @@ TRANSCRIBE_ABI_STREAM_UPDATE = 9
|
|
|
48
48
|
TRANSCRIBE_ABI_STREAM_TEXT = 10
|
|
49
49
|
TRANSCRIBE_ABI_SESSION_LIMITS = 11
|
|
50
50
|
TRANSCRIBE_ABI_EXT = 12
|
|
51
|
-
|
|
51
|
+
TRANSCRIBE_ABI_DEVICE_INFO = 13
|
|
52
|
+
TRANSCRIBE_ABI_SPEAKER_SEGMENT = 14
|
|
52
53
|
TRANSCRIBE_LOG_LEVEL_NONE = 0
|
|
53
54
|
TRANSCRIBE_LOG_LEVEL_INFO = 1
|
|
54
55
|
TRANSCRIBE_LOG_LEVEL_WARN = 2
|
|
@@ -71,6 +72,9 @@ TRANSCRIBE_PNC_MODE_ON = 2
|
|
|
71
72
|
TRANSCRIBE_ITN_MODE_DEFAULT = 0
|
|
72
73
|
TRANSCRIBE_ITN_MODE_OFF = 1
|
|
73
74
|
TRANSCRIBE_ITN_MODE_ON = 2
|
|
75
|
+
TRANSCRIBE_DIARIZE_MODE_DEFAULT = 0
|
|
76
|
+
TRANSCRIBE_DIARIZE_MODE_OFF = 1
|
|
77
|
+
TRANSCRIBE_DIARIZE_MODE_ON = 2
|
|
74
78
|
TRANSCRIBE_EXT_SLOT_RUN = 0
|
|
75
79
|
TRANSCRIBE_EXT_SLOT_STREAM = 1
|
|
76
80
|
TRANSCRIBE_BACKEND_AUTO = 0
|
|
@@ -79,6 +83,7 @@ TRANSCRIBE_BACKEND_METAL = 2
|
|
|
79
83
|
TRANSCRIBE_BACKEND_VULKAN = 3
|
|
80
84
|
TRANSCRIBE_BACKEND_CPU_ACCEL = 4
|
|
81
85
|
TRANSCRIBE_BACKEND_CUDA = 5
|
|
86
|
+
TRANSCRIBE_BACKEND_ROCM = 6
|
|
82
87
|
TRANSCRIBE_DEVICE_TYPE_CPU = 0
|
|
83
88
|
TRANSCRIBE_DEVICE_TYPE_GPU = 1
|
|
84
89
|
TRANSCRIBE_DEVICE_TYPE_IGPU = 2
|
|
@@ -89,6 +94,7 @@ TRANSCRIBE_FEATURE_LONG_FORM = 2
|
|
|
89
94
|
TRANSCRIBE_FEATURE_CANCELLATION = 3
|
|
90
95
|
TRANSCRIBE_FEATURE_PNC = 4
|
|
91
96
|
TRANSCRIBE_FEATURE_ITN = 5
|
|
97
|
+
TRANSCRIBE_FEATURE_DIARIZATION = 6
|
|
92
98
|
TRANSCRIBE_STREAM_IDLE = 0
|
|
93
99
|
TRANSCRIBE_STREAM_ACTIVE = 1
|
|
94
100
|
TRANSCRIBE_STREAM_FINISHED = 2
|
|
@@ -96,6 +102,10 @@ TRANSCRIBE_STREAM_FAILED = 3
|
|
|
96
102
|
TRANSCRIBE_STREAM_COMMIT_AUTO = 0
|
|
97
103
|
TRANSCRIBE_STREAM_COMMIT_ON_FINALIZE = 1
|
|
98
104
|
TRANSCRIBE_STREAM_COMMIT_STABLE_PREFIX = 2
|
|
105
|
+
TRANSCRIBE_SORTFORMER_PRESET_DEFAULT = 0
|
|
106
|
+
TRANSCRIBE_SORTFORMER_PRESET_VERY_HIGH_LATENCY = 1
|
|
107
|
+
TRANSCRIBE_SORTFORMER_PRESET_HIGH_LATENCY = 2
|
|
108
|
+
TRANSCRIBE_SORTFORMER_PRESET_LOW_LATENCY = 3
|
|
99
109
|
TRANSCRIBE_WHISPER_PROMPT_FIRST_SEGMENT = 0
|
|
100
110
|
TRANSCRIBE_WHISPER_PROMPT_ALL_SEGMENTS = 1
|
|
101
111
|
|
|
@@ -103,13 +113,14 @@ TRANSCRIBE_WHISPER_PROMPT_ALL_SEGMENTS = 1
|
|
|
103
113
|
TRANSCRIBE_EXT_KIND_MOONSHINE_STREAMING_STREAM = 1414746957
|
|
104
114
|
TRANSCRIBE_EXT_KIND_PARAKEET_BUFFERED_STREAM = 1396853584
|
|
105
115
|
TRANSCRIBE_EXT_KIND_PARAKEET_STREAM = 1414744912
|
|
116
|
+
TRANSCRIBE_EXT_KIND_SORTFORMER_STREAM = 1414743635
|
|
106
117
|
TRANSCRIBE_EXT_KIND_VOXTRAL_REALTIME_STREAM = 1414746710
|
|
107
118
|
TRANSCRIBE_EXT_KIND_WHISPER_RUN = 1314015319
|
|
108
119
|
|
|
109
120
|
# === structs ===
|
|
110
121
|
class transcribe_ext(_c.Structure):
|
|
111
122
|
pass
|
|
112
|
-
class
|
|
123
|
+
class transcribe_device_info(_c.Structure):
|
|
113
124
|
pass
|
|
114
125
|
class transcribe_model_load_params(_c.Structure):
|
|
115
126
|
pass
|
|
@@ -135,12 +146,16 @@ class transcribe_word(_c.Structure):
|
|
|
135
146
|
pass
|
|
136
147
|
class transcribe_token(_c.Structure):
|
|
137
148
|
pass
|
|
149
|
+
class transcribe_speaker_segment(_c.Structure):
|
|
150
|
+
pass
|
|
138
151
|
class transcribe_moonshine_streaming_stream_ext(_c.Structure):
|
|
139
152
|
pass
|
|
140
153
|
class transcribe_parakeet_stream_ext(_c.Structure):
|
|
141
154
|
pass
|
|
142
155
|
class transcribe_parakeet_buffered_stream_ext(_c.Structure):
|
|
143
156
|
pass
|
|
157
|
+
class transcribe_sortformer_stream_ext(_c.Structure):
|
|
158
|
+
pass
|
|
144
159
|
class transcribe_voxtral_realtime_stream_ext(_c.Structure):
|
|
145
160
|
pass
|
|
146
161
|
class transcribe_whisper_run_ext(_c.Structure):
|
|
@@ -149,22 +164,24 @@ class transcribe_whisper_chunk_trace(_c.Structure):
|
|
|
149
164
|
pass
|
|
150
165
|
|
|
151
166
|
transcribe_ext._fields_ = [("size", _c.c_uint64), ("kind", _c.c_uint32)]
|
|
152
|
-
|
|
153
|
-
transcribe_model_load_params._fields_ = [("struct_size", _c.c_uint64), ("backend", _c.c_int), ("
|
|
167
|
+
transcribe_device_info._fields_ = [("struct_size", _c.c_uint64), ("name", _c.c_char_p), ("description", _c.c_char_p), ("kind", _c.c_char_p), ("device_id", _c.c_char_p), ("memory_total", _c.c_uint64), ("memory_free", _c.c_uint64), ("device_type", _c.c_int)]
|
|
168
|
+
transcribe_model_load_params._fields_ = [("struct_size", _c.c_uint64), ("backend", _c.c_int), ("device", _c.c_void_p)]
|
|
154
169
|
transcribe_session_params._fields_ = [("struct_size", _c.c_uint64), ("n_threads", _c.c_int), ("kv_type", _c.c_int), ("n_ctx", _c.c_int32)]
|
|
155
|
-
transcribe_run_params._fields_ = [("struct_size", _c.c_uint64), ("task", _c.c_int), ("timestamps", _c.c_int), ("pnc", _c.c_int), ("itn", _c.c_int), ("language", _c.c_char_p), ("target_language", _c.c_char_p), ("keep_special_tags", _c.c_bool), ("family", _c.POINTER(transcribe_ext)), ("spec_k_drafts", _c.c_int32)]
|
|
170
|
+
transcribe_run_params._fields_ = [("struct_size", _c.c_uint64), ("task", _c.c_int), ("timestamps", _c.c_int), ("pnc", _c.c_int), ("itn", _c.c_int), ("diarize", _c.c_int), ("language", _c.c_char_p), ("target_language", _c.c_char_p), ("keep_special_tags", _c.c_bool), ("family", _c.POINTER(transcribe_ext)), ("spec_k_drafts", _c.c_int32)]
|
|
156
171
|
transcribe_capabilities._fields_ = [("struct_size", _c.c_uint64), ("native_sample_rate", _c.c_int32), ("n_languages", _c.c_int), ("languages", _c.POINTER(_c.c_char_p)), ("max_timestamp_kind", _c.c_int), ("supports_language_detect", _c.c_bool), ("supports_translate", _c.c_bool), ("supports_streaming", _c.c_bool), ("supports_spec_decode", _c.c_bool), ("max_audio_ms", _c.c_int64), ("n_translate_target_languages", _c.c_int), ("translate_target_languages", _c.POINTER(_c.c_char_p))]
|
|
157
172
|
transcribe_session_limits._fields_ = [("struct_size", _c.c_uint64), ("effective_n_ctx", _c.c_int32), ("effective_max_audio_ms", _c.c_int64), ("max_kv_bytes", _c.c_int64)]
|
|
158
173
|
transcribe_stream_params._fields_ = [("struct_size", _c.c_uint64), ("family", _c.POINTER(transcribe_ext)), ("commit_policy", _c.c_int), ("stable_prefix_agreement_n", _c.c_uint32)]
|
|
159
174
|
transcribe_stream_update._fields_ = [("struct_size", _c.c_uint64), ("result_changed", _c.c_bool), ("is_final", _c.c_bool), ("revision", _c.c_int32), ("input_received_ms", _c.c_int64), ("audio_committed_ms", _c.c_int64), ("buffered_ms", _c.c_int64), ("committed_changed", _c.c_bool), ("tentative_changed", _c.c_bool)]
|
|
160
175
|
transcribe_stream_text._fields_ = [("struct_size", _c.c_uint64), ("full_text", _c.c_char_p), ("full_text_bytes", _c.c_uint64), ("committed_text", _c.c_char_p), ("committed_text_bytes", _c.c_uint64), ("tentative_text", _c.c_char_p), ("tentative_text_bytes", _c.c_uint64), ("raw_tentative_start_bytes", _c.c_uint64)]
|
|
161
176
|
transcribe_timings._fields_ = [("struct_size", _c.c_uint64), ("load_ms", _c.c_float), ("mel_ms", _c.c_float), ("encode_ms", _c.c_float), ("decode_ms", _c.c_float)]
|
|
162
|
-
transcribe_segment._fields_ = [("struct_size", _c.c_uint64), ("t0_ms", _c.c_int64), ("t1_ms", _c.c_int64), ("first_word", _c.c_int), ("n_words", _c.c_int), ("first_token", _c.c_int), ("n_tokens", _c.c_int), ("text", _c.c_char_p)]
|
|
177
|
+
transcribe_segment._fields_ = [("struct_size", _c.c_uint64), ("t0_ms", _c.c_int64), ("t1_ms", _c.c_int64), ("first_word", _c.c_int), ("n_words", _c.c_int), ("first_token", _c.c_int), ("n_tokens", _c.c_int), ("text", _c.c_char_p), ("speaker_id", _c.c_int32)]
|
|
163
178
|
transcribe_word._fields_ = [("struct_size", _c.c_uint64), ("t0_ms", _c.c_int64), ("t1_ms", _c.c_int64), ("seg_index", _c.c_int), ("first_token", _c.c_int), ("n_tokens", _c.c_int), ("text", _c.c_char_p)]
|
|
164
179
|
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)]
|
|
180
|
+
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)]
|
|
165
181
|
transcribe_moonshine_streaming_stream_ext._fields_ = [("ext", transcribe_ext), ("min_decode_interval_ms", _c.c_int32)]
|
|
166
182
|
transcribe_parakeet_stream_ext._fields_ = [("ext", transcribe_ext), ("att_context_right", _c.c_int32)]
|
|
167
183
|
transcribe_parakeet_buffered_stream_ext._fields_ = [("ext", transcribe_ext), ("left_ms", _c.c_int32), ("chunk_ms", _c.c_int32), ("right_ms", _c.c_int32)]
|
|
184
|
+
transcribe_sortformer_stream_ext._fields_ = [("ext", transcribe_ext), ("preset", _c.c_int)]
|
|
168
185
|
transcribe_voxtral_realtime_stream_ext._fields_ = [("ext", transcribe_ext), ("num_delay_tokens", _c.c_int32), ("min_decode_interval_ms", _c.c_int32)]
|
|
169
186
|
transcribe_whisper_run_ext._fields_ = [("ext", transcribe_ext), ("initial_prompt", _c.c_char_p), ("prompt_tokens", _c.POINTER(_c.c_int32)), ("n_prompt_tokens", _c.c_size_t), ("prompt_condition", _c.c_int), ("condition_on_prev_tokens", _c.c_bool), ("max_prev_context_tokens", _c.c_int32), ("temperature", _c.c_float), ("temperature_inc", _c.c_float), ("compression_ratio_thold", _c.c_float), ("logprob_thold", _c.c_float), ("no_speech_thold", _c.c_float), ("seed", _c.c_uint32), ("max_initial_timestamp", _c.c_float)]
|
|
170
187
|
transcribe_whisper_chunk_trace._fields_ = [("struct_size", _c.c_uint64), ("t0_ms", _c.c_int64), ("t1_ms", _c.c_int64), ("temperature_used", _c.c_float), ("compression_ratio", _c.c_float), ("avg_logprob", _c.c_float), ("no_speech_prob", _c.c_float), ("no_speech_triggered", _c.c_bool), ("n_fallbacks", _c.c_int32)]
|
|
@@ -173,7 +190,7 @@ transcribe_whisper_chunk_trace._fields_ = [("struct_size", _c.c_uint64), ("t0_ms
|
|
|
173
190
|
# transcribe_abi_struct id per struct (for the native size/align check).
|
|
174
191
|
ABI_STRUCT_IDS = {
|
|
175
192
|
'transcribe_ext': 12,
|
|
176
|
-
'
|
|
193
|
+
'transcribe_device_info': 13,
|
|
177
194
|
'transcribe_model_load_params': 0,
|
|
178
195
|
'transcribe_session_params': 1,
|
|
179
196
|
'transcribe_run_params': 2,
|
|
@@ -186,27 +203,30 @@ ABI_STRUCT_IDS = {
|
|
|
186
203
|
'transcribe_segment': 6,
|
|
187
204
|
'transcribe_word': 7,
|
|
188
205
|
'transcribe_token': 8,
|
|
206
|
+
'transcribe_speaker_segment': 14,
|
|
189
207
|
}
|
|
190
208
|
|
|
191
209
|
# C-compiler layout captured at generation (for offset self-check).
|
|
192
210
|
STRUCT_LAYOUT = {
|
|
193
211
|
'transcribe_ext': {'size': 16, 'align': 8, 'offsets': {'size': 0, 'kind': 8}},
|
|
194
|
-
'
|
|
195
|
-
'transcribe_model_load_params': {'size':
|
|
212
|
+
'transcribe_device_info': {'size': 64, 'align': 8, 'offsets': {'struct_size': 0, 'name': 8, 'description': 16, 'kind': 24, 'device_id': 32, 'memory_total': 40, 'memory_free': 48, 'device_type': 56}},
|
|
213
|
+
'transcribe_model_load_params': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'backend': 8, 'device': 16}},
|
|
196
214
|
'transcribe_session_params': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'n_threads': 8, 'kv_type': 12, 'n_ctx': 16}},
|
|
197
|
-
'transcribe_run_params': {'size':
|
|
215
|
+
'transcribe_run_params': {'size': 72, 'align': 8, 'offsets': {'struct_size': 0, 'task': 8, 'timestamps': 12, 'pnc': 16, 'itn': 20, 'diarize': 24, 'language': 32, 'target_language': 40, 'keep_special_tags': 48, 'family': 56, 'spec_k_drafts': 64}},
|
|
198
216
|
'transcribe_capabilities': {'size': 56, 'align': 8, 'offsets': {'struct_size': 0, 'native_sample_rate': 8, 'n_languages': 12, 'languages': 16, 'max_timestamp_kind': 24, 'supports_language_detect': 28, 'supports_translate': 29, 'supports_streaming': 30, 'supports_spec_decode': 31, 'max_audio_ms': 32, 'n_translate_target_languages': 40, 'translate_target_languages': 48}},
|
|
199
217
|
'transcribe_session_limits': {'size': 32, 'align': 8, 'offsets': {'struct_size': 0, 'effective_n_ctx': 8, 'effective_max_audio_ms': 16, 'max_kv_bytes': 24}},
|
|
200
218
|
'transcribe_stream_params': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'family': 8, 'commit_policy': 16, 'stable_prefix_agreement_n': 20}},
|
|
201
219
|
'transcribe_stream_update': {'size': 48, 'align': 8, 'offsets': {'struct_size': 0, 'result_changed': 8, 'is_final': 9, 'revision': 12, 'input_received_ms': 16, 'audio_committed_ms': 24, 'buffered_ms': 32, 'committed_changed': 40, 'tentative_changed': 41}},
|
|
202
220
|
'transcribe_stream_text': {'size': 64, 'align': 8, 'offsets': {'struct_size': 0, 'full_text': 8, 'full_text_bytes': 16, 'committed_text': 24, 'committed_text_bytes': 32, 'tentative_text': 40, 'tentative_text_bytes': 48, 'raw_tentative_start_bytes': 56}},
|
|
203
221
|
'transcribe_timings': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'load_ms': 8, 'mel_ms': 12, 'encode_ms': 16, 'decode_ms': 20}},
|
|
204
|
-
'transcribe_segment': {'size':
|
|
222
|
+
'transcribe_segment': {'size': 56, 'align': 8, 'offsets': {'struct_size': 0, 't0_ms': 8, 't1_ms': 16, 'first_word': 24, 'n_words': 28, 'first_token': 32, 'n_tokens': 36, 'text': 40, 'speaker_id': 48}},
|
|
205
223
|
'transcribe_word': {'size': 48, 'align': 8, 'offsets': {'struct_size': 0, 't0_ms': 8, 't1_ms': 16, 'seg_index': 24, 'first_token': 28, 'n_tokens': 32, 'text': 40}},
|
|
206
224
|
'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}},
|
|
225
|
+
'transcribe_speaker_segment': {'size': 32, 'align': 8, 'offsets': {'struct_size': 0, 't0_ms': 8, 't1_ms': 16, 'speaker_id': 24, 'p': 28}},
|
|
207
226
|
'transcribe_moonshine_streaming_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'min_decode_interval_ms': 16}},
|
|
208
227
|
'transcribe_parakeet_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'att_context_right': 16}},
|
|
209
228
|
'transcribe_parakeet_buffered_stream_ext': {'size': 32, 'align': 8, 'offsets': {'ext': 0, 'left_ms': 16, 'chunk_ms': 20, 'right_ms': 24}},
|
|
229
|
+
'transcribe_sortformer_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'preset': 16}},
|
|
210
230
|
'transcribe_voxtral_realtime_stream_ext': {'size': 24, 'align': 8, 'offsets': {'ext': 0, 'num_delay_tokens': 16, 'min_decode_interval_ms': 20}},
|
|
211
231
|
'transcribe_whisper_run_ext': {'size': 80, 'align': 8, 'offsets': {'ext': 0, 'initial_prompt': 16, 'prompt_tokens': 24, 'n_prompt_tokens': 32, 'prompt_condition': 40, 'condition_on_prev_tokens': 44, 'max_prev_context_tokens': 48, 'temperature': 52, 'temperature_inc': 56, 'compression_ratio_thold': 60, 'logprob_thold': 64, 'no_speech_thold': 68, 'seed': 72, 'max_initial_timestamp': 76}},
|
|
212
232
|
'transcribe_whisper_chunk_trace': {'size': 48, 'align': 8, 'offsets': {'struct_size': 0, 't0_ms': 8, 't1_ms': 16, 'temperature_used': 24, 'compression_ratio': 28, 'avg_logprob': 32, 'no_speech_prob': 36, 'no_speech_triggered': 40, 'n_fallbacks': 44}},
|
|
@@ -221,16 +241,14 @@ def configure(lib):
|
|
|
221
241
|
lib.transcribe_abi_struct_size.argtypes = [_c.c_int]
|
|
222
242
|
lib.transcribe_backend_available.restype = _c.c_bool
|
|
223
243
|
lib.transcribe_backend_available.argtypes = [_c.c_int]
|
|
224
|
-
lib.transcribe_backend_device_count.restype = _c.c_int
|
|
225
|
-
lib.transcribe_backend_device_count.argtypes = []
|
|
226
|
-
lib.transcribe_backend_device_init.restype = None
|
|
227
|
-
lib.transcribe_backend_device_init.argtypes = [_c.POINTER(transcribe_backend_device)]
|
|
228
244
|
lib.transcribe_batch_detected_language.restype = _c.c_char_p
|
|
229
245
|
lib.transcribe_batch_detected_language.argtypes = [_c.c_void_p, _c.c_int]
|
|
230
246
|
lib.transcribe_batch_full_text.restype = _c.c_char_p
|
|
231
247
|
lib.transcribe_batch_full_text.argtypes = [_c.c_void_p, _c.c_int]
|
|
232
248
|
lib.transcribe_batch_get_segment.restype = _c.c_int
|
|
233
249
|
lib.transcribe_batch_get_segment.argtypes = [_c.c_void_p, _c.c_int, _c.c_int, _c.POINTER(transcribe_segment)]
|
|
250
|
+
lib.transcribe_batch_get_speaker_segment.restype = _c.c_int
|
|
251
|
+
lib.transcribe_batch_get_speaker_segment.argtypes = [_c.c_void_p, _c.c_int, _c.c_int, _c.POINTER(transcribe_speaker_segment)]
|
|
234
252
|
lib.transcribe_batch_get_timings.restype = _c.c_int
|
|
235
253
|
lib.transcribe_batch_get_timings.argtypes = [_c.c_void_p, _c.c_int, _c.POINTER(transcribe_timings)]
|
|
236
254
|
lib.transcribe_batch_get_token.restype = _c.c_int
|
|
@@ -241,10 +259,14 @@ def configure(lib):
|
|
|
241
259
|
lib.transcribe_batch_n_results.argtypes = [_c.c_void_p]
|
|
242
260
|
lib.transcribe_batch_n_segments.restype = _c.c_int
|
|
243
261
|
lib.transcribe_batch_n_segments.argtypes = [_c.c_void_p, _c.c_int]
|
|
262
|
+
lib.transcribe_batch_n_speaker_segments.restype = _c.c_int
|
|
263
|
+
lib.transcribe_batch_n_speaker_segments.argtypes = [_c.c_void_p, _c.c_int]
|
|
244
264
|
lib.transcribe_batch_n_tokens.restype = _c.c_int
|
|
245
265
|
lib.transcribe_batch_n_tokens.argtypes = [_c.c_void_p, _c.c_int]
|
|
246
266
|
lib.transcribe_batch_n_words.restype = _c.c_int
|
|
247
267
|
lib.transcribe_batch_n_words.argtypes = [_c.c_void_p, _c.c_int]
|
|
268
|
+
lib.transcribe_batch_raw_text.restype = _c.c_char_p
|
|
269
|
+
lib.transcribe_batch_raw_text.argtypes = [_c.c_void_p, _c.c_int]
|
|
248
270
|
lib.transcribe_batch_returned_timestamp_kind.restype = _c.c_int
|
|
249
271
|
lib.transcribe_batch_returned_timestamp_kind.argtypes = [_c.c_void_p, _c.c_int]
|
|
250
272
|
lib.transcribe_batch_status.restype = _c.c_int
|
|
@@ -255,16 +277,24 @@ def configure(lib):
|
|
|
255
277
|
lib.transcribe_close.argtypes = [_c.c_void_p]
|
|
256
278
|
lib.transcribe_detected_language.restype = _c.c_char_p
|
|
257
279
|
lib.transcribe_detected_language.argtypes = [_c.c_void_p]
|
|
280
|
+
lib.transcribe_device_count.restype = _c.c_int
|
|
281
|
+
lib.transcribe_device_count.argtypes = []
|
|
282
|
+
lib.transcribe_device_get.restype = _c.c_void_p
|
|
283
|
+
lib.transcribe_device_get.argtypes = [_c.c_int]
|
|
284
|
+
lib.transcribe_device_get_info.restype = _c.c_int
|
|
285
|
+
lib.transcribe_device_get_info.argtypes = [_c.c_void_p, _c.POINTER(transcribe_device_info)]
|
|
286
|
+
lib.transcribe_device_info_init.restype = None
|
|
287
|
+
lib.transcribe_device_info_init.argtypes = [_c.POINTER(transcribe_device_info)]
|
|
258
288
|
lib.transcribe_ext_check.restype = _c.c_int
|
|
259
289
|
lib.transcribe_ext_check.argtypes = [_c.POINTER(transcribe_ext), _c.c_uint32, _c.c_uint64]
|
|
260
290
|
lib.transcribe_full_text.restype = _c.c_char_p
|
|
261
291
|
lib.transcribe_full_text.argtypes = [_c.c_void_p]
|
|
262
|
-
lib.transcribe_get_backend_device.restype = _c.c_int
|
|
263
|
-
lib.transcribe_get_backend_device.argtypes = [_c.c_int, _c.POINTER(transcribe_backend_device)]
|
|
264
292
|
lib.transcribe_get_model.restype = _c.c_void_p
|
|
265
293
|
lib.transcribe_get_model.argtypes = [_c.c_void_p]
|
|
266
294
|
lib.transcribe_get_segment.restype = _c.c_int
|
|
267
295
|
lib.transcribe_get_segment.argtypes = [_c.c_void_p, _c.c_int, _c.POINTER(transcribe_segment)]
|
|
296
|
+
lib.transcribe_get_speaker_segment.restype = _c.c_int
|
|
297
|
+
lib.transcribe_get_speaker_segment.argtypes = [_c.c_void_p, _c.c_int, _c.POINTER(transcribe_speaker_segment)]
|
|
268
298
|
lib.transcribe_get_timings.restype = _c.c_int
|
|
269
299
|
lib.transcribe_get_timings.argtypes = [_c.c_void_p, _c.POINTER(transcribe_timings)]
|
|
270
300
|
lib.transcribe_get_token.restype = _c.c_int
|
|
@@ -287,12 +317,12 @@ def configure(lib):
|
|
|
287
317
|
lib.transcribe_model_arch_string.argtypes = [_c.c_void_p]
|
|
288
318
|
lib.transcribe_model_backend.restype = _c.c_char_p
|
|
289
319
|
lib.transcribe_model_backend.argtypes = [_c.c_void_p]
|
|
320
|
+
lib.transcribe_model_device.restype = _c.c_void_p
|
|
321
|
+
lib.transcribe_model_device.argtypes = [_c.c_void_p]
|
|
290
322
|
lib.transcribe_model_free.restype = None
|
|
291
323
|
lib.transcribe_model_free.argtypes = [_c.c_void_p]
|
|
292
324
|
lib.transcribe_model_get_capabilities.restype = _c.c_int
|
|
293
325
|
lib.transcribe_model_get_capabilities.argtypes = [_c.c_void_p, _c.POINTER(transcribe_capabilities)]
|
|
294
|
-
lib.transcribe_model_get_device.restype = _c.c_int
|
|
295
|
-
lib.transcribe_model_get_device.argtypes = [_c.c_void_p, _c.POINTER(transcribe_backend_device)]
|
|
296
326
|
lib.transcribe_model_load_file.restype = _c.c_int
|
|
297
327
|
lib.transcribe_model_load_file.argtypes = [_c.c_char_p, _c.POINTER(transcribe_model_load_params), _c.POINTER(_c.c_void_p)]
|
|
298
328
|
lib.transcribe_model_load_params_init.restype = None
|
|
@@ -307,6 +337,8 @@ def configure(lib):
|
|
|
307
337
|
lib.transcribe_moonshine_streaming_stream_ext_init.argtypes = [_c.POINTER(transcribe_moonshine_streaming_stream_ext)]
|
|
308
338
|
lib.transcribe_n_segments.restype = _c.c_int
|
|
309
339
|
lib.transcribe_n_segments.argtypes = [_c.c_void_p]
|
|
340
|
+
lib.transcribe_n_speaker_segments.restype = _c.c_int
|
|
341
|
+
lib.transcribe_n_speaker_segments.argtypes = [_c.c_void_p]
|
|
310
342
|
lib.transcribe_n_tokens.restype = _c.c_int
|
|
311
343
|
lib.transcribe_n_tokens.argtypes = [_c.c_void_p]
|
|
312
344
|
lib.transcribe_n_words.restype = _c.c_int
|
|
@@ -319,6 +351,8 @@ def configure(lib):
|
|
|
319
351
|
lib.transcribe_parakeet_stream_ext_init.argtypes = [_c.POINTER(transcribe_parakeet_stream_ext)]
|
|
320
352
|
lib.transcribe_print_timings.restype = None
|
|
321
353
|
lib.transcribe_print_timings.argtypes = [_c.c_void_p]
|
|
354
|
+
lib.transcribe_raw_text.restype = _c.c_char_p
|
|
355
|
+
lib.transcribe_raw_text.argtypes = [_c.c_void_p]
|
|
322
356
|
lib.transcribe_reset_timings.restype = None
|
|
323
357
|
lib.transcribe_reset_timings.argtypes = [_c.c_void_p]
|
|
324
358
|
lib.transcribe_returned_timestamp_kind.restype = _c.c_int
|
|
@@ -343,6 +377,10 @@ def configure(lib):
|
|
|
343
377
|
lib.transcribe_session_params_init.argtypes = [_c.POINTER(transcribe_session_params)]
|
|
344
378
|
lib.transcribe_set_abort_callback.restype = None
|
|
345
379
|
lib.transcribe_set_abort_callback.argtypes = [_c.c_void_p, _c.CFUNCTYPE(_c.c_bool, _c.c_void_p), _c.c_void_p]
|
|
380
|
+
lib.transcribe_sortformer_stream_ext_init.restype = None
|
|
381
|
+
lib.transcribe_sortformer_stream_ext_init.argtypes = [_c.POINTER(transcribe_sortformer_stream_ext)]
|
|
382
|
+
lib.transcribe_speaker_segment_init.restype = None
|
|
383
|
+
lib.transcribe_speaker_segment_init.argtypes = [_c.POINTER(transcribe_speaker_segment)]
|
|
346
384
|
lib.transcribe_status_string.restype = _c.c_char_p
|
|
347
385
|
lib.transcribe_status_string.argtypes = [_c.c_int]
|
|
348
386
|
lib.transcribe_stream_begin.restype = _c.c_int
|
|
@@ -27,7 +27,7 @@ or an object) with the contract fields:
|
|
|
27
27
|
Selecting a provider picks which native artifact loads into the process; the
|
|
28
28
|
per-model ``backend=`` request is a *separate* axis resolved inside it. Selection
|
|
29
29
|
policy: explicit ``provider`` argument → ``TRANSCRIBE_NATIVE_PROVIDER`` env var →
|
|
30
|
-
best accelerated (CUDA/Metal, then Vulkan) → CPU. A discovered provider whose
|
|
30
|
+
best accelerated (CUDA/ROCm/Metal, then Vulkan) → CPU. A discovered provider whose
|
|
31
31
|
declared version or header hash disagrees with this binding is a hard error
|
|
32
32
|
*before* dlopen — pip pins are not enough; this runtime check is the backstop.
|
|
33
33
|
|
|
@@ -55,7 +55,7 @@ ENTRY_POINT_GROUP = "transcribe_cpp.native"
|
|
|
55
55
|
|
|
56
56
|
#: Backend-kind preference when auto-selecting among installed providers. Higher
|
|
57
57
|
#: wins; a provider's rank is the max over the kinds it advertises.
|
|
58
|
-
_BACKEND_RANK = {"cuda": 3, "metal": 3, "vulkan": 2, "cpu_accel": 1, "cpu": 1}
|
|
58
|
+
_BACKEND_RANK = {"cuda": 3, "rocm": 3, "metal": 3, "vulkan": 2, "cpu_accel": 1, "cpu": 1}
|
|
59
59
|
|
|
60
60
|
#: Set after a successful load so the package can surface it for diagnostics.
|
|
61
61
|
#: None means the library came from the dev-tree / explicit-path fallback.
|
|
@@ -9,6 +9,8 @@ build configuration the suite runs against.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
+
from dataclasses import replace
|
|
13
|
+
|
|
12
14
|
import pytest
|
|
13
15
|
|
|
14
16
|
import transcribe_cpp as t
|
|
@@ -21,7 +23,7 @@ def test_at_least_one_device_registered():
|
|
|
21
23
|
for dev in devices:
|
|
22
24
|
assert dev.name
|
|
23
25
|
assert dev.kind in {
|
|
24
|
-
"cpu", "accel", "metal", "vulkan", "cuda", "sycl", "gpu", "unknown"
|
|
26
|
+
"cpu", "accel", "metal", "vulkan", "cuda", "rocm", "sycl", "gpu", "unknown"
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
|
|
@@ -31,8 +33,8 @@ def test_backends_non_empty():
|
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
def test_device_index_and_fields():
|
|
34
|
-
# Each device carries
|
|
35
|
-
#
|
|
36
|
+
# Each device carries a process-local display index and an opaque selection
|
|
37
|
+
# handle, plus well-formed metadata. Pin the device-selection surface.
|
|
36
38
|
devices = t.backends()
|
|
37
39
|
for i, dev in enumerate(devices):
|
|
38
40
|
assert dev.index == i, f"device {i} reported index {dev.index}"
|
|
@@ -46,6 +48,13 @@ def test_device_index_and_fields():
|
|
|
46
48
|
assert isinstance(dev.kind, str) and dev.kind
|
|
47
49
|
|
|
48
50
|
|
|
51
|
+
def test_device_equality_uses_native_identity():
|
|
52
|
+
device = t.backends()[0]
|
|
53
|
+
refreshed = replace(device, memory_free=device.memory_free + 1, index=None)
|
|
54
|
+
assert refreshed == device
|
|
55
|
+
assert hash(refreshed) == hash(device)
|
|
56
|
+
|
|
57
|
+
|
|
49
58
|
def test_cpu_always_available():
|
|
50
59
|
# Every shipped configuration includes a CPU backend (compiled in or as
|
|
51
60
|
# the baseline module); a process without one is mispackaged.
|
|
@@ -66,8 +75,8 @@ def test_available_kinds_match_device_list():
|
|
|
66
75
|
# answer. (cpu_accel is satisfied by a CPU device per the C contract.)
|
|
67
76
|
kinds = {d.kind for d in t.backends()}
|
|
68
77
|
for request, device_kind in (("metal", "metal"), ("vulkan", "vulkan"),
|
|
69
|
-
("cuda", "cuda"), ("
|
|
70
|
-
("cpu_accel", "cpu")):
|
|
78
|
+
("cuda", "cuda"), ("rocm", "rocm"),
|
|
79
|
+
("cpu", "cpu"), ("cpu_accel", "cpu")):
|
|
71
80
|
assert t.backend_available(request) == (device_kind in kinds), request
|
|
72
81
|
|
|
73
82
|
|
|
@@ -93,6 +102,6 @@ def test_init_backends_rejects_bad_dirs():
|
|
|
93
102
|
def test_init_backends_idempotent():
|
|
94
103
|
lib = t._lib
|
|
95
104
|
adir = str(_library.artifact_dir()).encode("utf-8")
|
|
96
|
-
n = lib.
|
|
105
|
+
n = lib.transcribe_device_count()
|
|
97
106
|
assert lib.transcribe_init_backends(adir) == 0
|
|
98
|
-
assert lib.
|
|
107
|
+
assert lib.transcribe_device_count() == n # no re-registration
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Exact opaque-device selection tests."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _primary_devices(transcribe_cpp):
|
|
7
|
+
return [d for d in transcribe_cpp.backends() if d.device_type != "accel"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_enumerated_device_can_be_passed_to_model(transcribe_cpp, model_path):
|
|
11
|
+
devices = _primary_devices(transcribe_cpp)
|
|
12
|
+
if not devices:
|
|
13
|
+
pytest.skip("no selectable devices")
|
|
14
|
+
|
|
15
|
+
device = devices[0]
|
|
16
|
+
try:
|
|
17
|
+
with transcribe_cpp.Model(model_path, device=device) as model:
|
|
18
|
+
assert model.device == device
|
|
19
|
+
except transcribe_cpp.BackendError:
|
|
20
|
+
# Registered devices may still fail driver initialization. Exact
|
|
21
|
+
# selection must report that failure rather than choosing another.
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_device_argument_rejects_non_device(transcribe_cpp, model_path):
|
|
26
|
+
with pytest.raises(TypeError):
|
|
27
|
+
transcribe_cpp.Model(model_path, device=0)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_backend_must_match_explicit_device(transcribe_cpp, model_path):
|
|
31
|
+
gpu = next(
|
|
32
|
+
(d for d in transcribe_cpp.backends() if d.device_type in ("gpu", "igpu")),
|
|
33
|
+
None,
|
|
34
|
+
)
|
|
35
|
+
if gpu is None:
|
|
36
|
+
pytest.skip("no GPU device")
|
|
37
|
+
with pytest.raises(transcribe_cpp.InvalidArgument):
|
|
38
|
+
transcribe_cpp.Model(model_path, backend="cpu", device=gpu)
|
|
@@ -22,6 +22,7 @@ ALL_OPTION_TYPES = [
|
|
|
22
22
|
t.MoonshineStreamingOptions,
|
|
23
23
|
t.ParakeetStreamOptions,
|
|
24
24
|
t.ParakeetBufferedStreamOptions,
|
|
25
|
+
t.SortformerStreamOptions,
|
|
25
26
|
t.VoxtralRealtimeStreamOptions,
|
|
26
27
|
]
|
|
27
28
|
|
|
@@ -73,6 +74,19 @@ def test_parakeet_buffered_partial_overrides():
|
|
|
73
74
|
assert built.right_ms == fresh.right_ms
|
|
74
75
|
|
|
75
76
|
|
|
77
|
+
def test_sortformer_preset_maps_to_enum_value():
|
|
78
|
+
built = t.SortformerStreamOptions(preset="very_high_latency")._build()
|
|
79
|
+
assert built.preset == _generated.TRANSCRIBE_SORTFORMER_PRESET_VERY_HIGH_LATENCY
|
|
80
|
+
# None keeps the init default (DEFAULT = GGUF-shipped cfg).
|
|
81
|
+
default = t.SortformerStreamOptions()._build()
|
|
82
|
+
assert default.preset == _generated.TRANSCRIBE_SORTFORMER_PRESET_DEFAULT
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_sortformer_unknown_preset_rejected():
|
|
86
|
+
with pytest.raises(ValueError, match="preset"):
|
|
87
|
+
t.SortformerStreamOptions(preset="ultra_low_latency") # type: ignore[arg-type]
|
|
88
|
+
|
|
89
|
+
|
|
76
90
|
# --- model-gated: resolve_family validation + a real extension run ----------
|
|
77
91
|
|
|
78
92
|
|
|
@@ -130,7 +144,7 @@ def test_run_batch_accepts_family(model_path, audio_pcm):
|
|
|
130
144
|
def test_supports_probe_all_features(model_path):
|
|
131
145
|
with t.Model(model_path) as model:
|
|
132
146
|
for feature in ("initial_prompt", "temperature_fallback", "long_form",
|
|
133
|
-
"cancellation", "pnc", "itn"):
|
|
147
|
+
"cancellation", "pnc", "itn", "diarization"):
|
|
134
148
|
assert model.supports(feature) in (True, False)
|
|
135
149
|
with pytest.raises(t.InvalidArgument, match="unknown feature"):
|
|
136
150
|
model.supports("levitation")
|
|
@@ -84,6 +84,7 @@ def test_matches_request(request_str, expected):
|
|
|
84
84
|
|
|
85
85
|
def test_rank_orders_accelerated_above_cpu():
|
|
86
86
|
assert make("m", ["metal", "cpu"]).rank == 3
|
|
87
|
+
assert make("r", ["rocm", "cpu"]).rank == 3
|
|
87
88
|
assert make("v", ["vulkan"]).rank == 2
|
|
88
89
|
assert make("c", ["cpu"]).rank == 1
|
|
89
90
|
assert make("u", ["wat"]).rank == 0
|
|
@@ -386,7 +386,7 @@ wheels = [
|
|
|
386
386
|
|
|
387
387
|
[[package]]
|
|
388
388
|
name = "transcribe-cpp"
|
|
389
|
-
version = "0.0
|
|
389
|
+
version = "0.2.0"
|
|
390
390
|
source = { editable = "." }
|
|
391
391
|
|
|
392
392
|
[package.optional-dependencies]
|
|
@@ -402,8 +402,8 @@ test = [
|
|
|
402
402
|
requires-dist = [
|
|
403
403
|
{ name = "numpy", marker = "extra == 'test'" },
|
|
404
404
|
{ name = "pytest", marker = "extra == 'test'", specifier = ">=7" },
|
|
405
|
-
{ name = "transcribe-cpp-native", specifier = "==0.0
|
|
406
|
-
{ name = "transcribe-cpp-native-cu12", marker = "extra == 'cu12'", specifier = "==0.0
|
|
405
|
+
{ name = "transcribe-cpp-native", specifier = "==0.2.0.*" },
|
|
406
|
+
{ name = "transcribe-cpp-native-cu12", marker = "extra == 'cu12'", specifier = "==0.2.0.*" },
|
|
407
407
|
]
|
|
408
408
|
|
|
409
409
|
[[package]]
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"""Model-gated device-selection tests.
|
|
2
|
-
|
|
3
|
-
These take the ``model_path`` / ``transcribe_cpp`` fixtures, which ``skip``
|
|
4
|
-
when the default whisper-tiny.en asset is absent (override with
|
|
5
|
-
``TRANSCRIBE_SMOKE_MODEL``). They pin the device-selection surface added
|
|
6
|
-
alongside the per-device ``index`` field: ``Model.device`` reports where the
|
|
7
|
-
model landed (its ``.index`` is ``None`` because it did not come from
|
|
8
|
-
enumeration), and an out-of-range / negative ``gpu_device`` is rejected with
|
|
9
|
-
``InvalidArgument``.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import pytest
|
|
15
|
-
|
|
16
|
-
import transcribe_cpp as t
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_model_device_matches_enumeration(transcribe_cpp, model_path):
|
|
20
|
-
# The model lands on some registered device. Model.device does not come
|
|
21
|
-
# from enumeration, so its .index is None; correlate it back to backends()
|
|
22
|
-
# by name (and by device_id when that is reported).
|
|
23
|
-
with transcribe_cpp.Model(model_path) as model:
|
|
24
|
-
dev = model.device
|
|
25
|
-
assert isinstance(dev, transcribe_cpp.BackendDevice)
|
|
26
|
-
assert dev.index is None, "Model.device should not carry a registry index"
|
|
27
|
-
|
|
28
|
-
devices = transcribe_cpp.backends()
|
|
29
|
-
by_name = [d for d in devices if d.name == dev.name]
|
|
30
|
-
assert by_name, (
|
|
31
|
-
f"model device {dev.name!r} not found among backends() "
|
|
32
|
-
f"{[d.name for d in devices]}"
|
|
33
|
-
)
|
|
34
|
-
if dev.device_id is not None:
|
|
35
|
-
assert any(d.device_id == dev.device_id for d in by_name), (
|
|
36
|
-
f"model device_id {dev.device_id!r} matched no enumerated device"
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_negative_gpu_device_rejected(transcribe_cpp, model_path):
|
|
41
|
-
with pytest.raises(transcribe_cpp.InvalidArgument):
|
|
42
|
-
transcribe_cpp.Model(model_path, gpu_device=-1)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def test_out_of_range_gpu_device_rejected(transcribe_cpp, model_path):
|
|
46
|
-
bad = len(transcribe_cpp.backends()) + 1000
|
|
47
|
-
with pytest.raises(transcribe_cpp.InvalidArgument):
|
|
48
|
-
transcribe_cpp.Model(model_path, gpu_device=bad)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def test_cpu_backend_with_gpu_index_rejected(transcribe_cpp, model_path):
|
|
52
|
-
# Hardware-independent: a CPU backend has no GPU to select, so a non-zero
|
|
53
|
-
# gpu_device is invalid regardless of what hardware is present.
|
|
54
|
-
with pytest.raises(transcribe_cpp.InvalidArgument):
|
|
55
|
-
transcribe_cpp.Model(model_path, backend="cpu", gpu_device=1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|