Skip to content

Commit baa166c

Browse files
authored
add robust logging and localization
1 parent 982d107 commit baa166c

10 files changed

+6162
-0
lines changed

AutoGGUF.py

Lines changed: 930 additions & 0 deletions
Large diffs are not rendered by default.

DownloadThread.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from PyQt6.QtWidgets import *
2+
from PyQt6.QtCore import *
3+
from PyQt6.QtGui import *
4+
import os
5+
import sys
6+
import psutil
7+
import subprocess
8+
import time
9+
import signal
10+
import json
11+
import platform
12+
import requests
13+
import zipfile
14+
from datetime import datetime
15+
16+
class DownloadThread(QThread):
17+
progress_signal = pyqtSignal(int)
18+
finished_signal = pyqtSignal(str)
19+
error_signal = pyqtSignal(str)
20+
21+
def __init__(self, url, save_path):
22+
super().__init__()
23+
self.url = url
24+
self.save_path = save_path
25+
26+
def run(self):
27+
try:
28+
response = requests.get(self.url, stream=True)
29+
response.raise_for_status()
30+
total_size = int(response.headers.get('content-length', 0))
31+
block_size = 8192
32+
downloaded = 0
33+
34+
with open(self.save_path, 'wb') as file:
35+
for data in response.iter_content(block_size):
36+
size = file.write(data)
37+
downloaded += size
38+
if total_size:
39+
progress = int((downloaded / total_size) * 100)
40+
self.progress_signal.emit(progress)
41+
42+
# Extract the downloaded zip file
43+
extract_dir = os.path.splitext(self.save_path)[0]
44+
with zipfile.ZipFile(self.save_path, 'r') as zip_ref:
45+
zip_ref.extractall(extract_dir)
46+
47+
# Remove the zip file after extraction
48+
os.remove(self.save_path)
49+
50+
self.finished_signal.emit(extract_dir)
51+
except Exception as e:
52+
self.error_signal.emit(str(e))
53+
if os.path.exists(self.save_path):
54+
os.remove(self.save_path)

KVOverrideEntry.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from PyQt6.QtWidgets import QWidget, QHBoxLayout, QLineEdit, QComboBox, QPushButton
2+
from PyQt6.QtCore import pyqtSignal
3+
4+
class KVOverrideEntry(QWidget):
5+
deleted = pyqtSignal(QWidget)
6+
7+
def __init__(self, parent=None):
8+
super().__init__(parent)
9+
layout = QHBoxLayout(self)
10+
layout.setContentsMargins(0, 0, 0, 0)
11+
12+
self.key_input = QLineEdit()
13+
self.key_input.setPlaceholderText("Key")
14+
layout.addWidget(self.key_input)
15+
16+
self.type_combo = QComboBox()
17+
self.type_combo.addItems(["int", "str", "float"])
18+
layout.addWidget(self.type_combo)
19+
20+
self.value_input = QLineEdit()
21+
self.value_input.setPlaceholderText("Value")
22+
layout.addWidget(self.value_input)
23+
24+
delete_button = QPushButton("X")
25+
delete_button.setFixedSize(30, 30)
26+
delete_button.clicked.connect(self.delete_clicked)
27+
layout.addWidget(delete_button)
28+
29+
def delete_clicked(self):
30+
self.deleted.emit(self)
31+
32+
def get_override_string(self):
33+
return f"{self.key_input.text()}={self.type_combo.currentText()}:{self.value_input.text()}"

Logger.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import logging
2+
from logging.handlers import RotatingFileHandler
3+
import os
4+
import sys
5+
from datetime import datetime
6+
7+
class Logger:
8+
def __init__(self, name, log_dir):
9+
self.logger = logging.getLogger(name)
10+
self.logger.setLevel(logging.DEBUG)
11+
12+
# Create logs directory if it doesn't exist
13+
os.makedirs(log_dir, exist_ok=True)
14+
15+
# Console handler
16+
console_handler = logging.StreamHandler()
17+
console_handler.setLevel(logging.INFO)
18+
console_format = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
19+
console_handler.setFormatter(console_format)
20+
21+
# File handler
22+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
23+
log_file = os.path.join(log_dir, f"latest_{timestamp}.log")
24+
file_handler = RotatingFileHandler(log_file, maxBytes=10*1024*1024, backupCount=5, encoding='utf-8')
25+
file_handler.setLevel(logging.DEBUG)
26+
file_format = logging.Formatter('%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s')
27+
file_handler.setFormatter(file_format)
28+
29+
# Add handlers to logger
30+
self.logger.addHandler(console_handler)
31+
self.logger.addHandler(file_handler)
32+
33+
def debug(self, message):
34+
self.logger.debug(message)
35+
36+
def info(self, message):
37+
self.logger.info(message)
38+
39+
def warning(self, message):
40+
self.logger.warning(message)
41+
42+
def error(self, message):
43+
self.logger.error(message)
44+
45+
def critical(self, message):
46+
self.logger.critical(message)

ModelInfoDialog.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from PyQt6.QtWidgets import *
2+
from PyQt6.QtCore import *
3+
from PyQt6.QtGui import *
4+
import os
5+
import sys
6+
import psutil
7+
import subprocess
8+
import time
9+
import signal
10+
import json
11+
import platform
12+
import requests
13+
import zipfile
14+
from datetime import datetime
15+
16+
class ModelInfoDialog(QDialog):
17+
def __init__(self, model_info, parent=None):
18+
super().__init__(parent)
19+
self.setWindowTitle("Model Information")
20+
self.setGeometry(200, 200, 600, 400)
21+
22+
layout = QVBoxLayout()
23+
24+
info_text = QTextEdit()
25+
info_text.setReadOnly(True)
26+
info_text.setHtml(self.format_model_info(model_info))
27+
28+
layout.addWidget(info_text)
29+
30+
close_button = QPushButton("Close")
31+
close_button.clicked.connect(self.accept)
32+
layout.addWidget(close_button)
33+
34+
self.setLayout(layout)
35+
36+
def format_model_info(self, model_info):
37+
html = "<h2>Model Information</h2>"
38+
html += f"<p><b>Architecture:</b> {model_info.get('architecture', 'N/A')}</p>"
39+
html += f"<p><b>Quantization Type:</b> {model_info.get('quantization_type', 'N/A')}</p>"
40+
html += f"<p><b>KV Pairs:</b> {model_info.get('kv_pairs', 'N/A')}</p>"
41+
html += f"<p><b>Tensors:</b> {model_info.get('tensors', 'N/A')}</p>"
42+
43+
html += "<h3>Key-Value Pairs:</h3>"
44+
for key, value in model_info.get('kv_data', {}).items():
45+
html += f"<p><b>{key}:</b> {value}</p>"
46+
47+
return html
48+

QuantizationThread.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from PyQt6.QtWidgets import *
2+
from PyQt6.QtCore import *
3+
from PyQt6.QtGui import *
4+
import os
5+
import sys
6+
import psutil
7+
import subprocess
8+
import time
9+
import signal
10+
import json
11+
import platform
12+
import requests
13+
import zipfile
14+
import traceback
15+
from datetime import datetime
16+
from imports_and_globals import open_file_safe
17+
18+
class QuantizationThread(QThread):
19+
output_signal = pyqtSignal(str)
20+
status_signal = pyqtSignal(str)
21+
finished_signal = pyqtSignal()
22+
error_signal = pyqtSignal(str)
23+
model_info_signal = pyqtSignal(dict)
24+
25+
def __init__(self, command, cwd, log_file):
26+
super().__init__()
27+
self.command = command
28+
self.cwd = cwd
29+
self.log_file = log_file
30+
self.process = None
31+
self.model_info = {}
32+
33+
def run(self):
34+
try:
35+
self.process = subprocess.Popen(self.command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
36+
text=True, cwd=self.cwd)
37+
with open_file_safe(self.log_file, 'w') as log:
38+
for line in self.process.stdout:
39+
line = line.strip()
40+
self.output_signal.emit(line)
41+
log.write(line + '\n')
42+
log.flush()
43+
self.status_signal.emit("In Progress")
44+
self.parse_model_info(line)
45+
self.process.wait()
46+
if self.process.returncode == 0:
47+
self.status_signal.emit("Completed")
48+
self.model_info_signal.emit(self.model_info)
49+
else:
50+
self.error_signal.emit(f"Process exited with code {self.process.returncode}")
51+
self.finished_signal.emit()
52+
except Exception as e:
53+
self.error_signal.emit(str(e))
54+
55+
def parse_model_info(self, line):
56+
if "llama_model_loader: loaded meta data with" in line:
57+
parts = line.split()
58+
self.model_info['kv_pairs'] = parts[6]
59+
self.model_info['tensors'] = parts[9]
60+
elif "general.architecture" in line:
61+
self.model_info['architecture'] = line.split('=')[-1].strip()
62+
elif line.startswith("llama_model_loader: - kv"):
63+
key = line.split(':')[2].strip()
64+
value = line.split('=')[-1].strip()
65+
self.model_info.setdefault('kv_data', {})[key] = value
66+
elif line.startswith("llama_model_loader: - type"):
67+
parts = line.split(':')
68+
if len(parts) > 1:
69+
quant_type = parts[1].strip()
70+
tensors = parts[2].strip().split()[0]
71+
self.model_info.setdefault('quantization_type', []).append(f"{quant_type}: {tensors} tensors")
72+
73+
def terminate(self):
74+
if self.process:
75+
os.kill(self.process.pid, signal.SIGTERM)
76+
self.process.wait(timeout=5)
77+
if self.process.poll() is None:
78+
os.kill(self.process.pid, signal.SIGKILL)
79+

