feat installer and download model on first launch

This commit is contained in:
fsylvain committed 2026-09-19 23:53:40 +02:00
1 parent 1648b44189
commit 5d4e19f24b
6 files changed
+358 -3

No files matched your search

+54 -2
View File
@@ -408,6 +408,23 @@ def _insert_message_card(cursor: QTextCursor, accent: str, bg: str) -> QTextCurs
return body_cell.firstCursorPosition()
class ModelDownloadThread(QThread):
# qint64, not int - int maps to a 32-bit C int for this signal's
# cross-thread queued connection, which silently wraps a ~15GB byte
# count (e.g. 14_977_484_704 truncates to 2_092_582_816).
progress = pyqtSignal("qint64", "qint64")
done = pyqtSignal()
failed = pyqtSignal(str)
def run(self) -> None:
try:
chatbot.download_model(on_progress=self.progress.emit)
except Exception as e:
self.failed.emit(str(e))
return
self.done.emit()
class ModelLoaderThread(QThread):
loaded = pyqtSignal(object)
failed = pyqtSignal(str)
@@ -586,9 +603,44 @@ class ChatWindow(QMainWindow):
self._init_gpu_monitor()
self.statusBar().showMessage("Loading model...")
self._set_input_enabled(False)
self._download_thread: ModelDownloadThread | None = None
self._loader_thread: ModelLoaderThread | None = None
if chatbot.MODEL_PATH.exists():
self._start_model_load()
else:
self._start_model_download()
def _start_model_download(self) -> None:
self.statusBar().showMessage("Downloading model (first run only)... 0%")
self._append_system(
"Downloading the language model for first-time setup. This is a "
"one-time, multi-gigabyte download and may take a while."
)
self._download_thread = ModelDownloadThread()
self._download_thread.progress.connect(self._on_download_progress)
self._download_thread.done.connect(self._on_download_done)
self._download_thread.failed.connect(self._on_model_failed)
self._download_thread.start()
def _on_download_progress(self, downloaded: int, total: int) -> None:
downloaded_gb = downloaded / (1024**3)
if total:
pct = 100 * downloaded / total
total_gb = total / (1024**3)
self.statusBar().showMessage(
f"Downloading model... {downloaded_gb:.2f}/{total_gb:.2f} GB ({pct:.0f}%)"
)
else:
self.statusBar().showMessage(f"Downloading model... {downloaded_gb:.2f} GB")
def _on_download_done(self) -> None:
self._download_thread = None
self._start_model_load()
def _start_model_load(self) -> None:
self.statusBar().showMessage("Loading model...")
self._loader_thread = ModelLoaderThread()
self._loader_thread.loaded.connect(self._on_model_loaded)
self._loader_thread.failed.connect(self._on_model_failed)
@@ -995,7 +1047,7 @@ class ChatWindow(QMainWindow):
# llama.cpp calls run in native code and can't be cancelled mid-call,
# so wait for any in-flight load/generation to finish rather than
# tearing down the thread while it's still running (which crashes Qt).
for thread in (self._loader_thread, self._ask_thread):
for thread in (self._download_thread, self._loader_thread, self._ask_thread):
if thread is not None and thread.isRunning():
thread.wait()
if self._gpu_handle is not None: