korrigiere alle fehler in meinem skript so wie diesen log und nenn mir dann den gesamten korrigierten code mit  vollständigem skript: 
(.venv) D:\_Projekte\_MyCodes\GitHub\AutoLLM>d:/_Projekte/_MyCodes/GitHub/AutoLLM/.venv/Scripts/python.exe d:/_Projekte/_MyCodes/GitHub/AutoLLM/AutoLLM.py
Traceback (most recent call last):
  File "d:\_Projekte\_MyCodes\GitHub\AutoLLM\AutoLLM.py", line 622, in <module>
    window = AutoLLMApp()
  File "d:\_Projekte\_MyCodes\GitHub\AutoLLM\AutoLLM.py", line 315, in __init__
    self.init_ui()
  File "d:\_Projekte\_MyCodes\GitHub\AutoLLM\AutoLLM.py", line 335, in init_ui
    self.chat_area.anchorClicked.connect(self.handle_link_click)
AttributeError: 'QTextEdit' object has no attribute 'anchorClicked'


import sys
import re
import subprocess
import json
import requests
import threading
import sqlite3
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QLineEdit,
    QPushButton, QLabel, QDialog, QFormLayout, QSlider, QMessageBox, QAction,
    QComboBox, QScrollArea, QFrame
)
from PyQt5.QtCore import (
    Qt, QTimer, pyqtSignal, QObject, QThread, QSize
)
from PyQt5.QtGui import QTextCursor, QColor, QFont, QTextCharFormat, QDesktopServices
from PyQt5.QtCore import QRegExp
from PyQt5.Qt import QUrl, QClipboard

