transcribe-cpp 0.3.1__tar.gz → 0.4.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.3.1 → transcribe_cpp-0.4.0}/.gitignore +2 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/PKG-INFO +73 -6
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/README.md +70 -3
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/pyproject.toml +3 -3
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/src/transcribe_cpp/__init__.py +909 -188
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/src/transcribe_cpp/_generated.py +182 -7
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/src/transcribe_cpp/errors.py +22 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/conftest.py +50 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_abi.py +2 -2
- transcribe_cpp-0.4.0/tests/test_compute_lock.py +671 -0
- transcribe_cpp-0.4.0/tests/test_diarize.py +83 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_errors.py +8 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_family_ext.py +10 -10
- transcribe_cpp-0.4.0/tests/test_langid.py +143 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_lifetime.py +15 -23
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_prompting.py +1 -1
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_streaming.py +76 -6
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_transcribe.py +12 -0
- transcribe_cpp-0.4.0/tests/test_vad.py +42 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/uv.lock +3 -3
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/LICENSE +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/_generate/README.md +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/_generate/check_version_sync.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/_generate/generate.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/examples/stream_wav.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/examples/transcribe_wav.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/src/transcribe_cpp/_abi.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/src/transcribe_cpp/_library.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/src/transcribe_cpp/py.typed +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_backends.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_device_select.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_example.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_pcm.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_provider_discovery.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_text_controls.py +0 -0
- {transcribe_cpp-0.3.1 → transcribe_cpp-0.4.0}/tests/test_utf8_paths.py +0 -0
|
@@ -59,6 +59,8 @@ __pycache__/
|
|
|
59
59
|
|
|
60
60
|
# WER evaluation data + generated working reports.
|
|
61
61
|
/samples/wer/
|
|
62
|
+
# FLEURS language ID corpus (scripts/langid/ingest.py).
|
|
63
|
+
/samples/langid/
|
|
62
64
|
# Diarization eval corpora (AMI audio + fetched RTTMs); regenerable via
|
|
63
65
|
# scripts/diar/ingest_ami.py + fetch_ami_forced_alignment.py. The committed
|
|
64
66
|
# oracle case lives at samples/sortformer-2spk-mix.wav (outside samples/diar/).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: transcribe-cpp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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.4.0.*
|
|
25
25
|
Provides-Extra: cu12
|
|
26
|
-
Requires-Dist: transcribe-cpp-native-cu12==0.
|
|
26
|
+
Requires-Dist: transcribe-cpp-native-cu12==0.4.0.*; extra == 'cu12'
|
|
27
27
|
Provides-Extra: test
|
|
28
28
|
Requires-Dist: numpy; extra == 'test'
|
|
29
29
|
Requires-Dist: pytest>=7; extra == 'test'
|
|
@@ -101,6 +101,72 @@ Long transcriptions can be cancelled from another thread with
|
|
|
101
101
|
`session.cancel()` — the run raises `Aborted` with the partial transcript on
|
|
102
102
|
`exc.partial_result` (same for `OutputTruncated`).
|
|
103
103
|
|
|
104
|
+
### Diarization
|
|
105
|
+
|
|
106
|
+
Models whose `model.roles` include `Role.DIARIZE` (Sortformer) answer "who
|
|
107
|
+
spoke when" through a diarize session. `run()` returns `SpeakerSegment`
|
|
108
|
+
rows (`speaker_id` in `1..model.diarize_info.max_speakers`), grouped by
|
|
109
|
+
speaker. Locking, `Busy`, `cancel()` and `close()` work as on `Session`. A
|
|
110
|
+
model without the role raises `UnsupportedRole`, as `model.session()` and
|
|
111
|
+
`model.capabilities` do on a model without `Role.ASR` (Sortformer serves only
|
|
112
|
+
`Role.DIARIZE`).
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
with model.diarize_session() as diarizer:
|
|
116
|
+
turns = diarizer.run(pcm, family=transcribe_cpp.SortformerDiarizeOptions(
|
|
117
|
+
preset="very_high_latency"))
|
|
118
|
+
for turn in turns:
|
|
119
|
+
print(turn.speaker_id, turn.t0_ms, turn.t1_ms)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Language ID
|
|
123
|
+
|
|
124
|
+
Models whose `model.roles` include `Role.LANGID` (VoxLingua107 ECAPA-TDNN)
|
|
125
|
+
identify the spoken language through a langid session. `run()` returns a
|
|
126
|
+
`LangIdResult` with candidates ranked by `p`. Codes are the model's own labels
|
|
127
|
+
(`"iw"`, `"jw"`; `model.langid_label_index("he")` resolves aliases), so match
|
|
128
|
+
`result.code` against the ASR model's `capabilities.languages` yourself.
|
|
129
|
+
`allowed=None` scores every label; `allowed_mass` is the unrestricted
|
|
130
|
+
probability the allowed set captured. An empty `allowed` list raises
|
|
131
|
+
`InvalidArgument`; clips under `model.langid_info.min_audio_ms` raise
|
|
132
|
+
`InputTooShort`, and longer ones than `max_audio_ms` are scored on their first
|
|
133
|
+
`max_audio_ms`. Every allowed label comes back as a candidate. Locking,
|
|
134
|
+
`Busy`, `cancel()` and `close()` work as on `Session`.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
with model.langid_session() as lid:
|
|
138
|
+
result = lid.run(pcm, allowed=["en", "de", "fr"])
|
|
139
|
+
print(result.code, result.candidates[0].p, result.allowed_mass)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Voice activity detection
|
|
143
|
+
|
|
144
|
+
Models whose `model.roles` include `Role.VAD` (Silero VAD, GGUF or the
|
|
145
|
+
whisper.cpp `.bin`) score speech probability per frame of
|
|
146
|
+
`model.vad_info.frame_samples` samples through a VAD session. `run()` scores one
|
|
147
|
+
clip and returns a `VadResult` with `probs` and speech `segments`
|
|
148
|
+
(`start_sample`/`end_sample`; options default to Silero's
|
|
149
|
+
`get_speech_timestamps`). `stream_feed()` streams audio of any chunk size and
|
|
150
|
+
returns the probabilities of the frames each call completed (`first_frame` is
|
|
151
|
+
the position of the first); `stream_flush()` scores the zero-padded remainder
|
|
152
|
+
and ends the stream, `stream_reset()` drops it. The VAD stream does not take
|
|
153
|
+
the model's ASR stream lease. Locking, `Busy`, `cancel()` and `close()` work as
|
|
154
|
+
on `Session`.
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
with model.vad_session() as vad:
|
|
158
|
+
for seg in vad.run(pcm, min_silence_ms=300).segments:
|
|
159
|
+
print(seg.start_sample, seg.end_sample)
|
|
160
|
+
|
|
161
|
+
frame = model.vad_info.frame_samples
|
|
162
|
+
probs = []
|
|
163
|
+
for chunk in chunks:
|
|
164
|
+
r = vad.stream_feed(chunk)
|
|
165
|
+
print(f"{len(r.probs)} frames from sample {r.first_frame * frame}")
|
|
166
|
+
probs.extend(r.probs)
|
|
167
|
+
probs.extend(vad.stream_flush().probs)
|
|
168
|
+
```
|
|
169
|
+
|
|
104
170
|
## Backends
|
|
105
171
|
|
|
106
172
|
`Model(backend=...)` applies a backend policy (`"auto"` uses the best
|
|
@@ -151,9 +217,10 @@ TRANSCRIBE_LIBRARY=../../build-shared/src/libtranscribe.dylib \
|
|
|
151
217
|
|
|
152
218
|
## Notes
|
|
153
219
|
|
|
154
|
-
- One
|
|
155
|
-
|
|
156
|
-
|
|
220
|
+
- One compute call at a time per `Model`: the binding serializes calls across
|
|
221
|
+
all sessions of a model with a model-wide lock (load one model per worker
|
|
222
|
+
for parallelism). While a stream is active, other runs and stream begins on
|
|
223
|
+
that model raise `transcribe_cpp.Busy`. See the `Model` docstring.
|
|
157
224
|
- Import package: `transcribe_cpp`
|
|
158
225
|
- Distribution: `transcribe-cpp`
|
|
159
226
|
- License: MIT
|
|
@@ -70,6 +70,72 @@ Long transcriptions can be cancelled from another thread with
|
|
|
70
70
|
`session.cancel()` — the run raises `Aborted` with the partial transcript on
|
|
71
71
|
`exc.partial_result` (same for `OutputTruncated`).
|
|
72
72
|
|
|
73
|
+
### Diarization
|
|
74
|
+
|
|
75
|
+
Models whose `model.roles` include `Role.DIARIZE` (Sortformer) answer "who
|
|
76
|
+
spoke when" through a diarize session. `run()` returns `SpeakerSegment`
|
|
77
|
+
rows (`speaker_id` in `1..model.diarize_info.max_speakers`), grouped by
|
|
78
|
+
speaker. Locking, `Busy`, `cancel()` and `close()` work as on `Session`. A
|
|
79
|
+
model without the role raises `UnsupportedRole`, as `model.session()` and
|
|
80
|
+
`model.capabilities` do on a model without `Role.ASR` (Sortformer serves only
|
|
81
|
+
`Role.DIARIZE`).
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
with model.diarize_session() as diarizer:
|
|
85
|
+
turns = diarizer.run(pcm, family=transcribe_cpp.SortformerDiarizeOptions(
|
|
86
|
+
preset="very_high_latency"))
|
|
87
|
+
for turn in turns:
|
|
88
|
+
print(turn.speaker_id, turn.t0_ms, turn.t1_ms)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Language ID
|
|
92
|
+
|
|
93
|
+
Models whose `model.roles` include `Role.LANGID` (VoxLingua107 ECAPA-TDNN)
|
|
94
|
+
identify the spoken language through a langid session. `run()` returns a
|
|
95
|
+
`LangIdResult` with candidates ranked by `p`. Codes are the model's own labels
|
|
96
|
+
(`"iw"`, `"jw"`; `model.langid_label_index("he")` resolves aliases), so match
|
|
97
|
+
`result.code` against the ASR model's `capabilities.languages` yourself.
|
|
98
|
+
`allowed=None` scores every label; `allowed_mass` is the unrestricted
|
|
99
|
+
probability the allowed set captured. An empty `allowed` list raises
|
|
100
|
+
`InvalidArgument`; clips under `model.langid_info.min_audio_ms` raise
|
|
101
|
+
`InputTooShort`, and longer ones than `max_audio_ms` are scored on their first
|
|
102
|
+
`max_audio_ms`. Every allowed label comes back as a candidate. Locking,
|
|
103
|
+
`Busy`, `cancel()` and `close()` work as on `Session`.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
with model.langid_session() as lid:
|
|
107
|
+
result = lid.run(pcm, allowed=["en", "de", "fr"])
|
|
108
|
+
print(result.code, result.candidates[0].p, result.allowed_mass)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Voice activity detection
|
|
112
|
+
|
|
113
|
+
Models whose `model.roles` include `Role.VAD` (Silero VAD, GGUF or the
|
|
114
|
+
whisper.cpp `.bin`) score speech probability per frame of
|
|
115
|
+
`model.vad_info.frame_samples` samples through a VAD session. `run()` scores one
|
|
116
|
+
clip and returns a `VadResult` with `probs` and speech `segments`
|
|
117
|
+
(`start_sample`/`end_sample`; options default to Silero's
|
|
118
|
+
`get_speech_timestamps`). `stream_feed()` streams audio of any chunk size and
|
|
119
|
+
returns the probabilities of the frames each call completed (`first_frame` is
|
|
120
|
+
the position of the first); `stream_flush()` scores the zero-padded remainder
|
|
121
|
+
and ends the stream, `stream_reset()` drops it. The VAD stream does not take
|
|
122
|
+
the model's ASR stream lease. Locking, `Busy`, `cancel()` and `close()` work as
|
|
123
|
+
on `Session`.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
with model.vad_session() as vad:
|
|
127
|
+
for seg in vad.run(pcm, min_silence_ms=300).segments:
|
|
128
|
+
print(seg.start_sample, seg.end_sample)
|
|
129
|
+
|
|
130
|
+
frame = model.vad_info.frame_samples
|
|
131
|
+
probs = []
|
|
132
|
+
for chunk in chunks:
|
|
133
|
+
r = vad.stream_feed(chunk)
|
|
134
|
+
print(f"{len(r.probs)} frames from sample {r.first_frame * frame}")
|
|
135
|
+
probs.extend(r.probs)
|
|
136
|
+
probs.extend(vad.stream_flush().probs)
|
|
137
|
+
```
|
|
138
|
+
|
|
73
139
|
## Backends
|
|
74
140
|
|
|
75
141
|
`Model(backend=...)` applies a backend policy (`"auto"` uses the best
|
|
@@ -120,9 +186,10 @@ TRANSCRIBE_LIBRARY=../../build-shared/src/libtranscribe.dylib \
|
|
|
120
186
|
|
|
121
187
|
## Notes
|
|
122
188
|
|
|
123
|
-
- One
|
|
124
|
-
|
|
125
|
-
|
|
189
|
+
- One compute call at a time per `Model`: the binding serializes calls across
|
|
190
|
+
all sessions of a model with a model-wide lock (load one model per worker
|
|
191
|
+
for parallelism). While a stream is active, other runs and stream begins on
|
|
192
|
+
that model raise `transcribe_cpp.Busy`. See the `Model` docstring.
|
|
126
193
|
- Import package: `transcribe_cpp`
|
|
127
194
|
- Distribution: `transcribe-cpp`
|
|
128
195
|
- License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "transcribe-cpp"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.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.4.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.4.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.
|