Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions interface/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
psutil = None


APP_NAME = "DrunkenBot LLM-IDE"
APP_NAME = "DrunkenBot-IDE"
# Bump on every release that should require a version-ceiling check against
# licenses -- this is what license_client.check_license_at_launch compares
# against a license's version_ceiling/grace_period_until.
Expand Down Expand Up @@ -195,35 +195,31 @@ def _ensure_valid_license(splash: "StartupValidationSplash") -> bool:
"[OK] License valid"
+ (" (offline grace period)" if result.used_offline_grace else "")
)
QApplication.instance().setProperty("license_valid", True)
return True
initial_message = result.reason
else:
initial_message = "No license activated on this machine yet."

# A QSplashScreen-style window is designed to stay on top of other
# windows during startup -- which means it can end up covering a newly
# created dialog instead of the other way around. Hide it while the
# dialog is up rather than fight window-stacking order; it isn't doing
# anything useful to look at during activation anyway.
splash.hide()
try:
while True:
dialog = LicenseActivationDialog(APP_VERSION, LICENSE_SERVER_URL, initial_message)
dialog.setWindowIcon(MainWindow._static_app_icon())
dialog.show()
dialog.raise_()
dialog.activateWindow()
if dialog.exec() != QDialog.Accepted:
LOGGER.info("License activation cancelled by user; exiting.")
QApplication.instance().setProperty("startup_aborted", True)
return False
dialog = LicenseActivationDialog(APP_VERSION, LICENSE_SERVER_URL, initial_message)
dialog.setWindowIcon(MainWindow._static_app_icon())
if dialog.exec() == QDialog.Accepted and dialog.result_info is not None:
QApplication.instance().setProperty("license_valid", True)
splash.append_log("[OK] License activated")
return True
if dialog.trial_requested:
QApplication.instance().setProperty("license_valid", False)
splash.append_log("[TRIAL] User selected trial version")
return True
LOGGER.info("License activation cancelled by user; exiting.")
QApplication.instance().setProperty("startup_aborted", True)
return False
finally:
splash.show()
splash.raise_()


def main(app: Optional[QApplication] = None, splash: Optional[StartupSplash] = None) -> None:
"""Launch the PySide6 desktop application."""

Expand Down
11 changes: 10 additions & 1 deletion interface/license_activation_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ def __init__(self, app_version: str, server_url: str, initial_message: str = "")
self._app_version = app_version
self._server_url = server_url
self.result_info: LicenseCheckResult | None = None
self.trial_requested = False

self.setWindowTitle("Activate DrunkenBot LLM-IDE")
self.setWindowTitle("Activate DrunkenBot-IDE")
self.setModal(True)
self.setMinimumWidth(480)

Expand All @@ -132,7 +133,10 @@ def __init__(self, app_version: str, server_url: str, initial_message: str = "")
self._activate_button.clicked.connect(self._on_activate_clicked)
self._exit_button = QPushButton("Exit")
self._exit_button.clicked.connect(self.reject)
self._trial_button = QPushButton("Open Trial Version")
self._trial_button.clicked.connect(self._open_trial)
button_row.addWidget(self._activate_button)
button_row.addWidget(self._trial_button)
button_row.addWidget(self._exit_button)
layout.addLayout(button_row)

Expand Down Expand Up @@ -162,6 +166,11 @@ def _on_activate_clicked(self) -> None:

self._show_status(result.reason)

def _open_trial(self) -> None:
"""Close the dialog and request restricted trial mode."""
self.trial_requested = True
self.accept()

