Just read thorugh the two codes and give me they do "don't analyze for bugs yet":  --gui.py-- : import sys
import os
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                             QLineEdit, QPushButton, QTextEdit, QLabel, QFrame, 
                             QFileDialog, QProgressDialog, QSpinBox)
from PyQt6.QtGui import QIcon, QFont
from PyQt6.QtCore import Qt, QSize, QThread, pyqtSignal, QTimer
from llama_cpp import Llama
import openrag_test
import re

class ChatRTXApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("ChatRTX")
        self.setGeometry(100, 100, 1200, 800)
        self.setStyleSheet("""
            QMainWindow {
                background-color: #1e1e1e;
                color: #ffffff;
            }
            QComboBox, QLineEdit, QPushButton, QTextEdit, QSpinBox {
                background-color: #2d2d2d;
                color: #ffffff;
                border: 1px solid #3d3d3d;
                padding: 5px;
            }
            QPushButton {
                background-color: #3d3d3d;
            }
            QPushButton:hover {
                background-color: #4d4d4d;
            }
            QLabel {
                color: #ffffff;
            }
        """)

        self.llm_model = None
        self.search_engine = openrag_test.LocalSemanticSearchEngine()
        self.model_config = {}
        self.loading_thread = None
        self.loading_progress = None
        self.context_length = 512
        self.loaded_model_name = None 

        self.create_widgets()

        # Timer for handling the response stream
        self.response_timer = QTimer()
        self.response_timer.timeout.connect(self.process_response)
        self.response_timer.setInterval(100)  # Adjust interval if needed

        self.rag_llm = None  # Store the RAG-enabled LLM instance

    def create_widgets(self):
        central_widget = QWidget()
        main_layout = QHBoxLayout()

        # Left panel (AI model and Dataset)
        left_panel = QWidget()
        left_layout = QVBoxLayout()

        # AI model section
        ai_model_frame = QFrame()
        ai_model_frame.setFrameShape(QFrame.Shape.StyledPanel)
        ai_model_layout = QVBoxLayout()

        ai_model_label = QLabel("AI model")
        ai_model_label.setFont(QFont("Arial", 12))
        ai_model_layout.addWidget(ai_model_label)

        self.model_path = QLineEdit()
        self.model_path.setPlaceholderText("Model Path")
        ai_model_layout.addWidget(self.model_path)

        self.browse_model_button = QPushButton(QIcon("path_to_folder_icon.png"), "")
        self.browse_model_button.setIconSize(QSize(20, 20))
        self.browse_model_button.clicked.connect(self.browse_model)
        ai_model_layout.addWidget(self.browse_model_button)

        self.load_model_button = QPushButton("Load Model")
        self.load_model_button.clicked.connect(self.load_model)
        ai_model_layout.addWidget(self.load_model_button)

        ai_model_frame.setLayout(ai_model_layout)
        left_layout.addWidget(ai_model_frame)

        # Dataset section
        dataset_frame = QFrame()
        dataset_frame.setFrameShape(QFrame.Shape.StyledPanel)
        dataset_layout = QVBoxLayout()

        dataset_label = QLabel("Dataset")
        dataset_label.setFont(QFont("Arial", 12))
        dataset_layout.addWidget(dataset_label)

        self.dataset_path = QLineEdit()
        self.dataset_path.setPlaceholderText("Folder Path")
        dataset_layout.addWidget(self.dataset_path)

        self.browse_dataset_button = QPushButton(QIcon("path_to_folder_icon.png"), "")
        self.browse_dataset_button.setIconSize(QSize(20, 20))
        self.browse_dataset_button.clicked.connect(self.browse_dataset)
        dataset_layout.addWidget(self.browse_dataset_button)

        self.load_dataset_button = QPushButton("Load Dataset")
        self.load_dataset_button.clicked.connect(self.load_dataset)
        dataset_layout.addWidget(self.load_dataset_button)

        # Context Length
        context_length_layout = QHBoxLayout()
        context_length_label = QLabel("Context Length:")
        context_length_label.setFont(QFont("Arial", 10))
        context_length_layout.addWidget(context_length_label)

        self.context_length_spinbox = QSpinBox()
        self.context_length_spinbox.setRange(1, 4096)
        self.context_length_spinbox.setValue(self.context_length)
        self.context_length_spinbox.valueChanged.connect(self.update_context_length)
        context_length_layout.addWidget(self.context_length_spinbox)

        dataset_layout.addLayout(context_length_layout)

        dataset_frame.setLayout(dataset_layout)
        left_layout.addWidget(dataset_frame)

        left_panel.setLayout(left_layout)
        main_layout.addWidget(left_panel)

        # Right panel (Chat interface)
        right_panel = QWidget()
        right_layout = QVBoxLayout()

        self.chat_history = QTextEdit()
        self.chat_history.setReadOnly(True)
        right_layout.addWidget(self.chat_history)

        # Input area
        input_layout = QHBoxLayout()
        self.input_area = QLineEdit()
        self.input_area.setPlaceholderText("ChatRTX: Type your question")
        input_layout.addWidget(self.input_area)

        self.send_button = QPushButton("SEND")
        self.send_button.setStyleSheet("background-color: #76b900; color: #ffffff;")
        self.send_button.clicked.connect(self.send_message)
        input_layout.addWidget(self.send_button)

        right_layout.addLayout(input_layout)

        right_panel.setLayout(right_layout)
        main_layout.addWidget(right_panel)

        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

    def browse_model(self):
        model_path = QFileDialog.getOpenFileName(self, "Select Model File")[0]
        if model_path:
            self.model_path.setText(model_path)

    def load_model(self):
        model_path = self.model_path.text()
        if not model_path:
            self.chat_history.append("Error: Please select a model file.")
            return

        try:
            self.llm_model = Llama(model_path=model_path)
            self.chat_history.append(f"LLM model '{os.path.basename(model_path)}' loaded successfully.")
        except Exception as e:
            self.chat_history.append(f"Error: Failed to load model: {str(e)}")

    def browse_dataset(self):
        folder_path = QFileDialog.getExistingDirectory(self, "Select Dataset Folder")
        if folder_path:
            self.dataset_path.setText(folder_path)

    def load_dataset(self):
        dataset_path = self.dataset_path.text()
        if not dataset_path:
            self.chat_history.append("Error: Please select a dataset folder.")
            return
        
        self.loading_thread = DatasetLoadingThread(dataset_path, self.search_engine)
        self.loading_thread.progress_update.connect(self.update_progress)
        self.loading_thread.finished.connect(self.loading_finished)
        self.loading_thread.start()

        self.loading_progress = QProgressDialog("Loading Dataset...", "Cancel", 0, 100, self)
        self.loading_progress.setWindowModality(Qt.WindowModality.WindowModal)
        self.loading_progress.show()

    def update_progress(self, value):
        self.loading_progress.setValue(value)

    def loading_finished(self):
        self.loading_progress.close()
        if self.loading_thread.error:
            self.chat_history.append(f"Error: {self.loading_thread.error}")
        else:
            self.chat_history.append("Dataset loaded successfully.")

    def send_message(self):
        user_message = self.input_area.text().strip()
        if not user_message:
            return

        if not self.llm_model:
            self.chat_history.append("Error: Please load an LLM model first.")
            return

        if not self.search_engine.corpus:
            self.chat_history.append("Error: Please load a dataset first.")
            return

        try:
            # Create RAG-enabled LLM instance
            self.rag_llm = openrag_test.RAGEnabledLLM(self.llm_model, self.search_engine)

            # Start the timer for processing streamed response
            self.response_timer.start()

            self.chat_history.append(f"You: {user_message}")
            self.chat_history.append("ChatRTX: ")

            # Initialize the response buffer
            self.rag_llm.response_update.connect(self._stream_response_update)
            self._stream_response_update("")  # Trigger process_response with an empty string

            # Send the message to the model (start the stream)
            self.rag_llm.generate_response(user_message, context_length=self.context_length, stream=True)

            self.input_area.clear()
        except Exception as e:
            self.chat_history.append(f"Error: An error occurred during processing: {str(e)}")
            print(f"Detailed error: {e}")

    def _stream_response_update(self, token):
        # Handle "
" tokens to avoid unwanted output
        if token == "
":
            return 

        # If the response buffer is empty, add an empty string to trigger processing
        if not self.rag_llm.response:
            self.rag_llm.response = ""  # Add an empty string to trigger processing

        self.chat_history.insertPlainText(token)
        self.chat_history.ensureCursorVisible()

    def update_context_length(self, value):
        self.context_length = value

    def extract_questions(self, response):
        """Extract questions from the response using a special token."""
        questions = []
        question_token = "?"  # You can change this token if needed
        parts = response.split(question_token)
        for part in parts[1:]:  # Skip the first part
            # Look for the question start indicator:
            if "<=Context:" in part:
                questions.append(part.split("<=Context:")[0].strip())
            elif "```" in part:
                questions.append(part.split("```")[0].strip()) 
        return questions
    
    def process_response(self):
        """Process the streamed response and extract the final answer."""
        print(f"Processing response: {self.rag_llm.response}")  # Add this line
        if self.rag_llm and self.rag_llm.response:
            response = self.rag_llm.response.strip()
        
            # Remove backticks and 'ANSWER:' prefix
            response = re.sub(r'`+', '', response)
            response = re.sub(r'^ANSWER:\s*', '', response, flags=re.IGNORECASE)
        
            # Extract the answer part (first non-empty line)
            answer_lines = [line.strip() for line in response.split('\n') if line.strip()]
            if answer_lines:
                answer = answer_lines[0]
            
                # Clear previous content and display the final answer
                self.chat_history.clear()
                self.chat_history.append("Dataset loaded successfully.")
                if self.loaded_model_name:
                    self.chat_history.append(f"LLM model '{self.loaded_model_name}' loaded successfully.")
                else:
                    self.chat_history.append("LLM model loaded successfully.")
                self.chat_history.append(f"You: {self.input_area.text()}")
                self.chat_history.append(f"ChatRTX: {answer}")
        
            self.response_timer.stop()  # Stop the timer when processing is done
            self.rag_llm.response = ""  # Reset the response buffer

class DatasetLoadingThread(QThread):
    progress_update = pyqtSignal(int)
    finished = pyqtSignal()
    error = pyqtSignal(str)

    def __init__(self, dataset_path, search_engine):
        super().__init__()
        self.dataset_path = dataset_path
        self.search_engine = search_engine
        self.error = None

    def run(self):
        try:
            self.search_engine.load_documents(self.dataset_path)
        except Exception as e:
            self.error = str(e)
        finally:
            self.finished.emit()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = ChatRTXApp()
    window.show()
    sys.exit(app.exec())     ; --openrag_test.py-- : from transformers import AutoTokenizer, AutoModel
import torch
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import re
import nltk
import os
import pickle
from PyQt6.QtCore import QObject, pyqtSignal

# Download Punkt sentence tokenizer if not already downloaded
nltk.download('punkt', quiet=True)

# ---- Embedding Model ----
def meanpooling(output, mask):
    embeddings = output[0]
    mask = mask.unsqueeze(-1).expand(embeddings.size()).float()
    return torch.sum(embeddings * mask, 1) / torch.clamp(mask.sum(1), min=1e-9)

tokenizer = AutoTokenizer.from_pretrained("neuml/pubmedbert-base-embeddings")
model = AutoModel.from_pretrained("neuml/pubmedbert-base-embeddings")

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

def get_embedding(texts, batch_size=32):
    """Calculates embeddings for a list of texts in batches."""
    all_embeddings = []
    for i in range(0, len(texts), batch_size):
        batch = texts[i : i + batch_size]
        inputs = tokenizer(batch, padding=True, truncation=False, return_tensors='pt').to(device)
        with torch.no_grad():
            output = model(**inputs)
        batch_embeddings = meanpooling(output, inputs['attention_mask']).cpu().numpy()
        all_embeddings.append(batch_embeddings)
    return np.concatenate(all_embeddings, axis=0)

# ---- Search Engine ----
class LocalSemanticSearchEngine(QObject):
    progress_update = pyqtSignal(int)
    
    def __init__(self, embeddings_file="embeddings.pkl"):
        super().__init__()
        self.corpus = {}  
        self.embeddings = {} 
        self.embeddings_file = embeddings_file

    def load_documents(self, path):
        if os.path.isdir(path):
            self._load_embeddings_from_disk(path)
            files = [f for f in os.listdir(path) if f.endswith(".txt")]
            total_files = len(files)
            for i, filename in enumerate(files):
                file_path = os.path.join(path, filename)
                if file_path not in self.embeddings:
                    self.load_document(file_path)
                self.progress_update.emit(int((i + 1) / total_files * 100))
            self._save_embeddings_to_disk(path)
        elif os.path.isfile(path) and path.endswith(".txt"):
            self.load_document(path)
            self._save_embeddings_to_disk(os.path.dirname(path))
            self.progress_update.emit(100)
        else:
            raise ValueError("Invalid path provided. Please provide a .txt file or a directory.")

    def load_document(self, file_path):
        with open(file_path, 'r', encoding='utf-8') as file:
            text = file.read()
            sentences = nltk.sent_tokenize(text)
            self.corpus[file_path] = sentences
            self.embeddings[file_path] = get_embedding(sentences)

    def _load_embeddings_from_disk(self, path):
        embeddings_path = os.path.join(path, self.embeddings_file)
        if os.path.exists(embeddings_path):
            with open(embeddings_path, "rb") as f:
                self.embeddings = pickle.load(f)
                self.corpus = {file_path: nltk.sent_tokenize(open(file_path, 'r', encoding='utf-8').read())
                               for file_path in self.embeddings.keys()}

    def _save_embeddings_to_disk(self, path):
        embeddings_path = os.path.join(path, self.embeddings_file)
        with open(embeddings_path, "wb") as f:
            pickle.dump(self.embeddings, f)

    def search(self, query):
        query_embedding = get_embedding([query])[0] 
        max_similarity = -1
        most_similar_file = None

        for file_path, file_embeddings in self.embeddings.items():
            similarities = cosine_similarity(query_embedding.reshape(1, -1), file_embeddings)
            max_sim = np.max(similarities) 
            if max_sim > max_similarity:
                max_similarity = max_sim
                most_similar_file = file_path

        return most_similar_file, max_similarity

# ---- RAG Enhanced LLM Inference ----
class RAGEnabledLLM(QObject):
    response_update = pyqtSignal(str)
    
    def __init__(self, llm, search_engine):
        super().__init__()
        self.llm = llm
        self.search_engine = search_engine
        self.response = ""  # Add this attribute
        self.chat_history = ""

    def generate_response(self, query, context_length=512, stream=True, similarity_threshold=0.4):
        most_similar_file, similarity_score = self.search_engine.search(query)
        
        if most_similar_file and similarity_score > similarity_threshold:
            with open(most_similar_file, 'r', encoding='utf-8') as file:
                context = file.read()
            context = context[:context_length]  # Truncate context if it's too long
        else:
            context = ""

        prompt = f"
 Context: ```{context}```\n\nQuestion: {query}\n\nPlease answer the question based on the context that you have and some of your prior knowledge, you should be straight forward and only give the answer, don't repreat anything, you'll get punished for that. 
"
        
        if stream:
            return self._stream_response(prompt)
        else:
            return self._generate_full_response(prompt)

    def _stream_response(self, prompt):
        print(f"Stream prompt: {prompt}")
        self.response = "" 
        answer_found = False  # Flag to track if answer is complete
        answer_parts = []   # Store answer parts in case it's split by tokens
        for output in self.llm(prompt, max_tokens=512, stop=["
", "
"], echo=False, stream=True):
            token = output['choices'][0]['text']
            print(f"Stream token: {token}")

            # Remove backticks and instruction tokens
            token = re.sub(r'`+', '', token)
            token = re.sub(r'\[/?INST\]', '', token)

            self.response += token
            self.response_update.emit(token)

            # Check for "Answer:" and extract answer part
            if "Answer:" in token:
                answer_parts.append(token.split("Answer:")[1].strip())
                answer_found = True  # Mark answer as partially found

            # Stop the timer if the answer is complete
            if answer_found and token == "
" or "```python INST" in token: 
                self.response_timer.stop() 
                break  

        # Join answer parts if multiple tokens 
        final_answer = " ".join(answer_parts)
        self.chat_history.append(f"ChatRTX: {final_answer}")  # Display the answer in the GUI
        self.response = "" 

    def _generate_full_response(self, prompt):
        output = self.llm(
            prompt,
            max_tokens=512,
            stop=["
", "
"],
            echo=False
        )
        response = output['choices'][0]['text']
        response = re.sub(r'\[/?INST\]', '', response)
        response = re.sub(r'\s+', ' ', response).strip()
        return response