mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-11 19:06:11 +00:00
Put the three providers in a table, and test the request rather than the list
Reworked on top of master. The Groq request itself needed nothing beyond a key, a base URL and a model id, but two providers fit in an if and an else where three do not, and each one was written out four times: in transcribe_target(), in the key rows of the settings window, in save and in load. config.TRANSCRIBERS holds them now, one row each: the name the user sees and the three settings that keep its key, its endpoint and its model. The variable an empty key falls back to is the name of its setting, shouted. The provider box, save, load, `dikte models --provider` and `dikte test-key` all read that table, so a fourth provider is a row and a default rather than a branch in five files. The key field, its Test button and its answer line are built once and the three signals became one that carries which key was asked about. The tests moved into the files of the modules they cover and check what a provider actually changes: that the request goes to api.groq.com with the right model and fields, that the glossary now reaches everything except OpenRouter, that a Groq error says Groq, and that the settings window carries the key and the model there and back. GROQ_API_KEY is cleared for the test run like the other two, so a developer who has one does not send a test to the network. Co-authored-by: Muzaffer Emre <[email protected]>
This commit is contained in:
co-authored by
Muzaffer Emre
parent
22edd2f247
commit
d04efe235a
+1
-1
@@ -22,7 +22,7 @@ atexit.register(shutil.rmtree, _SANDBOX, True)
|
||||
# A key sitting in the environment would otherwise reach the code that falls
|
||||
# back to it, and the tests for "there is no key" would pass only on a machine
|
||||
# without one.
|
||||
for _var in ("OPENAI_API_KEY", "OPENROUTER_API_KEY"):
|
||||
for _var in ("OPENAI_API_KEY", "GROQ_API_KEY", "OPENROUTER_API_KEY"):
|
||||
os.environ.pop(_var, None)
|
||||
|
||||
# The interface language leaks through module-level state, so the tests fix it
|
||||
|
||||
+48
-2
@@ -21,6 +21,7 @@ from tests.support import (
|
||||
)
|
||||
|
||||
OPENAI = api.Target("openai", "OpenAI", "sk-test", api.OPENAI_URL, "gpt-4o-transcribe")
|
||||
GROQ = api.Target("groq", "Groq", "gsk-test", api.GROQ_URL, "whisper-large-v3-turbo")
|
||||
OPENROUTER = api.Target("openrouter", "OpenRouter", "sk-or-test",
|
||||
api.OPENROUTER_URL, "openai/gpt-4o-transcribe")
|
||||
|
||||
@@ -32,6 +33,18 @@ class TimestampModel(unittest.TestCase):
|
||||
def test_openrouter_namespaces_the_id(self):
|
||||
self.assertEqual(api.timestamp_model("openrouter"), "openai/whisper-1")
|
||||
|
||||
def test_groq_keeps_the_model_that_was_chosen(self):
|
||||
"""Every model it transcribes with is a whisper, so all of them do times."""
|
||||
self.assertEqual(api.timestamp_model("groq", "whisper-large-v3"),
|
||||
"whisper-large-v3")
|
||||
|
||||
def test_groq_with_nothing_chosen_falls_back(self):
|
||||
self.assertEqual(api.timestamp_model("groq"), "whisper-large-v3-turbo")
|
||||
|
||||
def test_the_others_ignore_what_was_chosen(self):
|
||||
self.assertEqual(api.timestamp_model("openai", "gpt-4o-transcribe"),
|
||||
"whisper-1")
|
||||
|
||||
|
||||
class Explain(DikteTest):
|
||||
def error(self, status):
|
||||
@@ -176,13 +189,28 @@ class Transcribe(DikteTest):
|
||||
self.assertEqual(multipart_fields(calls[0])["language"], "tr")
|
||||
self.assertNotIn("language", multipart_fields(calls[1]))
|
||||
|
||||
def test_the_glossary_goes_to_openai_only(self):
|
||||
def test_the_glossary_goes_everywhere_but_openrouter(self):
|
||||
"""OpenRouter takes the field and throws it away, so spare it the bytes."""
|
||||
with fake_urlopen({"text": "hi"}) as calls:
|
||||
api.transcribe(OPENAI, self.wav, prompt="Paraşüt, OpenFrame")
|
||||
api.transcribe(GROQ, self.wav, prompt="Paraşüt, OpenFrame")
|
||||
api.transcribe(OPENROUTER, self.wav, prompt="Paraşüt, OpenFrame")
|
||||
self.assertIn("prompt", multipart_fields(calls[0]))
|
||||
self.assertNotIn("prompt", multipart_fields(calls[1]))
|
||||
self.assertIn("prompt", multipart_fields(calls[1]))
|
||||
self.assertNotIn("prompt", multipart_fields(calls[2]))
|
||||
|
||||
def test_groq_goes_to_groq(self):
|
||||
with fake_urlopen({"text": "hi"}) as calls:
|
||||
api.transcribe(GROQ, self.wav)
|
||||
self.assertEqual(calls[0].full_url,
|
||||
"https://api.groq.com/openai/v1/audio/transcriptions")
|
||||
self.assertEqual(multipart_fields(calls[0])["model"], "whisper-large-v3-turbo")
|
||||
|
||||
def test_a_refused_groq_key_is_explained_in_groq_s_name(self):
|
||||
with fake_urlopen(http_error(401, '{"error": {"message": "bad key"}}')), \
|
||||
self.assertRaises(api.ApiError) as caught:
|
||||
api.transcribe(GROQ, self.wav)
|
||||
self.assertIn("Groq", str(caught.exception))
|
||||
|
||||
def test_openrouter_is_attributed(self):
|
||||
with fake_urlopen({"text": "hi"}) as calls:
|
||||
@@ -242,6 +270,12 @@ class TranscribeSegments(DikteTest):
|
||||
api.transcribe_segments(OPENROUTER, self.wav)
|
||||
self.assertEqual(multipart_fields(calls[0])["model"], "openai/whisper-1")
|
||||
|
||||
def test_groq_stays_on_the_model_it_was_given(self):
|
||||
target = GROQ._replace(model="whisper-large-v3")
|
||||
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"], "whisper-large-v3")
|
||||
|
||||
def test_the_segments_come_back_as_numbers(self):
|
||||
with fake_urlopen(self.reply([
|
||||
{"start": "0.5", "end": "2.25", "text": " hello "},
|
||||
@@ -432,6 +466,18 @@ class ModelLists(DikteTest):
|
||||
with self.assertRaises(api.ApiError):
|
||||
api.openai_models("")
|
||||
|
||||
def test_the_same_list_read_from_groq(self):
|
||||
with fake_urlopen({"data": [{"id": "llama-3.3-70b"},
|
||||
{"id": "whisper-large-v3"}]}) as calls:
|
||||
models = api.openai_models("gsk-test", api.GROQ_URL, "Groq")
|
||||
self.assertEqual(calls[0].full_url, "https://api.groq.com/openai/v1/models")
|
||||
self.assertEqual(models, ["whisper-large-v3"])
|
||||
|
||||
def test_a_missing_groq_key_says_groq(self):
|
||||
with self.assertRaises(api.ApiError) as caught:
|
||||
api.openai_models("", api.GROQ_URL, "Groq")
|
||||
self.assertIn("Groq", str(caught.exception))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
+34
-1
@@ -15,7 +15,7 @@ from unittest import mock
|
||||
import cli
|
||||
import config as cfg
|
||||
import ipc
|
||||
from tests.support import DikteTest
|
||||
from tests.support import DikteTest, fake_urlopen
|
||||
|
||||
|
||||
class Options:
|
||||
@@ -316,6 +316,39 @@ class ConfigCommands(DikteTest):
|
||||
{"cleanup", "subtitles", "meeting", "agent"})
|
||||
|
||||
|
||||
class Providers(DikteTest):
|
||||
"""The terminal reaches every provider the settings window does."""
|
||||
|
||||
def run_cmd(self, func, **values):
|
||||
with captured() as (out, err):
|
||||
code = func(Options(**values))
|
||||
return code, out.getvalue(), err.getvalue()
|
||||
|
||||
def test_a_provider_the_settings_window_offers_is_a_choice_here_too(self):
|
||||
parser = cli.build_parser()
|
||||
for provider in cfg.TRANSCRIBERS:
|
||||
with self.subTest(provider=provider):
|
||||
opts = parser.parse_args(["models", "--provider", provider])
|
||||
self.assertEqual(opts.provider, provider)
|
||||
self.assertEqual(parser.parse_args(["test-key", provider]).which,
|
||||
provider)
|
||||
|
||||
def test_the_model_list_is_read_from_the_chosen_provider(self):
|
||||
self.write_config({"groq_api_key": "gsk-test"})
|
||||
with fake_urlopen({"data": [{"id": "whisper-large-v3"}]}) as calls:
|
||||
code, out, _ = self.run_cmd(cli.cmd_models, provider="groq",
|
||||
transcription=False)
|
||||
self.assertEqual(code, 0)
|
||||
self.assertEqual(calls[0].full_url, "https://api.groq.com/openai/v1/models")
|
||||
self.assertEqual(out.strip(), "whisper-large-v3")
|
||||
|
||||
def test_a_key_that_is_not_there_is_reported_under_its_own_name(self):
|
||||
code, out, _ = self.run_cmd(cli.cmd_test_key, which="groq")
|
||||
self.assertEqual(code, 1)
|
||||
self.assertIn("groq", out)
|
||||
self.assertIn("Groq", out)
|
||||
|
||||
|
||||
class Finding(DikteTest):
|
||||
def test_no_history_at_all(self):
|
||||
self.assertIsNone(cli._find_history("last"))
|
||||
|
||||
@@ -125,6 +125,10 @@ class Keys(DikteTest):
|
||||
def test_no_key_anywhere(self):
|
||||
self.assertEqual(cfg.Config().openai_key(), "")
|
||||
|
||||
def test_every_provider_falls_back_to_the_variable_of_its_own_name(self):
|
||||
with mock.patch.dict(os.environ, {"GROQ_API_KEY": "gsk-env"}):
|
||||
self.assertEqual(cfg.Config().groq_key(), "gsk-env")
|
||||
|
||||
|
||||
class TranscribeTarget(DikteTest):
|
||||
def test_openai_by_default(self):
|
||||
@@ -145,6 +149,21 @@ class TranscribeTarget(DikteTest):
|
||||
self.assertEqual(target.api_key, "sk-or-test")
|
||||
self.assertEqual(target.model, "openai/whisper-1")
|
||||
|
||||
def test_groq_when_it_is_picked(self):
|
||||
conf = self.config(transcribe_provider="groq", groq_api_key="gsk-test",
|
||||
groq_transcribe_model="whisper-large-v3")
|
||||
target = conf.transcribe_target()
|
||||
self.assertEqual(target.provider, "groq")
|
||||
self.assertEqual(target.service, "Groq")
|
||||
self.assertEqual(target.api_key, "gsk-test")
|
||||
self.assertEqual(target.base_url, api.GROQ_URL)
|
||||
self.assertEqual(target.model, "whisper-large-v3")
|
||||
|
||||
def test_a_provider_this_version_has_never_heard_of(self):
|
||||
"""A config written by a fork, or by a version that dropped one."""
|
||||
target = self.config(transcribe_provider="deepgram").transcribe_target()
|
||||
self.assertEqual(target.provider, "openai")
|
||||
|
||||
def test_a_self_hosted_endpoint(self):
|
||||
conf = self.config(openai_base_url="http://localhost:8080/v1")
|
||||
self.assertEqual(conf.transcribe_target().base_url, "http://localhost:8080/v1")
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import json
|
||||
import pathlib
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import api
|
||||
import config
|
||||
import settings_ui
|
||||
|
||||
|
||||
class GroqCompatibilityTests(unittest.TestCase):
|
||||
def test_old_config_gains_groq_defaults(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = pathlib.Path(directory) / "config.json"
|
||||
path.write_text(json.dumps({"transcribe_provider": "openai"}), encoding="utf-8")
|
||||
with mock.patch.object(config, "CONFIG_FILE", path):
|
||||
conf = config.Config()
|
||||
self.assertEqual(conf["groq_base_url"], api.GROQ_URL)
|
||||
self.assertEqual(conf["groq_transcribe_model"], "whisper-large-v3-turbo")
|
||||
|
||||
def test_groq_target_and_timestamp_model(self):
|
||||
with mock.patch.object(config.Config, "load"):
|
||||
conf = config.Config()
|
||||
conf["transcribe_provider"] = "groq"
|
||||
conf["groq_api_key"] = "test-key"
|
||||
target = conf.transcribe_target()
|
||||
self.assertEqual(target.provider, "groq")
|
||||
self.assertEqual(target.base_url, api.GROQ_URL)
|
||||
self.assertEqual(api.timestamp_model(target.provider, target.model), target.model)
|
||||
|
||||
def test_groq_is_available_in_settings(self):
|
||||
self.assertIn(("Groq", "groq"), settings_ui.TRANSCRIBE_PROVIDERS)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+28
-2
@@ -36,9 +36,11 @@ CHANGED = {
|
||||
"filter_hallucinations": False,
|
||||
"keep_audio": True,
|
||||
"openai_api_key": "sk-test-key",
|
||||
"groq_api_key": "gsk-test-key",
|
||||
"openrouter_api_key": "sk-or-test-key",
|
||||
"transcribe_provider": "openrouter",
|
||||
"transcribe_model": "whisper-1",
|
||||
"groq_transcribe_model": "whisper-large-v3",
|
||||
"openrouter_transcribe_model": "openai/whisper-1",
|
||||
"cleanup_enabled": False,
|
||||
"cleanup_model": "some/other-model",
|
||||
@@ -145,14 +147,38 @@ class Settings(DikteTest):
|
||||
def test_each_provider_keeps_its_own_transcription_model(self):
|
||||
self.write_config({"transcribe_provider": "openai",
|
||||
"transcribe_model": "gpt-4o-transcribe",
|
||||
"groq_transcribe_model": "whisper-large-v3",
|
||||
"openrouter_transcribe_model": "openai/whisper-1"})
|
||||
conf = cfg.Config()
|
||||
window = self.window(conf)
|
||||
window.transcribe_provider.setCurrentIndex(
|
||||
window.transcribe_provider.findData("openrouter"))
|
||||
for provider in ("groq", "openrouter"):
|
||||
window.transcribe_provider.setCurrentIndex(
|
||||
window.transcribe_provider.findData(provider))
|
||||
window._save()
|
||||
self.assertEqual(conf["transcribe_provider"], "openrouter")
|
||||
self.assertEqual(conf["transcribe_model"], "gpt-4o-transcribe")
|
||||
self.assertEqual(conf["groq_transcribe_model"], "whisper-large-v3")
|
||||
|
||||
def test_the_provider_box_offers_every_provider_config_knows(self):
|
||||
window = self.window(cfg.Config())
|
||||
offered = [window.transcribe_provider.itemData(i)
|
||||
for i in range(window.transcribe_provider.count())]
|
||||
self.assertEqual(offered, list(cfg.TRANSCRIBERS))
|
||||
|
||||
def test_the_answer_to_a_test_lands_under_the_key_it_was_asked_about(self):
|
||||
"""One signal serves all three buttons, so it carries which one asked."""
|
||||
window = self.window(cfg.Config())
|
||||
window._on_test_done("groq", True, "it works")
|
||||
button, answer = window._testers["groq"]
|
||||
self.assertEqual(answer.text(), "✓ it works")
|
||||
self.assertTrue(button.isEnabled())
|
||||
self.assertEqual(window._testers["openai"][1].text(), "")
|
||||
|
||||
def test_a_key_lands_in_the_field_of_its_own_provider(self):
|
||||
self.write_config({"groq_api_key": "gsk-mine"})
|
||||
window = self.window(cfg.Config())
|
||||
self.assertEqual(window.groq_key.text(), "gsk-mine")
|
||||
self.assertEqual(window.openai_key.text(), "")
|
||||
|
||||
def test_saving_applies_the_lowered_history_limit_at_once(self):
|
||||
for index in range(10):
|
||||
|
||||
Reference in New Issue
Block a user