# -------------------- Database Manager --------------------
class DatabaseManager:
    """Manages SQLite database for storing chat context."""
    def __init__(self, db_name="autollm_context.db"):
        self.conn = sqlite3.connect(db_name)
        self.create_table()

    def create_table(self):
        """Creates the context table if it doesn't exist."""
        cursor = self.conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS context (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                sender TEXT NOT NULL,
                message TEXT NOT NULL,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        self.conn.commit()

    def add_message(self, sender, message):
        """Adds a message to the context."""
        cursor = self.conn.cursor()
        cursor.execute('''
            INSERT INTO context (sender, message) VALUES (?, ?)
        ''', (sender, message))
        self.conn.commit()

    def get_context(self, max_length=5000):
        """Retrieves the context up to the specified maximum length."""
        cursor = self.conn.cursor()
        cursor.execute('SELECT sender, message FROM context ORDER BY id DESC')
        rows = cursor.fetchall()
        context = []
        current_length = 0
        for row in reversed(rows):
            msg = f"{row[0]}: {row[1]}"
            msg_length = len(msg)
            if current_length + msg_length > max_length:
                break
            context.insert(0, msg)
            current_length += msg_length
        return "\n".join(context)

    def clear_context(self):
        """Clears the entire chat context."""
        cursor = self.conn.cursor()
        cursor.execute('DELETE FROM context')
        self.conn.commit()

# -------------------- Context Manager --------------------
class ContextManager:
    """Interfaces with DatabaseManager to manage chat context."""
    def __init__(self):
        self.db = DatabaseManager()

    def add_message(self, sender, message):
        """Adds a message to the context."""
        self.db.add_message(sender, message)

    def get_context(self):
        """Retrieves the current chat context."""
        return self.db.get_context()

    def clear_context(self):
        """Clears the chat context."""
        self.db.clear_context()

# -------------------- Syntax Highlighter --------------------
class CodeHighlighter(QThread):
    """Applies syntax highlighting to code blocks."""
    def __init__(self, parent=None):
        super().__init__(parent)
        # This can be expanded for more sophisticated syntax highlighting
        self.parent = parent

    def run(self):
        pass  # Placeholder for potential future enhancements

# -------------------- Ollama Controller --------------------
class OllamaController(QObject):
    """Manages Ollama server and handles API interactions."""
    server_status_changed = pyqtSignal(bool)
    response_received = pyqtSignal(str)
    error_occurred = pyqtSignal(str)
    llm_list_updated = pyqtSignal(list)

    def __init__(self):
        super().__init__()
        self.process = None
        self.selected_llm = None
        self.api_port = 11434  # Default Ollama API port
        self.update_llm_list()

    def start_server(self):
        """Starts the Ollama server using QProcess."""
        if not self.is_server_running():
            try:
                self.process = subprocess.Popen(
                    ["ollama", "serve"], 
                    stdout=subprocess.PIPE, 
                    stderr=subprocess.PIPE
                )
                self.server_status_changed.emit(True)
                self.update_llm_list()
            except Exception as e:
                self.error_occurred.emit(f"Failed to start Ollama server: {str(e)}")

    def stop_server(self):
        """Stops the Ollama server."""
        if self.is_server_running():
            try:
                self.process.terminate()
                self.process.wait(timeout=5)
                self.server_status_changed.emit(False)
                self.process = None
                self.update_llm_list()
            except Exception as e:
                self.error_occurred.emit(f"Failed to stop Ollama server: {str(e)}")

    def is_server_running(self):
        """Checks if the Ollama server is running."""
        return self.process is not None and self.process.poll() is None

    def get_installed_llms(self):
        """Fetches the list of installed LLMs from Ollama API."""
        try:
            url = f"http://localhost:{self.api_port}/v1/engines"
            response = requests.get(url)
            if response.status_code == 200:
                engines = response.json().get("engines", [])
                return [engine.get("name") for engine in engines]
            else:
                self.error_occurred.emit(f"Failed to fetch LLMs: {response.status_code}")
                return []
        except Exception as e:
            self.error_occurred.emit(f"Failed to fetch LLMs: {str(e)}")
            return []

    def update_llm_list(self):
        """Updates the list of installed LLMs."""
        llms = self.get_installed_llms()
        if llms:
            self.llm_list_updated.emit(llms)
            if not self.selected_llm:
                self.selected_llm = llms[0]
        else:
            self.llm_list_updated.emit([])
            self.selected_llm = None

    def set_selected_llm(self, llm_name):
        """Sets the selected LLM for interactions."""
        self.selected_llm = llm_name

    def send_request(self, prompt, parameters):
        """Sends a prompt to the selected LLM and handles the response."""
        if not self.is_server_running():
            self.error_occurred.emit("Ollama server is not running.")
            return

        if not self.selected_llm:
            self.error_occurred.emit("No LLM selected.")
            return

        threading.Thread(target=self._handle_request, args=(prompt, parameters), daemon=True).start()

    def _handle_request(self, prompt, parameters):
        """Handles the HTTP request to the Ollama API."""
        try:
            url = f"http://localhost:{self.api_port}/v1/engines/{self.selected_llm}/completions"
            headers = {
                "Content-Type": "application/json"
            }
            payload = {
                "prompt": prompt,
                "temperature": parameters.get("temperature", 0.5),
                "max_tokens": parameters.get("output_length", 1500),
                "top_p": 1.0,
                "frequency_penalty": 0.0,
                "presence_penalty": 0.0
            }
            response = requests.post(url, headers=headers, json=payload)
            if response.status_code == 200:
                result = response.json().get("choices", [{}])[0].get("text", "").strip()
                self.response_received.emit(result)
            else:
                self.error_occurred.emit(f"API Error {response.status_code}: {response.text}")
        except Exception as e:
            self.error_occurred.emit(f"Request failed: {str(e)}")

    def interrupt_processing(self):
        """Interrupts the current processing (Not implemented)."""
        # Actual implementation would require server-side support
        self.error_occurred.emit("Interrupt functionality not supported by Ollama API.")

# -------------------- Settings Dialog --------------------
class SettingsDialog(QDialog):
    """Dialog for adjusting LLM parameters."""
    settings_updated = pyqtSignal(dict)

    def __init__(self, current_settings, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Settings")
        self.current_settings = current_settings
        self.init_ui()

    def init_ui(self):
        layout = QFormLayout()

        # Temperature Slider
        self.temperature_slider = QSlider(Qt.Horizontal)
        self.temperature_slider.setRange(0, 100)
        self.temperature_slider.setValue(int(self.current_settings.get("temperature", 0.5) * 100))
        self.temperature_slider.valueChanged.connect(self.update_temperature_label)
        self.temperature_label = QLabel(f"{self.current_settings.get('temperature', 0.5):.2f}")
        temp_layout = QHBoxLayout()
        temp_layout.addWidget(self.temperature_slider)
        temp_layout.addWidget(self.temperature_label)
        layout.addRow("Temperature:", temp_layout)

        # Context Size Slider
        self.context_slider = QSlider(Qt.Horizontal)
        self.context_slider.setRange(1000, 10000)
        self.context_slider.setValue(self.current_settings.get("context_size", 5000))
        self.context_slider.valueChanged.connect(self.update_context_label)
        self.context_label = QLabel(str(self.current_settings.get("context_size", 5000)))
        context_layout = QHBoxLayout()
        context_layout.addWidget(self.context_slider)
        context_layout.addWidget(self.context_label)
        layout.addRow("Context Size:", context_layout)

        # Output Length Slider
        self.output_slider = QSlider(Qt.Horizontal)
        self.output_slider.setRange(100, 2000)
        self.output_slider.setValue(self.current_settings.get("output_length", 1500))
        self.output_slider.valueChanged.connect(self.update_output_label)
        self.output_label = QLabel(str(self.current_settings.get("output_length", 1500)))
        output_layout = QHBoxLayout()
        output_layout.addWidget(self.output_slider)
        output_layout.addWidget(self.output_label)
        layout.addRow("Output Length:", output_layout)

        # Save and Cancel Buttons
        buttons_layout = QHBoxLayout()
        self.save_button = QPushButton("Save")
        self.save_button.clicked.connect(self.save_settings)
        self.cancel_button = QPushButton("Cancel")
        self.cancel_button.clicked.connect(self.reject)
        buttons_layout.addWidget(self.save_button)
        buttons_layout.addWidget(self.cancel_button)
        layout.addRow(buttons_layout)

        self.setLayout(layout)

    def update_temperature_label(self, value):
        temperature = value / 100
        self.temperature_label.setText(f"{temperature:.2f}")

    def update_context_label(self, value):
        self.context_label.setText(str(value))

    def update_output_label(self, value):
        self.output_label.setText(str(value))

    def save_settings(self):
        temperature = self.temperature_slider.value() / 100
        context_size = self.context_slider.value()
        output_length = self.output_slider.value()
        settings = {
            "temperature": temperature,
            "context_size": context_size,
            "output_length": output_length
        }
        self.settings_updated.emit(settings)
        QMessageBox.information(self, "Settings Saved", "LLM parameters have been updated.")
        self.accept()

# -------------------- Main Application Window --------------------
class AutoLLMApp(QWidget):
    """Main application window for AutoLLM."""
    def __init__(self):
        super().__init__()
        self.setWindowTitle("AutoLLM")
        self.resize(800, 600)

        # Initialize components
        self.ollama = OllamaController()
        self.ollama.server_status_changed.connect(self.update_server_status)
        self.ollama.response_received.connect(self.handle_llm_response)
        self.ollama.error_occurred.connect(self.show_error)
        self.ollama.llm_list_updated.connect(self.update_llm_list)
        self.context_manager = ContextManager()
        self.current_parameters = self.get_default_parameters()

        # Initialize UI
        self.init_ui()

        # Initialize timers for typewriter effect and loading indicator
        self.llm_timer = QTimer()
        self.llm_timer.timeout.connect(self.update_llm_response)
        self.llm_response = ""
        self.llm_response_index = 0

        self.blink_timer = QTimer()
        self.blink_timer.timeout.connect(self.toggle_loading_indicator)
        self.loading = False

    def init_ui(self):
        """Sets up the user interface."""
        main_layout = QVBoxLayout()

        # Chat Area with Syntax Highlighter
        self.chat_area = QTextEdit()
        self.chat_area.setReadOnly(True)
        self.chat_area.setFont(QFont("Courier", 10))
        self.chat_area.anchorClicked.connect(self.handle_link_click)
        self.chat_area.setContextMenuPolicy(Qt.CustomContextMenu)
        self.chat_area.customContextMenuRequested.connect(self.handle_context_menu)
        main_layout.addWidget(self.chat_area)

        # Input Field and Send Button
        input_layout = QHBoxLayout()
        self.input_field = QLineEdit()
        self.input_field.setPlaceholderText("Enter your message here...")
        self.input_field.returnPressed.connect(self.send_message)
        self.send_button = QPushButton("Send")
        self.send_button.clicked.connect(self.send_message)
        input_layout.addWidget(self.input_field)
        input_layout.addWidget(self.send_button)
        main_layout.addLayout(input_layout)

        # Interrupt Button
        self.interrupt_button = QPushButton("Interrupt")
        self.interrupt_button.clicked.connect(self.interrupt_processing)
        main_layout.addWidget(self.interrupt_button)

        # Server Monitoring Area
        server_layout = QHBoxLayout()
        self.server_status = QLabel("🔴 Server Stopped")
        self.server_button = QPushButton("Start Server")
        self.server_button.clicked.connect(self.toggle_server)
        server_layout.addWidget(self.server_status)
        server_layout.addWidget(self.server_button)
        main_layout.addLayout(server_layout)

        # LLM Selection
        llm_layout = QHBoxLayout()
        self.llm_label = QLabel("Select LLM:")
        self.llm_combo = QComboBox()
        self.llm_combo.setEnabled(False)
        self.llm_combo.currentTextChanged.connect(self.change_llm)
        llm_layout.addWidget(self.llm_label)
        llm_layout.addWidget(self.llm_combo)
        main_layout.addLayout(llm_layout)

        # Settings Button
        self.settings_button = QPushButton("Settings")
        self.settings_button.clicked.connect(self.open_settings)
        main_layout.addWidget(self.settings_button)

        # Set the layout
        self.setLayout(main_layout)

    def get_default_parameters(self):
        """Returns the default LLM parameters."""
        return {
            "temperature": 0.5,
            "context_size": 5000,
            "output_length": 1500
        }

    def send_message(self):
        """Handles sending messages to the LLM."""
        user_message = self.input_field.text().strip()
        if user_message:
            self.context_manager.add_message("User", user_message)
            self.append_message("User", user_message)
            self.input_field.clear()
            # Send the message to the LLM
            self.ollama.send_request(user_message, self.current_parameters)
            self.show_loading_indicator()

    def append_message(self, sender, message):
        """Appends a message to the chat area with appropriate styling."""
        if sender == "User":
            color = "blue"
            prefix = "<b>User:</b> "
        elif sender == "LLM":
            color = "green"
            prefix = "<b>LLM:</b> "
        else:
            color = "gray"
            prefix = "<b>System:</b> "

        formatted_message = self.format_message(message)
        html_message = f"{prefix}{formatted_message}"

        # Set text color except for LLM messages (handled separately)
        if sender != "LLM":
            self.chat_area.setTextColor(QColor(color))
        self.chat_area.append(html_message)
        self.chat_area.moveCursor(QTextCursor.End)

    def format_message(self, message):
        """Formats messages by detecting links and code blocks."""
        # Detect URLs and format them as clickable links
        message = re.sub(r'(https?://\S+)', r'<a href="\1">\1</a>', message)

        # Detect code blocks enclosed in ``` and format them
        message = re.sub(r'```(.*?)```', r'<pre style="background-color:#f0f0f0; padding:5px;">\1</pre>', message, flags=re.DOTALL)

        return message

    def handle_link_click(self, url):
        """Opens clicked links in the default web browser."""
        QDesktopServices.openUrl(url)

    def handle_context_menu(self, position):
        """Provides context menu options for copying links and code."""
        cursor = self.chat_area.cursorForPosition(position)
        cursor.select(QTextCursor.WordUnderCursor)
        selected_text = cursor.selectedText()

        menu = self.chat_area.createStandardContextMenu()
        if re.match(r'^https?://', selected_text):
            copy_action = QAction("Copy Link", self)
            copy_action.triggered.connect(lambda: self.copy_to_clipboard(selected_text))
            menu.addAction(copy_action)
        elif selected_text.startswith("```") and selected_text.endswith("```"):
            code = selected_text.strip("`")
            copy_code_action = QAction("Copy Code", self)
            copy_code_action.triggered.connect(lambda: self.copy_to_clipboard(code))
            menu.addAction(copy_code_action)

        menu.exec_(self.chat_area.mapToGlobal(position))

    def copy_to_clipboard(self, text):
        """Copies the provided text to the clipboard."""
        clipboard = QApplication.clipboard()
        clipboard.setText(text)
        QMessageBox.information(self, "Copied", "Text has been copied to clipboard.")

    def interrupt_processing(self):
        """Attempts to interrupt the current LLM processing."""
        self.ollama.interrupt_processing()
        self.append_message("System", "LLM processing has been interrupted.")

    def toggle_server(self):
        """Starts or stops the Ollama server based on its current state."""
        if self.ollama.is_server_running():
            self.ollama.stop_server()
        else:
            self.ollama.start_server()

    def update_server_status(self, is_running):
        """Updates the server status display."""
        if is_running:
            self.server_status.setText("🟢 Server Running")
            self.server_button.setText("Stop Server")
            self.llm_combo.setEnabled(True)
        else:
            self.server_status.setText("🔴 Server Stopped")
            self.server_button.setText("Start Server")
            self.llm_combo.setEnabled(False)
            self.llm_combo.clear()
            self.llm_combo.addItem("No LLMs Installed")

    def update_llm_list(self, llm_list):
        """Updates the LLM selection dropdown."""
        self.llm_combo.blockSignals(True)
        self.llm_combo.clear()
        if llm_list:
            self.llm_combo.addItems(llm_list)
            self.ollama.set_selected_llm(llm_list[0])
        else:
            self.llm_combo.addItem("No LLMs Installed")
            self.ollama.set_selected_llm(None)
        self.llm_combo.blockSignals(False)

    def change_llm(self, llm_name):
        """Handles changes in the selected LLM."""
        if llm_name != "No LLMs Installed":
            self.ollama.set_selected_llm(llm_name)
        else:
            self.ollama.set_selected_llm(None)

    def show_loading_indicator(self):
        """Displays the loading indicator while waiting for LLM response."""
        if not self.loading:
            self.loading = True
            self.chat_area.append("<i>LLM is typing...</i>")
            self.chat_area.moveCursor(QTextCursor.End)
            self.blink_timer.start(500)

    def hide_loading_indicator(self):
        """Hides the loading indicator once LLM response is received."""
        if self.loading:
            self.loading = False
            self.blink_timer.stop()
            # Remove the last line (loading indicator)
            cursor = self.chat_area.textCursor()
            cursor.movePosition(QTextCursor.End)
            cursor.select(QTextCursor.BlockUnderCursor)
            cursor.removeSelectedText()
            self.chat_area.moveCursor(QTextCursor.End)

    def toggle_loading_indicator(self):
        """Creates a blinking effect for the loading indicator."""
        cursor = self.chat_area.textCursor()
        cursor.movePosition(QTextCursor.End)
        current_text = self.chat_area.toPlainText()
        if current_text.endswith("LLM is typing..."):
            self.chat_area.setPlainText(current_text.replace("LLM is typing...", "LLM is typing"))
        else:
            self.chat_area.setPlainText(current_text.replace("LLM is typing", "LLM is typing..."))
        self.chat_area.moveCursor(QTextCursor.End)

    def handle_llm_response(self, response):
        """Handles the response received from the LLM."""
        self.hide_loading_indicator()
        self.context_manager.add_message("LLM", response)
        self.llm_response = response
        self.llm_response_index = 0
        self.chat_area.setTextCursor(QTextCursor.End)
        self.chat_area.setTextColor(QColor("green"))
        self.chat_area.insertHtml("<b>LLM:</b> ")
        self.chat_area.moveCursor(QTextCursor.End)
        self.llm_timer.start(50)  # 50 ms between characters for typewriter effect

    def update_llm_response(self):
        """Updates the chat area with the LLM response character by character."""
        if self.llm_response_index < len(self.llm_response):
            char = self.llm_response[self.llm_response_index]
            if char == '\n':
                char = '<br>'
            elif char == ' ':
                char = '&nbsp;'
            self.chat_area.insertHtml(char)
            self.llm_response_index += 1
        else:
            self.llm_timer.stop()
            self.llm_response = ""
            self.llm_response_index = 0

    def open_settings(self):
        """Opens the settings dialog for adjusting LLM parameters."""
        settings_dialog = SettingsDialog(self.current_parameters, self)
        settings_dialog.settings_updated.connect(self.update_parameters)
        settings_dialog.exec_()

    def update_parameters(self, params):
        """Updates the current LLM parameters based on user settings."""
        self.current_parameters = params
        QMessageBox.information(self, "Settings Updated", "LLM parameters have been updated.")

    def show_error(self, message):
        """Displays error messages to the user."""
        QMessageBox.critical(self, "Error", message)

# -------------------- Main Entry Point --------------------
if __name__ == "__main__":
    app = QApplication(sys.argv)
    
    # Apply a dark theme for better user experience
    app.setStyleSheet("""
        QWidget {
            background-color: #2b2b2b;
            color: #ffffff;
        }
        QPushButton {
            background-color: #3c3f41;
            border: none;
            padding: 5px;
            color: #ffffff;
            border-radius: 3px;
        }
        QPushButton:hover {
            background-color: #4b4e52;
        }
        QLineEdit {
            background-color: #3c3f41;
            border: 1px solid #5c5c5c;
            padding: 5px;
            color: #ffffff;
        }
        QTextEdit {
            background-color: #2b2b2b;
            border: 1px solid #5c5c5c;
            padding: 5px;
            color: #ffffff;
        }
        QComboBox {
            background-color: #3c3f41;
            border: 1px solid #5c5c5c;
            padding: 5px;
            color: #ffffff;
        }
        QLabel {
            color: #ffffff;
        }
    """)

    window = AutoLLMApp()
    window.show()
    sys.exit(app.exec_())