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(). # 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 # `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. # `model`, where the two differ; empty means the provider's own whisper.
Target = collections.namedtuple( Target = collections.namedtuple(
"Target", "provider service api_key base_url model subtitle_model", "Target", "provider service api_key base_url model file_model",
defaults=[""]) defaults=[""])
# What answers with segment times on OpenRouter when nothing else was chosen. # 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. """Which model answers with segment times.
OpenAI keeps them to whisper-1. Everything Groq transcribes with is a 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 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 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 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"): if provider in ("groq", "local"):
return selected or "whisper-large-v3-turbo" return selected or "whisper-large-v3-turbo"
if provider == "openrouter": if provider == "openrouter":
return subtitle or OPENROUTER_SUBTITLE_MODEL return file_model or OPENROUTER_FILE_MODEL
return "whisper-1" 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.""" """[(start_seconds, end_seconds, text)] using whisper-1's verbose response."""
data = _transcribe_request( data = _transcribe_request(
target._replace(model=timestamp_model(target.provider, target.model, target._replace(model=timestamp_model(target.provider, target.model,
target.subtitle_model)), target.file_model)),
audio_path, language, prompt, "verbose_json", audio_path, language, prompt, "verbose_json",
granularity="segment", timeout=timeout, aborter=aborter, granularity="segment", timeout=timeout, aborter=aborter,
) )
+3 -3
View File
@@ -399,7 +399,7 @@ DEFAULTS = {
"openrouter_transcribe_model": "openai/gpt-4o-transcribe", "openrouter_transcribe_model": "openai/gpt-4o-transcribe",
# What a timestamped run (subtitles) asks OpenRouter for: not every model # What a timestamped run (subtitles) asks OpenRouter for: not every model
# there returns segment times. Empty -> openai/whisper-1. # there returns segment times. Empty -> openai/whisper-1.
"openrouter_subtitle_model": "", "openrouter_file_model": "",
"language": "tr", "language": "tr",
"transcribe_prompt": "", "transcribe_prompt": "",
@@ -672,9 +672,9 @@ class Config:
# to land on rather than reading it from there. # to land on rather than reading it from there.
name = "openai" name = "openai"
who = TRANSCRIBERS[name] 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), 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): def transcribe_ready(self):
"""Whether speech to text could run right now, without opening Settings.""" """Whether speech to text could run right now, without opening Settings."""
+3 -3
View File
@@ -228,10 +228,10 @@ TR = {
"Transcript cleanup": "Transkripti temizleme", "Transcript cleanup": "Transkripti temizleme",
"API key": "API anahtarı", "API key": "API anahtarı",
"Model": "Model", "Model": "Model",
"Subtitle model": "Altyazı modeli", "Audio file model": "Ses dosyası modeli",
"The model a timestamped run (subtitles) asks for. Not every model on " "The model a timestamped audio file (subtitles) is sent to. Not every model on "
"OpenRouter returns segment times; empty means openai/whisper-1.": "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.", "model segment zamanı döndürmez; boşsa openai/whisper-1 kullanılır.",
"Provider": "Sağlayıcı", "Provider": "Sağlayıcı",
"sk-… (falls back to OPENAI_API_KEY)": "sk-… (boşsa OPENAI_API_KEY kullanılır)", "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) self.refresh_transcribe_models)
stt_form.addRow(t("Model"), self.transcribe_model_row) stt_form.addRow(t("Model"), self.transcribe_model_row)
# OpenRouter only: which of its models a timestamped run asks for. # OpenRouter only: which of its models a timestamped run asks for.
self.subtitle_model = QComboBox() self.file_model = QComboBox()
self.subtitle_model.setEditable(True) self.file_model.setEditable(True)
self.subtitle_model.lineEdit().setPlaceholderText(api.OPENROUTER_SUBTITLE_MODEL) self.file_model.lineEdit().setPlaceholderText(api.OPENROUTER_FILE_MODEL)
self.subtitle_model.setToolTip( self.file_model.setToolTip(
t("The model a timestamped run (subtitles) asks for. Not every model " t("The model a timestamped audio file (subtitles) is sent to. Not every model "
"on OpenRouter returns segment times; empty means openai/whisper-1.")) "on OpenRouter returns segment times; empty means openai/whisper-1."))
self.subtitle_model_row = self._row(self.subtitle_model) self.file_model_row = self._row(self.file_model)
stt_form.addRow(t("Subtitle model"), self.subtitle_model_row) stt_form.addRow(t("Audio file model"), self.file_model_row)
# A spanning row: in the narrow field column a wrapped label gets a # 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. # height that fits one line, and the rest of the text is cut off.
self.transcribe_status = QLabel("") self.transcribe_status = QLabel("")
@@ -1756,7 +1756,7 @@ class SettingsWindow(QDialog):
self._shown_provider = "" self._shown_provider = ""
self._select_data(self.transcribe_provider, conf["transcribe_provider"]) self._select_data(self.transcribe_provider, conf["transcribe_provider"])
self._provider_changed() # selecting index 0 fires no signal 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_gpu.setChecked(conf["local_gpu"])
self.local_preload.setChecked(conf["local_preload"]) self.local_preload.setChecked(conf["local_preload"])
self.local_threads.setValue(int(conf["local_threads"])) self.local_threads.setValue(int(conf["local_threads"]))
@@ -1874,7 +1874,7 @@ class SettingsWindow(QDialog):
for name, who in cfg.TRANSCRIBERS.items(): for name, who in cfg.TRANSCRIBERS.items():
conf[who.key] = self._key_fields[name].text().strip() conf[who.key] = self._key_fields[name].text().strip()
conf[who.model] = self._models[name].strip() or cfg.DEFAULTS[who.model] 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["gemini_api_key"] = self.gemini_key.text().strip()
conf["opencode_api_key"] = self.opencode_key.text().strip() conf["opencode_api_key"] = self.opencode_key.text().strip()
conf["local_model"] = self.local_whisper.selected() conf["local_model"] = self.local_whisper.selected()
@@ -2048,7 +2048,7 @@ class SettingsWindow(QDialog):
self._shown_provider = provider self._shown_provider = provider
local = provider == "local" local = provider == "local"
self.stt_form.setRowVisible(self.transcribe_model_row, not 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.transcribe_status, not local)
self.stt_form.setRowVisible(self.local_whisper, local) self.stt_form.setRowVisible(self.local_whisper, local)
self.stt_form.setRowVisible(self.local_options, 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.addItems(TRANSCRIBE_MODELS[provider])
self.transcribe_model.setCurrentText(self._models[provider]) self.transcribe_model.setCurrentText(self._models[provider])
if provider == "openrouter": if provider == "openrouter":
self._fill_subtitle_models(TRANSCRIBE_MODELS[provider]) self._fill_file_models(TRANSCRIBE_MODELS[provider])
self.transcribe_status.setText("") self.transcribe_status.setText("")
def _fill_subtitle_models(self, models): def _fill_file_models(self, models):
current = self.subtitle_model.currentText() current = self.file_model.currentText()
self.subtitle_model.clear() self.file_model.clear()
self.subtitle_model.addItems(models) self.file_model.addItems(models)
self.subtitle_model.setCurrentText(current) self.file_model.setCurrentText(current)
def _load_transcribe_models(self): def _load_transcribe_models(self):
"""The model list of whichever provider is selected.""" """The model list of whichever provider is selected."""
@@ -2096,7 +2096,7 @@ class SettingsWindow(QDialog):
self.transcribe_model.addItems(models) self.transcribe_model.addItems(models)
self.transcribe_model.setCurrentText(current) self.transcribe_model.setCurrentText(current)
if self._shown_provider == "openrouter": 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))) self.transcribe_status.setText(t("{count} models loaded.", count=len(models)))
def _load_models(self): 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"), self.assertEqual(api.timestamp_model("openai", "gpt-4o-transcribe"),
"whisper-1") "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( self.assertEqual(
api.timestamp_model("openrouter", "openai/gpt-4o-transcribe", api.timestamp_model("openrouter", "openai/gpt-4o-transcribe",
"openai/whisper-large-v3"), "openai/whisper-large-v3"),
"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", ""), self.assertEqual(api.timestamp_model("openrouter", "openai/gpt-4o-transcribe", ""),
"openai/whisper-1") "openai/whisper-1")
@@ -328,8 +328,8 @@ class TranscribeSegments(DikteTest):
api.transcribe_segments(OPENROUTER, self.wav) api.transcribe_segments(OPENROUTER, self.wav)
self.assertEqual(multipart_fields(calls[0])["model"], "openai/whisper-1") self.assertEqual(multipart_fields(calls[0])["model"], "openai/whisper-1")
def test_openrouter_asks_for_the_subtitle_model_when_one_is_set(self): def test_openrouter_asks_for_the_file_model_when_one_is_set(self):
target = OPENROUTER._replace(subtitle_model="mistralai/voxtral-mini-transcribe") target = OPENROUTER._replace(file_model="mistralai/voxtral-mini-transcribe")
with fake_urlopen(self.reply([{"start": 0, "end": 1, "text": "hi"}])) as calls: with fake_urlopen(self.reply([{"start": 0, "end": 1, "text": "hi"}])) as calls:
api.transcribe_segments(target, self.wav) api.transcribe_segments(target, self.wav)
self.assertEqual(multipart_fields(calls[0])["model"], 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.service, "OpenRouter")
self.assertEqual(target.api_key, "sk-or-test") self.assertEqual(target.api_key, "sk-or-test")
self.assertEqual(target.model, "openai/whisper-1") 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", conf = self.config(transcribe_provider="openrouter",
openrouter_api_key="sk-or-test", openrouter_api_key="sk-or-test",
openrouter_subtitle_model=" openai/whisper-large-v3 ") openrouter_file_model=" openai/whisper-large-v3 ")
self.assertEqual(conf.transcribe_target().subtitle_model, self.assertEqual(conf.transcribe_target().file_model,
"openai/whisper-large-v3") "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", conf = self.config(transcribe_provider="openai", openai_api_key="sk-test",
openrouter_subtitle_model="openai/whisper-large-v3") openrouter_file_model="openai/whisper-large-v3")
self.assertEqual(conf.transcribe_target().subtitle_model, "") self.assertEqual(conf.transcribe_target().file_model, "")
def test_groq_when_it_is_picked(self): def test_groq_when_it_is_picked(self):
conf = self.config(transcribe_provider="groq", groq_api_key="gsk-test", 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["transcribe_model"], "gpt-4o-transcribe")
self.assertEqual(conf["groq_transcribe_model"], "whisper-large-v3") 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", self.write_config({"transcribe_provider": "openrouter",
"openrouter_subtitle_model": "openai/whisper-large-v3"}) "openrouter_file_model": "openai/whisper-large-v3"})
conf = cfg.Config() conf = cfg.Config()
window = self.window(conf) window = self.window(conf)
self.assertEqual(window.subtitle_model.currentText(), "openai/whisper-large-v3") self.assertEqual(window.file_model.currentText(), "openai/whisper-large-v3")
self.assertTrue(window.stt_form.isRowVisible(window.subtitle_model_row)) self.assertTrue(window.stt_form.isRowVisible(window.file_model_row))
window.subtitle_model.setCurrentText(" deepgram/nova-3 ") window.file_model.setCurrentText(" deepgram/nova-3 ")
window._save() 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.setCurrentIndex(
window.transcribe_provider.findData("openai")) 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): def test_the_provider_box_offers_every_provider_config_knows(self):
window = self.window(cfg.Config()) window = self.window(cfg.Config())