mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-11 19:06:11 +00:00
Transcribe and clean up on this machine, without installing anything first
whisper-server is started on --inference-path /v1/audio/transcriptions, which is exactly the path api.py already builds for the hosted providers, and llama-server answers /chat/completions the way OpenRouter does. So the local half is one more base URL rather than a second code path: worker.py, filetranscribe.py and meeting.py are untouched, and dictation, subtitles and meetings all work here on the first try. Three findings worth naming, none of them in the new code: whisper.cpp cuts segments on tokens, which in Turkish lands inside a word about as often as between two. Pasted raw that gives "akraba değ\niller."; in a subtitle it gives a cue reading "değ". Whisper marks the start of a word with a leading space, so a piece that does not begin with one continues the word above it. A small model will repeat the transcript until the context is full, and every one of those tokens is a second of somebody waiting: measured at 206 seconds, and 25 with a ceiling on the reply. Hosted models are left alone, where the same runaway is rare and a ceiling would cut the minutes short. A server outlives SIGTERM and SIGKILL holding its model in memory. Signals are now turned into an event Qt delivers, since Qt blocks in C where a Python handler never runs, and a pid file lets the next start sweep up what a SIGKILL left behind. The minutes keep their own provider rather than following cleanup's. The two jobs are not the same size: a 4B model here will strip the filler words out of a dictation and will not write up an hour long meeting. The suite runs offline now: a test that reaches the network says so instead of quietly going there.
This commit is contained in:
@@ -18,6 +18,7 @@ import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import wave
|
||||
from unittest import mock
|
||||
|
||||
@@ -40,6 +41,12 @@ linux_only = unittest.skipUnless(
|
||||
)
|
||||
|
||||
|
||||
def _no_network(*args, **kwargs):
|
||||
raise AssertionError(
|
||||
"a test reached the network; wrap the call in support.fake_urlopen"
|
||||
)
|
||||
|
||||
|
||||
def _no_exec(*args, **kwargs):
|
||||
raise AssertionError(
|
||||
"a test reached os.execv, which would replace the test process with the "
|
||||
@@ -78,6 +85,11 @@ class DikteTest(unittest.TestCase):
|
||||
# with it and hang, so it fails loudly here instead.
|
||||
self.patch_attr(os, "execv", _no_exec)
|
||||
|
||||
# Every way out of here goes through urllib, so closing it is enough to
|
||||
# keep the suite offline. A test that means to answer a request patches
|
||||
# this again through fake_urlopen.
|
||||
self.patch_attr(urllib.request, "urlopen", _no_network)
|
||||
|
||||
# ---- helpers ---------------------------------------------------------
|
||||
|
||||
def path(self, *parts):
|
||||
|
||||
+182
-10
@@ -10,6 +10,7 @@ import os
|
||||
import unittest
|
||||
|
||||
import api
|
||||
import ggml
|
||||
from tests.support import (
|
||||
DikteTest,
|
||||
fake_urlopen,
|
||||
@@ -27,10 +28,18 @@ OPENROUTER = api.Target("openrouter", "OpenRouter", "sk-or-test",
|
||||
|
||||
class TimestampModel(unittest.TestCase):
|
||||
def test_only_whisper_returns_segment_times(self):
|
||||
self.assertEqual(api.timestamp_model("openai"), "whisper-1")
|
||||
self.assertEqual(api.timestamp_model("openai", "gpt-4o-transcribe"),
|
||||
"whisper-1")
|
||||
|
||||
def test_openrouter_namespaces_the_id(self):
|
||||
self.assertEqual(api.timestamp_model("openrouter"), "openai/whisper-1")
|
||||
self.assertEqual(api.timestamp_model("openrouter", "openai/gpt-4o-transcribe"),
|
||||
"openai/whisper-1")
|
||||
|
||||
def test_the_local_server_stays_on_the_model_it_loaded(self):
|
||||
# Asking it for whisper-1 would name a model it has never heard of, and
|
||||
# it is running whisper whatever the file is called.
|
||||
self.assertEqual(api.timestamp_model("local", "ggml-base.bin"),
|
||||
"ggml-base.bin")
|
||||
|
||||
|
||||
class Explain(DikteTest):
|
||||
@@ -278,10 +287,15 @@ def chat_reply(content):
|
||||
return {"choices": [{"message": {"content": content}}]}
|
||||
|
||||
|
||||
def openrouter(model="some/model", key="sk-or-test", reasoning="",
|
||||
base_url="https://openrouter.ai/api/v1"):
|
||||
return api.Target("openrouter", "OpenRouter", key, base_url, model, reasoning)
|
||||
|
||||
|
||||
class Cleanup(DikteTest):
|
||||
def call(self, replies, **kwargs):
|
||||
def call(self, replies, target=None, **kwargs):
|
||||
with fake_urlopen(replies) as calls:
|
||||
result = api.cleanup("uh, hello", "sk-or-test", "some/model",
|
||||
result = api.cleanup(target or openrouter(), "uh, hello",
|
||||
"you clean up text", **kwargs)
|
||||
return result, calls
|
||||
|
||||
@@ -311,32 +325,34 @@ class Cleanup(DikteTest):
|
||||
self.assertNotIn("reasoning", sent_json(calls[0]))
|
||||
|
||||
def test_an_effort_is_passed_on_and_the_thinking_left_out(self):
|
||||
_, calls = self.call(chat_reply("Hello."), reasoning="high")
|
||||
_, calls = self.call(chat_reply("Hello."),
|
||||
target=openrouter(reasoning="high"))
|
||||
self.assertEqual(sent_json(calls[0])["reasoning"],
|
||||
{"effort": "high", "exclude": True})
|
||||
|
||||
def test_a_local_base_url(self):
|
||||
_, calls = self.call(chat_reply("Hello."), base_url="http://localhost:1234/v1")
|
||||
_, calls = self.call(chat_reply("Hello."),
|
||||
target=openrouter(base_url="http://localhost:1234/v1"))
|
||||
self.assertEqual(calls[0].full_url, "http://localhost:1234/v1/chat/completions")
|
||||
|
||||
def test_no_key(self):
|
||||
with self.assertRaises(api.ApiError):
|
||||
api.cleanup("hello", "", "some/model", "prompt")
|
||||
api.cleanup(openrouter(key=""), "hello", "prompt")
|
||||
|
||||
def test_a_reply_with_no_choices_says_why(self):
|
||||
with fake_urlopen({"error": {"message": "model is offline"}}), \
|
||||
self.assertRaises(api.ApiError) as caught:
|
||||
api.cleanup("hello", "k", "m", "p")
|
||||
api.cleanup(openrouter(), "hello", "p")
|
||||
self.assertIn("model is offline", str(caught.exception))
|
||||
|
||||
def test_an_empty_answer(self):
|
||||
with fake_urlopen(chat_reply(" ")), self.assertRaises(api.ApiError):
|
||||
api.cleanup("hello", "k", "m", "p")
|
||||
api.cleanup(openrouter(), "hello", "p")
|
||||
|
||||
def test_a_rate_limit_is_explained(self):
|
||||
with fake_urlopen(http_error(429)), \
|
||||
self.assertRaises(api.ApiError) as caught:
|
||||
api.cleanup("hello", "k", "m", "p")
|
||||
api.cleanup(openrouter(), "hello", "p")
|
||||
self.assertIn("OpenRouter", str(caught.exception))
|
||||
|
||||
|
||||
@@ -435,3 +451,159 @@ class ModelLists(DikteTest):
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class FakeServer:
|
||||
"""A ggml.Server as far as api.py is concerned."""
|
||||
|
||||
def __init__(self, url="http://127.0.0.1:9999/v1", fails="", log=""):
|
||||
self.url = url
|
||||
self.fails = fails
|
||||
self.log = log
|
||||
self.starts = 0
|
||||
|
||||
def serve(self):
|
||||
self.starts += 1
|
||||
if self.fails:
|
||||
raise ggml.LocalError(self.fails)
|
||||
return self.url
|
||||
|
||||
def error(self):
|
||||
return self.log
|
||||
|
||||
|
||||
LOCAL = api.Target("local", "Local whisper", "", "", "ggml-base.bin")
|
||||
LOCAL_LLM = api.Target("local-llm", "Local model", "", "", "gemma.gguf", "none")
|
||||
|
||||
|
||||
class TranscribeHere(DikteTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.wav = str(self.path("clip.wav"))
|
||||
os.makedirs(self.root, exist_ok=True)
|
||||
with open(self.wav, "wb") as fh:
|
||||
fh.write(b"RIFFfake")
|
||||
self.server = FakeServer()
|
||||
self.patch_attr(ggml, "whisper", self.server)
|
||||
|
||||
def test_the_address_comes_from_the_server_it_starts(self):
|
||||
with fake_urlopen({"text": "hello"}) as calls:
|
||||
api.transcribe(LOCAL, self.wav)
|
||||
self.assertEqual(self.server.starts, 1)
|
||||
self.assertEqual(calls[0].full_url,
|
||||
"http://127.0.0.1:9999/v1/audio/transcriptions")
|
||||
|
||||
def test_nothing_local_is_authorised(self):
|
||||
with fake_urlopen({"text": "hello"}) as calls:
|
||||
api.transcribe(LOCAL, self.wav)
|
||||
self.assertNotIn("Authorization", calls[0].headers)
|
||||
|
||||
def test_a_server_that_will_not_start_is_the_error_shown(self):
|
||||
self.patch_attr(ggml, "whisper", FakeServer(fails="no model downloaded"))
|
||||
with self.assertRaises(api.ApiError) as caught:
|
||||
api.transcribe(LOCAL, self.wav)
|
||||
self.assertIn("no model downloaded", str(caught.exception))
|
||||
|
||||
def test_a_server_that_dies_mid_request_says_what_it_printed(self):
|
||||
self.patch_attr(ggml, "whisper", FakeServer(log="out of memory"))
|
||||
with fake_urlopen(url_error("connection reset")):
|
||||
with self.assertRaises(api.ApiError) as caught:
|
||||
api.transcribe(LOCAL, self.wav)
|
||||
self.assertIn("out of memory", str(caught.exception))
|
||||
|
||||
def test_the_hint_reaches_whisper_as_its_initial_prompt(self):
|
||||
with fake_urlopen({"text": "hi"}) as calls:
|
||||
api.transcribe(LOCAL, self.wav, prompt="Dikte, Paraşüt")
|
||||
self.assertEqual(multipart_fields(calls[0])["prompt"], "Dikte, Paraşüt")
|
||||
|
||||
def test_a_word_broken_over_two_lines_is_put_back_together(self):
|
||||
# whisper.cpp cuts on tokens and writes one segment per line, which in
|
||||
# Turkish lands inside a word about as often as between two.
|
||||
with fake_urlopen({"text": "Onlar akraba değ\niller. Ve\n devamı."}):
|
||||
# The line break inside a word leaves nothing in its place; the
|
||||
# one between two words is where whisper's own leading space is.
|
||||
self.assertEqual(api.transcribe(LOCAL, self.wav),
|
||||
"Onlar akraba değiller. Ve devamı.")
|
||||
|
||||
def test_a_local_timeout_is_not_a_hosted_one(self):
|
||||
# Nothing is being spent but time, and a long file on a machine without
|
||||
# a graphics card takes a good deal of it.
|
||||
with fake_urlopen({"text": "hi"}):
|
||||
api.transcribe(LOCAL, self.wav, timeout=300)
|
||||
self.assertGreaterEqual(api.LOCAL_TIMEOUT, 600)
|
||||
|
||||
def test_segments_that_continue_a_word_are_merged(self):
|
||||
reply = {"segments": [
|
||||
{"start": 0.0, "end": 1.0, "text": " Onlar akraba değ"},
|
||||
{"start": 1.0, "end": 1.4, "text": "iller."},
|
||||
{"start": 2.0, "end": 3.0, "text": " Başka bir cümle."},
|
||||
]}
|
||||
with fake_urlopen(reply):
|
||||
out = api.transcribe_segments(LOCAL, self.wav)
|
||||
self.assertEqual([text for _, _, text in out],
|
||||
["Onlar akraba değiller.", "Başka bir cümle."])
|
||||
self.assertEqual(out[0][1], 1.4) # the merged cue covers the whole word
|
||||
|
||||
def test_the_loaded_model_is_the_one_asked_for_again(self):
|
||||
with fake_urlopen({"segments": [{"start": 0, "end": 1, "text": " hi"}]}) as calls:
|
||||
api.transcribe_segments(LOCAL, self.wav)
|
||||
self.assertEqual(multipart_fields(calls[0])["model"], "ggml-base.bin")
|
||||
|
||||
|
||||
class CleanupHere(DikteTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.server = FakeServer("http://127.0.0.1:8888/v1")
|
||||
self.patch_attr(ggml, "llm", self.server)
|
||||
|
||||
def test_it_goes_to_the_server_it_starts(self):
|
||||
with fake_urlopen(chat_reply("Hello.")) as calls:
|
||||
result = api.cleanup(LOCAL_LLM, "uh, hello", "clean it up")
|
||||
self.assertEqual(result, "Hello.")
|
||||
self.assertEqual(calls[0].full_url,
|
||||
"http://127.0.0.1:8888/v1/chat/completions")
|
||||
|
||||
def test_no_key_is_wanted_and_none_is_sent(self):
|
||||
with fake_urlopen(chat_reply("Hello.")) as calls:
|
||||
api.cleanup(LOCAL_LLM, "hello", "prompt")
|
||||
self.assertNotIn("Authorization", calls[0].headers)
|
||||
|
||||
def test_thinking_is_turned_off_in_the_words_llama_cpp_uses(self):
|
||||
with fake_urlopen(chat_reply("Hello.")) as calls:
|
||||
api.cleanup(LOCAL_LLM, "hello", "prompt")
|
||||
self.assertEqual(sent_json(calls[0])["chat_template_kwargs"],
|
||||
{"enable_thinking": False})
|
||||
|
||||
def test_the_models_own_default_asks_for_nothing(self):
|
||||
with fake_urlopen(chat_reply("Hello.")) as calls:
|
||||
api.cleanup(LOCAL_LLM._replace(reasoning=""), "hello", "prompt")
|
||||
self.assertNotIn("chat_template_kwargs", sent_json(calls[0]))
|
||||
|
||||
def test_a_reply_that_was_all_thinking_names_the_setting_that_fixes_it(self):
|
||||
reply = {"choices": [{"message": {"content": "", "reasoning": "hmm"}}]}
|
||||
with fake_urlopen(reply), self.assertRaises(api.ApiError) as caught:
|
||||
api.cleanup(LOCAL_LLM, "hello", "prompt")
|
||||
self.assertIn("Thinking", str(caught.exception))
|
||||
|
||||
def test_a_reply_longer_than_the_transcript_is_cut_off(self):
|
||||
# A small model will repeat the transcript until the context is full,
|
||||
# and every one of those tokens is a second of somebody waiting.
|
||||
with fake_urlopen(chat_reply("Hello.")) as calls:
|
||||
api.cleanup(LOCAL_LLM, "x" * 4000, "prompt")
|
||||
self.assertEqual(sent_json(calls[0])["max_tokens"], 4000)
|
||||
|
||||
def test_a_short_dictation_still_gets_room_to_answer(self):
|
||||
with fake_urlopen(chat_reply("Hello.")) as calls:
|
||||
api.cleanup(LOCAL_LLM, "uh, hi", "prompt")
|
||||
self.assertEqual(sent_json(calls[0])["max_tokens"], 512)
|
||||
|
||||
def test_a_hosted_model_is_left_to_answer_at_length(self):
|
||||
with fake_urlopen(chat_reply("Hello.")) as calls:
|
||||
api.cleanup(openrouter(), "uh, hi", "prompt")
|
||||
self.assertNotIn("max_tokens", sent_json(calls[0]))
|
||||
|
||||
def test_a_server_that_will_not_start_is_the_error_shown(self):
|
||||
self.patch_attr(ggml, "llm", FakeServer(fails="llama.cpp is not installed"))
|
||||
with self.assertRaises(api.ApiError) as caught:
|
||||
api.cleanup(LOCAL_LLM, "hello", "prompt")
|
||||
self.assertIn("llama.cpp", str(caught.exception))
|
||||
|
||||
+84
-3
@@ -13,6 +13,7 @@ from unittest import mock
|
||||
|
||||
import api
|
||||
import config as cfg
|
||||
import ggml
|
||||
import i18n
|
||||
from tests.support import DikteTest
|
||||
|
||||
@@ -127,8 +128,17 @@ class Keys(DikteTest):
|
||||
|
||||
|
||||
class TranscribeTarget(DikteTest):
|
||||
def test_openai_by_default(self):
|
||||
target = self.config(openai_api_key="sk-test").transcribe_target()
|
||||
def test_this_machine_by_default(self):
|
||||
target = cfg.Config().transcribe_target()
|
||||
self.assertEqual(target.provider, "local")
|
||||
self.assertEqual(target.api_key, "")
|
||||
# Empty on purpose: the server picks a port when it starts, and reading
|
||||
# a setting must not be what starts it.
|
||||
self.assertEqual(target.base_url, "")
|
||||
|
||||
def test_openai_when_it_is_picked(self):
|
||||
target = self.config(transcribe_provider="openai",
|
||||
openai_api_key="sk-test").transcribe_target()
|
||||
self.assertEqual(target.provider, "openai")
|
||||
self.assertEqual(target.service, "OpenAI")
|
||||
self.assertEqual(target.api_key, "sk-test")
|
||||
@@ -146,7 +156,8 @@ class TranscribeTarget(DikteTest):
|
||||
self.assertEqual(target.model, "openai/whisper-1")
|
||||
|
||||
def test_a_self_hosted_endpoint(self):
|
||||
conf = self.config(openai_base_url="http://localhost:8080/v1")
|
||||
conf = self.config(transcribe_provider="openai",
|
||||
openai_base_url="http://localhost:8080/v1")
|
||||
self.assertEqual(conf.transcribe_target().base_url, "http://localhost:8080/v1")
|
||||
|
||||
|
||||
@@ -425,3 +436,73 @@ class Defaults(unittest.TestCase):
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class LocalTargets(DikteTest):
|
||||
def test_cleanup_can_run_here_while_the_minutes_do_not(self):
|
||||
# The two jobs are not the same size: a small model on this machine
|
||||
# strips filler words perfectly well and will not write up an hour.
|
||||
conf = self.config(cleanup_provider="local", local_llm_model="gemma.gguf")
|
||||
self.assertEqual(conf.cleanup_target().provider, "local-llm")
|
||||
self.assertEqual(conf.minutes_target().provider, "openrouter")
|
||||
self.assertEqual(conf.minutes_target().model, cfg.DEFAULTS["meeting_model"])
|
||||
|
||||
def test_the_minutes_can_run_here_on_their_own(self):
|
||||
conf = self.config(meeting_provider="local", local_llm_model="gemma.gguf")
|
||||
self.assertEqual(conf.minutes_target().model, "gemma.gguf")
|
||||
self.assertEqual(conf.cleanup_target().provider, "openrouter")
|
||||
|
||||
def test_the_local_cleanup_target_carries_the_thinking_level(self):
|
||||
conf = self.config(cleanup_provider="local", local_llm_model="gemma.gguf",
|
||||
local_llm_reasoning="none")
|
||||
target = conf.cleanup_target()
|
||||
self.assertEqual(target.reasoning, "none")
|
||||
self.assertEqual(target.api_key, "")
|
||||
|
||||
def test_either_of_them_counts_as_using_the_local_model(self):
|
||||
self.assertFalse(cfg.Config().uses_local_llm())
|
||||
self.assertTrue(self.config(cleanup_provider="local").uses_local_llm())
|
||||
self.assertTrue(self.config(meeting_provider="local").uses_local_llm())
|
||||
|
||||
|
||||
class ReadyToRun(DikteTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.patch_attr(ggml, "MODELS_DIR", self.path("models"))
|
||||
|
||||
def install(self, name):
|
||||
path = ggml.whisper_model_path(name)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(b"model")
|
||||
|
||||
def test_a_missing_program_is_not_ready(self):
|
||||
with mock.patch("shutil.which", return_value=None):
|
||||
self.install("ggml-base.bin")
|
||||
conf = self.config(local_model="ggml-base.bin")
|
||||
self.assertFalse(conf.transcribe_ready())
|
||||
|
||||
def test_a_missing_model_is_not_ready_either(self):
|
||||
with mock.patch("shutil.which", return_value="/usr/bin/whisper-server"):
|
||||
conf = self.config(local_model="ggml-base.bin")
|
||||
self.assertFalse(conf.transcribe_ready())
|
||||
|
||||
def test_both_halves_in_place(self):
|
||||
with mock.patch("shutil.which", return_value="/usr/bin/whisper-server"):
|
||||
self.install("ggml-base.bin")
|
||||
conf = self.config(local_model="ggml-base.bin")
|
||||
self.assertTrue(conf.transcribe_ready())
|
||||
|
||||
def test_a_hosted_provider_is_ready_when_it_has_a_key(self):
|
||||
conf = self.config(transcribe_provider="openai", openai_api_key="sk-test")
|
||||
self.assertTrue(conf.transcribe_ready())
|
||||
|
||||
def test_the_settings_reach_the_servers(self):
|
||||
conf = self.config(local_model="ggml-base.bin", local_threads=4,
|
||||
local_gpu=False, local_llm_model="gemma.gguf",
|
||||
local_llm_context=4096)
|
||||
conf.apply_local()
|
||||
self.addCleanup(ggml.whisper.configure, model="", threads=0, gpu=True)
|
||||
self.assertEqual(ggml.whisper.settings()["model"], "ggml-base.bin")
|
||||
self.assertEqual(ggml.whisper.settings()["threads"], 4)
|
||||
self.assertFalse(ggml.whisper.settings()["gpu"])
|
||||
self.assertEqual(ggml.llm.settings()["context"], 4096)
|
||||
|
||||
@@ -197,7 +197,7 @@ class Transcriber(DikteTest):
|
||||
|
||||
def test_cleanup_is_told_it_is_writing_subtitles(self):
|
||||
_, _, _, cleanup_call = self.run_chain(cleanup=True)
|
||||
prompt = cleanup_call.call_args.args[3]
|
||||
prompt = cleanup_call.call_args.args[2]
|
||||
self.assertEqual(prompt, self.conf.cleanup_prompt(subtitles=True))
|
||||
|
||||
def test_timestamps_come_back_as_segments_and_as_stamped_lines(self):
|
||||
|
||||
@@ -41,8 +41,18 @@ CHANGED = {
|
||||
"transcribe_model": "whisper-1",
|
||||
"openrouter_transcribe_model": "openai/whisper-1",
|
||||
"cleanup_enabled": False,
|
||||
"cleanup_provider": "local",
|
||||
"cleanup_model": "some/other-model",
|
||||
"cleanup_reasoning": "high",
|
||||
"local_model": "ggml-small.bin",
|
||||
"local_gpu": False,
|
||||
"local_preload": False,
|
||||
"local_threads": 6,
|
||||
"local_llm_model": "gemma-3-4b-it-Q4_K_M.gguf",
|
||||
"local_llm_repo": "ggml-org/gemma-4-E2B-it-GGUF",
|
||||
"local_llm_gpu": False,
|
||||
"local_llm_preload": True,
|
||||
"local_llm_reasoning": "low",
|
||||
"cleanup_prompt": "Only fix the punctuation.",
|
||||
"file_cleanup_prompt": "Keep the stamps where they are.",
|
||||
"transcribe_prompt": "Paraşüt, OpenFrame",
|
||||
@@ -259,3 +269,53 @@ class Overlay(DikteTest):
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class LocalModels(DikteTest):
|
||||
"""The download boxes, without a network and without either program."""
|
||||
|
||||
def window(self, conf):
|
||||
window = settings_ui.SettingsWindow(conf, "dikte toggle")
|
||||
self.addCleanup(window.deleteLater)
|
||||
self.addCleanup(window.close)
|
||||
return window
|
||||
|
||||
def test_it_opens_where_the_missing_model_is_fixed(self):
|
||||
# Nothing can transcribe on a fresh install, which is why this window
|
||||
# was opened at all.
|
||||
window = self.window(cfg.Config())
|
||||
self.assertEqual(window.tabs.currentIndex(), window.api_tab_index)
|
||||
|
||||
def test_it_opens_where_it_was_left_when_everything_works(self):
|
||||
conf = self.config(transcribe_provider="openai", openai_api_key="sk-test")
|
||||
self.assertEqual(self.window(conf).tabs.currentIndex(), 0)
|
||||
|
||||
def test_a_model_that_is_not_here_yet_survives_a_save(self):
|
||||
# The box is filled from what is on this disk, so a model that was
|
||||
# deleted from underneath is not in the list. Dropping it on save would
|
||||
# quietly empty the setting instead of asking for the download again.
|
||||
conf = self.config(local_model="ggml-large-v3-turbo-q5_0.bin")
|
||||
with mock.patch.object(QMessageBox, "information"):
|
||||
self.window(conf)._save()
|
||||
self.assertEqual(conf["local_model"], "ggml-large-v3-turbo-q5_0.bin")
|
||||
|
||||
def test_nothing_is_fetched_for_a_window_nobody_opened(self):
|
||||
# DikteTest closes the network, so a request would fail the test. The
|
||||
# lists are asked for when the box is shown, not when it is built.
|
||||
window = self.window(cfg.Config())
|
||||
self.assertTrue(window.local_whisper._pending)
|
||||
|
||||
def test_the_hosted_boxes_go_away_when_the_work_happens_here(self):
|
||||
window = self.window(self.config(transcribe_provider="openai"))
|
||||
self.assertTrue(window.hosted_stt.isVisibleTo(window))
|
||||
self.assertFalse(window.local_whisper.isVisibleTo(window))
|
||||
window._select_data(window.transcribe_provider, "local")
|
||||
self.assertFalse(window.hosted_stt.isVisibleTo(window))
|
||||
self.assertTrue(window.local_whisper.isVisibleTo(window))
|
||||
|
||||
def test_the_same_for_cleanup(self):
|
||||
window = self.window(cfg.Config())
|
||||
self.assertTrue(window.hosted_cleanup.isVisibleTo(window))
|
||||
window._select_data(window.cleanup_provider, "local")
|
||||
self.assertTrue(window.local_llm.isVisibleTo(window))
|
||||
self.assertFalse(window.hosted_cleanup.isVisibleTo(window))
|
||||
|
||||
@@ -234,7 +234,7 @@ class Chain(DikteTest):
|
||||
self.assertEqual(row["raw"], "uh, book it for Thursday")
|
||||
self.assertEqual(row["text"], "Book it for Thursday.")
|
||||
self.assertEqual(row["duration"], 2.0)
|
||||
self.assertEqual(row["model"], self.conf["transcribe_model"])
|
||||
self.assertEqual(row["model"], self.conf.transcribe_target().model)
|
||||
self.assertEqual(row["mode"], "")
|
||||
|
||||
def test_a_command_is_recorded_as_one(self):
|
||||
|
||||
Reference in New Issue
Block a user