diff --git a/README.md b/README.md index 9b9ceae..c05f9b8 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,8 @@ runs on either one (`gpt-4o-transcribe` by default), cleanup always on OpenRouter (`google/gemini-3.5-flash-lite`), so a single OpenRouter key can cover both. They fall back to `OPENAI_API_KEY` and `OPENROUTER_API_KEY`, and are stored in `~/.config/dikte/config.json`, mode 600. Cleanup can be switched off, -in which case the raw transcript is pasted. +in which case the raw transcript is pasted, and a thinking model's effort can be +set next to it. ## Using it diff --git a/README.tr.md b/README.tr.md index 83018c9..d7cc8ef 100644 --- a/README.tr.md +++ b/README.tr.md @@ -38,7 +38,7 @@ temizleme her zaman OpenRouter'da (`google/gemini-3.5-flash-lite`), yani tek bir OpenRouter anahtarı ikisine de yeter. Boş bırakırsan `OPENAI_API_KEY` ve `OPENROUTER_API_KEY` kullanılır; anahtarlar `~/.config/dikte/config.json` içinde, izinler 600. Temizlemeyi tamamen kapatabilirsin, o zaman ham transkript -yapıştırılır. +yapıştırılır; modelin yanındaki kutudan düşünme seviyesini de seçebilirsin. ## Kullanım diff --git a/api.py b/api.py index 246cc8a..7482ad5 100644 --- a/api.py +++ b/api.py @@ -173,7 +173,8 @@ def transcribe_segments(target, wav_path, language="", prompt="", timeout=300): return out -def cleanup(text, api_key, model, system_prompt, base_url=OPENROUTER_URL, timeout=180): +def cleanup(text, api_key, model, system_prompt, reasoning="", + base_url=OPENROUTER_URL, timeout=180): if not api_key: raise ApiError(t("{service} API key is empty. Add it in Settings.", service="OpenRouter")) @@ -185,6 +186,11 @@ def cleanup(text, api_key, model, system_prompt, base_url=OPENROUTER_URL, timeou {"role": "user", "content": f"\n{text}\n"}, ], } + # An empty level means "whatever the model does on its own"; anything else is + # one of OpenRouter's efforts. The thinking itself is never shown, so ask for + # it to be left out of the reply. + if reasoning: + payload["reasoning"] = {"effort": reasoning, "exclude": True} try: data = _request( f"{base_url.rstrip('/')}/chat/completions", diff --git a/config.py b/config.py index 03f0c13..9204054 100644 --- a/config.py +++ b/config.py @@ -116,6 +116,7 @@ DEFAULTS = { "transcribe_prompt": "", "cleanup_enabled": True, "cleanup_model": "google/gemini-3.5-flash-lite", + "cleanup_reasoning": "", # empty -> whatever the model does by default "cleanup_prompt": "", # empty -> language-specific default "auto_paste": True, "paste_shortcut": "ctrl+v", diff --git a/filetranscribe.py b/filetranscribe.py index 00c58e1..333e131 100644 --- a/filetranscribe.py +++ b/filetranscribe.py @@ -135,6 +135,7 @@ class FileTranscriber(QObject): conf.openrouter_key(), conf["cleanup_model"], prompt, + reasoning=conf["cleanup_reasoning"], base_url=conf["openrouter_base_url"], )) return ("\n" if timestamps else "\n\n").join(out) diff --git a/i18n.py b/i18n.py index 6670b38..1b55013 100644 --- a/i18n.py +++ b/i18n.py @@ -158,6 +158,21 @@ TR = { "Connection works. {count} audio models visible.": "Bağlantı tamam. {count} ses modeli görünüyor.", "Clean the transcript with a model": "Transkripti bir modelle temizle", + "Thinking": "Düşünme", + "Model's own default": "Modelin kendi varsayılanı", + "Off": "Kapalı", + "Minimal": "En az", + "Low": "Düşük", + "Medium": "Orta", + "High": "Yüksek", + "Very high": "Çok yüksek", + "Maximum": "En yüksek", + "How long a thinking model may reason before it answers. Cleanup is a light " + "job, so more thinking mostly costs time and tokens. Models that cannot " + "think ignore this.": + "Düşünebilen bir modelin yanıtlamadan önce ne kadar düşüneceği. Temizleme " + "hafif bir iş, fazla düşünmenin çoğunlukla getirisi süre ve token. " + "Düşünemeyen modeller bunu yok sayar.", "Fetch model list": "Model listesini çek", "Fetching model list…": "Model listesi çekiliyor…", "Could not fetch the list: {error}": "Liste alınamadı: {error}", diff --git a/settings_ui.py b/settings_ui.py index dd1a7fb..f6a3112 100644 --- a/settings_ui.py +++ b/settings_ui.py @@ -43,6 +43,13 @@ CLEANUP_MODELS = [ "google/gemini-2.5-flash-lite", "anthropic/claude-haiku-4.5", "openai/gpt-5-mini", "meta-llama/llama-3.3-70b-instruct", ] +# How hard the cleanup model may think before it answers, in OpenRouter's own +# effort levels. A model that ignores the field simply answers as it always did. +REASONING_LEVELS = [ + ("Model's own default", ""), ("Off", "none"), ("Minimal", "minimal"), + ("Low", "low"), ("Medium", "medium"), ("High", "high"), + ("Very high", "xhigh"), ("Maximum", "max"), +] PASTE_SHORTCUTS = ["ctrl+v", "ctrl+shift+v", "shift+insert"] AUDIO_FILTER = ("*.mp3 *.wav *.m4a *.ogg *.opus *.flac *.aac *.wma " "*.mp4 *.mkv *.webm *.mov *.avi") @@ -231,6 +238,17 @@ class SettingsWindow(QDialog): self.refresh_models = QPushButton(t("Fetch model list")) self.refresh_models.clicked.connect(self._load_models) orr_form.addRow(t("Model"), self._row(self.cleanup_model, self.refresh_models)) + + self.cleanup_reasoning = QComboBox() + for label, value in REASONING_LEVELS: + self.cleanup_reasoning.addItem(t(label), value) + self.cleanup_reasoning.setToolTip( + t("How long a thinking model may reason before it answers. Cleanup is " + "a light job, so more thinking mostly costs time and tokens. Models " + "that cannot think ignore this.") + ) + orr_form.addRow(t("Thinking"), self.cleanup_reasoning) + self.models_label = QLabel(t("Runs on OpenRouter.")) self.models_label.setWordWrap(True) orr_form.addRow(self.models_label) @@ -456,6 +474,7 @@ class SettingsWindow(QDialog): self._provider_changed() # selecting index 0 fires no signal self.cleanup_enabled.setChecked(conf["cleanup_enabled"]) self.cleanup_model.setCurrentText(conf["cleanup_model"]) + self._select_data(self.cleanup_reasoning, conf["cleanup_reasoning"]) self.cleanup_prompt.setPlainText(conf["cleanup_prompt"] or cfg.default_cleanup_prompt()) self.transcribe_prompt.setPlainText(conf["transcribe_prompt"]) @@ -498,6 +517,7 @@ class SettingsWindow(QDialog): conf["cleanup_enabled"] = self.cleanup_enabled.isChecked() conf["cleanup_model"] = self.cleanup_model.currentText().strip() + conf["cleanup_reasoning"] = self.cleanup_reasoning.currentData() or "" # Store an empty prompt when it matches the default, so switching the # interface language also switches the prompt language. diff --git a/worker.py b/worker.py index d7ec483..b5dc8ef 100644 --- a/worker.py +++ b/worker.py @@ -83,6 +83,7 @@ class Pipeline(QObject): conf.openrouter_key(), conf["cleanup_model"], conf.cleanup_prompt(), + reasoning=conf["cleanup_reasoning"], base_url=conf["openrouter_base_url"], ) except api.ApiError as exc: