Update README.md
Browse files
README.md
CHANGED
@@ -1,3 +1,266 @@
|
|
1 |
-
---
|
2 |
-
license: apache-2.0
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
license: apache-2.0
|
3 |
+
language:
|
4 |
+
- en
|
5 |
+
base_model:
|
6 |
+
- HuggingFaceTB/SmolLM2-135M
|
7 |
+
library_name: transformers
|
8 |
+
---
|
9 |
+
# SmolLM2-135M-Instruct-GGUF
|
10 |
+
|
11 |
+
All right reserved to the original owners of the model.
|
12 |
+
For more data refer to the original model card.
|
13 |
+
https://huggingface.co/HuggingFaceTB
|
14 |
+
|
15 |
+
## Introduction
|
16 |
+
SmolLM2 is a family of compact language models available in three size: 135M, 360M, and 1.7B parameters. They are capable of solving a wide range of tasks while being lightweight enough to run on-device.
|
17 |
+
|
18 |
+
|
19 |
+
## Quickstart
|
20 |
+
SmolLM2-135M-Instruct-GGUF can be loaded and used via Llama.cpp, here is a program with GUI.
|
21 |
+
|
22 |
+
```bash
|
23 |
+
pip install PyQt5 llama-cpp-python pymupdf
|
24 |
+
```
|
25 |
+
|
26 |
+
```python
|
27 |
+
import sys
|
28 |
+
import os
|
29 |
+
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QPushButton,
|
30 |
+
QLineEdit, QTextEdit, QVBoxLayout, QHBoxLayout,
|
31 |
+
QFileDialog, QProgressBar, QMessageBox, QMenu)
|
32 |
+
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
33 |
+
from llama_cpp import Llama
|
34 |
+
import fitz # For PDF processing
|
35 |
+
|
36 |
+
class Worker(QThread):
|
37 |
+
finished = pyqtSignal(str)
|
38 |
+
progress = pyqtSignal(int, int)
|
39 |
+
|
40 |
+
def __init__(self, model, messages, max_tokens):
|
41 |
+
super().__init__()
|
42 |
+
self.model = model
|
43 |
+
self.messages = messages
|
44 |
+
self.max_tokens = max_tokens
|
45 |
+
|
46 |
+
def run(self):
|
47 |
+
try:
|
48 |
+
response = self.model.create_chat_completion(
|
49 |
+
messages=self.messages,
|
50 |
+
max_tokens=self.max_tokens,
|
51 |
+
temperature=0.7,
|
52 |
+
stream=True
|
53 |
+
)
|
54 |
+
|
55 |
+
total_tokens = 0
|
56 |
+
full_response = ""
|
57 |
+
for chunk in response:
|
58 |
+
if "choices" in chunk:
|
59 |
+
content = chunk["choices"][0]["delta"].get("content", "")
|
60 |
+
full_response += content
|
61 |
+
total_tokens += 1
|
62 |
+
self.progress.emit(total_tokens, self.max_tokens)
|
63 |
+
self.finished.emit(full_response)
|
64 |
+
except Exception as e:
|
65 |
+
self.finished.emit(f"Error generating response: {str(e)}")
|
66 |
+
|
67 |
+
class ChatbotGUI(QWidget):
|
68 |
+
def __init__(self):
|
69 |
+
super().__init__()
|
70 |
+
self.setWindowTitle("Chatbot GUI")
|
71 |
+
self.resize(800, 600)
|
72 |
+
|
73 |
+
self.model = None
|
74 |
+
self.messages = [
|
75 |
+
{"role": "system", "content": "You are a helpful AI assistant."}
|
76 |
+
]
|
77 |
+
self.thread_count = 12
|
78 |
+
self.pdf_content = ""
|
79 |
+
|
80 |
+
self.initUI()
|
81 |
+
|
82 |
+
def initUI(self):
|
83 |
+
# Model loading section
|
84 |
+
model_label = QLabel("Model: No model loaded")
|
85 |
+
load_button = QPushButton("Load GGUF Model")
|
86 |
+
load_button.clicked.connect(self.load_model)
|
87 |
+
|
88 |
+
model_layout = QHBoxLayout()
|
89 |
+
model_layout.addWidget(model_label)
|
90 |
+
model_layout.addWidget(load_button)
|
91 |
+
|
92 |
+
# PDF upload section
|
93 |
+
pdf_label = QLabel("PDF: No PDF loaded")
|
94 |
+
upload_pdf_button = QPushButton("Upload PDF")
|
95 |
+
upload_pdf_button.clicked.connect(self.upload_pdf)
|
96 |
+
|
97 |
+
pdf_layout = QHBoxLayout()
|
98 |
+
pdf_layout.addWidget(pdf_label)
|
99 |
+
pdf_layout.addWidget(upload_pdf_button)
|
100 |
+
|
101 |
+
# Thread count section
|
102 |
+
thread_label = QLabel(f"Thread Count: {self.thread_count}")
|
103 |
+
self.thread_input = QLineEdit()
|
104 |
+
self.thread_input.setPlaceholderText("Enter new thread count")
|
105 |
+
update_thread_button = QPushButton("Update Threads")
|
106 |
+
update_thread_button.clicked.connect(self.update_thread_count)
|
107 |
+
|
108 |
+
thread_layout = QHBoxLayout()
|
109 |
+
thread_layout.addWidget(thread_label)
|
110 |
+
thread_layout.addWidget(self.thread_input)
|
111 |
+
thread_layout.addWidget(update_thread_button)
|
112 |
+
|
113 |
+
# Chat display
|
114 |
+
self.chat_display = QTextEdit()
|
115 |
+
self.chat_display.setReadOnly(True)
|
116 |
+
self.chat_display.setContextMenuPolicy(Qt.CustomContextMenu)
|
117 |
+
self.chat_display.customContextMenuRequested.connect(self.show_context_menu)
|
118 |
+
|
119 |
+
# User input
|
120 |
+
self.user_input = QLineEdit()
|
121 |
+
self.user_input.returnPressed.connect(self.send_message)
|
122 |
+
send_button = QPushButton("Send")
|
123 |
+
send_button.clicked.connect(self.send_message)
|
124 |
+
|
125 |
+
input_layout = QHBoxLayout()
|
126 |
+
input_layout.addWidget(self.user_input)
|
127 |
+
input_layout.addWidget(send_button)
|
128 |
+
|
129 |
+
# Progress bar
|
130 |
+
self.progress_bar = QProgressBar()
|
131 |
+
self.progress_bar.hide()
|
132 |
+
|
133 |
+
# Clear conversation button
|
134 |
+
clear_button = QPushButton("Clear Conversation")
|
135 |
+
clear_button.clicked.connect(self.clear_conversation)
|
136 |
+
|
137 |
+
# Main layout
|
138 |
+
main_layout = QVBoxLayout()
|
139 |
+
main_layout.addLayout(model_layout)
|
140 |
+
main_layout.addLayout(pdf_layout) # PDF before threads
|
141 |
+
main_layout.addLayout(thread_layout)
|
142 |
+
main_layout.addWidget(self.chat_display)
|
143 |
+
main_layout.addWidget(self.progress_bar)
|
144 |
+
main_layout.addLayout(input_layout)
|
145 |
+
main_layout.addWidget(clear_button)
|
146 |
+
|
147 |
+
self.setLayout(main_layout)
|
148 |
+
|
149 |
+
def load_model(self):
|
150 |
+
model_path, _ = QFileDialog.getOpenFileName(self, "Load GGUF Model", "", "GGUF Files (*.gguf)")
|
151 |
+
if model_path:
|
152 |
+
try:
|
153 |
+
self.model = Llama(model_path=model_path, n_ctx=2048, n_gpu_layers=-1, n_threads=self.thread_count)
|
154 |
+
model_name = os.path.basename(model_path)
|
155 |
+
self.layout().itemAt(0).itemAt(0).widget().setText(f"Model: {model_name}")
|
156 |
+
QMessageBox.information(self, "Success", "Model loaded successfully!")
|
157 |
+
except Exception as e:
|
158 |
+
error_message = f"Error loading model: {str(e)}"
|
159 |
+
QMessageBox.critical(self, "Error", error_message)
|
160 |
+
|
161 |
+
def update_thread_count(self):
|
162 |
+
try:
|
163 |
+
new_thread_count = int(self.thread_input.text())
|
164 |
+
if new_thread_count > 0:
|
165 |
+
self.thread_count = new_thread_count
|
166 |
+
self.layout().itemAt(2).itemAt(0).widget().setText(f"Thread Count: {self.thread_count}") # Updated index
|
167 |
+
self.thread_input.clear()
|
168 |
+
if self.model:
|
169 |
+
self.model.set_thread_count(self.thread_count)
|
170 |
+
QMessageBox.information(self, "Success", f"Thread count updated to {self.thread_count}")
|
171 |
+
else:
|
172 |
+
raise ValueError("Thread count must be a positive integer")
|
173 |
+
except ValueError as e:
|
174 |
+
QMessageBox.warning(self, "Invalid Input", str(e))
|
175 |
+
|
176 |
+
def upload_pdf(self):
|
177 |
+
pdf_path, _ = QFileDialog.getOpenFileName(self, "Upload PDF", "", "PDF Files (*.pdf)")
|
178 |
+
if pdf_path:
|
179 |
+
try:
|
180 |
+
doc = fitz.open(pdf_path)
|
181 |
+
self.pdf_content = ""
|
182 |
+
for page in doc:
|
183 |
+
self.pdf_content += page.get_text()
|
184 |
+
self.layout().itemAt(1).itemAt(0).widget().setText(f"PDF: {os.path.basename(pdf_path)}") # Updated index
|
185 |
+
QMessageBox.information(self, "Success", "PDF loaded successfully!")
|
186 |
+
except Exception as e:
|
187 |
+
QMessageBox.critical(self, "Error", f"Error loading PDF: {str(e)}")
|
188 |
+
|
189 |
+
def send_message(self):
|
190 |
+
user_message = self.user_input.text()
|
191 |
+
if user_message and self.model:
|
192 |
+
self.messages.append({"role": "user", "content": user_message})
|
193 |
+
self.update_chat_display(f"You: {user_message}")
|
194 |
+
self.user_input.clear()
|
195 |
+
|
196 |
+
max_tokens = 1000
|
197 |
+
self.progress_bar.show()
|
198 |
+
self.progress_bar.setRange(0, max_tokens)
|
199 |
+
self.progress_bar.setValue(0)
|
200 |
+
|
201 |
+
# Add PDF content if available
|
202 |
+
if self.pdf_content:
|
203 |
+
self.messages.append({"role": "user", "content": self.pdf_content})
|
204 |
+
|
205 |
+
self.worker = Worker(self.model, self.messages, max_tokens)
|
206 |
+
self.worker.finished.connect(self.on_response_finished)
|
207 |
+
self.worker.progress.connect(self.on_response_progress)
|
208 |
+
self.worker.start()
|
209 |
+
|
210 |
+
def on_response_finished(self, assistant_message):
|
211 |
+
self.progress_bar.hide()
|
212 |
+
self.messages.append({"role": "assistant", "content": assistant_message})
|
213 |
+
self.update_chat_display(f"Assistant: {assistant_message}")
|
214 |
+
|
215 |
+
# Python Code Download
|
216 |
+
if assistant_message.startswith("```python") and assistant_message.endswith("```"):
|
217 |
+
self.offer_code_download(assistant_message)
|
218 |
+
|
219 |
+
def on_response_progress(self, current_tokens, total_tokens):
|
220 |
+
self.progress_bar.setValue(current_tokens)
|
221 |
+
|
222 |
+
def offer_code_download(self, code):
|
223 |
+
reply = QMessageBox.question(self, "Download Code",
|
224 |
+
"The assistant generated Python code. Do you want to download it?",
|
225 |
+
QMessageBox.Yes | QMessageBox.No)
|
226 |
+
if reply == QMessageBox.Yes:
|
227 |
+
file_path, _ = QFileDialog.getSaveFileName(self, "Save Python Code", "code.py", "Python Files (*.py)")
|
228 |
+
if file_path:
|
229 |
+
try:
|
230 |
+
with open(file_path, "w") as f:
|
231 |
+
f.write(code.strip("```python").strip("```"))
|
232 |
+
QMessageBox.information(self, "Success", "Code saved successfully!")
|
233 |
+
except Exception as e:
|
234 |
+
QMessageBox.critical(self, "Error", f"Error saving code: {str(e)}")
|
235 |
+
|
236 |
+
def update_chat_display(self, message):
|
237 |
+
self.chat_display.append(message + "\n")
|
238 |
+
self.chat_display.verticalScrollBar().setValue(self.chat_display.verticalScrollBar().maximum())
|
239 |
+
|
240 |
+
def clear_conversation(self):
|
241 |
+
self.messages = [
|
242 |
+
{"role": "system", "content": "You are a helpful AI assistant."}
|
243 |
+
]
|
244 |
+
self.chat_display.clear()
|
245 |
+
self.pdf_content = "" # Clear PDF content
|
246 |
+
self.layout().itemAt(1).itemAt(0).widget().setText("PDF: No PDF loaded") # Updated index
|
247 |
+
|
248 |
+
def show_context_menu(self, point):
|
249 |
+
menu = QMenu(self)
|
250 |
+
copy_action = menu.addAction("Copy")
|
251 |
+
copy_action.triggered.connect(self.copy_text)
|
252 |
+
menu.exec_(self.chat_display.mapToGlobal(point))
|
253 |
+
|
254 |
+
def copy_text(self):
|
255 |
+
cursor = self.chat_display.textCursor()
|
256 |
+
if cursor.hasSelection():
|
257 |
+
text = cursor.selectedText()
|
258 |
+
QApplication.clipboard().setText(text)
|
259 |
+
|
260 |
+
|
261 |
+
if __name__ == "__main__":
|
262 |
+
app = QApplication(sys.argv)
|
263 |
+
gui = ChatbotGUI()
|
264 |
+
gui.show()
|
265 |
+
sys.exit(app.exec_())
|
266 |
+
```
|