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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dictation": false,
"profanity_filter": false,
"diarize": false,
"diarize_version": "",
"diarize_model": "",
"detect_entities": false,
"multichannel": false,
"utterances": false,
Expand Down
4 changes: 2 additions & 2 deletions static/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ function appData() {
dictation: false,
profanity_filter: false,
diarize: false,
diarize_version: '',
diarize_model: '',
detect_entities: false,
multichannel: false,
utterances: false,
Expand Down Expand Up @@ -1089,7 +1089,7 @@ function appData() {
dictation: false,
profanity_filter: false,
diarize: false,
diarize_version: '',
diarize_model: '',
detect_entities: false,
multichannel: false,
utterances: false,
Expand Down
16 changes: 16 additions & 0 deletions stt/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ def clean_params(params: dict, mode: Mode) -> dict:
continue
_put(result, wire, v)

# `diarize_model` is mutually exclusive with the deprecated `diarize` and
# `diarize_version`. Deepgram does not pick a winner, it rejects outright:
#
# 400 diarize_model cannot be used together with diarize or
# diarize_version. (INVALID_QUERY_PARAMETER)
#
# `diarize_model` wins here because it is the current parameter and the only
# way to reach the v2 diarizer: the boolean `diarize` ALWAYS routes to v1,
# and `diarize_version` is legacy. The UI already makes the two controls
# exclusive; this covers the `extra` escape hatch and any saved config from
# before the field was renamed, so an old preset degrades to the modern
# parameter instead of a hard 400.
if "diarize_model" in result:
for legacy in ("diarize", "diarize_version"):
result.pop(legacy, None)

return result


Expand Down
59 changes: 47 additions & 12 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,19 @@
letter-spacing: 0.06em;
}

.field-hint {
font-size: 11px;
line-height: 1.45;
color: #8b93a7;
margin-top: 5px;
}
.field-hint code {
background: #1a2234;
padding: 1px 4px;
border-radius: 3px;
color: #a5b4fc;
}

.field-input {
background: #1a1f2e;
border: 1px solid #2d3748;
Expand Down Expand Up @@ -1319,9 +1332,14 @@
</div>
<div x-show="sections.features" class="param-section-body">
<div class="checkbox-grid">
<!-- Deprecated boolean, kept because it is what legacy customer
configs send and replicating those is a job this tool has.
Always v1. Clears diarize_model because Deepgram 400s on the
pair. -->
<div class="checkbox-item">
<input type="checkbox" id="diarize" x-model="params.diarize">
<label for="diarize">Diarize</label>
<input type="checkbox" id="diarize" x-model="params.diarize"
@change="if (params.diarize) params.diarize_model = ''">
<label for="diarize" title="Deprecated; always selects the v1 diarizer">Diarize (v1, deprecated)</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="detect_entities" x-model="params.detect_entities">
Expand All @@ -1340,17 +1358,34 @@
</div>
</div>

<!-- Diarize version -->
<template x-if="params.diarize">
<div class="field mt-2">
<div class="field-label">Diarize Version</div>
<select class="field-select" x-model="params.diarize_version">
<option value="">Default</option>
<option value="2021-07-14.0">2021-07-14.0</option>
<option value="latest">latest</option>
</select>
<!-- Diarizer selection.
DO NOT re-gate this behind params.diarize. It used to be, and
that made v2 unreachable: `diarize` is the deprecated boolean
and ALWAYS routes to v1, while `diarize_model` both enables
diarization and picks the version, so it must be sent WITHOUT
`diarize`. Deepgram rejects the pair outright:
400 diarize_model cannot be used together with diarize or
diarize_version. (INVALID_QUERY_PARAMETER)
The two controls are mutually exclusive in the UI for that
reason; setting one clears the other. -->
<div class="field mt-2">
<div class="field-label">Diarizer (diarize_model)</div>
<select class="field-select" x-model="params.diarize_model"
@change="if (params.diarize_model) params.diarize = false">
<option value="">Off / use the deprecated Diarize checkbox</option>
<option value="v1">v1</option>
<option value="v2">v2 (batch only)</option>
<option value="latest">latest</option>
</select>
<div class="field-hint" x-show="params.diarize_model === 'v2' && mode !== 'batch'">
v2 is batch only. Deepgram rejects the WebSocket handshake for
<code>diarize_model=v2</code> on a stream; use <code>latest</code> there.
</div>
</template>
<div class="field-hint" x-show="params.diarize_model">
Sends <code>diarize_model</code> and omits <code>diarize</code>.
The deprecated <code>diarize</code> checkbox always selects v1.
</div>
</div>

<!-- Entity types when detect_entities enabled -->
<template x-if="params.detect_entities">
Expand Down
49 changes: 48 additions & 1 deletion tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,11 @@ def test_sdk_accepts_every_serialized_param_name():
# lost. Regression guard for the keyterms crash class. filler_words is NOT in
# this list: Deepgram's docs mark it stream-unavailable, so it is stripped in
# streaming mode by design — see test_stream_unavailable_params_are_stripped.
for name in ("no_delay", "word_confidence", "diarize_version", "entity_prompt"):
# `diarize_version` is deliberately absent from this list: the UI now models
# the current `diarize_model`, which deepgram-sdk 7 names as a real kwarg, so
# it rides via_kwargs rather than the passthrough. See
# test_diarize_model_is_a_named_sdk_kwarg_not_passthrough.
for name in ("no_delay", "word_confidence", "entity_prompt", "alternatives"):
assert name in via_query, f"{name} is not being forwarded"


Expand Down Expand Up @@ -283,3 +287,46 @@ def test_alias_cannot_smuggle_a_denied_wire_name():
options.PARAM_ALIASES = original

assert "callback" in DENIED_PARAMS


def test_diarize_model_wins_over_the_deprecated_diarize_params():
"""Deepgram rejects the combination outright rather than picking a winner:

400 diarize_model cannot be used together with diarize or
diarize_version. (INVALID_QUERY_PARAMETER)

Verified live. diarize_model is kept because it is the current parameter and
the only route to the v2 diarizer; the boolean `diarize` always selects v1.
"""
from stt.options import clean_params, Mode
out = clean_params(
{"model": "nova-3", "diarize": True, "extra": {"diarize_model": "v2"}},
Mode.BATCH,
)
assert out["diarize_model"] == "v2"
assert "diarize" not in out
assert "diarize_version" not in out

out = clean_params(
{"model": "nova-3", "extra": {"diarize_model": "latest", "diarize_version": "latest"}},
Mode.BATCH,
)
assert out["diarize_model"] == "latest"
assert "diarize_version" not in out


def test_deprecated_diarize_still_works_alone():
"""Legacy customer configs send the boolean, and replicating those is a job
this tool has. It must keep working when diarize_model is absent."""
from stt.options import clean_params, Mode
out = clean_params({"model": "nova-3", "diarize": True}, Mode.BATCH)
assert out["diarize"] is True
assert "diarize_model" not in out


def test_diarize_model_is_a_named_sdk_kwarg_not_passthrough():
"""deepgram-sdk 7 added diarize_model to connect(); it should ride as a real
kwarg rather than through additional_query_parameters."""
import app
built = app._params_to_sdk_kwargs({"model": "nova-3", "extra": {"diarize_model": "latest"}})
assert built.get("diarize_model") == "latest"