Merge master, and let one function pick the archive

master grew _pick_asset() while this branch grew a second answer to the
same question inside install_program(). Two functions deciding which
archive this machine wants is one too many, so the managed build is
asked for at the top of the picker: Dikte's own Vulkan whisper-server
first where the machine is one it is built for, then upstream's newest,
then the nightly pointer, then the newest release that carries a build.

install_program() is back to master's three lines and reads which of the
two landed off the asset name.
This commit is contained in:
2026-09-05 10:06:38 +03:00
12 changed files with 431 additions and 60 deletions
+17
View File
@@ -53,6 +53,16 @@ class TimestampModel(unittest.TestCase):
self.assertEqual(api.timestamp_model("openai", "gpt-4o-transcribe"),
"whisper-1")
def test_openrouter_takes_the_file_model_that_was_set(self):
self.assertEqual(
api.timestamp_model("openrouter", "openai/gpt-4o-transcribe",
"openai/whisper-large-v3"),
"openai/whisper-large-v3")
def test_openrouter_with_no_file_model_falls_back_to_whisper(self):
self.assertEqual(api.timestamp_model("openrouter", "openai/gpt-4o-transcribe", ""),
"openai/whisper-1")
class Explain(DikteTest):
def error(self, status):
@@ -318,6 +328,13 @@ class TranscribeSegments(DikteTest):
api.transcribe_segments(OPENROUTER, self.wav)
self.assertEqual(multipart_fields(calls[0])["model"], "openai/whisper-1")
def test_openrouter_asks_for_the_file_model_when_one_is_set(self):
target = OPENROUTER._replace(file_model="mistralai/voxtral-mini-transcribe")
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"],
"mistralai/voxtral-mini-transcribe")
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:
+5
View File
@@ -9,6 +9,7 @@ socket is faked, and everything that runs locally runs for real.
import contextlib
import io
import json
import sys
import unittest
import webbrowser
from typing import ClassVar
@@ -668,7 +669,11 @@ class WithoutAnInstance(DikteTest):
def run_verb(self, argv):
# launch_gui replaces this process with the application, so it never
# comes back in real use and must not be allowed to here.
# `ask` with no text reads what was piped in, and the runner's own
# stdin is not that: under pytest it is an object that refuses to be
# read at all.
with mock.patch.object(ipc, "send", return_value=None), \
mock.patch.object(sys, "stdin", io.StringIO()), \
mock.patch.object(cli, "launch_gui") as launch, \
captured() as (out, err):
code = cli.run(argv)
+13
View File
@@ -220,6 +220,19 @@ class TranscribeTarget(DikteTest):
self.assertEqual(target.service, "OpenRouter")
self.assertEqual(target.api_key, "sk-or-test")
self.assertEqual(target.model, "openai/whisper-1")
self.assertEqual(target.file_model, "")
def test_openrouter_carries_its_file_model(self):
conf = self.config(transcribe_provider="openrouter",
openrouter_api_key="sk-or-test",
openrouter_file_model=" openai/whisper-large-v3 ")
self.assertEqual(conf.transcribe_target().file_model,
"openai/whisper-large-v3")
def test_only_openrouter_has_a_file_model(self):
conf = self.config(transcribe_provider="openai", openai_api_key="sk-test",
openrouter_file_model="openai/whisper-large-v3")
self.assertEqual(conf.transcribe_target().file_model, "")
def test_groq_when_it_is_picked(self):
conf = self.config(transcribe_provider="groq", groq_api_key="gsk-test",
+38
View File
@@ -240,6 +240,44 @@ class InstallProgram(Local):
"whisper-bin-ubuntu-x64.tar.gz")
self.assertTrue(urls[1].endswith("whisper-bin-ubuntu-x64.tar.gz"))
def test_the_nightly_pointer_is_followed_to_where_the_builds_are(self):
"""llama.cpp's latest release carries a tag name, not the binaries."""
self.patch_attr(ggml, "_arch", lambda: "x64")
self.patch_attr(ggml, "_has_vulkan", lambda: False)
marker = self.release(ggml.NIGHTLY_TAG)
nightly = dict(self.release("llama-b10809-bin-ubuntu-x64.tar.gz"),
tag_name="b10809")
def opener(request, timeout=None):
url = request.full_url
if url.endswith("/releases/latest"):
return json_body(marker)
if url.endswith("/releases/tags/b10809"):
return json_body(nightly)
if url.endswith(ggml.NIGHTLY_TAG):
return body(b"b10809\n")
return body(self.archive)
with mock.patch("urllib.request.urlopen", side_effect=opener):
tag, found = ggml._pick_asset(ggml.LLAMA)
self.assertEqual(tag, "b10809")
self.assertEqual(found.name, "llama-b10809-bin-ubuntu-x64.tar.gz")
def test_without_a_pointer_the_newest_release_that_has_a_build_is_taken(self):
self.patch_attr(ggml, "_arch", lambda: "x64")
self.patch_attr(ggml, "_has_vulkan", lambda: False)
marker = self.release("source.zip")
listing = [dict(self.release("llama-b2-bin-win-cpu-x64.zip"), tag_name="b2"),
dict(self.release("llama-b1-bin-ubuntu-x64.tar.gz"), tag_name="b1")]
def opener(request, timeout=None):
url = request.full_url
return json_body(listing if "per_page" in url else marker)
with mock.patch("urllib.request.urlopen", side_effect=opener):
tag, found = ggml._pick_asset(ggml.LLAMA)
self.assertEqual(tag, "b1")
self.assertEqual(found.name, "llama-b1-bin-ubuntu-x64.tar.gz")
def test_linux_x64_with_vulkan_takes_diktes_accelerated_build(self):
self.patch_attr(ggml, "_arch", lambda: "x64")
self.patch_attr(ggml, "_has_vulkan", lambda: True)
+5 -2
View File
@@ -42,9 +42,12 @@ class Directories(unittest.TestCase):
def test_a_mac_does_not_read_the_xdg_variables(self):
"""A Mac with them set from some other tool still stores in one place."""
with mock.patch.dict(os.environ, {"XDG_CONFIG_HOME": "/c"}):
# Something no temporary directory can be called: the home this runs
# under is a mkdtemp path, and a two-letter needle matched the "/c" in
# somebody's TMPDIR rather than the variable being read.
with mock.patch.dict(os.environ, {"XDG_CONFIG_HOME": "/xdg-elsewhere"}):
config_dir, _ = paths.directories("darwin")
self.assertNotIn("/c", config_dir.as_posix())
self.assertNotIn("xdg-elsewhere", config_dir.as_posix())
def test_windows_keeps_the_models_out_of_the_roaming_profile(self):
"""Settings roam with the account; several gigabytes must not."""
+85 -1
View File
@@ -9,6 +9,7 @@ next time anybody presses Save. That is the failure this catches.
import json
import os
import sys
import time
import unittest
from typing import ClassVar
from unittest import mock
@@ -22,12 +23,13 @@ from dikte import cleanup
from dikte import config as cfg
from dikte import ggml
from dikte import hotkey
from dikte.i18n import t
from dikte import hub
from dikte import ipc
from dikte import overlay as overlay_module
from dikte import paste
from dikte import settings_ui
from dikte import update
from dikte.i18n import t
from tests.support import DikteTest, only_these_tools
# The harness below replaces this method on the class so that opening a window
@@ -506,6 +508,20 @@ class Settings(DikteTest):
self.assertEqual(conf["transcribe_model"], "gpt-4o-transcribe")
self.assertEqual(conf["groq_transcribe_model"], "whisper-large-v3")
def test_the_file_model_is_saved_and_only_shown_for_openrouter(self):
self.write_config({"transcribe_provider": "openrouter",
"openrouter_file_model": "openai/whisper-large-v3"})
conf = cfg.Config()
window = self.window(conf)
self.assertEqual(window.file_model.currentText(), "openai/whisper-large-v3")
self.assertTrue(window.stt_form.isRowVisible(window.file_model_row))
window.file_model.setCurrentText(" deepgram/nova-3 ")
window._save()
self.assertEqual(conf["openrouter_file_model"], "deepgram/nova-3")
window.transcribe_provider.setCurrentIndex(
window.transcribe_provider.findData("openai"))
self.assertFalse(window.stt_form.isRowVisible(window.file_model_row))
def test_the_provider_box_offers_every_provider_config_knows(self):
window = self.window(cfg.Config())
offered = [window.transcribe_provider.itemData(i)
@@ -1216,6 +1232,74 @@ class LocalModels(DikteTest):
for row in range(box.repo.count()))
self.assertGreaterEqual(view.minimumWidth(), widest)
@staticmethod
def _item(name, size=1 << 20):
return hub.Item(name, f"https://example.invalid/{name}", size, "")
def test_a_row_with_nothing_to_fetch_does_not_offer_a_download(self):
# The model the settings name is not in the list any more, so its row
# was rebuilt from the name alone and carries no file to fetch. The
# button stayed lit and the press did nothing at all.
box = self.window(self.config(local_llm_model="gone.gguf")).local_llm
box.load("gone.gguf", "ggml-org/SmolLM3-3B-GGUF")
self.assertEqual(box.selected(), "gone.gguf")
self.assertFalse(box.download_button.isEnabled())
self.assertIn("gone.gguf", box.status.text())
self.assertIn("publisher", box.status.text())
def test_a_model_without_its_program_does_not_say_it_is_ready(self):
# The model runs on the program above it, and "Ready" over a missing
# one is what had people asking why nothing transcribed.
box = self.window(cfg.Config()).local_whisper
path = ggml.whisper_model_path("ggml-small.bin")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(b"not really a model")
box.load("ggml-small.bin")
self.assertFalse(ggml.program_path(ggml.WHISPER))
self.assertNotIn("Ready", box.status.text())
self.assertIn("program", box.status.text())
def test_changing_the_publisher_changes_the_model(self):
# The model chosen under the old publisher is not published by the new
# one. Carried over, it was added back as "not downloaded" and selected
# again, and the box looked as though the change had not taken.
box = self.window(self.config(local_llm_model="gemma-3-4b-it-Q4_K_M.gguf",
local_llm_repo="ggml-org/gemma-3-4b-it-GGUF")).local_llm
box.load("gemma-3-4b-it-Q4_K_M.gguf", "ggml-org/gemma-3-4b-it-GGUF")
box.repo.blockSignals(True)
box.repo.setCurrentText("ggml-org/SmolLM3-3B-GGUF")
box.repo.blockSignals(False)
box._on_listed([("models", [self._item("SmolLM3-Q4_K_M.gguf")],
"ggml-org/SmolLM3-3B-GGUF")], "")
self.assertEqual(box.selected(), "SmolLM3-Q4_K_M.gguf")
self.assertEqual(box.model.count(), 1)
def test_a_list_for_a_publisher_that_is_no_longer_chosen_is_dropped(self):
# Every change starts its own request, and they do not come back in the
# order they went out.
box = self.window(cfg.Config()).local_llm
box.load("", "ggml-org/SmolLM3-3B-GGUF")
box.repo.blockSignals(True)
box.repo.setCurrentText("ggml-org/SmolLM3-3B-GGUF")
box.repo.blockSignals(False)
box._on_listed([("models", [self._item("SmolLM3-Q4_K_M.gguf")],
"ggml-org/SmolLM3-3B-GGUF")], "")
box._on_listed([("models", [self._item("gemma-3-4b-it-Q4_K_M.gguf")],
"ggml-org/gemma-3-4b-it-GGUF")], "")
self.assertEqual(box.selected(), "SmolLM3-Q4_K_M.gguf")
def test_the_publisher_box_is_not_asked_on_every_keystroke(self):
box = self.window(cfg.Config()).local_llm
with mock.patch.object(box, "_fetch_models") as fetch:
for text in ("g", "gg", "ggm", "ggml-org/SmolLM3-3B-GGUF"):
box.repo.setCurrentText(text)
fetch.assert_not_called()
box._later.setInterval(0)
box._later.start()
_app.processEvents()
time.sleep(0.05)
_app.processEvents()
self.assertEqual(fetch.call_count, 1)
def test_a_processor_build_where_the_vulkan_one_belongs_says_so(self):
# The Vulkan whisper-server is published by hand, and until it is
# there the download lands upstream's processor build. Said nowhere,