transcribe-cpp 0.2.4__tar.gz → 0.3.1__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.2.4 → transcribe_cpp-0.3.1}/PKG-INFO +14 -3
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/README.md +11 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/pyproject.toml +3 -3
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/src/transcribe_cpp/__init__.py +84 -23
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/src/transcribe_cpp/_generated.py +28 -3
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/src/transcribe_cpp/errors.py +13 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_errors.py +9 -0
- transcribe_cpp-0.3.1/tests/test_prompting.py +116 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/uv.lock +3 -3
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/.gitignore +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/LICENSE +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/_generate/README.md +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/_generate/check_version_sync.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/_generate/generate.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/examples/stream_wav.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/examples/transcribe_wav.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/src/transcribe_cpp/_abi.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/src/transcribe_cpp/_library.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/src/transcribe_cpp/py.typed +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/conftest.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_abi.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_backends.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_device_select.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_example.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_family_ext.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_lifetime.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_pcm.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_provider_discovery.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_streaming.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_text_controls.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_transcribe.py +0 -0
- {transcribe_cpp-0.2.4 → transcribe_cpp-0.3.1}/tests/test_utf8_paths.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: transcribe-cpp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
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.3.1.*
|
|
25
25
|
Provides-Extra: cu12
|
|
26
|
-
Requires-Dist: transcribe-cpp-native-cu12==0.
|
|
26
|
+
Requires-Dist: transcribe-cpp-native-cu12==0.3.1.*; extra == 'cu12'
|
|
27
27
|
Provides-Extra: test
|
|
28
28
|
Requires-Dist: numpy; extra == 'test'
|
|
29
29
|
Requires-Dist: pytest>=7; extra == 'test'
|
|
@@ -74,6 +74,17 @@ and the one-shot `transcribe()` helper.
|
|
|
74
74
|
result = session.run(pcm, pnc="off", itn="on")
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
### Prompting
|
|
78
|
+
|
|
79
|
+
`vocabulary` (custom terms), `prompt` (context, or the instruction under
|
|
80
|
+
`task="instruct"`) and `prefix` (text the model continues from) take effect
|
|
81
|
+
where `model.supports()` reports `"vocabulary"`, `"context_prompt"`,
|
|
82
|
+
`"instruct"` or `"transcript_prefix"`.
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
result = session.run(pcm, vocabulary=["Kubernetes", "gRPC"])
|
|
86
|
+
```
|
|
87
|
+
|
|
77
88
|
Streaming models expose incremental transcription with committed/tentative
|
|
78
89
|
text views — see `examples/stream_wav.py`:
|
|
79
90
|
|
|
@@ -43,6 +43,17 @@ and the one-shot `transcribe()` helper.
|
|
|
43
43
|
result = session.run(pcm, pnc="off", itn="on")
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
### Prompting
|
|
47
|
+
|
|
48
|
+
`vocabulary` (custom terms), `prompt` (context, or the instruction under
|
|
49
|
+
`task="instruct"`) and `prefix` (text the model continues from) take effect
|
|
50
|
+
where `model.supports()` reports `"vocabulary"`, `"context_prompt"`,
|
|
51
|
+
`"instruct"` or `"transcript_prefix"`.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
result = session.run(pcm, vocabulary=["Kubernetes", "gRPC"])
|
|
55
|
+
```
|
|
56
|
+
|
|
46
57
|
Streaming models expose incremental transcription with committed/tentative
|
|
47
58
|
text views — see `examples/stream_wav.py`:
|
|
48
59
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "transcribe-cpp"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.1"
|
|
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.3.1.*"]
|
|
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.3.1.*"]
|
|
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.
|
|
@@ -39,6 +39,7 @@ from .errors import (
|
|
|
39
39
|
ModelLoadError,
|
|
40
40
|
NotImplementedByModel,
|
|
41
41
|
OutOfMemory,
|
|
42
|
+
OutputRepetition,
|
|
42
43
|
OutputTruncated,
|
|
43
44
|
TranscribeError,
|
|
44
45
|
UnsupportedRequest,
|
|
@@ -46,12 +47,12 @@ from .errors import (
|
|
|
46
47
|
raise_for_status,
|
|
47
48
|
)
|
|
48
49
|
|
|
49
|
-
__version__ = "0.
|
|
50
|
+
__version__ = "0.3.1"
|
|
50
51
|
|
|
51
52
|
# String-enum types, exported so callers (and type checkers) can name them.
|
|
52
53
|
Backend = Literal["auto", "cpu", "metal", "vulkan", "cpu_accel", "cuda", "rocm"]
|
|
53
54
|
KVType = Literal["auto", "f32", "f16"]
|
|
54
|
-
Task = Literal["transcribe", "translate"]
|
|
55
|
+
Task = Literal["transcribe", "translate", "instruct"]
|
|
55
56
|
Timestamps = Literal["none", "auto", "segment", "word", "token"]
|
|
56
57
|
Pnc = Literal["default", "off", "on"]
|
|
57
58
|
Itn = Literal["default", "off", "on"]
|
|
@@ -61,6 +62,7 @@ CommitPolicy = Literal["auto", "on_finalize", "stable_prefix"]
|
|
|
61
62
|
Feature = Literal[
|
|
62
63
|
"initial_prompt", "temperature_fallback", "long_form",
|
|
63
64
|
"cancellation", "pnc", "itn", "diarization",
|
|
65
|
+
"vocabulary", "context_prompt", "instruct", "transcript_prefix",
|
|
64
66
|
]
|
|
65
67
|
|
|
66
68
|
__all__ = [
|
|
@@ -108,6 +110,7 @@ __all__ = [
|
|
|
108
110
|
"Aborted",
|
|
109
111
|
"InputTooLong",
|
|
110
112
|
"OutputTruncated",
|
|
113
|
+
"OutputRepetition",
|
|
111
114
|
"native_version",
|
|
112
115
|
"native_commit",
|
|
113
116
|
"library_path",
|
|
@@ -201,6 +204,7 @@ _KV_TYPES = {
|
|
|
201
204
|
_TASKS = {
|
|
202
205
|
"transcribe": _generated.TRANSCRIBE_TASK_TRANSCRIBE,
|
|
203
206
|
"translate": _generated.TRANSCRIBE_TASK_TRANSLATE,
|
|
207
|
+
"instruct": _generated.TRANSCRIBE_TASK_INSTRUCT,
|
|
204
208
|
}
|
|
205
209
|
_TIMESTAMPS = {
|
|
206
210
|
"none": _generated.TRANSCRIBE_TIMESTAMPS_NONE,
|
|
@@ -248,6 +252,10 @@ _FEATURES = {
|
|
|
248
252
|
"pnc": _generated.TRANSCRIBE_FEATURE_PNC,
|
|
249
253
|
"itn": _generated.TRANSCRIBE_FEATURE_ITN,
|
|
250
254
|
"diarization": _generated.TRANSCRIBE_FEATURE_DIARIZATION,
|
|
255
|
+
"vocabulary": _generated.TRANSCRIBE_FEATURE_VOCABULARY,
|
|
256
|
+
"context_prompt": _generated.TRANSCRIBE_FEATURE_CONTEXT_PROMPT,
|
|
257
|
+
"instruct": _generated.TRANSCRIBE_FEATURE_INSTRUCT,
|
|
258
|
+
"transcript_prefix": _generated.TRANSCRIBE_FEATURE_TRANSCRIPT_PREFIX,
|
|
251
259
|
}
|
|
252
260
|
|
|
253
261
|
|
|
@@ -651,9 +659,17 @@ def _stream_update_from(u) -> StreamUpdate:
|
|
|
651
659
|
)
|
|
652
660
|
|
|
653
661
|
|
|
662
|
+
def _cstr(value: str, name: str) -> bytes:
|
|
663
|
+
"""UTF-8 bytes for a C string option; a NUL would silently cut it in C."""
|
|
664
|
+
if "\x00" in value:
|
|
665
|
+
raise InvalidArgument(f"{name} contains a NUL character")
|
|
666
|
+
return value.encode("utf-8")
|
|
667
|
+
|
|
668
|
+
|
|
654
669
|
def _build_run_params(task, language, target_language, timestamps,
|
|
655
670
|
keep_special_tags, spec_k_drafts, diarize="default",
|
|
656
|
-
pnc="default", itn="default"
|
|
671
|
+
pnc="default", itn="default", vocabulary=None,
|
|
672
|
+
prompt=None, prefix=None):
|
|
657
673
|
if not isinstance(spec_k_drafts, int) or spec_k_drafts < -1:
|
|
658
674
|
raise InvalidArgument(
|
|
659
675
|
f"spec_k_drafts must be -1 (family default), 0 (disabled), or a "
|
|
@@ -666,10 +682,27 @@ def _build_run_params(task, language, target_language, timestamps,
|
|
|
666
682
|
params.pnc = _enum(_PNC, pnc, "pnc")
|
|
667
683
|
params.itn = _enum(_ITN, itn, "itn")
|
|
668
684
|
params.diarize = _enum(_DIARIZE, diarize, "diarize")
|
|
669
|
-
params.language = language
|
|
670
|
-
params.target_language = target_language
|
|
685
|
+
params.language = _cstr(language, "language") if language else None
|
|
686
|
+
params.target_language = _cstr(target_language, "target_language") if target_language else None
|
|
671
687
|
params.keep_special_tags = keep_special_tags
|
|
672
688
|
params.spec_k_drafts = spec_k_drafts
|
|
689
|
+
if vocabulary is not None:
|
|
690
|
+
if isinstance(vocabulary, (str, bytes)):
|
|
691
|
+
raise InvalidArgument("vocabulary must be a sequence of terms, not a single string")
|
|
692
|
+
if isinstance(vocabulary, (set, frozenset, dict)):
|
|
693
|
+
raise InvalidArgument("vocabulary is in priority order; pass a list, not a set or dict")
|
|
694
|
+
vocabulary = list(vocabulary) # an iterator would be used up by the check below
|
|
695
|
+
if not all(isinstance(t, str) for t in vocabulary):
|
|
696
|
+
raise InvalidArgument("vocabulary terms must be strings")
|
|
697
|
+
terms = [_cstr(t, "vocabulary") for t in vocabulary]
|
|
698
|
+
if terms:
|
|
699
|
+
arr = (ctypes.c_char_p * len(terms))(*terms)
|
|
700
|
+
params.vocabulary = ctypes.cast(arr, ctypes.POINTER(ctypes.c_char_p))
|
|
701
|
+
params.n_vocabulary = len(terms)
|
|
702
|
+
# The C struct holds raw pointers; keep the buffers alive with it.
|
|
703
|
+
params._prompting_keepalive = (arr, terms)
|
|
704
|
+
params.prompt = _cstr(prompt, "prompt") if prompt else None
|
|
705
|
+
params.prefix = _cstr(prefix, "prefix") if prefix else None
|
|
673
706
|
return params
|
|
674
707
|
|
|
675
708
|
|
|
@@ -738,7 +771,7 @@ class WhisperRunOptions(FamilyExtension):
|
|
|
738
771
|
|
|
739
772
|
def _apply(self, ext) -> None:
|
|
740
773
|
if self.initial_prompt is not None:
|
|
741
|
-
ext.initial_prompt = self.initial_prompt
|
|
774
|
+
ext.initial_prompt = _cstr(self.initial_prompt, "initial_prompt")
|
|
742
775
|
if self.condition_on_prev_tokens is not None:
|
|
743
776
|
ext.condition_on_prev_tokens = self.condition_on_prev_tokens
|
|
744
777
|
if self.temperature is not None:
|
|
@@ -979,8 +1012,7 @@ class Model:
|
|
|
979
1012
|
)
|
|
980
1013
|
|
|
981
1014
|
def supports(self, feature: Feature) -> bool:
|
|
982
|
-
"""Whether the model exposes a behavioral feature (
|
|
983
|
-
temperature fallback, long-form, cancellation, pnc, itn)."""
|
|
1015
|
+
"""Whether the model exposes a behavioral feature (see ``Feature``)."""
|
|
984
1016
|
return bool(_lib.transcribe_model_supports(
|
|
985
1017
|
self._h, _enum(_FEATURES, feature, "feature")))
|
|
986
1018
|
|
|
@@ -1101,7 +1133,10 @@ class Session:
|
|
|
1101
1133
|
diarize: Diarize = "default",
|
|
1102
1134
|
keep_special_tags: bool = False,
|
|
1103
1135
|
spec_k_drafts: int = -1,
|
|
1104
|
-
family: FamilyExtension | None = None
|
|
1136
|
+
family: FamilyExtension | None = None,
|
|
1137
|
+
vocabulary: Sequence[str] | None = None,
|
|
1138
|
+
prompt: str | None = None,
|
|
1139
|
+
prefix: str | None = None) -> Result:
|
|
1105
1140
|
"""Transcribe 16 kHz mono float32 PCM and return a materialized Result.
|
|
1106
1141
|
|
|
1107
1142
|
``pnc`` controls punctuation/capitalization and ``itn`` controls
|
|
@@ -1111,14 +1146,23 @@ class Session:
|
|
|
1111
1146
|
``spec_k_drafts`` tunes speculative decoding on models whose
|
|
1112
1147
|
capabilities advertise ``supports_spec_decode`` (-1 = family default,
|
|
1113
1148
|
0 = disabled, >0 = draft length; silently ignored elsewhere).
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
``
|
|
1149
|
+
``vocabulary`` (terms, priority order), ``prompt`` and ``prefix`` are
|
|
1150
|
+
the generic prompting inputs; probe ``model.supports()`` for
|
|
1151
|
+
``"vocabulary"``, ``"context_prompt"``, ``"instruct"`` and
|
|
1152
|
+
``"transcript_prefix"``. With ``task="instruct"`` the ``prompt`` is the
|
|
1153
|
+
required instruction and the output is free text. Unsupported
|
|
1154
|
+
vocabulary/context is ignored with a warning; an unsupported prefix
|
|
1155
|
+
or instruct task raises. With ``prefix``, ``text`` holds only the
|
|
1156
|
+
continuation and ``raw_text`` leads with the prefix.
|
|
1157
|
+
|
|
1158
|
+
On ``Aborted`` (via :meth:`cancel`) and ``OutputTruncated`` (including
|
|
1159
|
+
its ``OutputRepetition`` subclass) the partial transcript is preserved
|
|
1160
|
+
and attached to the exception as ``partial_result``."""
|
|
1118
1161
|
self._cancel.clear()
|
|
1119
1162
|
array, n_samples = _pcm_to_carray(pcm)
|
|
1120
1163
|
params = _build_run_params(task, language, target_language, timestamps,
|
|
1121
|
-
keep_special_tags, spec_k_drafts, diarize, pnc, itn
|
|
1164
|
+
keep_special_tags, spec_k_drafts, diarize, pnc, itn,
|
|
1165
|
+
vocabulary, prompt, prefix)
|
|
1122
1166
|
ext = self._resolve_family(family, "run") if family is not None else None
|
|
1123
1167
|
if ext is not None:
|
|
1124
1168
|
params.family = ctypes.cast(
|
|
@@ -1128,7 +1172,8 @@ class Session:
|
|
|
1128
1172
|
"transcribe_run")
|
|
1129
1173
|
except (Aborted, OutputTruncated) as exc:
|
|
1130
1174
|
# The C API preserves the partial transcript on the session for
|
|
1131
|
-
#
|
|
1175
|
+
# these statuses (OutputRepetition included, as an OutputTruncated
|
|
1176
|
+
# subclass); surface it rather than discard it.
|
|
1132
1177
|
exc.partial_result = self._materialize()
|
|
1133
1178
|
raise
|
|
1134
1179
|
return self._materialize()
|
|
@@ -1143,7 +1188,9 @@ class Session:
|
|
|
1143
1188
|
keep_special_tags: bool = False,
|
|
1144
1189
|
spec_k_drafts: int = -1,
|
|
1145
1190
|
family: FamilyExtension | None = None,
|
|
1146
|
-
return_exceptions: bool = False
|
|
1191
|
+
return_exceptions: bool = False,
|
|
1192
|
+
vocabulary: Sequence[str] | None = None,
|
|
1193
|
+
prompt: str | None = None) -> list[Result | TranscribeError]:
|
|
1147
1194
|
"""Transcribe several utterances in one dispatch — one Result each.
|
|
1148
1195
|
|
|
1149
1196
|
Families with a batched compute path process every utterance in a single
|
|
@@ -1160,7 +1207,10 @@ class Session:
|
|
|
1160
1207
|
view (``Result`` or ``TranscribeError`` each) so completed work is
|
|
1161
1208
|
never discarded. With ``return_exceptions=True`` no exception is
|
|
1162
1209
|
raised for utterance failures and that mixed list is returned
|
|
1163
|
-
directly (the ``asyncio.gather`` convention).
|
|
1210
|
+
directly (the ``asyncio.gather`` convention).
|
|
1211
|
+
|
|
1212
|
+
``vocabulary`` / ``prompt`` apply to every utterance (see :meth:`run`);
|
|
1213
|
+
a transcript prefix is per-utterance and so is not accepted here."""
|
|
1164
1214
|
self._cancel.clear()
|
|
1165
1215
|
pcms = list(pcms)
|
|
1166
1216
|
if not pcms:
|
|
@@ -1176,7 +1226,8 @@ class Session:
|
|
|
1176
1226
|
counts[k] = n
|
|
1177
1227
|
|
|
1178
1228
|
params = _build_run_params(task, language, target_language, timestamps,
|
|
1179
|
-
keep_special_tags, spec_k_drafts, diarize, pnc, itn
|
|
1229
|
+
keep_special_tags, spec_k_drafts, diarize, pnc, itn,
|
|
1230
|
+
vocabulary, prompt)
|
|
1180
1231
|
ext = self._resolve_family(family, "run") if family is not None else None
|
|
1181
1232
|
if ext is not None:
|
|
1182
1233
|
params.family = ctypes.cast(
|
|
@@ -1231,19 +1282,24 @@ class Session:
|
|
|
1231
1282
|
diarize: Diarize = "default",
|
|
1232
1283
|
keep_special_tags: bool = False, commit_policy: CommitPolicy = "auto",
|
|
1233
1284
|
stable_prefix_agreement_n: int = 0,
|
|
1234
|
-
family: FamilyExtension | None = None
|
|
1285
|
+
family: FamilyExtension | None = None,
|
|
1286
|
+
vocabulary: Sequence[str] | None = None,
|
|
1287
|
+
prompt: str | None = None) -> Stream:
|
|
1235
1288
|
"""Begin streaming on this session and return a Stream to feed audio to.
|
|
1236
1289
|
|
|
1237
1290
|
Requires a model whose capabilities advertise ``supports_streaming``;
|
|
1238
1291
|
otherwise raises NotImplementedByModel. ``family`` is an optional
|
|
1239
1292
|
family-specific stream extension (e.g. MoonshineStreamingOptions). The
|
|
1240
1293
|
session is single-threaded and runs at most one stream at a time. Use
|
|
1241
|
-
the Stream as a context manager so it is reset when you are done.
|
|
1294
|
+
the Stream as a context manager so it is reset when you are done.
|
|
1295
|
+
``vocabulary`` and ``prompt`` are as in :meth:`run`; ``task="instruct"``
|
|
1296
|
+
raises."""
|
|
1242
1297
|
self._cancel.clear()
|
|
1243
1298
|
# spec_k_drafts is an offline-decode knob; streaming always uses the
|
|
1244
1299
|
# family default (-1).
|
|
1245
1300
|
run_params = _build_run_params(task, language, target_language, timestamps,
|
|
1246
|
-
keep_special_tags, -1, diarize, pnc, itn
|
|
1301
|
+
keep_special_tags, -1, diarize, pnc, itn,
|
|
1302
|
+
vocabulary, prompt)
|
|
1247
1303
|
sp = _StreamParams()
|
|
1248
1304
|
_lib.transcribe_stream_params_init(_byref(sp))
|
|
1249
1305
|
sp.commit_policy = _enum(_COMMIT_POLICIES, commit_policy, "commit_policy")
|
|
@@ -1496,6 +1552,9 @@ def transcribe(
|
|
|
1496
1552
|
keep_special_tags: bool = False,
|
|
1497
1553
|
spec_k_drafts: int = -1,
|
|
1498
1554
|
family: FamilyExtension | None = None,
|
|
1555
|
+
vocabulary: Sequence[str] | None = None,
|
|
1556
|
+
prompt: str | None = None,
|
|
1557
|
+
prefix: str | None = None,
|
|
1499
1558
|
) -> Result:
|
|
1500
1559
|
"""Transcribe *pcm* in one call and return a materialized Result.
|
|
1501
1560
|
|
|
@@ -1504,13 +1563,15 @@ def transcribe(
|
|
|
1504
1563
|
many clips keep a Model and call ``model.session().run(...)`` yourself; this
|
|
1505
1564
|
helper is for the one-shot case. ``backend`` / ``device`` apply only when
|
|
1506
1565
|
*model* is a path — they are ignored when an already-loaded Model is passed.
|
|
1507
|
-
``family`` / ``spec_k_drafts``
|
|
1566
|
+
``family`` / ``spec_k_drafts`` and the prompting inputs (``vocabulary``,
|
|
1567
|
+
``prompt``, ``prefix``) pass through to :meth:`Session.run`.
|
|
1508
1568
|
"""
|
|
1509
1569
|
session_opts = dict(n_threads=n_threads, kv_type=kv_type, n_ctx=n_ctx)
|
|
1510
1570
|
run_opts = dict(task=task, language=language, target_language=target_language,
|
|
1511
1571
|
timestamps=timestamps, pnc=pnc, itn=itn, diarize=diarize,
|
|
1512
1572
|
keep_special_tags=keep_special_tags,
|
|
1513
|
-
spec_k_drafts=spec_k_drafts, family=family
|
|
1573
|
+
spec_k_drafts=spec_k_drafts, family=family,
|
|
1574
|
+
vocabulary=vocabulary, prompt=prompt, prefix=prefix)
|
|
1514
1575
|
|
|
1515
1576
|
if isinstance(model, Model):
|
|
1516
1577
|
with model.session(**session_opts) as session:
|
|
@@ -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 = "57b1af43650f195d"
|
|
17
17
|
|
|
18
18
|
# === enum constants ===
|
|
19
19
|
TRANSCRIBE_OK = 0
|
|
@@ -35,6 +35,7 @@ TRANSCRIBE_ERR_UNSUPPORTED_PNC = 15
|
|
|
35
35
|
TRANSCRIBE_ERR_UNSUPPORTED_ITN = 16
|
|
36
36
|
TRANSCRIBE_ERR_INPUT_TOO_LONG = 17
|
|
37
37
|
TRANSCRIBE_ERR_OUTPUT_TRUNCATED = 18
|
|
38
|
+
TRANSCRIBE_ERR_OUTPUT_REPETITION = 19
|
|
38
39
|
TRANSCRIBE_ABI_MODEL_LOAD_PARAMS = 0
|
|
39
40
|
TRANSCRIBE_ABI_SESSION_PARAMS = 1
|
|
40
41
|
TRANSCRIBE_ABI_RUN_PARAMS = 2
|
|
@@ -50,6 +51,7 @@ TRANSCRIBE_ABI_SESSION_LIMITS = 11
|
|
|
50
51
|
TRANSCRIBE_ABI_EXT = 12
|
|
51
52
|
TRANSCRIBE_ABI_DEVICE_INFO = 13
|
|
52
53
|
TRANSCRIBE_ABI_SPEAKER_SEGMENT = 14
|
|
54
|
+
TRANSCRIBE_ABI_BACKEND_INIT_PARAMS = 15
|
|
53
55
|
TRANSCRIBE_LOG_LEVEL_NONE = 0
|
|
54
56
|
TRANSCRIBE_LOG_LEVEL_INFO = 1
|
|
55
57
|
TRANSCRIBE_LOG_LEVEL_WARN = 2
|
|
@@ -58,6 +60,7 @@ TRANSCRIBE_LOG_LEVEL_DEBUG = 4
|
|
|
58
60
|
TRANSCRIBE_LOG_LEVEL_CONT = 5
|
|
59
61
|
TRANSCRIBE_TASK_TRANSCRIBE = 0
|
|
60
62
|
TRANSCRIBE_TASK_TRANSLATE = 1
|
|
63
|
+
TRANSCRIBE_TASK_INSTRUCT = 2
|
|
61
64
|
TRANSCRIBE_TIMESTAMPS_NONE = 0
|
|
62
65
|
TRANSCRIBE_TIMESTAMPS_AUTO = 1
|
|
63
66
|
TRANSCRIBE_TIMESTAMPS_SEGMENT = 2
|
|
@@ -95,6 +98,10 @@ TRANSCRIBE_FEATURE_CANCELLATION = 3
|
|
|
95
98
|
TRANSCRIBE_FEATURE_PNC = 4
|
|
96
99
|
TRANSCRIBE_FEATURE_ITN = 5
|
|
97
100
|
TRANSCRIBE_FEATURE_DIARIZATION = 6
|
|
101
|
+
TRANSCRIBE_FEATURE_VOCABULARY = 7
|
|
102
|
+
TRANSCRIBE_FEATURE_CONTEXT_PROMPT = 8
|
|
103
|
+
TRANSCRIBE_FEATURE_INSTRUCT = 9
|
|
104
|
+
TRANSCRIBE_FEATURE_TRANSCRIPT_PREFIX = 10
|
|
98
105
|
TRANSCRIBE_STREAM_IDLE = 0
|
|
99
106
|
TRANSCRIBE_STREAM_ACTIVE = 1
|
|
100
107
|
TRANSCRIBE_STREAM_FINISHED = 2
|
|
@@ -110,6 +117,13 @@ TRANSCRIBE_WHISPER_PROMPT_FIRST_SEGMENT = 0
|
|
|
110
117
|
TRANSCRIBE_WHISPER_PROMPT_ALL_SEGMENTS = 1
|
|
111
118
|
|
|
112
119
|
# === macro constants (integer object-like macros) ===
|
|
120
|
+
TRANSCRIBE_BACKEND_MASK_ALL = 4294967295
|
|
121
|
+
TRANSCRIBE_BACKEND_MASK_CPU = 1
|
|
122
|
+
TRANSCRIBE_BACKEND_MASK_CUDA = 8
|
|
123
|
+
TRANSCRIBE_BACKEND_MASK_METAL = 2
|
|
124
|
+
TRANSCRIBE_BACKEND_MASK_OTHER = 2147483648
|
|
125
|
+
TRANSCRIBE_BACKEND_MASK_ROCM = 16
|
|
126
|
+
TRANSCRIBE_BACKEND_MASK_VULKAN = 4
|
|
113
127
|
TRANSCRIBE_EXT_KIND_MOONSHINE_STREAMING_STREAM = 1414746957
|
|
114
128
|
TRANSCRIBE_EXT_KIND_PARAKEET_BUFFERED_STREAM = 1396853584
|
|
115
129
|
TRANSCRIBE_EXT_KIND_PARAKEET_STREAM = 1414744912
|
|
@@ -120,6 +134,8 @@ TRANSCRIBE_EXT_KIND_WHISPER_RUN = 1314015319
|
|
|
120
134
|
# === structs ===
|
|
121
135
|
class transcribe_ext(_c.Structure):
|
|
122
136
|
pass
|
|
137
|
+
class transcribe_backend_init_params(_c.Structure):
|
|
138
|
+
pass
|
|
123
139
|
class transcribe_device_info(_c.Structure):
|
|
124
140
|
pass
|
|
125
141
|
class transcribe_model_load_params(_c.Structure):
|
|
@@ -164,10 +180,11 @@ class transcribe_whisper_chunk_trace(_c.Structure):
|
|
|
164
180
|
pass
|
|
165
181
|
|
|
166
182
|
transcribe_ext._fields_ = [("size", _c.c_uint64), ("kind", _c.c_uint32)]
|
|
183
|
+
transcribe_backend_init_params._fields_ = [("struct_size", _c.c_uint64), ("artifact_dir", _c.c_char_p), ("allowed_backends", _c.c_uint32)]
|
|
167
184
|
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
185
|
transcribe_model_load_params._fields_ = [("struct_size", _c.c_uint64), ("backend", _c.c_int), ("device", _c.c_void_p)]
|
|
169
186
|
transcribe_session_params._fields_ = [("struct_size", _c.c_uint64), ("n_threads", _c.c_int), ("kv_type", _c.c_int), ("n_ctx", _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)]
|
|
187
|
+
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), ("vocabulary", _c.POINTER(_c.c_char_p)), ("n_vocabulary", _c.c_int32), ("prompt", _c.c_char_p), ("prefix", _c.c_char_p)]
|
|
171
188
|
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))]
|
|
172
189
|
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)]
|
|
173
190
|
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)]
|
|
@@ -190,6 +207,7 @@ transcribe_whisper_chunk_trace._fields_ = [("struct_size", _c.c_uint64), ("t0_ms
|
|
|
190
207
|
# transcribe_abi_struct id per struct (for the native size/align check).
|
|
191
208
|
ABI_STRUCT_IDS = {
|
|
192
209
|
'transcribe_ext': 12,
|
|
210
|
+
'transcribe_backend_init_params': 15,
|
|
193
211
|
'transcribe_device_info': 13,
|
|
194
212
|
'transcribe_model_load_params': 0,
|
|
195
213
|
'transcribe_session_params': 1,
|
|
@@ -209,10 +227,11 @@ ABI_STRUCT_IDS = {
|
|
|
209
227
|
# C-compiler layout captured at generation (for offset self-check).
|
|
210
228
|
STRUCT_LAYOUT = {
|
|
211
229
|
'transcribe_ext': {'size': 16, 'align': 8, 'offsets': {'size': 0, 'kind': 8}},
|
|
230
|
+
'transcribe_backend_init_params': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'artifact_dir': 8, 'allowed_backends': 16}},
|
|
212
231
|
'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
232
|
'transcribe_model_load_params': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'backend': 8, 'device': 16}},
|
|
214
233
|
'transcribe_session_params': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'n_threads': 8, 'kv_type': 12, 'n_ctx': 16}},
|
|
215
|
-
'transcribe_run_params': {'size':
|
|
234
|
+
'transcribe_run_params': {'size': 104, '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, 'vocabulary': 72, 'n_vocabulary': 80, 'prompt': 88, 'prefix': 96}},
|
|
216
235
|
'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}},
|
|
217
236
|
'transcribe_session_limits': {'size': 32, 'align': 8, 'offsets': {'struct_size': 0, 'effective_n_ctx': 8, 'effective_max_audio_ms': 16, 'max_kv_bytes': 24}},
|
|
218
237
|
'transcribe_stream_params': {'size': 24, 'align': 8, 'offsets': {'struct_size': 0, 'family': 8, 'commit_policy': 16, 'stable_prefix_agreement_n': 20}},
|
|
@@ -239,8 +258,12 @@ def configure(lib):
|
|
|
239
258
|
lib.transcribe_abi_struct_align.argtypes = [_c.c_int]
|
|
240
259
|
lib.transcribe_abi_struct_size.restype = _c.c_size_t
|
|
241
260
|
lib.transcribe_abi_struct_size.argtypes = [_c.c_int]
|
|
261
|
+
lib.transcribe_allowed_backends.restype = _c.c_uint32
|
|
262
|
+
lib.transcribe_allowed_backends.argtypes = []
|
|
242
263
|
lib.transcribe_backend_available.restype = _c.c_bool
|
|
243
264
|
lib.transcribe_backend_available.argtypes = [_c.c_int]
|
|
265
|
+
lib.transcribe_backend_init_params_init.restype = None
|
|
266
|
+
lib.transcribe_backend_init_params_init.argtypes = [_c.POINTER(transcribe_backend_init_params)]
|
|
244
267
|
lib.transcribe_batch_detected_language.restype = _c.c_char_p
|
|
245
268
|
lib.transcribe_batch_detected_language.argtypes = [_c.c_void_p, _c.c_int]
|
|
246
269
|
lib.transcribe_batch_full_text.restype = _c.c_char_p
|
|
@@ -309,6 +332,8 @@ def configure(lib):
|
|
|
309
332
|
lib.transcribe_init_backends.argtypes = [_c.c_char_p]
|
|
310
333
|
lib.transcribe_init_backends_default.restype = _c.c_int
|
|
311
334
|
lib.transcribe_init_backends_default.argtypes = []
|
|
335
|
+
lib.transcribe_init_backends_ex.restype = _c.c_int
|
|
336
|
+
lib.transcribe_init_backends_ex.argtypes = [_c.POINTER(transcribe_backend_init_params)]
|
|
312
337
|
lib.transcribe_log_set.restype = None
|
|
313
338
|
lib.transcribe_log_set.argtypes = [_c.CFUNCTYPE(None, _c.c_int, _c.c_char_p, _c.c_void_p), _c.c_void_p]
|
|
314
339
|
lib.transcribe_model_accepts_ext_kind.restype = _c.c_bool
|
|
@@ -22,6 +22,7 @@ from ._generated import (
|
|
|
22
22
|
TRANSCRIBE_ERR_INVALID_ARG as ERR_INVALID_ARG,
|
|
23
23
|
TRANSCRIBE_ERR_NOT_IMPLEMENTED as ERR_NOT_IMPLEMENTED,
|
|
24
24
|
TRANSCRIBE_ERR_OOM as ERR_OOM,
|
|
25
|
+
TRANSCRIBE_ERR_OUTPUT_REPETITION as ERR_OUTPUT_REPETITION,
|
|
25
26
|
TRANSCRIBE_ERR_OUTPUT_TRUNCATED as ERR_OUTPUT_TRUNCATED,
|
|
26
27
|
TRANSCRIBE_ERR_SAMPLE_RATE as ERR_SAMPLE_RATE,
|
|
27
28
|
TRANSCRIBE_ERR_UNSUPPORTED_ARCH as ERR_UNSUPPORTED_ARCH,
|
|
@@ -113,6 +114,17 @@ class OutputTruncated(TranscribeError):
|
|
|
113
114
|
partial_result: "Optional[Result]" = None
|
|
114
115
|
|
|
115
116
|
|
|
117
|
+
class OutputRepetition(OutputTruncated):
|
|
118
|
+
"""The decode was stopped because the output began repeating itself —
|
|
119
|
+
the transcript is incomplete by contract.
|
|
120
|
+
|
|
121
|
+
A subclass of :class:`OutputTruncated`, so a handler for incomplete
|
|
122
|
+
transcripts catches both. ``partial_result`` holds the partial transcript
|
|
123
|
+
with the repeats dropped (one copy kept), or None when the status surfaced
|
|
124
|
+
outside a result-bearing call.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
|
|
116
128
|
_STATUS_TO_EXC = {
|
|
117
129
|
ERR_INVALID_ARG: InvalidArgument,
|
|
118
130
|
ERR_NOT_IMPLEMENTED: NotImplementedByModel,
|
|
@@ -132,6 +144,7 @@ _STATUS_TO_EXC = {
|
|
|
132
144
|
ERR_UNSUPPORTED_ITN: UnsupportedRequest,
|
|
133
145
|
ERR_INPUT_TOO_LONG: InputTooLong,
|
|
134
146
|
ERR_OUTPUT_TRUNCATED: OutputTruncated,
|
|
147
|
+
ERR_OUTPUT_REPETITION: OutputRepetition,
|
|
135
148
|
}
|
|
136
149
|
|
|
137
150
|
|
|
@@ -41,6 +41,7 @@ def test_every_status_maps_to_documented_subclass():
|
|
|
41
41
|
errors.ERR_UNSUPPORTED_ITN: t.UnsupportedRequest,
|
|
42
42
|
errors.ERR_INPUT_TOO_LONG: t.InputTooLong,
|
|
43
43
|
errors.ERR_OUTPUT_TRUNCATED: t.OutputTruncated,
|
|
44
|
+
errors.ERR_OUTPUT_REPETITION: t.OutputRepetition,
|
|
44
45
|
}
|
|
45
46
|
# The mapping table covers every non-OK status the header defines, and
|
|
46
47
|
# nothing else (a new C status must be mapped deliberately, not by
|
|
@@ -64,6 +65,14 @@ def test_unknown_status_degrades_to_base_class():
|
|
|
64
65
|
assert exc.status == 999
|
|
65
66
|
|
|
66
67
|
|
|
68
|
+
def test_output_repetition_is_an_output_truncated():
|
|
69
|
+
# A handler that keeps the partial of an incomplete transcript catches both.
|
|
70
|
+
exc = errors.exception_for_status(errors.ERR_OUTPUT_REPETITION, "looped", "run")
|
|
71
|
+
assert isinstance(exc, t.OutputRepetition)
|
|
72
|
+
assert isinstance(exc, t.OutputTruncated)
|
|
73
|
+
assert exc.partial_result is None
|
|
74
|
+
|
|
75
|
+
|
|
67
76
|
def test_exception_for_status_builds_without_raising():
|
|
68
77
|
exc = errors.exception_for_status(errors.ERR_ABORTED, "aborted", "run")
|
|
69
78
|
assert isinstance(exc, t.Aborted)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Generic prompting inputs: run-params marshalling and model-gated behavior."""
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
import transcribe_cpp as t
|
|
8
|
+
from transcribe_cpp import _generated
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_run_params_carry_prompting_fields():
|
|
12
|
+
params = t._build_run_params("instruct", None, None, "none", False, -1,
|
|
13
|
+
vocabulary=["GGUF", "ggml"], prompt="Summarize.",
|
|
14
|
+
prefix="And so")
|
|
15
|
+
assert params.task == _generated.TRANSCRIBE_TASK_INSTRUCT
|
|
16
|
+
assert params.n_vocabulary == 2
|
|
17
|
+
assert [params.vocabulary[i] for i in range(2)] == [b"GGUF", b"ggml"]
|
|
18
|
+
assert params.prompt == b"Summarize."
|
|
19
|
+
assert params.prefix == b"And so"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_run_params_default_to_no_prompting():
|
|
23
|
+
params = t._build_run_params("transcribe", None, None, "auto", False, -1)
|
|
24
|
+
assert params.n_vocabulary == 0
|
|
25
|
+
assert not params.vocabulary
|
|
26
|
+
assert params.prompt is None and params.prefix is None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_vocabulary_rejects_single_string():
|
|
30
|
+
with pytest.raises(t.InvalidArgument):
|
|
31
|
+
t._build_run_params("transcribe", None, None, "auto", False, -1, vocabulary="GGUF")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_nul_in_string_option_is_rejected():
|
|
35
|
+
for kwargs in ({"prompt": "a\x00b"}, {"prefix": "a\x00b"}, {"vocabulary": ["a\x00b"]}):
|
|
36
|
+
with pytest.raises(t.InvalidArgument):
|
|
37
|
+
t._build_run_params("transcribe", None, None, "auto", False, -1, **kwargs)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_vocabulary_accepts_iterators_rejects_sets():
|
|
41
|
+
params = t._build_run_params("transcribe", None, None, "auto", False, -1,
|
|
42
|
+
vocabulary=(w for w in ["GGUF", "ggml"]))
|
|
43
|
+
assert params.n_vocabulary == 2
|
|
44
|
+
with pytest.raises(t.InvalidArgument):
|
|
45
|
+
t._build_run_params("transcribe", None, None, "auto", False, -1, vocabulary={"GGUF"})
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_prompting_features_probe(model_path):
|
|
49
|
+
with t.Model(model_path, backend="cpu") as model:
|
|
50
|
+
for feature in ("vocabulary", "context_prompt", "instruct", "transcript_prefix"):
|
|
51
|
+
assert isinstance(model.supports(feature), bool)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_unsupported_prefix_raises(streaming_model_path, audio_pcm):
|
|
55
|
+
with t.Model(streaming_model_path, backend="cpu") as model, model.session() as session:
|
|
56
|
+
if model.supports("transcript_prefix"):
|
|
57
|
+
pytest.skip("model supports a transcript prefix")
|
|
58
|
+
with pytest.raises(t.InvalidArgument):
|
|
59
|
+
session.run(audio_pcm, prefix="And so")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_stream_prompting(streaming_model_path):
|
|
63
|
+
with t.Model(streaming_model_path, backend="cpu") as model, model.session() as session:
|
|
64
|
+
with pytest.raises(t.UnsupportedRequest):
|
|
65
|
+
session.stream(task="instruct", prompt="Summarize.")
|
|
66
|
+
with session.stream(vocabulary=["Kennedy", "Americans"], prompt="A speech."):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _whisper(model):
|
|
71
|
+
return model.arch == "whisper"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_whisper_prefix_contract(model_path, audio_pcm):
|
|
75
|
+
"""The prefix is forced decoder text: text holds only the continuation,
|
|
76
|
+
raw_text leads with the prefix, and nothing is duplicated."""
|
|
77
|
+
prefix = "And so my fellow Americans,"
|
|
78
|
+
with t.Model(model_path, backend="cpu") as model, model.session() as session:
|
|
79
|
+
if not _whisper(model):
|
|
80
|
+
pytest.skip("whisper-specific rendering")
|
|
81
|
+
assert model.supports("transcript_prefix")
|
|
82
|
+
res = session.run(audio_pcm, prefix=prefix)
|
|
83
|
+
assert res.raw_text.strip().startswith(prefix)
|
|
84
|
+
assert not res.text.lower().startswith("and so")
|
|
85
|
+
assert "ask not" in res.text.lower()
|
|
86
|
+
# Whisper's timestamp rules do not compose with a prefix.
|
|
87
|
+
with t.Model(model_path, backend="cpu") as model, model.session() as session:
|
|
88
|
+
with pytest.raises(t.InvalidArgument):
|
|
89
|
+
session.run(audio_pcm, prefix=prefix, timestamps="segment")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_whisper_vocabulary_and_context(model_path, audio_pcm):
|
|
93
|
+
with t.Model(model_path, backend="cpu") as model, model.session() as session:
|
|
94
|
+
if not _whisper(model):
|
|
95
|
+
pytest.skip("whisper-specific rendering")
|
|
96
|
+
assert model.supports("vocabulary") and model.supports("context_prompt")
|
|
97
|
+
res = session.run(audio_pcm, vocabulary=["Kennedy", "Americans"],
|
|
98
|
+
prompt="An inaugural address.")
|
|
99
|
+
assert "country" in res.text.lower()
|
|
100
|
+
batch = session.run_batch([audio_pcm, audio_pcm], vocabulary=["Kennedy", "Americans"])
|
|
101
|
+
assert len(batch) == 2 and all("country" in r.text.lower() for r in batch)
|
|
102
|
+
# The whisper extension's prompt and the generic fields share one slot.
|
|
103
|
+
with pytest.raises(t.InvalidArgument):
|
|
104
|
+
session.run(audio_pcm, vocabulary=["Kennedy"],
|
|
105
|
+
family=t.WhisperRunOptions(initial_prompt="x"))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_control_token_literal_rejected(model_path, audio_pcm):
|
|
109
|
+
"""A control-token literal is rejected and leaves the previous result."""
|
|
110
|
+
with t.Model(model_path, backend="cpu") as model, model.session() as session:
|
|
111
|
+
if not _whisper(model):
|
|
112
|
+
pytest.skip("whisper-specific rendering")
|
|
113
|
+
first = session.run(audio_pcm)
|
|
114
|
+
with pytest.raises(t.InvalidArgument):
|
|
115
|
+
session.run(audio_pcm, prompt="hello <|endoftext|>")
|
|
116
|
+
assert session._materialize().text == first.text
|
|
@@ -387,7 +387,7 @@ wheels = [
|
|
|
387
387
|
|
|
388
388
|
[[package]]
|
|
389
389
|
name = "transcribe-cpp"
|
|
390
|
-
version = "0.
|
|
390
|
+
version = "0.3.1"
|
|
391
391
|
source = { editable = "." }
|
|
392
392
|
|
|
393
393
|
[package.optional-dependencies]
|
|
@@ -403,8 +403,8 @@ test = [
|
|
|
403
403
|
requires-dist = [
|
|
404
404
|
{ name = "numpy", marker = "extra == 'test'" },
|
|
405
405
|
{ name = "pytest", marker = "extra == 'test'", specifier = ">=7" },
|
|
406
|
-
{ name = "transcribe-cpp-native", specifier = "==0.
|
|
407
|
-
{ name = "transcribe-cpp-native-cu12", marker = "extra == 'cu12'", specifier = "==0.
|
|
406
|
+
{ name = "transcribe-cpp-native", specifier = "==0.3.1.*" },
|
|
407
|
+
{ name = "transcribe-cpp-native-cu12", marker = "extra == 'cu12'", specifier = "==0.3.1.*" },
|
|
408
408
|
]
|
|
409
409
|
provides-extras = ["cu12", "test"]
|
|
410
410
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|