feat installer and download model on first launch
This commit is contained in:
1 parent
1648b44189
commit
5d4e19f24b
6 files changed
+358
-3
No files matched your search
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user