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
+51
-1
@@ -1,15 +1,22 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import ssl
|
||||
import time
|
||||
import urllib.request
|
||||
import uuid
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import certifi
|
||||
|
||||
# CUDA 13.x moved its runtime DLLs to bin/x64, but llama_cpp's loader only
|
||||
# checks CUDA_PATH/bin, so point it at the right folder before importing.
|
||||
# Only relevant when running from source against a system CUDA toolkit - a
|
||||
# packaged build ships the runtime DLLs next to llama_cpp's own, so this is a
|
||||
# no-op there (CUDA_PATH won't be set on a machine with just an NVIDIA driver).
|
||||
_cuda_path = os.environ.get("CUDA_PATH")
|
||||
if _cuda_path:
|
||||
_cuda_bin_x64 = Path(_cuda_path) / "bin" / "x64"
|
||||
@@ -19,7 +26,16 @@ if _cuda_path:
|
||||
from ddgs import DDGS # noqa: E402
|
||||
from llama_cpp import Llama # noqa: E402
|
||||
|
||||
MODEL_PATH = Path(__file__).parent / "Qwen3.5-27B-IQ4_XS.gguf"
|
||||
# Model lives outside the install/source directory - a packaged build's own
|
||||
# folder may be read-only (Program Files) or discarded on update, and the
|
||||
# model is far too large to embed in an installer, so it's fetched into the
|
||||
# user's profile on first run instead.
|
||||
MODEL_FILENAME = "Qwen3.5-27B-IQ4_XS.gguf"
|
||||
MODELS_DIR = Path.home() / ".dev_assist_chatbot" / "models"
|
||||
MODEL_PATH = MODELS_DIR / MODEL_FILENAME
|
||||
MODEL_DOWNLOAD_URL = (
|
||||
"https://huggingface.co/unsloth/Qwen3.5-27B-GGUF/resolve/main/Qwen3.5-27B-IQ4_XS.gguf"
|
||||
)
|
||||
|
||||
SYSTEM_PROMPT = (
|
||||
"You are a knowledgeable developer assistant. Answer questions about "
|
||||
@@ -192,6 +208,40 @@ def build_messages(history: list[tuple[str, str]], question: str) -> list[dict[s
|
||||
return messages
|
||||
|
||||
|
||||
def download_model(on_progress: Callable[[int, int], None] | None = None) -> None:
|
||||
"""Fetch MODEL_PATH from MODEL_DOWNLOAD_URL if it isn't already present.
|
||||
Downloads to a .part file alongside the target and renames on success, so
|
||||
a crash or interrupted download can't leave a truncated file that
|
||||
load_model() would then try (and fail) to use. If given, on_progress is
|
||||
invoked with (bytes_downloaded, total_bytes) as each chunk arrives -
|
||||
total_bytes is 0 if the server doesn't report a Content-Length."""
|
||||
if MODEL_PATH.exists():
|
||||
return
|
||||
MODELS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
part_path = MODEL_PATH.with_suffix(MODEL_PATH.suffix + ".part")
|
||||
|
||||
request = urllib.request.Request(
|
||||
MODEL_DOWNLOAD_URL, headers={"User-Agent": "dev-assist-chatbot"}
|
||||
)
|
||||
# A fresh/sandboxed Windows install may not have fetched the intermediate
|
||||
# CA cert for huggingface.co's chain yet (normally done transparently via
|
||||
# Windows Update on first need, which a locked-down or offline sandbox
|
||||
# can't reach) - use certifi's bundled CA list instead of the OS trust
|
||||
# store so this doesn't depend on that happening.
|
||||
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
with urllib.request.urlopen(request, context=ssl_context) as response:
|
||||
total = int(response.headers.get("Content-Length", 0))
|
||||
downloaded = 0
|
||||
with open(part_path, "wb") as f:
|
||||
while chunk := response.read(1024 * 1024):
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
if on_progress is not None:
|
||||
on_progress(downloaded, total)
|
||||
|
||||
part_path.rename(MODEL_PATH)
|
||||
|
||||
|
||||
def load_model() -> Llama:
|
||||
return Llama(
|
||||
model_path=str(MODEL_PATH),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# PyInstaller spec for Dev Assist Chatbot.
|
||||
#
|
||||
# Build (from the repo root, in an environment with the CUDA 13.x toolkit
|
||||
# installed - the toolkit is only needed at BUILD time, to copy its runtime
|
||||
# DLLs into the bundle; end users only need an NVIDIA driver):
|
||||
#
|
||||
# uv run pyinstaller packaging/build.spec --noconfirm
|
||||
#
|
||||
# Output lands in dist/DevAssistChatbot/. That whole folder is the app -
|
||||
# onedir rather than onefile, since unpacking a multi-GB bundle to a temp
|
||||
# dir on every launch would make startup unusable.
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from PyInstaller.utils.hooks import collect_submodules
|
||||
|
||||
PROJECT_ROOT = Path(SPECPATH).parent # noqa: F821 - injected by PyInstaller
|
||||
VENV_SITE_PACKAGES = PROJECT_ROOT / ".venv" / "Lib" / "site-packages"
|
||||
LLAMA_CPP_LIB = VENV_SITE_PACKAGES / "llama_cpp" / "lib"
|
||||
|
||||
if not LLAMA_CPP_LIB.is_dir():
|
||||
raise SystemExit(
|
||||
f"Expected llama_cpp's compiled libs at {LLAMA_CPP_LIB} - run this via "
|
||||
"'uv run pyinstaller ...' from the repo root so the project venv is used."
|
||||
)
|
||||
|
||||
CUDA_PATH = os.environ.get("CUDA_PATH")
|
||||
if not CUDA_PATH:
|
||||
raise SystemExit(
|
||||
"CUDA_PATH is not set. Building requires the CUDA toolkit that "
|
||||
"llama-cpp-python was compiled against, so its runtime DLLs can be "
|
||||
"copied into the bundle (end users won't need the toolkit installed)."
|
||||
)
|
||||
CUDA_BIN = Path(CUDA_PATH) / "bin" / "x64"
|
||||
|
||||
# llama.cpp's ggml backend (ggml-cuda.dll etc.) is loaded dynamically at
|
||||
# runtime, not linked at import time, so PyInstaller's static analysis can't
|
||||
# discover it - every DLL in llama_cpp/lib/ has to be added explicitly. They
|
||||
# all go into the same output folder so Windows' DLL search (which checks a
|
||||
# loaded DLL's own directory for its dependencies) can resolve cublas from
|
||||
# next to ggml-cuda.dll.
|
||||
CUDA_RUNTIME_DLLS = [
|
||||
"cudart64_13.dll",
|
||||
"cublas64_13.dll",
|
||||
"cublasLt64_13.dll",
|
||||
"nvrtc64_130_0.dll",
|
||||
"nvrtc-builtins64_134.dll",
|
||||
]
|
||||
|
||||
binaries = [(str(dll), "llama_cpp/lib") for dll in LLAMA_CPP_LIB.glob("*.dll")]
|
||||
for name in CUDA_RUNTIME_DLLS:
|
||||
dll_path = CUDA_BIN / name
|
||||
if not dll_path.is_file():
|
||||
raise SystemExit(f"Expected CUDA runtime DLL not found: {dll_path}")
|
||||
binaries.append((str(dll_path), "llama_cpp/lib"))
|
||||
|
||||
# ddgs picks its search backend (duckduckgo, bing, ...) via importlib at
|
||||
# runtime rather than a static import, so PyInstaller won't find those
|
||||
# engine modules on its own either.
|
||||
hidden_imports = collect_submodules("ddgs") + ["pynvml"]
|
||||
|
||||
a = Analysis( # noqa: F821
|
||||
[str(PROJECT_ROOT / "main.py")],
|
||||
pathex=[str(PROJECT_ROOT)],
|
||||
binaries=binaries,
|
||||
datas=[],
|
||||
hiddenimports=hidden_imports,
|
||||
hookspath=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure) # noqa: F821
|
||||
|
||||
exe = EXE( # noqa: F821
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name="DevAssistChatbot",
|
||||
console=False,
|
||||
)
|
||||
|
||||
coll = COLLECT( # noqa: F821
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
# UPX would try (slowly, and sometimes badly) to compress the ~650MB of
|
||||
# CUDA runtime DLLs; skip it.
|
||||
upx=False,
|
||||
name="DevAssistChatbot",
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
; Inno Setup script for Dev Assist Chatbot.
|
||||
;
|
||||
; Build the app first (packaging/build.spec), then compile this with the
|
||||
; Inno Setup Compiler (ISCC.exe) or the IDE, from the repo root:
|
||||
;
|
||||
; iscc packaging\installer.iss
|
||||
;
|
||||
; Output lands in packaging\Output\DevAssistChatbot-Setup.exe.
|
||||
|
||||
#define MyAppName "Dev Assist Chatbot"
|
||||
#define MyAppVersion "0.1.0"
|
||||
#define MyAppExeName "DevAssistChatbot.exe"
|
||||
|
||||
[Setup]
|
||||
AppId={{A9F1D6C2-4B3E-4A7F-9C1D-3E5B7A2F8D41}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
DefaultDirName={autopf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputDir=Output
|
||||
OutputBaseFilename=DevAssistChatbot-Setup
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
WizardStyle=modern
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"
|
||||
|
||||
[Files]
|
||||
Source: "..\dist\DevAssistChatbot\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; Optional: bundle the VC++ redistributable for machines that don't already
|
||||
; have it (most Windows 11 installs do, via Windows Update or another app).
|
||||
; Download it from https://aka.ms/vs/17/release/vc_redist.x64.exe, place it
|
||||
; at packaging\redist\vc_redist.x64.exe, then uncomment these two lines and
|
||||
; the matching [Run] entry below.
|
||||
; Source: "redist\vc_redist.x64.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
; Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ runtime..."; Check: not VCRedistInstalled
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "Launch {#MyAppName}"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Code]
|
||||
function VCRedistInstalled: Boolean;
|
||||
begin
|
||||
// Present once the VC++ 2015-2022 x64 runtime is installed (also true on
|
||||
// most Windows 11 machines already, via Windows Update or another app).
|
||||
Result := RegKeyExists(HKLM64, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\X64');
|
||||
end;
|
||||
@@ -5,6 +5,7 @@ description = ""
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"certifi>=2025.1.31",
|
||||
"ddgs>=9.16.0",
|
||||
"llama-cpp-python>=0.3.35",
|
||||
"nvidia-ml-py>=13.610.43",
|
||||
@@ -14,6 +15,8 @@ dependencies = [
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pyinstaller>=6.11",
|
||||
"pyinstaller-hooks-contrib>=2025.8",
|
||||
"pytest>=8.3",
|
||||
"ruff>=0.8",
|
||||
]
|
||||
|
||||
@@ -2,6 +2,24 @@ version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
|
||||
[[package]]
|
||||
name = "altgraph"
|
||||
version = "0.17.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428, upload-time = "2025-11-21T20:35:50.583Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228, upload-time = "2025-11-21T20:35:49.444Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.7.22"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.5.0"
|
||||
@@ -186,6 +204,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/b7/44edd7de434181c582892e68d1ffe6775ca403ce14aea07cb5a218a936cf/lxml-6.1.3-cp315-cp315t-win_arm64.whl", hash = "sha256:5a721a98c649855963811b59b55755b30566e7f7fc40bdc9803d66dee9f811cf", size = 3822836, upload-time = "2026-09-02T14:51:42.471Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macholib"
|
||||
version = "1.16.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "altgraph" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362", size = 59427, upload-time = "2025-11-22T08:28:38.373Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/d1/a9f36f8ecdf0fb7c9b1e78c8d7af12b8c8754e74851ac7b94a8305540fc7/macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea", size = 38117, upload-time = "2025-11-22T08:28:36.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.3"
|
||||
@@ -340,6 +370,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pefile"
|
||||
version = "2024.8.26"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/03/4f/2750f7f6f025a1507cd3b7218691671eecfd0bbebebe8b39aa0fe1d360b8/pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632", size = 76008, upload-time = "2024-08-26T20:58:38.155Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/16/12b82f791c7f50ddec566873d5bdd245baa1491bac11d15ffb98aecc8f8b/pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f", size = 74766, upload-time = "2024-08-26T21:01:02.632Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
@@ -396,6 +435,46 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller"
|
||||
version = "6.22.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "altgraph" },
|
||||
{ name = "macholib", marker = "sys_platform == 'darwin'" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pefile", marker = "sys_platform == 'win32'" },
|
||||
{ name = "pyinstaller-hooks-contrib" },
|
||||
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
|
||||
{ name = "setuptools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/63/41/f90302845945abd4ed647933ff5ee7c6ac93983187be67f897b6cb613331/pyinstaller-6.22.3.tar.gz", hash = "sha256:05eb2f5615503e72939a7224d68b4aff572c6b0438ee4a17d0a4b481f399362d", size = 4437180, upload-time = "2026-09-12T21:54:09.669Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/af/898f70947e27f8e65d55183c780e77112e023c0b4a6fba6255d4d563a27d/pyinstaller-6.22.3-py3-none-macosx_10_13_universal2.whl", hash = "sha256:052f4a1cd4f81092ccb7a18fe8ebc9ecf832a5917317e39fb3775af4e959b253", size = 1071847, upload-time = "2026-09-12T21:53:01.711Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/96/99f5184985141c2b024bfa61335e9142ae444555224f303186e59b7bb438/pyinstaller-6.22.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:312da84b4b31ab9750066a823734c11b7c68757fdd67c9f038bf19c330a954e4", size = 758172, upload-time = "2026-09-12T21:53:06.171Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/9f/9ccb12cc1e6617042cb028fe79f0a756f5f03305bd14b584a4e043fe3844/pyinstaller-6.22.3-py3-none-manylinux2014_i686.whl", hash = "sha256:020d4fe0627f5c73dfbcfad1a6578ef354d5fe4bdccd9d8e5a615528f5a45d2b", size = 772135, upload-time = "2026-09-12T21:53:10.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/43/4b1d5193a88628c052db89f064433681076959a9f41d0d9f660001b49079/pyinstaller-6.22.3-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:9e4f7bd64b2103f14b4a6240fb9155e9fe7bc61391b4043bbddb7d3ebc8ab0fa", size = 770461, upload-time = "2026-09-12T21:53:14.592Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/83/f173f8757c618d83e116fa2b8e57c5cd796e5ad09792e664eabd6ac5edae/pyinstaller-6.22.3-py3-none-manylinux2014_s390x.whl", hash = "sha256:f82d0fb89f21c3ed6f482ee16f341bc59e8a31dbabb9bae9ef04e53ade4dc0d9", size = 764997, upload-time = "2026-09-12T21:53:19.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/b6/dd21d61fc5e25377be24eabf13e99e4e46548a5abe2bf7b15d8b3d9cc74a/pyinstaller-6.22.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:451a4ae14b719365bf1a2f0a99dae7b3463060061c3a394c70d5264cfb439528", size = 764974, upload-time = "2026-09-12T21:53:23.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/fd/d9590942972b1fc205519f7ccffb0d5cd8eb1d6fa61edad319c71d25baed/pyinstaller-6.22.3-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:ea240b5dfa831ff673c97505eee0ec24670bccf71396c83109440348944c83d0", size = 764450, upload-time = "2026-09-12T21:53:27.419Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/d6/f22c55d1768433b65c460b19a3136dd9769b6c74b786149b829344654c7d/pyinstaller-6.22.3-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:a2b6fc26601e8c2596c3d54981566934a3382e85d05d0fe91b55b6a8dcc6b282", size = 763863, upload-time = "2026-09-12T21:53:31.609Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/87/cf28cfd42ceca889171e6dd5e7f1553d4372db83d047049ffb7cf95fcbd4/pyinstaller-6.22.3-py3-none-win32.whl", hash = "sha256:46a3e30a118341a1ec20667a5a95aa7e0aa8c4fa129893c0151b3eb3f801a722", size = 1390905, upload-time = "2026-09-12T21:53:38.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/83/c5422204772a27aef643280fdc887b7ef55679989096b21dceb1b9625fc4/pyinstaller-6.22.3-py3-none-win_amd64.whl", hash = "sha256:500bd58c7bf7e584a8435adccbd763a0b918d5c12b08d74ff50fd79b2915458b", size = 1454907, upload-time = "2026-09-12T21:53:44.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/2e/ebc93968bf63f81b122e205da116b16036b653b7ce2cc8ad86e3ddd19586/pyinstaller-6.22.3-py3-none-win_arm64.whl", hash = "sha256:31df3cc4261e804a53d358b7897c293ec8b88b167a7cca06c78018085c855f97", size = 1402956, upload-time = "2026-09-12T21:53:51.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller-hooks-contrib"
|
||||
version = "2026.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "packaging" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/26/60/d881fa1ba8c160c18d8e6f782bb16ec4640c08bc08fc50f704c368ad4f9e/pyinstaller_hooks_contrib-2026.7.tar.gz", hash = "sha256:5fbcaacb22c4f4aac869a127dce283f67a4b4cfcc37d496f2446603e6d68aefa", size = 175992, upload-time = "2026-08-24T21:26:58.713Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/67/350377af7b50416344ab8792756d414eef7629c618a73e9a0b13bb1552d9/pyinstaller_hooks_contrib-2026.7-py3-none-any.whl", hash = "sha256:24257a04c7a5a7a034cf28e39dcee20fbeeb9f043076729480f2e1b69904408a", size = 459445, upload-time = "2026-08-24T21:26:57.274Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyqt6"
|
||||
version = "6.11.0"
|
||||
@@ -470,6 +549,7 @@ name = "python-inference"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "ddgs" },
|
||||
{ name = "llama-cpp-python" },
|
||||
{ name = "nvidia-ml-py" },
|
||||
@@ -479,12 +559,15 @@ dependencies = [
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "pyinstaller" },
|
||||
{ name = "pyinstaller-hooks-contrib" },
|
||||
{ name = "pytest" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "certifi", specifier = ">=2025.1.31" },
|
||||
{ name = "ddgs", specifier = ">=9.16.0" },
|
||||
{ name = "llama-cpp-python", specifier = ">=0.3.35" },
|
||||
{ name = "nvidia-ml-py", specifier = ">=13.610.43" },
|
||||
@@ -494,10 +577,21 @@ requires-dist = [
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "pyinstaller", specifier = ">=6.11" },
|
||||
{ name = "pyinstaller-hooks-contrib", specifier = ">=2025.8" },
|
||||
{ name = "pytest", specifier = ">=8.3" },
|
||||
{ name = "ruff", specifier = ">=0.8" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pywin32-ctypes"
|
||||
version = "0.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.16.7"
|
||||
@@ -523,6 +617,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/4b/51327018d056f0dad2c2238f26d1fb0f53707a9d91b75dea6d1b3039f136/ruff-0.16.7-py3-none-win_arm64.whl", hash = "sha256:aab7f39e2c9df6c596216070f98eef1207b94f8516cca20c808826974971855b", size = 10412401, upload-time = "2026-09-10T18:04:04.098Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "84.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.16.0"
|
||||
|
||||
Reference in new issue
Block a user