transcribe-cpp 0.1.2__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.2 → transcribe_cpp-0.2.0}/.gitignore +4 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/PKG-INFO +19 -7
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/README.md +15 -3
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/pyproject.toml +3 -3
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/src/transcribe_cpp/__init__.py +152 -35
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/src/transcribe_cpp/_generated.py +58 -20
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/src/transcribe_cpp/_library.py +2 -2
- {transcribe_cpp-0.1.2 → 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.2 → transcribe_cpp-0.2.0}/tests/test_family_ext.py +15 -1
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_provider_discovery.py +1 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/uv.lock +3 -3
- transcribe_cpp-0.1.2/tests/test_device_select.py +0 -55
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/LICENSE +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/_generate/README.md +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/_generate/check_version_sync.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/_generate/generate.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/examples/stream_wav.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/examples/transcribe_wav.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/src/transcribe_cpp/_abi.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/src/transcribe_cpp/errors.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/src/transcribe_cpp/py.typed +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/conftest.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_abi.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_errors.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_example.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_lifetime.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_pcm.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_streaming.py +0 -0
- {transcribe_cpp-0.1.2 → transcribe_cpp-0.2.0}/tests/test_transcribe.py +0 -0
- {transcribe_cpp-0.1.2 → 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,17 +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
|
-
# None when the device came from Model.device, since the underlying
|
|
275
|
-
# transcribe_model_get_device() does not expose an index; correlate such a
|
|
276
|
-
# device back to backends() by device_id / name instead. The index is
|
|
277
|
-
# 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.
|
|
278
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)
|
|
279
306
|
|
|
280
307
|
|
|
281
|
-
def _backend_device_from_raw(dev, index: Optional[int] = None) -> BackendDevice:
|
|
282
|
-
"""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."""
|
|
283
310
|
return BackendDevice(
|
|
284
311
|
name=_decode(dev.name),
|
|
285
312
|
description=_decode(dev.description),
|
|
@@ -289,6 +316,7 @@ def _backend_device_from_raw(dev, index: Optional[int] = None) -> BackendDevice:
|
|
|
289
316
|
memory_total=int(dev.memory_total),
|
|
290
317
|
memory_free=int(dev.memory_free),
|
|
291
318
|
index=index,
|
|
319
|
+
_handle=handle,
|
|
292
320
|
)
|
|
293
321
|
|
|
294
322
|
|
|
@@ -300,12 +328,15 @@ def backends() -> list[BackendDevice]:
|
|
|
300
328
|
Each device's ``memory_free`` is live as of the call; call again to poll
|
|
301
329
|
a device's available memory over time."""
|
|
302
330
|
devices = []
|
|
303
|
-
for i in range(_lib.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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)),
|
|
307
338
|
f"reading backend device {i}")
|
|
308
|
-
devices.append(_backend_device_from_raw(dev, index=i))
|
|
339
|
+
devices.append(_backend_device_from_raw(dev, int(handle), index=i))
|
|
309
340
|
return devices
|
|
310
341
|
|
|
311
342
|
|
|
@@ -433,6 +464,18 @@ class Segment:
|
|
|
433
464
|
n_words: int
|
|
434
465
|
first_token: int
|
|
435
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
|
|
436
479
|
|
|
437
480
|
|
|
438
481
|
@dataclass(frozen=True)
|
|
@@ -498,9 +541,14 @@ class Result:
|
|
|
498
541
|
returned, so it stays valid after later runs."""
|
|
499
542
|
|
|
500
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
|
|
501
548
|
language: str
|
|
502
549
|
timestamp_kind: str
|
|
503
550
|
segments: tuple[Segment, ...]
|
|
551
|
+
speaker_segments: tuple[SpeakerSegment, ...]
|
|
504
552
|
words: tuple[Word, ...]
|
|
505
553
|
tokens: tuple[Token, ...]
|
|
506
554
|
timings: Timings
|
|
@@ -547,6 +595,14 @@ def _segment_from(s) -> Segment:
|
|
|
547
595
|
text=_decode(s.text), t0_ms=s.t0_ms, t1_ms=s.t1_ms,
|
|
548
596
|
first_word=s.first_word, n_words=s.n_words,
|
|
549
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,
|
|
550
606
|
)
|
|
551
607
|
|
|
552
608
|
|
|
@@ -582,7 +638,7 @@ def _stream_update_from(u) -> StreamUpdate:
|
|
|
582
638
|
|
|
583
639
|
|
|
584
640
|
def _build_run_params(task, language, target_language, timestamps,
|
|
585
|
-
keep_special_tags, spec_k_drafts):
|
|
641
|
+
keep_special_tags, spec_k_drafts, diarize="default"):
|
|
586
642
|
if not isinstance(spec_k_drafts, int) or spec_k_drafts < -1:
|
|
587
643
|
raise InvalidArgument(
|
|
588
644
|
f"spec_k_drafts must be -1 (family default), 0 (disabled), or a "
|
|
@@ -592,6 +648,7 @@ def _build_run_params(task, language, target_language, timestamps,
|
|
|
592
648
|
_lib.transcribe_run_params_init(_byref(params))
|
|
593
649
|
params.task = _enum(_TASKS, task, "task")
|
|
594
650
|
params.timestamps = _enum(_TIMESTAMPS, timestamps, "timestamps")
|
|
651
|
+
params.diarize = _enum(_DIARIZE, diarize, "diarize")
|
|
595
652
|
params.language = language.encode("utf-8") if language else None
|
|
596
653
|
params.target_language = target_language.encode("utf-8") if target_language else None
|
|
597
654
|
params.keep_special_tags = keep_special_tags
|
|
@@ -762,6 +819,40 @@ class VoxtralRealtimeStreamOptions(FamilyExtension):
|
|
|
762
819
|
ext.min_decode_interval_ms = self.min_decode_interval_ms
|
|
763
820
|
|
|
764
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
|
+
|
|
765
856
|
# --- high-level handles ---------------------------------------------------
|
|
766
857
|
|
|
767
858
|
|
|
@@ -782,7 +873,7 @@ class Model:
|
|
|
782
873
|
"""
|
|
783
874
|
|
|
784
875
|
def __init__(self, path: str | os.PathLike, *,
|
|
785
|
-
backend: Backend = "auto",
|
|
876
|
+
backend: Backend = "auto", device: BackendDevice | None = None):
|
|
786
877
|
# Live sessions, tracked weakly: close() must free them before the
|
|
787
878
|
# model, because transcribe_model_free is only valid once every
|
|
788
879
|
# derived session is gone (use-after-free otherwise). Created before
|
|
@@ -795,7 +886,10 @@ class Model:
|
|
|
795
886
|
params = _ModelLoadParams()
|
|
796
887
|
_lib.transcribe_model_load_params_init(_byref(params))
|
|
797
888
|
params.backend = _enum(_BACKENDS, backend, backend_source)
|
|
798
|
-
|
|
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
|
|
799
893
|
|
|
800
894
|
handle = ctypes.c_void_p()
|
|
801
895
|
status = _lib.transcribe_model_load_file(
|
|
@@ -830,11 +924,14 @@ class Model:
|
|
|
830
924
|
live snapshot, so read this again to poll how much device memory is
|
|
831
925
|
left after the model loaded. Raises if the model has no resolved
|
|
832
926
|
compute device."""
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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))
|
|
838
935
|
|
|
839
936
|
@property
|
|
840
937
|
def capabilities(self) -> Capabilities:
|
|
@@ -982,6 +1079,7 @@ class Session:
|
|
|
982
1079
|
language: str | None = None,
|
|
983
1080
|
target_language: str | None = None,
|
|
984
1081
|
timestamps: Timestamps = "auto",
|
|
1082
|
+
diarize: Diarize = "default",
|
|
985
1083
|
keep_special_tags: bool = False,
|
|
986
1084
|
spec_k_drafts: int = -1,
|
|
987
1085
|
family: FamilyExtension | None = None) -> Result:
|
|
@@ -999,7 +1097,7 @@ class Session:
|
|
|
999
1097
|
self._cancel.clear()
|
|
1000
1098
|
array, n_samples = _pcm_to_carray(pcm)
|
|
1001
1099
|
params = _build_run_params(task, language, target_language, timestamps,
|
|
1002
|
-
keep_special_tags, spec_k_drafts)
|
|
1100
|
+
keep_special_tags, spec_k_drafts, diarize)
|
|
1003
1101
|
ext = self._resolve_family(family, "run") if family is not None else None
|
|
1004
1102
|
if ext is not None:
|
|
1005
1103
|
params.family = ctypes.cast(
|
|
@@ -1018,6 +1116,7 @@ class Session:
|
|
|
1018
1116
|
language: str | None = None,
|
|
1019
1117
|
target_language: str | None = None,
|
|
1020
1118
|
timestamps: Timestamps = "auto",
|
|
1119
|
+
diarize: Diarize = "default",
|
|
1021
1120
|
keep_special_tags: bool = False,
|
|
1022
1121
|
spec_k_drafts: int = -1,
|
|
1023
1122
|
family: FamilyExtension | None = None,
|
|
@@ -1054,7 +1153,7 @@ class Session:
|
|
|
1054
1153
|
counts[k] = n
|
|
1055
1154
|
|
|
1056
1155
|
params = _build_run_params(task, language, target_language, timestamps,
|
|
1057
|
-
keep_special_tags, spec_k_drafts)
|
|
1156
|
+
keep_special_tags, spec_k_drafts, diarize)
|
|
1058
1157
|
ext = self._resolve_family(family, "run") if family is not None else None
|
|
1059
1158
|
if ext is not None:
|
|
1060
1159
|
params.family = ctypes.cast(
|
|
@@ -1105,6 +1204,7 @@ class Session:
|
|
|
1105
1204
|
|
|
1106
1205
|
def stream(self, *, task: Task = "transcribe", language: str | None = None,
|
|
1107
1206
|
target_language: str | None = None, timestamps: Timestamps = "none",
|
|
1207
|
+
diarize: Diarize = "default",
|
|
1108
1208
|
keep_special_tags: bool = False, commit_policy: CommitPolicy = "auto",
|
|
1109
1209
|
stable_prefix_agreement_n: int = 0,
|
|
1110
1210
|
family: FamilyExtension | None = None) -> Stream:
|
|
@@ -1119,7 +1219,7 @@ class Session:
|
|
|
1119
1219
|
# spec_k_drafts is an offline-decode knob; streaming always uses the
|
|
1120
1220
|
# family default (-1).
|
|
1121
1221
|
run_params = _build_run_params(task, language, target_language, timestamps,
|
|
1122
|
-
keep_special_tags, -1)
|
|
1222
|
+
keep_special_tags, -1, diarize)
|
|
1123
1223
|
sp = _StreamParams()
|
|
1124
1224
|
_lib.transcribe_stream_params_init(_byref(sp))
|
|
1125
1225
|
sp.commit_policy = _enum(_COMMIT_POLICIES, commit_policy, "commit_policy")
|
|
@@ -1151,7 +1251,10 @@ class Session:
|
|
|
1151
1251
|
get_word = lambda j, out: _lib.transcribe_get_word(h, j, out)
|
|
1152
1252
|
n_tok = lambda: _lib.transcribe_n_tokens(h)
|
|
1153
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)
|
|
1154
1256
|
full_text = _lib.transcribe_full_text(h)
|
|
1257
|
+
raw_text = _lib.transcribe_raw_text(h)
|
|
1155
1258
|
language = _lib.transcribe_detected_language(h)
|
|
1156
1259
|
kind = _lib.transcribe_returned_timestamp_kind(h)
|
|
1157
1260
|
get_tim = lambda out: _lib.transcribe_get_timings(h, out)
|
|
@@ -1162,7 +1265,10 @@ class Session:
|
|
|
1162
1265
|
get_word = lambda j, out: _lib.transcribe_batch_get_word(h, utt, j, out)
|
|
1163
1266
|
n_tok = lambda: _lib.transcribe_batch_n_tokens(h, utt)
|
|
1164
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)
|
|
1165
1270
|
full_text = _lib.transcribe_batch_full_text(h, utt)
|
|
1271
|
+
raw_text = _lib.transcribe_batch_raw_text(h, utt)
|
|
1166
1272
|
language = _lib.transcribe_batch_detected_language(h, utt)
|
|
1167
1273
|
kind = _lib.transcribe_batch_returned_timestamp_kind(h, utt)
|
|
1168
1274
|
get_tim = lambda out: _lib.transcribe_batch_get_timings(h, utt, out)
|
|
@@ -1188,15 +1294,24 @@ class Session:
|
|
|
1188
1294
|
get_tok(j, _byref(tok))
|
|
1189
1295
|
tokens.append(_token_from(tok))
|
|
1190
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
|
+
|
|
1191
1304
|
tm = _Timings()
|
|
1192
1305
|
_lib.transcribe_timings_init(_byref(tm))
|
|
1193
1306
|
get_tim(_byref(tm))
|
|
1194
1307
|
|
|
1195
1308
|
return Result(
|
|
1196
1309
|
text=_decode(full_text),
|
|
1310
|
+
raw_text=_decode(raw_text),
|
|
1197
1311
|
language=_decode(language),
|
|
1198
1312
|
timestamp_kind=_TIMESTAMP_NAMES.get(kind, "unknown"),
|
|
1199
1313
|
segments=tuple(segments),
|
|
1314
|
+
speaker_segments=tuple(speaker_segments),
|
|
1200
1315
|
words=tuple(words),
|
|
1201
1316
|
tokens=tuple(tokens),
|
|
1202
1317
|
timings=_timings_from(tm),
|
|
@@ -1338,7 +1453,7 @@ def transcribe(
|
|
|
1338
1453
|
pcm: PCMLike,
|
|
1339
1454
|
*,
|
|
1340
1455
|
backend: Backend = "auto",
|
|
1341
|
-
|
|
1456
|
+
device: BackendDevice | None = None,
|
|
1342
1457
|
n_threads: int = 0,
|
|
1343
1458
|
kv_type: KVType = "auto",
|
|
1344
1459
|
n_ctx: int = 0,
|
|
@@ -1346,6 +1461,7 @@ def transcribe(
|
|
|
1346
1461
|
language: str | None = None,
|
|
1347
1462
|
target_language: str | None = None,
|
|
1348
1463
|
timestamps: Timestamps = "auto",
|
|
1464
|
+
diarize: Diarize = "default",
|
|
1349
1465
|
keep_special_tags: bool = False,
|
|
1350
1466
|
spec_k_drafts: int = -1,
|
|
1351
1467
|
family: FamilyExtension | None = None,
|
|
@@ -1355,19 +1471,20 @@ def transcribe(
|
|
|
1355
1471
|
*model* may be a path (loaded and freed within this call) or an existing
|
|
1356
1472
|
Model (reused and left open). Loading a model is not free, so to transcribe
|
|
1357
1473
|
many clips keep a Model and call ``model.session().run(...)`` yourself; this
|
|
1358
|
-
helper is for the one-shot case. ``backend`` / ``
|
|
1474
|
+
helper is for the one-shot case. ``backend`` / ``device`` apply only when
|
|
1359
1475
|
*model* is a path — they are ignored when an already-loaded Model is passed.
|
|
1360
1476
|
``family`` / ``spec_k_drafts`` pass through to :meth:`Session.run`.
|
|
1361
1477
|
"""
|
|
1362
1478
|
session_opts = dict(n_threads=n_threads, kv_type=kv_type, n_ctx=n_ctx)
|
|
1363
1479
|
run_opts = dict(task=task, language=language, target_language=target_language,
|
|
1364
|
-
timestamps=timestamps,
|
|
1480
|
+
timestamps=timestamps, diarize=diarize,
|
|
1481
|
+
keep_special_tags=keep_special_tags,
|
|
1365
1482
|
spec_k_drafts=spec_k_drafts, family=family)
|
|
1366
1483
|
|
|
1367
1484
|
if isinstance(model, Model):
|
|
1368
1485
|
with model.session(**session_opts) as session:
|
|
1369
1486
|
return session.run(pcm, **run_opts)
|
|
1370
1487
|
|
|
1371
|
-
with Model(model, backend=backend,
|
|
1488
|
+
with Model(model, backend=backend, device=device) as owned:
|
|
1372
1489
|
with owned.session(**session_opts) as session:
|
|
1373
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
|