TaskListItem.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from PyQt6.QtWidgets import *
2+
from PyQt6.QtCore import *
3+
from PyQt6.QtGui import *
4+
import os
5+
import sys
6+
import psutil
7+
import subprocess
8+
import time
9+
import signal
10+
import json
11+
import platform
12+
import requests
13+
import zipfile
14+
from datetime import datetime
15+
16+
class TaskListItem(QWidget):
17+
def __init__(self, task_name, log_file, parent=None):
18+
super().__init__(parent)
19+
self.task_name = task_name
20+
self.log_file = log_file
21+
self.status = "Pending"
22+
layout = QHBoxLayout(self)
23+
self.task_label = QLabel(task_name)
24+
self.progress_bar = QProgressBar()
25+
self.progress_bar.setRange(0, 100)
26+
self.status_label = QLabel(self.status)
27+
layout.addWidget(self.task_label)
28+
layout.addWidget(self.progress_bar)
29+
layout.addWidget(self.status_label)
30+
self.progress_timer = QTimer(self)
31+
self.progress_timer.timeout.connect(self.update_progress)
32+
self.progress_value = 0
33+
34+
def update_status(self, status):
35+
self.status = status
36+
self.status_label.setText(status)
37+
if status == "In Progress":
38+
self.progress_bar.setRange(0, 100)
39+
self.progress_timer.start(100)
40+
elif status == "Completed":
41+
self.progress_timer.stop()
42+
self.progress_bar.setValue(100)
43+
elif status == "Canceled":
44+
self.progress_timer.stop()
45+
self.progress_bar.setValue(0)
46+
47+
def set_error(self):
48+
self.status = "Error"
49+
self.status_label.setText("Error")
50+
self.status_label.setStyleSheet("color: red;")
51+
self.progress_bar.setRange(0, 100)
52+
self.progress_timer.stop()
53+
54+
def update_progress(self):
55+
self.progress_value = (self.progress_value + 1) % 101
56+
self.progress_bar.setValue(self.progress_value)
57+

imports_and_globals.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import os
2+
import sys
3+
import psutil
4+
import subprocess
5+
import time
6+
import signal
7+
import json
8+
import platform
9+
import requests
10+
import zipfile
11+
from datetime import datetime
12+
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QPushButton,
13+
QListWidget, QLineEdit, QLabel, QFileDialog, QProgressBar, QComboBox, QTextEdit,
14+
QCheckBox, QGroupBox, QFormLayout, QScrollArea, QSlider, QSpinBox, QListWidgetItem,
15+
QMessageBox, QDialog, QPlainTextEdit, QMenu)
16+
from PyQt6.QtCore import QTimer, QThread, pyqtSignal, Qt, QSize
17+
from PyQt6.QtGui import QCloseEvent, QAction
18+
19+
def ensure_directory(path):
20+
if not os.path.exists(path):
21+
os.makedirs(path)
22+
23+
def open_file_safe(file_path, mode='r'):
24+
encodings = ['utf-8', 'latin-1', 'ascii', 'utf-16']
25+
for encoding in encodings:
26+
try:
27+
return open(file_path, mode, encoding=encoding)
28+
except UnicodeDecodeError:
29+
continue
30+
raise ValueError(f"Unable to open file {file_path} with any of the encodings: {encodings}")

0 commit comments

Comments
 (0)