Browse Source

feat: add model and vocabulary settings

master
Yutsuo 4 days ago
parent
commit
974a2e6029
  1. 27
      src/voice_transcriptor/ui/settings_dialog.py
  2. 10
      tests/ui/test_settings_dialog.py

27
src/voice_transcriptor/ui/settings_dialog.py

@ -10,11 +10,13 @@ from PySide6.QtWidgets import (
QDialog, QDialog,
QDialogButtonBox, QDialogButtonBox,
QCheckBox, QCheckBox,
QComboBox,
QFileDialog, QFileDialog,
QFormLayout, QFormLayout,
QHBoxLayout, QHBoxLayout,
QLineEdit, QLineEdit,
QMessageBox, QMessageBox,
QPlainTextEdit,
QPushButton, QPushButton,
QSpinBox, QSpinBox,
QToolButton, QToolButton,
@ -24,7 +26,17 @@ from PySide6.QtWidgets import (
from voice_transcriptor.models import AppSettings from voice_transcriptor.models import AppSettings
from voice_transcriptor.services.credentials import CredentialError, CredentialService from voice_transcriptor.services.credentials import CredentialError, CredentialService
from voice_transcriptor.services.settings import SettingsError, SettingsRepository from voice_transcriptor.services.settings import SUPPORTED_MODEL_SUGGESTIONS, SettingsError, SettingsRepository
class EditableModelCombo(QComboBox):
"""Editable model picker with the previous line-edit convenience API."""
def text(self) -> str:
return self.currentText()
def setText(self, value: str) -> None:
self.setCurrentText(value)
class SettingsDialog(QDialog): class SettingsDialog(QDialog):
@ -44,7 +56,10 @@ class SettingsDialog(QDialog):
self._credentials = credentials self._credentials = credentials
self.setWindowTitle("Settings") self.setWindowTitle("Settings")
self.model_input = QLineEdit(settings.model, self) self.model_input = EditableModelCombo(self)
self.model_input.setEditable(True)
self.model_input.addItems(SUPPORTED_MODEL_SUGGESTIONS)
self.model_input.setCurrentText(settings.model)
self.model_input.setObjectName("modelInput") self.model_input.setObjectName("modelInput")
self.language_input = QLineEdit(settings.language, self) self.language_input = QLineEdit(settings.language, self)
self.language_input.setObjectName("languageInput") self.language_input.setObjectName("languageInput")
@ -53,6 +68,9 @@ class SettingsDialog(QDialog):
self.api_key_input.setEchoMode(QLineEdit.EchoMode.Password) self.api_key_input.setEchoMode(QLineEdit.EchoMode.Password)
self.output_directory_input = QLineEdit(str(settings.output_directory), self) self.output_directory_input = QLineEdit(str(settings.output_directory), self)
self.output_directory_input.setObjectName("outputDirectoryInput") self.output_directory_input.setObjectName("outputDirectoryInput")
self.context_input = QPlainTextEdit(settings.context_vocabulary, self)
self.context_input.setObjectName("contextVocabularyInput")
self.context_input.setPlaceholderText("Names, acronyms, technical terms, and conversation context")
self.choose_directory_button = QPushButton("Browse…", self) self.choose_directory_button = QPushButton("Browse…", self)
self.choose_directory_button.setObjectName("chooseDirectoryButton") self.choose_directory_button.setObjectName("chooseDirectoryButton")
self.choose_directory_button.clicked.connect(self.choose_output_directory) self.choose_directory_button.clicked.connect(self.choose_output_directory)
@ -65,6 +83,7 @@ class SettingsDialog(QDialog):
form.addRow("OpenAI API key", self.api_key_input) form.addRow("OpenAI API key", self.api_key_input)
form.addRow("Model", self.model_input) form.addRow("Model", self.model_input)
form.addRow("Language", self.language_input) form.addRow("Language", self.language_input)
form.addRow("Context / Vocabulary", self.context_input)
form.addRow("Output directory", output_directory_layout) form.addRow("Output directory", output_directory_layout)
self.advanced_toggle = QToolButton(self) self.advanced_toggle = QToolButton(self)
@ -133,7 +152,7 @@ class SettingsDialog(QDialog):
) )
return return
model = self.model_input.text().strip() model = self.model_input.currentText().strip()
language = self.language_input.text().strip() language = self.language_input.text().strip()
if not model or not language: if not model or not language:
QMessageBox.warning(self, "Invalid settings", "Model and language are required.") QMessageBox.warning(self, "Invalid settings", "Model and language are required.")
@ -146,7 +165,7 @@ class SettingsDialog(QDialog):
return return
updated_settings = AppSettings( updated_settings = AppSettings(
model, language, output_directory, chunk_duration, overlap, model, language, output_directory, chunk_duration, overlap,
self.retain_temporary_files_input.isChecked(), self.retain_temporary_files_input.isChecked(), self.context_input.toPlainText().strip(),
) )
try: try:
self._repository.save(updated_settings) self._repository.save(updated_settings)

10
tests/ui/test_settings_dialog.py

@ -3,11 +3,12 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
import pytest import pytest
from PySide6.QtWidgets import QLineEdit, QMessageBox from PySide6.QtWidgets import QComboBox, QLineEdit, QMessageBox
from voice_transcriptor.models import AppSettings from voice_transcriptor.models import AppSettings
from voice_transcriptor.services.credentials import CredentialError from voice_transcriptor.services.credentials import CredentialError
from voice_transcriptor.services.settings import SettingsError from voice_transcriptor.services.settings import SettingsError
from voice_transcriptor.services.settings import SUPPORTED_MODEL_SUGGESTIONS
from voice_transcriptor.ui.settings_dialog import SettingsDialog from voice_transcriptor.ui.settings_dialog import SettingsDialog
@ -53,6 +54,10 @@ def test_populates_fields_from_settings(qtbot, settings: AppSettings) -> None:
assert dialog.advanced_panel.isVisible() is False assert dialog.advanced_panel.isVisible() is False
assert dialog.chunk_duration_input.value() == 900 assert dialog.chunk_duration_input.value() == 900
assert dialog.chunk_overlap_input.value() == 15 assert dialog.chunk_overlap_input.value() == 15
assert isinstance(dialog.model_input, QComboBox)
assert dialog.model_input.isEditable()
assert tuple(dialog.model_input.itemText(i) for i in range(dialog.model_input.count())) == SUPPORTED_MODEL_SUGGESTIONS
assert dialog.context_input.toPlainText() == settings.context_vocabulary
def test_advanced_settings_are_saved(qtbot, settings: AppSettings) -> None: def test_advanced_settings_are_saved(qtbot, settings: AppSettings) -> None:
@ -98,10 +103,11 @@ def test_save_persists_new_api_key_and_emits_saved_settings(qtbot, settings: App
dialog.api_key_input.setText(supplied_key) dialog.api_key_input.setText(supplied_key)
dialog.model_input.setText("gpt-4o-mini-transcribe") dialog.model_input.setText("gpt-4o-mini-transcribe")
dialog.context_input.setPlainText("Pix, Banco do Brasil")
dialog.save() dialog.save()
assert saved_with.args == [ assert saved_with.args == [
AppSettings("gpt-4o-mini-transcribe", "pt-BR", settings.output_directory) AppSettings("gpt-4o-mini-transcribe", "pt-BR", settings.output_directory, context_vocabulary="Pix, Banco do Brasil")
] ]
assert credentials.api_key == supplied_key assert credentials.api_key == supplied_key
assert repository.saved == saved_with.args assert repository.saved == saved_with.args

Loading…
Cancel
Save