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