def _show_status(self, message: str) -> None:
"""Display a status/error message in the dialog.

Expand Down
12 changes: 10 additions & 2 deletions interface/main_window_part1.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ def __init__(self) -> None:
LOGGER.info("Creating %s main window", APP_NAME)
if QApplication.instance():
QApplication.instance().setFont(QFont("Arial", 10))
self.setWindowTitle(APP_NAME)
licensed = bool(QApplication.instance().property("license_valid"))
self.setWindowTitle(
f"{APP_NAME} {APP_VERSION} "
f"({'licensed' if licensed else 'Trial Version'})"
)
self.setWindowIcon(self._app_icon())
self._windows_icon_handles: list[int] = []
self.resize(1240, 820)
Expand Down Expand Up @@ -372,7 +376,11 @@ def show_chat_only_mode(self) -> None:
if hasattr(self, "side_rail"):
self.side_rail.hide()
self._switch_page(8)
self.setWindowTitle("DrunkenBot - Chat")
licensed = bool(QApplication.instance().property("license_valid"))
self.setWindowTitle(
f"{APP_NAME} {APP_VERSION} "
f"({'licensed' if licensed else 'Trial Version'})"
)
self.resize(980, 760)

def resizeEvent(self, event: Any) -> None:
Expand Down
9 changes: 7 additions & 2 deletions interface/main_window_part11.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class MainWindowPart11:
def prepare_dataset(self) -> None:
if not bool(QApplication.instance().property("license_valid")):
self.context_length.setValue(min(self.context_length.value(), 1000))
"""Collect dataset options and start dataset preparation."""

config = self._dataset_config_from_ui()
Expand Down Expand Up @@ -282,6 +284,11 @@ def _update_online_dataset_stage_controls(self) -> None:

if not hasattr(self, "dataset_stage"):
return
if not bool(QApplication.instance().property("license_valid")):
self.include_conversation_datasets.setChecked(False)
self.include_conversation_datasets.setEnabled(False)
if hasattr(self, "external_dataset_download_button"):
self.external_dataset_download_button.setEnabled(False)
stage = self._dataset_stage_value()
allowed = set(CONVERSATION_DATASET_PRESETS)
include_online = self.include_conversation_datasets.isChecked()
Expand Down Expand Up @@ -422,5 +429,3 @@ def _selected_default_data_paths(self) -> list[Path]:
for path, item in self.default_data_actions.items()
if item.checkState(0) == Qt.Checked
]


3 changes: 2 additions & 1 deletion interface/main_window_part16.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ def _run_training_preflight(self, model_config: ModelConfig, training_config: Tr
return True

def start_training(self) -> None:
if not bool(QApplication.instance().property("license_valid")):
self.train_context_length.setValue(min(self.train_context_length.value(), 1000))
"""Collect training options and start model training."""

launch_target = self._training_launch_target_value()
Expand Down Expand Up @@ -388,4 +390,3 @@ def start_training(self) -> None:
task_kind="training",
)


8 changes: 7 additions & 1 deletion interface/main_window_part17.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ def _append_chat_markdown(self, role: str, content: str) -> None:
self._add_chat_message("user" if role.lower() in {"you", "user"} else "assistant", content)

def create_bundle(self) -> None:
if not bool(QApplication.instance().property("license_valid")):
self.export_log.append("Export Bay is available only in the licensed version.")
return
"""Create a portable model export bundle."""

self.export_log.append("Creating model bundle...")
Expand All @@ -397,6 +400,8 @@ def create_bundle(self) -> None:
self.export_status.setText("Export: bundle created")

def quantize_model(self) -> None:
if not bool(QApplication.instance().property("license_valid")):
return
"""Create a quantized FP16 checkpoint when selected."""

mode = self.quant_mode.currentText()
Expand All @@ -418,6 +423,8 @@ def quantize_model(self) -> None:
self.export_status.setText("Export: FP16 checkpoint ready")

def export_hf_package(self) -> None:
if not bool(QApplication.instance().property("license_valid")):
return
"""Create an HF-style MicroGPT package."""

self.export_log.append("Creating HF-style MicroGPT package...")
Expand All @@ -433,4 +440,3 @@ def export_hf_package(self) -> None:
self.export_log.append("Note: this package is MicroGPT model_type, not a llama.cpp-supported Llama model.")
self.export_status.setText("Export: HF package ready")


5 changes: 4 additions & 1 deletion interface/main_window_part18.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class MainWindowPart18:
def export_llama_adapter(self) -> None:
if not bool(QApplication.instance().property("license_valid")):
return
"""Create a directly loadable Llama-family package when compatible."""

self.export_log.append("Creating Llama-compatible adapter package...")
Expand All @@ -23,6 +25,8 @@ def export_llama_adapter(self) -> None:
self.export_status.setText("Export: Llama adapter ready")

def convert_hf_to_gguf(self) -> None:
if not bool(QApplication.instance().property("license_valid")):
return
"""Convert an HF-compatible model folder to GGUF through llama.cpp."""

model_dir_text = self.export_model_dir.text().strip()
Expand Down Expand Up @@ -92,4 +96,3 @@ def _apply_preset(self, preset: str) -> None:
self.n_layer.setValue(8)



4 changes: 2 additions & 2 deletions interface/startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
)


APP_NAME = "DrunkenBot LLM-IDE"
APP_NAME = "DrunkenBot-IDE"
WINDOWS_APP_ID = "DrunkenBot.LLMIDE"
LOGGER = logging.getLogger(__name__)
APP_HOME_DIR = Path.home() / ".drunkenbot_ide"
Expand Down Expand Up @@ -353,7 +353,7 @@ def _build_ui(self) -> None:

body = QLabel(
"Startup checks are complete.\n"
"Choose how you want to begin with DrunkenBot LLM-IDE."
"Choose how you want to begin with DrunkenBot-IDE."
)
body.setObjectName("Body")
body.setAlignment(Qt.AlignLeft)
Expand Down
4 changes: 2 additions & 2 deletions interface/startup_splash.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class StartupSplash(QDialog):

def __init__(self) -> None:
super().__init__()
self.setWindowTitle("DrunkenBot LLM-IDE")
self.setWindowTitle("DrunkenBot-IDE")
self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.setModal(True)
self.setMinimumSize(560, 760)
Expand All @@ -42,7 +42,7 @@ def __init__(self) -> None:
else:
logo.setPixmap(pixmap.scaled(118, 118, Qt.KeepAspectRatio, Qt.SmoothTransformation))
logo.setAlignment(Qt.AlignCenter)
title = QLabel("DrunkenBot LLM-IDE")
title = QLabel("DrunkenBot-IDE")
title.setObjectName("Title")
title.setFont(QFont("Arial", 22))
header.addWidget(logo)
Expand Down
6 changes: 5 additions & 1 deletion interface/tabs/dataset_plan_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QApplication,
QCheckBox,
QComboBox,
QFormLayout,
Expand Down Expand Up @@ -265,6 +266,8 @@ def build_dataset_plan_tab(window) -> QWidget:
window.external_dataset_download_button = QPushButton("Download latest dataset")
window._tip(window.external_dataset_download_button, "Download, verify, and extract the latest dataset release into the install folder.")
window.external_dataset_download_button.clicked.connect(window.download_latest_external_dataset)
trial_mode = not bool(QApplication.instance().property("license_valid"))
window.external_dataset_download_button.setEnabled(not trial_mode)
external_form.addRow("Install folder", window._path_row(window.external_dataset_dir, directory=True))
external_form.addRow("Status", window.external_dataset_version)
external_form.addRow("", window.external_dataset_download_button)
Expand All @@ -287,6 +290,7 @@ def build_dataset_plan_tab(window) -> QWidget:
window.dataset_stage.setMaximumWidth(240)
window.include_conversation_datasets = QCheckBox("Online")
window.include_conversation_datasets.setChecked(False)
window.include_conversation_datasets.setEnabled(not trial_mode)
purpose_row = QWidget()
purpose_layout = QHBoxLayout(purpose_row)
purpose_layout.setContentsMargins(0, 0, 0, 0)
Expand Down Expand Up @@ -320,6 +324,7 @@ def build_dataset_plan_tab(window) -> QWidget:
conversation_form.addRow("Custom HF dataset", window.custom_huggingface_dataset)
window.custom_huggingface_download = QPushButton("Download custom dataset")
window.custom_huggingface_download.clicked.connect(window._download_custom_huggingface_dataset)
window.custom_huggingface_download.setEnabled(not trial_mode)
window._tip(window.custom_huggingface_download, "Enable the custom dataset and download it during the next dataset preparation run.")
conversation_form.addRow("", window.custom_huggingface_download)
window.conversation_sample_limit = window._spin(0, 2_000_000, 20000)
Expand Down Expand Up @@ -455,4 +460,3 @@ def populate_default_data_tree(window: Any, root: Path) -> None:
tree.blockSignals(False)
if not window.default_data_actions:
tree.addTopLevelItem(QTreeWidgetItem(["No project/default data files were found.", "", ""]))

2 changes: 1 addition & 1 deletion interface/tabs/dataset_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def build_dataset_tab(window) -> QWidget:
window._tip(window.auto_vocab_label, "The actual vocabulary size selected after reading the corpus.")
window.min_frequency = window._spin(1, 1000, 2)
window._tip(window.min_frequency, "Minimum token frequency for tokenizer training. Higher values remove rare fragments and can reduce noise.")
window.context_length = window._spin(16, 4096, 128)
window.context_length = window._spin(16, 1000, 128)
window._tip(window.context_length, "Number of tokens per training sequence. Longer context lets the model learn longer dependencies but uses more memory.")
window.validation_split = window._double_spin(0.0, 0.5, 0.1, 0.01, 3)
window._tip(window.validation_split, "Fraction of tokens held out for validation. Validation helps detect overfitting during training.")
Expand Down
6 changes: 5 additions & 1 deletion interface/tabs/export_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QApplication,
QComboBox,
QFormLayout,
QHBoxLayout,
Expand Down Expand Up @@ -84,6 +85,10 @@ def build_export_tab(window) -> QWidget:
window.gguf_convert_button = QPushButton("Convert HF to GGUF")
window._tip(window.gguf_convert_button, "Run llama.cpp convert_hf_to_gguf.py for model_core/hf_model when the architecture is supported by llama.cpp.")
window.gguf_convert_button.clicked.connect(window.convert_hf_to_gguf)
window.export_buttons = [bundle_button, quant_button, hf_button, llama_button, window.gguf_convert_button]
if not bool(QApplication.instance().property("license_valid")):
for button in window.export_buttons:
button.setEnabled(False)
bundle_button.setMaximumWidth(220)
quant_button.setMaximumWidth(220)
hf_button.setMaximumWidth(220)
Expand Down Expand Up @@ -116,4 +121,3 @@ def build_export_tab(window) -> QWidget:
window.export_progress = window._thin_progress()
outer.addWidget(window.export_progress)
return page

2 changes: 1 addition & 1 deletion interface/tabs/training_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def build_training_tab(window) -> QWidget:
window._tip(window.attention_window, "Sliding attention window. 0 uses full context; higher values restrict attention to recent tokens.")
window.n_layer = window._spin(1, 64, 4)
window._tip(window.n_layer, "Transformer layer count. More layers improve capacity and reasoning patterns but slow training.")
window.train_context_length = window._spin(16, 4096, 128)
window.train_context_length = window._spin(16, 1000, 128)
window._tip(window.train_context_length, "Training context length in tokens. Must fit your GPU/CPU memory.")
window.dropout = window._double_spin(0.0, 0.9, 0.1, 0.01, 3)
window._tip(window.dropout, "Dropout regularization. Higher values reduce overfitting but can slow learning.")
Expand Down
2 changes: 1 addition & 1 deletion packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

ROOT = Path(__file__).resolve().parent
OUTPUT_ROOT = ROOT / "packaging" / "artifacts"
APP_NAME = "DrunkenBot-LLM-IDE"
APP_NAME = "DrunkenBot-IDE"


def _architecture() -> str:
Expand Down
Loading