mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-12 03:16:19 +00:00
Say what the local models are running on
"Use the graphics card" was a flag and nothing else: whisper got -ng when it was off, llama got -ngl 99 or 0, and nobody looked at what happened next. A build without a GPU backend runs on the processor while the box stays ticked, which is what the release archives for Linux do, every time. Nothing anywhere reported whether the local server was even up. Both servers already say where the model went, in the log Dikte captures. It is read back once the server reports ready and turned into a backend, a card and, for llama, the layers it offloaded. The verdict comes from what whisper committed to -- "using X backend" and the model buffer -- rather than from the devices it merely listed: a card that is found and then fails to initialise sends it back to the processor, and the listing alone would have called that a graphics card. A log that says nothing stays "could not tell" instead of being guessed at; a hand-built macOS whisper has Metal compiled in and prints no backend line at all. The state is then somewhere to be seen. Server.state() is a snapshot of the process and what it settled on, and it reaches `dikte status`, `dikte doctor` and a line under each local model box in the settings window. doctor reads the log from disk when no instance is running, so it still answers on a machine where Dikte is closed, and it says which of the three it is: what the last run used, that the last run said nothing, or that none ever ran here. Where the card was asked for and not obtained, the line says which of the two it is -- none was found, or this build carries none -- because only the second is worth replacing a download over. The tests grew a second isolation. They read ggml's own data directory, which is the real one on the machine running them, and program_path prefers a whisper-server on the PATH, so the suite answered from whatever the developer happened to have installed. Both are now the test's own. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019zqCqhmeaNT6m1GPp8ZWPp
This commit is contained in:
+62
-1
@@ -6,7 +6,7 @@ import shutil
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from PyQt6.QtCore import QEvent, QObject, QRect, Qt, QUrl, pyqtSignal
|
||||
from PyQt6.QtCore import QEvent, QObject, QRect, Qt, QTimer, QUrl, pyqtSignal
|
||||
from PyQt6.QtGui import QDesktopServices, QGuiApplication, QKeySequence, QShortcut
|
||||
from PyQt6.QtWidgets import (
|
||||
QAbstractItemView, QAbstractSpinBox, QCheckBox, QComboBox, QDialog,
|
||||
@@ -670,6 +670,57 @@ class SettingsWindow(QDialog):
|
||||
# because of that, so open it on the tab that fixes it.
|
||||
if not conf.transcribe_ready():
|
||||
self.tabs.setCurrentIndex(self.api_tab_index)
|
||||
# A model takes up to ggml.STARTUP_TIMEOUT to load, so a line written
|
||||
# once as the window opens would be wrong for most of the wait. Runs
|
||||
# only while the window is on screen: there is nobody to read it
|
||||
# otherwise, and it costs a lock and a poll() each time.
|
||||
self._local_state_timer = QTimer(self)
|
||||
self._local_state_timer.setInterval(2000)
|
||||
self._local_state_timer.timeout.connect(self._show_local_state)
|
||||
self._show_local_state()
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
self._show_local_state()
|
||||
self._local_state_timer.start()
|
||||
|
||||
def hideEvent(self, event):
|
||||
self._local_state_timer.stop()
|
||||
super().hideEvent(event)
|
||||
|
||||
def _show_local_state(self):
|
||||
"""What each model on this machine is loaded on, as it is now."""
|
||||
local = ggml.state()
|
||||
self.local_state.setText(self._local_state_text(local.get("whisper", {})))
|
||||
self.local_llm_state.setText(self._local_state_text(local.get("llama", {})))
|
||||
|
||||
@staticmethod
|
||||
def _local_state_text(entry):
|
||||
"""One line: whether the model is loaded, and what it ended up on.
|
||||
|
||||
Four answers rather than two, because "could not tell" is a real one: a
|
||||
whisper built by hand on a Mac prints nothing about its backend, and
|
||||
answering "the processor" there would be a confident lie about the one
|
||||
thing this line exists to be honest about.
|
||||
"""
|
||||
kind = ggml.accel_kind(entry)
|
||||
if kind == "off":
|
||||
return t("Not loaded.")
|
||||
# The backend and the card keep the names the server printed for them.
|
||||
detail = ggml.accel_detail(entry)
|
||||
if kind == "unknown":
|
||||
return t("Loaded; it did not say what it is running on.")
|
||||
if kind == "gpu":
|
||||
return t("Loaded on the graphics card ({detail}).", detail=detail)
|
||||
if not entry.get("gpu_wanted"):
|
||||
return t("Loaded on the processor ({detail}).", detail=detail)
|
||||
if ggml.cpu_only_build(entry):
|
||||
return t("Loaded on the processor: this build carries no graphics "
|
||||
"backend, so the box above cannot change that. A build "
|
||||
"from your distribution, or one you point at above, may "
|
||||
"reach the card.")
|
||||
return t("Loaded on the processor: the graphics card is switched on, "
|
||||
"but none was found.")
|
||||
|
||||
def _scrolled(self, page):
|
||||
"""A tab that scrolls instead of growing the window to fit."""
|
||||
@@ -903,6 +954,12 @@ class SettingsWindow(QDialog):
|
||||
options_form.addRow("", self.local_preload)
|
||||
options_form.addRow(t("Threads"), self.local_threads)
|
||||
stt_form.addRow(self.local_options)
|
||||
# What the model is actually doing, as against what the boxes above
|
||||
# ask for. The checkbox can only ask: whether a card was found is
|
||||
# decided by the build and by the machine, and is read back off the
|
||||
# server's own log once it has loaded.
|
||||
self.local_state = WrappedLabel("")
|
||||
stt_form.addRow(self.local_state)
|
||||
|
||||
self.transcribe_provider.currentIndexChanged.connect(self._provider_changed)
|
||||
outer.addWidget(stt)
|
||||
@@ -1004,6 +1061,8 @@ class SettingsWindow(QDialog):
|
||||
llm_form.addRow("", self.local_llm_preload)
|
||||
llm_form.addRow(t("Thinking"), self.local_llm_reasoning)
|
||||
orr_form.addRow(self.local_llm_options)
|
||||
self.local_llm_state = WrappedLabel("")
|
||||
orr_form.addRow(self.local_llm_state)
|
||||
|
||||
outer.addWidget(orr)
|
||||
outer.addStretch(1)
|
||||
@@ -2040,6 +2099,7 @@ class SettingsWindow(QDialog):
|
||||
self.stt_form.setRowVisible(self.transcribe_status, not local)
|
||||
self.stt_form.setRowVisible(self.local_whisper, local)
|
||||
self.stt_form.setRowVisible(self.local_options, local)
|
||||
self.stt_form.setRowVisible(self.local_state, local)
|
||||
if local:
|
||||
return
|
||||
self.transcribe_model.clear()
|
||||
@@ -2538,6 +2598,7 @@ class SettingsWindow(QDialog):
|
||||
provider != "local")
|
||||
self.cleanup_form.setRowVisible(self.local_llm, provider == "local")
|
||||
self.cleanup_form.setRowVisible(self.local_llm_options, provider == "local")
|
||||
self.cleanup_form.setRowVisible(self.local_llm_state, provider == "local")
|
||||
binary = cleanup.executable(provider)
|
||||
found = shutil.which(binary) if binary else ""
|
||||
if provider == "local":
|
||||
|
||||
Reference in New Issue
Block a user