Call the OpenRouter subtitle model the audio file model

Timestamps are a file transcription option, so the model box is named
after the file rather than the format it ends up in.
This commit is contained in:
2026-09-02 12:43:07 +03:00
parent 6e307bd8d0
commit eda1398a2b
7 changed files with 48 additions and 48 deletions
+7 -7
View File
@@ -48,17 +48,17 @@ LOCAL_TIMEOUT = 3600
# Where a transcription request goes; built by config.Config.transcribe_target().
# `service` is the name the user sees in an error, `provider` the one the code
# branches on. `subtitle_model` is what a timestamped run asks for instead of
# branches on. `file_model` is what a timestamped run asks for instead of
# `model`, where the two differ; empty means the provider's own whisper.
Target = collections.namedtuple(
"Target", "provider service api_key base_url model subtitle_model",
"Target", "provider service api_key base_url model file_model",
defaults=[""])
# What answers with segment times on OpenRouter when nothing else was chosen.
OPENROUTER_SUBTITLE_MODEL = "openai/whisper-1"
OPENROUTER_FILE_MODEL = "openai/whisper-1"
def timestamp_model(provider, selected="", subtitle=""):
def timestamp_model(provider, selected="", file_model=""):
"""Which model answers with segment times.
OpenAI keeps them to whisper-1. Everything Groq transcribes with is a
@@ -68,12 +68,12 @@ def timestamp_model(provider, selected="", subtitle=""):
one it has never heard of. OpenRouter fronts several models that do times
and several that do not, and a request to the wrong one gets a transcript
with no segments in it, so the one to use is a setting of its own
(`subtitle`) and whisper-1 is only where that setting is left empty.
(`file_model`) and whisper-1 is only where that setting is left empty.
"""
if provider in ("groq", "local"):
return selected or "whisper-large-v3-turbo"
if provider == "openrouter":
return subtitle or OPENROUTER_SUBTITLE_MODEL
return file_model or OPENROUTER_FILE_MODEL
return "whisper-1"
@@ -456,7 +456,7 @@ def transcribe_segments(target, audio_path, language="", prompt="", timeout=300,
"""[(start_seconds, end_seconds, text)] using whisper-1's verbose response."""
data = _transcribe_request(
target._replace(model=timestamp_model(target.provider, target.model,
target.subtitle_model)),
target.file_model)),
audio_path, language, prompt, "verbose_json",
granularity="segment", timeout=timeout, aborter=aborter,
)
+3 -3
View File
@@ -399,7 +399,7 @@ DEFAULTS = {
"openrouter_transcribe_model": "openai/gpt-4o-transcribe",
# What a timestamped run (subtitles) asks OpenRouter for: not every model
# there returns segment times. Empty -> openai/whisper-1.
"openrouter_subtitle_model": "",
"openrouter_file_model": "",
"language": "tr",
"transcribe_prompt": "",
@@ -672,9 +672,9 @@ class Config:
# to land on rather than reading it from there.
name = "openai"
who = TRANSCRIBERS[name]
subtitle = self["openrouter_subtitle_model"] if name == "openrouter" else ""
file_model = self["openrouter_file_model"] if name == "openrouter" else ""
return api.Target(name, who.service, self.api_key(who.key),
self[who.url], self[who.model], subtitle.strip())
self[who.url], self[who.model], file_model.strip())
def transcribe_ready(self):
"""Whether speech to text could run right now, without opening Settings."""
+3 -3
View File
@@ -228,10 +228,10 @@ TR = {
"Transcript cleanup": "Transkripti temizleme",
"API key": "API anahtarı",
"Model": "Model",
"Subtitle model": "Altyazı modeli",
"The model a timestamped run (subtitles) asks for. Not every model on "
"Audio file model": "Ses dosyası modeli",
"The model a timestamped audio file (subtitles) is sent to. Not every model on "
"OpenRouter returns segment times; empty means openai/whisper-1.":
"Zaman damgalı bir çeviride (altyazı) istenen model. OpenRouter'daki her "
"Zaman damgalı bir ses dosyasının (altyazı) gönderildiği model. OpenRouter'daki her "
"model segment zamanı döndürmez; boşsa openai/whisper-1 kullanılır.",
"Provider": "Sağlayıcı",
"sk-… (falls back to OPENAI_API_KEY)": "sk-… (boşsa OPENAI_API_KEY kullanılır)",
+17 -17
View File
@@ -865,14 +865,14 @@ class SettingsWindow(QDialog):
self.refresh_transcribe_models)
stt_form.addRow(t("Model"), self.transcribe_model_row)
# OpenRouter only: which of its models a timestamped run asks for.
self.subtitle_model = QComboBox()
self.subtitle_model.setEditable(True)
self.subtitle_model.lineEdit().setPlaceholderText(api.OPENROUTER_SUBTITLE_MODEL)
self.subtitle_model.setToolTip(
t("The model a timestamped run (subtitles) asks for. Not every model "
self.file_model = QComboBox()
self.file_model.setEditable(True)
self.file_model.lineEdit().setPlaceholderText(api.OPENROUTER_FILE_MODEL)
self.file_model.setToolTip(
t("The model a timestamped audio file (subtitles) is sent to. Not every model "
"on OpenRouter returns segment times; empty means openai/whisper-1."))
self.subtitle_model_row = self._row(self.subtitle_model)
stt_form.addRow(t("Subtitle model"), self.subtitle_model_row)
self.file_model_row = self._row(self.file_model)
stt_form.addRow(t("Audio file model"), self.file_model_row)
# A spanning row: in the narrow field column a wrapped label gets a
# height that fits one line, and the rest of the text is cut off.
self.transcribe_status = QLabel("")
@@ -1756,7 +1756,7 @@ class SettingsWindow(QDialog):
self._shown_provider = ""
self._select_data(self.transcribe_provider, conf["transcribe_provider"])
self._provider_changed() # selecting index 0 fires no signal
self.subtitle_model.setCurrentText(conf["openrouter_subtitle_model"])
self.file_model.setCurrentText(conf["openrouter_file_model"])
self.local_gpu.setChecked(conf["local_gpu"])
self.local_preload.setChecked(conf["local_preload"])
self.local_threads.setValue(int(conf["local_threads"]))
@@ -1874,7 +1874,7 @@ class SettingsWindow(QDialog):
for name, who in cfg.TRANSCRIBERS.items():
conf[who.key] = self._key_fields[name].text().strip()
conf[who.model] = self._models[name].strip() or cfg.DEFAULTS[who.model]
conf["openrouter_subtitle_model"] = self.subtitle_model.currentText().strip()
conf["openrouter_file_model"] = self.file_model.currentText().strip()
conf["gemini_api_key"] = self.gemini_key.text().strip()
conf["opencode_api_key"] = self.opencode_key.text().strip()
conf["local_model"] = self.local_whisper.selected()
@@ -2048,7 +2048,7 @@ class SettingsWindow(QDialog):
self._shown_provider = provider
local = provider == "local"
self.stt_form.setRowVisible(self.transcribe_model_row, not local)
self.stt_form.setRowVisible(self.subtitle_model_row, provider == "openrouter")
self.stt_form.setRowVisible(self.file_model_row, provider == "openrouter")
self.stt_form.setRowVisible(self.transcribe_status, not local)
self.stt_form.setRowVisible(self.local_whisper, local)
self.stt_form.setRowVisible(self.local_options, local)
@@ -2058,14 +2058,14 @@ class SettingsWindow(QDialog):
self.transcribe_model.addItems(TRANSCRIBE_MODELS[provider])
self.transcribe_model.setCurrentText(self._models[provider])
if provider == "openrouter":
self._fill_subtitle_models(TRANSCRIBE_MODELS[provider])
self._fill_file_models(TRANSCRIBE_MODELS[provider])
self.transcribe_status.setText("")
def _fill_subtitle_models(self, models):
current = self.subtitle_model.currentText()
self.subtitle_model.clear()
self.subtitle_model.addItems(models)
self.subtitle_model.setCurrentText(current)
def _fill_file_models(self, models):
current = self.file_model.currentText()
self.file_model.clear()
self.file_model.addItems(models)
self.file_model.setCurrentText(current)
def _load_transcribe_models(self):
"""The model list of whichever provider is selected."""
@@ -2096,7 +2096,7 @@ class SettingsWindow(QDialog):
self.transcribe_model.addItems(models)
self.transcribe_model.setCurrentText(current)
if self._shown_provider == "openrouter":
self._fill_subtitle_models(models)
self._fill_file_models(models)
self.transcribe_status.setText(t("{count} models loaded.", count=len(models)))
def _load_models(self):
+4 -4
View File
@@ -53,13 +53,13 @@ class TimestampModel(unittest.TestCase):
self.assertEqual(api.timestamp_model("openai", "gpt-4o-transcribe"),
"whisper-1")
def test_openrouter_takes_the_subtitle_model_that_was_set(self):
def test_openrouter_takes_the_file_model_that_was_set(self):
self.assertEqual(
api.timestamp_model("openrouter", "openai/gpt-4o-transcribe",
"openai/whisper-large-v3"),
"openai/whisper-large-v3")
def test_openrouter_with_no_subtitle_model_falls_back_to_whisper(self):
def test_openrouter_with_no_file_model_falls_back_to_whisper(self):
self.assertEqual(api.timestamp_model("openrouter", "openai/gpt-4o-transcribe", ""),
"openai/whisper-1")
@@ -328,8 +328,8 @@ class TranscribeSegments(DikteTest):
api.transcribe_segments(OPENROUTER, self.wav)
self.assertEqual(multipart_fields(calls[0])["model"], "openai/whisper-1")
def test_openrouter_asks_for_the_subtitle_model_when_one_is_set(self):
target = OPENROUTER._replace(subtitle_model="mistralai/voxtral-mini-transcribe")
def test_openrouter_asks_for_the_file_model_when_one_is_set(self):
target = OPENROUTER._replace(file_model="mistralai/voxtral-mini-transcribe")
with fake_urlopen(self.reply([{"start": 0, "end": 1, "text": "hi"}])) as calls:
api.transcribe_segments(target, self.wav)
self.assertEqual(multipart_fields(calls[0])["model"],
+7 -7
View File
@@ -220,19 +220,19 @@ class TranscribeTarget(DikteTest):
self.assertEqual(target.service, "OpenRouter")
self.assertEqual(target.api_key, "sk-or-test")
self.assertEqual(target.model, "openai/whisper-1")
self.assertEqual(target.subtitle_model, "")
self.assertEqual(target.file_model, "")
def test_openrouter_carries_its_subtitle_model(self):
def test_openrouter_carries_its_file_model(self):
conf = self.config(transcribe_provider="openrouter",
openrouter_api_key="sk-or-test",
openrouter_subtitle_model=" openai/whisper-large-v3 ")
self.assertEqual(conf.transcribe_target().subtitle_model,
openrouter_file_model=" openai/whisper-large-v3 ")
self.assertEqual(conf.transcribe_target().file_model,
"openai/whisper-large-v3")
def test_only_openrouter_has_a_subtitle_model(self):
def test_only_openrouter_has_a_file_model(self):
conf = self.config(transcribe_provider="openai", openai_api_key="sk-test",
openrouter_subtitle_model="openai/whisper-large-v3")
self.assertEqual(conf.transcribe_target().subtitle_model, "")
openrouter_file_model="openai/whisper-large-v3")
self.assertEqual(conf.transcribe_target().file_model, "")
def test_groq_when_it_is_picked(self):
conf = self.config(transcribe_provider="groq", groq_api_key="gsk-test",
+7 -7
View File
@@ -504,19 +504,19 @@ class Settings(DikteTest):
self.assertEqual(conf["transcribe_model"], "gpt-4o-transcribe")
self.assertEqual(conf["groq_transcribe_model"], "whisper-large-v3")
def test_the_subtitle_model_is_saved_and_only_shown_for_openrouter(self):
def test_the_file_model_is_saved_and_only_shown_for_openrouter(self):
self.write_config({"transcribe_provider": "openrouter",
"openrouter_subtitle_model": "openai/whisper-large-v3"})
"openrouter_file_model": "openai/whisper-large-v3"})
conf = cfg.Config()
window = self.window(conf)
self.assertEqual(window.subtitle_model.currentText(), "openai/whisper-large-v3")
self.assertTrue(window.stt_form.isRowVisible(window.subtitle_model_row))
window.subtitle_model.setCurrentText(" deepgram/nova-3 ")
self.assertEqual(window.file_model.currentText(), "openai/whisper-large-v3")
self.assertTrue(window.stt_form.isRowVisible(window.file_model_row))
window.file_model.setCurrentText(" deepgram/nova-3 ")
window._save()
self.assertEqual(conf["openrouter_subtitle_model"], "deepgram/nova-3")
self.assertEqual(conf["openrouter_file_model"], "deepgram/nova-3")
window.transcribe_provider.setCurrentIndex(
window.transcribe_provider.findData("openai"))
self.assertFalse(window.stt_form.isRowVisible(window.subtitle_model_row))
self.assertFalse(window.stt_form.isRowVisible(window.file_model_row))
def test_the_provider_box_offers_every_provider_config_knows(self):
window = self.window(cfg.Config())