Dieses skript soll überarbeitet werden und benötigt folgende konkrete Erweiterungen:

TaskManager erweitern:
class TaskManager:
    def analyze_request(self, user_request):
        analysis_prompt = {
            "task": "Analysiere diese Anfrage",
            "input": user_request,
            "format": {
                "komplexität": None,  # 1-5
                "kategorie": None,    # coding/research/explanation/creative
                "zerlegung_nötig": None,
                "werkzeuge": [],
                "begründung": ""
            }
        }
        return self.send_llm_request(analysis_prompt)

    def create_task_breakdown(self, request):
        breakdown_prompt = {
            "task": "Erstelle Arbeitsplan",
            "input": request,
            "format": {
                "schritte": [{
                    "nummer": None,
                    "beschreibung": "",
                    "abhängigkeiten": [],
                    "erwartetes_ergebnis": ""
                }]
            }
        }
        return self.send_llm_request(breakdown_prompt)

    def evaluate_result(self, result, criteria):
        eval_prompt = {
            "task": "Bewerte Ergebnis",
            "input": result,
            "kriterien": {
                "vollständigkeit": None,  # 0-100%
                "relevanz": None,         # 1-5
                "verständlichkeit": None, # 1-5
                "korrektheit": None      # 1-5
            }
        }
        return self.send_llm_request(eval_prompt)


AutoLLM_New_v2.py
DatabaseManager erweitern:
class DatabaseManager:
    def create_table(self):
        self.conn.execute('''
            CREATE TABLE IF NOT EXISTS context (
                id INTEGER PRIMARY KEY,
                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                type TEXT NOT NULL,
                content TEXT NOT NULL,
                importance INTEGER,
                related_task INTEGER,
                is_active BOOLEAN DEFAULT 1
            )
        ''')

    def add_message(self, type, content, importance=1, task_id=None):
        self.conn.execute(
            "INSERT INTO context (type, content, importance, related_task) VALUES (?, ?, ?, ?)",
            (type, content, importance, task_id)
        )


AutoLLM_New_v2.py
ContextManager erweitern:
class ContextManager:
    def optimize_context(self):
        context = self.db.get_context()
        optimization_prompt = {
            "task": "Optimiere Kontext",
            "input": context,
            "format": {
                "wichtige_infos": [],
                "zusammenfassbar": [],
                "entfernbar": []
            }
        }
        return self.send_llm_request(optimization_prompt)

    def summarize_section(self, section):
        summary_prompt = {
            "task": "Erstelle Zusammenfassung",
            "input": section,
            "max_length": 200
        }
        return self.send_llm_request(summary_prompt)

AutoLLM_New_v2.py
OllamaController erweitern:
class OllamaController:
    def __init__(self):
        self.retry_config = {
            "max_attempts": 3,
            "delay": 1000,
            "backoff": 2
        }
        self.state_machine = {
            "ANALYZING": self.handle_analysis,
            "PLANNING": self.handle_planning,
            "EXECUTING": self.handle_execution,
            "VALIDATING": self.handle_validation,
            "ADJUSTING": self.handle_adjustment,
            "FINALIZING": self.handle_finalization
        }

    def send_request_with_retry(self, endpoint, data):
        attempts = 0
        while attempts < self.retry_config["max_attempts"]:
            try:
                return self.send_request(endpoint, data)
            except Exception as e:
                attempts += 1
                if attempts == self.retry_config["max_attempts"]:
                    raise e
                time.sleep(self.retry_config["delay"] * (self.retry_config["backoff"] ** attempts))


AutoLLM_New_v2.py
AutoLLMApp erweitern:
class AutoLLMApp:
    def update_progress_display(self, state, progress):
        self.status_label.setText(f"Status: {state}")
        self.progress_bar.setValue(progress)
        
    def show_task_details(self, task):
        details = {
            "Beschreibung": task["beschreibung"],
            "Abhängigkeiten": task["abhängigkeiten"],
            "Erwartetes Ergebnis": task["erwartetes_ergebnis"],
            "Status": task["status"]
        }
        self.details_browser.setText(json.dumps(details, indent=2))


AutoLLM_New_v2.py
Diese Erweiterungen implementieren:

Strukturierte Aufgabenanalyse
Detaillierte Arbeitsplanung
Qualitätssicherung
Intelligentes Kontextmanagement
Robuste Fehlerbehandlung
Verbesserte UI-Funktionalität



Skript:

import sys
import os
import re
import subprocess
import json
import threading
import sqlite3
import time
import logging
import requests
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit,
    QPushButton, QLabel, QDialog, QFormLayout, QSlider, QMessageBox, QComboBox,
    QTextBrowser, QScrollArea, QListWidget, QListWidgetItem, QProgressBar, QInputDialog, QStackedWidget
)
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QObject, QRegularExpression, QSize
from PyQt5.QtGui import QTextCursor, QColor, QFont, QTextCharFormat, QSyntaxHighlighter, QIcon

# Logging configuration
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def find_ollama_executable():
    if os.name == 'nt':  # Windows
        direct_path = "C:\\Users\\Admin\\AppData\\Local\\Programs\\Ollama\\ollama.exe"
        if os.path.exists(direct_path):
            return direct_path
        alt_path = os.path.join("C:", "Users", "Admin", "AppData", "Local", "Programs", "Ollama", "ollama.exe")
        if os.path.exists(alt_path):
            return alt_path
    return None

# Database Manager
class DatabaseManager:
    def __init__(self, db_name="autollm_context.db", max_context_length=10000):
        try:
            self.conn = sqlite3.connect(db_name, check_same_thread=False)
            self.max_context_length = max_context_length
            self.create_table()
        except sqlite3.Error as e:
            logging.error(f"Database connection error: {e}")
    
    def create_table(self):
        with self.conn:
            self.conn.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
                )
            ''')

    def add_message(self, sender, message):
        with self.conn:
            self.conn.execute("INSERT INTO context (sender, message) VALUES (?, ?)", (sender, message))
            self.ensure_context_limit()

    def get_context(self, max_length=None):
        if max_length is None:
            max_length = self.max_context_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):
        with self.conn:
            self.conn.execute("DELETE FROM context")

    def ensure_context_limit(self):
        cursor = self.conn.cursor()
        cursor.execute("SELECT SUM(LENGTH(message)) FROM context")
        total_length = cursor.fetchone()[0] or 0
        if total_length > self.max_context_length:
            cursor.execute("DELETE FROM context WHERE id IN (SELECT id FROM context ORDER BY id ASC LIMIT 100)")
            self.conn.commit()

    def close_connection(self):
        if self.conn:
            self.conn.close()

# Context Manager
class ContextManager(QObject):
    summarization_requested = pyqtSignal(str)

    def __init__(self, ollama_controller):
        super().__init__()
        self.db = DatabaseManager()
        self.summary = ""
        self.ollama = ollama_controller
        self.ollama.summarization_received.connect(self.handle_summarization_response)
        self.summarize_request_event = threading.Event()
        self.summarization_result = ""
        self.lock = threading.Lock()

    def add_message(self, sender, message):
        self.db.add_message(sender, message)
        self.check_and_summarize_context()

    def get_context(self):
        with self.lock:
            if self.summary:
                return self.summary + "\n" + self.db.get_context()
            return self.db.get_context()

    def clear_context(self):
        self.db.clear_context()
        with self.lock:
            self.summary = ""

    def check_and_summarize_context(self):
        context = self.db.get_context()
        if len(context) > self.db.max_context_length * 0.8:
            self.summarize_context(context)

    def summarize_context(self, context_text):
        summary_prompt = (
            "Please summarize the following context to make space for new information. "
            "Retain all important details and key points:\n\n"
            f"{context_text}"
        )
        self.summarization_result = ""
        self.summarize_request_event.clear()

        self.ollama.send_request("/api/generate", {
            "model": self.ollama.selected_llm,
            "prompt": summary_prompt,
            "options": {
                "temperature": 0.5,
                "max_new_tokens": 1000,
                "stream": False,
                "format": "json"
            }
        })

        threading.Thread(target=self.wait_for_summary, daemon=True).start()

    def wait_for_summary(self):
        if self.summarize_request_event.wait(timeout=60):
            if self.summarization_result:
                with self.lock:
                    self.summary = self.summarization_result
                self.db.clear_context()
                self.db.add_message("System", f"Summary of previous context:\n{self.summary}")
        else:
            logging.error("Timeout during context summarization.")
            self.summarize_request_event.set()

    def handle_summarization_response(self, response):
        if "response" in response and response["response"]:
            summary = response["response"]
            if summary:
                with self.lock:
                    self.summarization_result = summary
        else:
            logging.error("No response received for summarization.")
        self.summarize_request_event.set()

# Code Highlighter
class CodeHighlighter(QSyntaxHighlighter):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.highlightingRules = []
        format_keyword = QTextCharFormat()
        format_keyword.setForeground(QColor("orange"))
        keywords = [
            "if", "else", "for", "while", "def", "return", "class", "import", "from",
            "try", "except", "elif", "with", "as", "and", "or", "not", "in", "is", "lambda"
        ]
        for keyword in keywords:
            pattern = QRegularExpression(r"\b" + keyword + r"\b")
            self.highlightingRules.append((pattern, format_keyword))

    def highlightBlock(self, text):
        for pattern, format in self.highlightingRules:
            match_iterator = pattern.globalMatch(text)
            while match_iterator.hasNext():
                match = match_iterator.next()
                self.setFormat(match.capturedStart(), match.capturedLength(), format)

# Ollama Controller
class OllamaController(QObject):
    server_status_changed = pyqtSignal(bool)
    response_received = pyqtSignal(dict)
    error_occurred = pyqtSignal(str)
    llm_list_updated = pyqtSignal(list)
    summarization_received = pyqtSignal(dict)

    def __init__(self):
        super().__init__()
        self.process = None
        self.selected_llm = None
        self.api_port = 11434
        self.server_started = False
        self.interrupt_event = threading.Event()
        self.thread_lock = threading.Lock()
        self.check_server_status()

    def check_server_status(self):
        try:
            response = requests.get(f"http://localhost:{self.api_port}/api/tags", timeout=2)
            if response.status_code == 200:
                self.server_started = True
                self.server_status_changed.emit(True)
                self.update_llm_list()
            else:
                self.server_started = False
                self.server_status_changed.emit(False)
                self.error_occurred.emit(f"API check failed: {response.status_code}")
        except requests.exceptions.RequestException:
            self.server_started = False
            self.server_status_changed.emit(False)
            self.error_occurred.emit("Failed to connect to the server.")

    def update_llm_list(self):
        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 start_server(self):
        if not self.is_server_running():
            try:
                ollama_path = find_ollama_executable()
                if not ollama_path:
                    self.error_occurred.emit("Ollama executable not found.")
                    return

                startupinfo = None
                if os.name == 'nt':
                    startupinfo = subprocess.STARTUPINFO()
                    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

                self.process = subprocess.Popen(
                    [ollama_path, 'serve'],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    shell=False,
                    startupinfo=startupinfo,
                    cwd=os.path.expanduser("~")
                )
                threading.Thread(target=self.delayed_check, daemon=True).start()
            except Exception as e:
                self.error_occurred.emit(f"Error starting server: {str(e)}")
        else:
            self.server_started = True
            self.server_status_changed.emit(True)
            self.update_llm_list()

    def delayed_check(self):
        time.sleep(5)
        self.check_server_status()

    def stop_server(self):
        if self.is_server_running() and self.process:
            try:
                self.interrupt_event.set()
                self.process.terminate()
                self.process.wait(timeout=10)
                self.server_started = False
                self.server_status_changed.emit(False)
                self.process = None
                self.selected_llm = None
                self.llm_list_updated.emit([])
            except Exception as e:
                self.error_occurred.emit(f"Error stopping the server: {e}")

    def get_installed_llms(self):
        try:
            response = requests.get(f"http://localhost:{self.api_port}/api/tags", timeout=2)
            response.raise_for_status()
            data = response.json()
            return [model["name"] for model in data.get("models", [])]
        except requests.exceptions.RequestException as e:
            self.error_occurred.emit(f"Error fetching models: {e}")
            return []

    def is_server_running(self):
        try:
            response = requests.get(f"http://localhost:{self.api_port}/api/tags", timeout=2)
            return response.status_code == 200
        except Exception:
            return False

    def set_selected_llm(self, llm_name):
        self.selected_llm = llm_name

    def send_request(self, endpoint, data):
        if not self.is_server_running():
            self.error_occurred.emit("Ollama server is not running.")
            return
        threading.Thread(target=self._handle_request, args=(endpoint, data), daemon=True).start()

    def _handle_request(self, endpoint, data):
        self.interrupt_event.clear()
        try:
            url = f"http://localhost:{self.api_port}{endpoint}"
            response = requests.post(url, json=data, stream=True)
            response.raise_for_status()

            for line in response.iter_lines():
                if self.interrupt_event.is_set():
                    raise InterruptedError("Request interrupted by user.")
                if line:
                    decoded_line = line.decode('utf-8')
                    try:
                        data = json.loads(decoded_line)
                        if "error" in data:
                            raise Exception(f"Ollama API error: {data['error']}")
                        self.response_received.emit(data)
                    except json.JSONDecodeError as e:
                        self.error_occurred.emit(f"Invalid JSON response: {e}")
                        return
        except InterruptedError:
            self.error_occurred.emit("Request interrupted.")
        except requests.exceptions.RequestException as e:
            self.error_occurred.emit(f"API request failed: {e}")
        except Exception as e:
            self.error_occurred.emit(f"Error handling request: {e}")

# Task Manager
class TaskManager(QObject):
    task_completed = pyqtSignal(str)
    task_failed = pyqtSignal(str)
    progress_updated = pyqtSignal(int, int)
    final_answer_ready = pyqtSignal(str)
    
    def __init__(self, ollama_controller, context_manager):
        super().__init__()
        self.ollama = ollama_controller
        self.context_manager = context_manager
        self.task_queue = []
        self.completed_tasks = []
        self.failed_tasks = []
        self.current_task = None
        self.total_tasks = 0
        self.completed_count = 0
        self.finalizing = False
        self.analysis_phase = True
        self.planning_phase = False
        self.execution_phase = False
        self.ollama.response_received.connect(self.handle_response)
        self.ollama.error_occurred.connect(self.on_error)

    def add_task(self, user_request):
        self.current_user_request = user_request
        self.analysis_phase = True
        self.planning_phase = False
        self.execution_phase = False
        self.finalizing = False
        
        analysis_prompt = (
            "Analysiere diese Anfrage und bewerte ihre Komplexität:\n\n"
            f"Anfrage: {user_request}\n\n"
            "Antworte im Format:\n"
            "{\n"
            "  'komplexität': [1-5],\n"
            "  'grund': 'Begründung',\n"
            "  'benötigt_zerlegung': true/false,\n"
            "  'geschätzte_teilschritte': [Anzahl]\n"
            "}"
        )
        
        self.ollama.send_request("/api/generate", {
            "model": self.ollama.selected_llm,
            "prompt": analysis_prompt,
            "options": {
                "temperature": 0.2,
                "format": "json"
            }
        })
        
        self.task_queue.clear()
        self.completed_tasks.clear()
        self.failed_tasks.clear()
        self.current_task = None
        self.total_tasks = 0
        self.completed_count = 0
        self.progress_updated.emit(0, 0)

    def handle_response(self, response):
        if "response" not in response:
            return

        if self.analysis_phase:
            try:
                analysis = json.loads(response["response"])
                self.analysis_phase = False
                if analysis["benötigt_zerlegung"]:
                    self.planning_phase = True
                    self.create_task_plan()
                else:
                    self.direct_response()
            except json.JSONDecodeError:
                self.task_failed.emit("Fehler bei der Analyse der Anfrage")
                
        elif self.planning_phase:
            steps = self.parse_breakdown(response["response"])
            if steps:
                self.task_queue = steps
                self.total_tasks = len(steps)
                self.completed_count = 0
                self.progress_updated.emit(0, self.total_tasks)
                self.planning_phase = False
                self.execution_phase = True
                self.process_next_task()
            else:
                self.task_failed.emit("Fehler beim Erstellen des Arbeitsplans")
                
        elif self.execution_phase:
            if self.current_task:
                self.completed_tasks.append(self.current_task)
                self.completed_count += 1
                self.progress_updated.emit(self.completed_count, self.total_tasks)
                self.task_completed.emit(response["response"])
                self.current_task = None
                self.process_next_task()
                
        elif self.finalizing:
            self.final_answer_ready.emit(response["response"])

    def create_task_plan(self):
        planning_prompt = (
            f"Erstelle einen detaillierten Arbeitsplan für diese Aufgabe:\n\n"
            f"Aufgabe: {self.current_user_request}\n\n"
            "Erstelle eine nummerierte Liste von Teilschritten. "
            "Jeder Schritt muss:\n"
            "1. Klar definiert und eigenständig sein\n"
            "2. Auf vorherigen Schritten aufbauen\n"
            "3. Ein konkretes Zwischenergebnis liefern\n\n"
            "Verwende das Format: 1. [Beschreibung des Teilschritts]"
        )
        
        self.ollama.send_request("/api/generate", {
            "model": self.ollama.selected_llm,
            "prompt": planning_prompt,
            "options": {
                "temperature": 0.7
            }
        })

    def direct_response(self):
        self.execution_phase = True
        self.total_tasks = 1
        prompt = (
            f"Beantworte diese Anfrage direkt und präzise:\n{self.current_user_request}\n\n"
            "Berücksichtige dabei den vorhandenen Kontext:\n"
            f"{self.context_manager.get_context()}"
        )
        
        self.ollama.send_request("/api/generate", {
            "model": self.ollama.selected_llm,
            "prompt": prompt,
            "options": {
                "temperature": 0.7
            }
        })

    def process_next_task(self):
        if not self.task_queue:
            if self.execution_phase:
                self.execution_phase = False
                self.finalize()
            return
            
        self.current_task = self.task_queue.pop(0)
        context = self.context_manager.get_context()
        task_prompt = (
            f"Bisheriger Kontext:\n{context}\n\n"
            f"Bearbeite diese Teilaufgabe:\n{self.current_task}\n\n"
            "Liefere ein konkretes, verwertbares Zwischenergebnis."
        )
        
        self.ollama.send_request("/api/generate", {
            "model": self.ollama.selected_llm,
            "prompt": task_prompt,
            "options": {
                "temperature": 0.7
            }
        })

    def finalize(self):
        if self.finalizing:
            return
            
        self.finalizing = True
        results = "\n".join([f"{i+1}. {task}" for i, task in enumerate(self.completed_tasks)])
        final_prompt = (
            f"Erstelle eine zusammenfassende Antwort auf die ursprüngliche Anfrage:\n"
            f"Anfrage: {self.current_user_request}\n\n"
            f"Verwendete Zwischenergebnisse:\n{results}\n\n"
            "Erstelle daraus eine kohärente, vollständige Antwort."
        )
        
        self.ollama.send_request("/api/generate", {
            "model": self.ollama.selected_llm,
            "prompt": final_prompt,
            "options": {
                "temperature": 0.7
            }
        })

    def parse_breakdown(self, breakdown_text):
        steps = re.findall(r'^\d+\.\s+(.*)', breakdown_text, re.MULTILINE)
        return [step.strip() for step in steps if step.strip()]

    def on_error(self, error_message):
        if self.current_task:
            self.failed_tasks.append(self.current_task)
            self.completed_count += 1
            self.progress_updated.emit(self.completed_count, self.total_tasks)
            self.task_failed.emit(f"Fehler bei '{self.current_task}': {error_message}")
            self.current_task = None
            self.process_next_task()
        else:
            self.task_failed.emit(f"Fehler: {error_message}")


# AutoLLM App
class AutoLLMApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("AutoLLM")
        self.resize(1200, 800)
        self.ollama = OllamaController()
        self.context_manager = ContextManager(self.ollama)
        self.current_parameters = {"temperature": 0.5, "max_new_tokens": 1500, "additional_options": {}}

        # Initialize UI
        self.init_ui()
        self.ollama.start_server()
        self.enable_ui(False)

        # Task Manager
        self.task_manager = TaskManager(self.ollama, self.context_manager)
        self.task_manager.task_completed.connect(self.on_task_completed_step)
        self.task_manager.task_failed.connect(self.on_task_failed_step)
        self.task_manager.progress_updated.connect(self.update_progress)
        self.task_manager.final_answer_ready.connect(self.display_final_answer)

        self.ollama.server_status_changed.connect(self.update_server_status)
        self.ollama.error_occurred.connect(self.show_error)
        self.ollama.llm_list_updated.connect(self.update_llm_list)

        self.highlighter = CodeHighlighter(self.chat_area.document())

        # For blinking "..."
        self.blink_timer = QTimer()
        self.blink_timer.timeout.connect(self.toggle_ellipsis)
        self.ellipsis_visible = True

    def init_ui(self):
        main_layout = QVBoxLayout()

        # Top bar
        top_bar = QHBoxLayout()
        self.server_status = QLabel("🔴 Server Stopped")
        self.server_button = QPushButton("Start Server")
        self.server_button.clicked.connect(self.toggle_server)
        self.llm_label = QLabel("Select LLM:")
        self.llm_combo = QComboBox()
        self.llm_combo.setEnabled(False)
        self.llm_combo.currentTextChanged.connect(self.change_llm)
        self.settings_button = QPushButton("Settings")
        self.settings_button.clicked.connect(self.open_settings)
        self.interrupt_button = QPushButton("Interrupt")
        self.interrupt_button.clicked.connect(self.interrupt_processing)
        top_bar.addWidget(self.server_status)
        top_bar.addWidget(self.server_button)
        top_bar.addWidget(self.llm_label)
        top_bar.addWidget(self.llm_combo)
        top_bar.addWidget(self.settings_button)
        top_bar.addWidget(self.interrupt_button)
        main_layout.addLayout(top_bar)

        # Chat and input area
        chat_input_layout = QHBoxLayout()

        # Chat area with scrollbar
        self.chat_area = QTextBrowser()
        self.chat_area.setReadOnly(True)
        self.chat_area.setFont(QFont("Courier", 10))
        self.chat_area.setOpenExternalLinks(True)

        chat_scroll_area = QScrollArea()
        chat_scroll_area.setWidgetResizable(True)
        chat_scroll_area.setWidget(self.chat_area)
        chat_input_layout.addWidget(chat_scroll_area, 3)

        # Input field and send button
        input_layout = QVBoxLayout()
        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)
        chat_input_layout.addLayout(input_layout, 1)

        main_layout.addLayout(chat_input_layout)

        # Progress and plan area
        progress_plan_layout = QHBoxLayout()

        # Progress area
        self.task_progress = QProgressBar()
        self.task_progress.setValue(0)
        progress_plan_layout.addWidget(self.task_progress, 2)

        # Plan and steps area
        plan_layout = QVBoxLayout()
        self.plan_label = QLabel("Current Plan:")
        self.plan_list = QListWidget()
        self.plan_list.setFixedHeight(200)
        plan_layout.addWidget(self.plan_label)
        plan_layout.addWidget(self.plan_list)
        progress_plan_layout.addLayout(plan_layout, 1)

        main_layout.addLayout(progress_plan_layout)

        # Interaction buttons
        interaction_layout = QHBoxLayout()
        self.edit_button = QPushButton("Edit Task")
        self.edit_button.clicked.connect(self.edit_task)
        self.skip_button = QPushButton("Skip Task")
        self.skip_button.clicked.connect(self.skip_task)
        interaction_layout.addWidget(self.edit_button)
        interaction_layout.addWidget(self.skip_button)
        main_layout.addLayout(interaction_layout)

        self.setLayout(main_layout)

    def enable_ui(self, enabled):
        self.input_field.setEnabled(enabled)
        self.send_button.setEnabled(enabled)
        self.interrupt_button.setEnabled(enabled)
        self.settings_button.setEnabled(enabled)
        self.llm_combo.setEnabled(enabled and self.llm_combo.count() > 0)

    def toggle_ellipsis(self):
        if self.ellipsis_visible:
            self.chat_area.moveCursor(QTextCursor.End)
            cursor = self.chat_area.textCursor()
            cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 3)
            cursor.removeSelectedText()
            self.ellipsis_visible = False
        else:
            self.chat_area.moveCursor(QTextCursor.End)
            self.chat_area.insertPlainText("...")
            self.ellipsis_visible = True

    def start_typing_animation(self):
        self.blink_timer.start(500)  # Blink every 500 ms

    def stop_typing_animation(self):
        self.blink_timer.stop()

    def closeEvent(self, event):
        self.ollama.stop_server()
        self.context_manager.db.close_connection()
        event.accept()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.interrupt_processing()

    def update_server_status(self, is_running):
        if is_running:
            self.server_status.setText("🟢 Server Running")
            self.server_button.setText("Stop Server")
            self.enable_ui(True)
        else:
            self.enable_ui(False)
            self.llm_combo.clear()
            self.llm_combo.addItem("No LLMs Installed")
            self.ollama.set_selected_llm(None)
            self.server_status.setText("🔴 Server Stopped")
            self.server_button.setText("Start Server")

    def update_llm_list(self, llm_list):
        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])
            self.llm_combo.setEnabled(True)
        else:
            self.llm_combo.addItem("No LLMs Installed")
            self.ollama.set_selected_llm(None)
            self.llm_combo.setEnabled(False)
        self.llm_combo.blockSignals(False)
        self.enable_ui(bool(llm_list))

    def change_llm(self, llm_name):
        if llm_name != "No LLMs Installed":
            self.ollama.set_selected_llm(llm_name)
        else:
            self.ollama.set_selected_llm(None)

    def show_error(self, message):
        logging.error(message)
        self.append_message("System", message, message_type="error")

    def send_message(self):
        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()
            self.task_manager.add_task(user_message)
            self.start_typing_animation()

    def append_message(self, sender, message, message_type="normal"):
        color = "gray"
        if sender == "User":
            color = "#00ccff"
            prefix = "<b>User:</b> "
        elif sender == "LLM":
            color = "green"
            prefix = "<b>LLM:</b> "
        else:
            prefix = "<b>System:</b> "

        formatted_message = self.format_message(message)
        html_message = f'<p style="color: {color};">{prefix}{formatted_message}</p>'
        self.chat_area.append(html_message)
        self.chat_area.moveCursor(QTextCursor.End)

    def format_message(self, message):
        message = re.sub(r'(https?://\S+)', r'<a href="\1">\1</a>', message)
        message = re.sub(
            r'```(.*?)```',
            r'<pre style="background-color:#f0f0f0; padding:5px; font-family: monospace; white-space: pre-wrap;"><code>\1</code></pre>',
            message,
            flags=re.DOTALL | re.MULTILINE
        )
        return message

    def interrupt_processing(self):
        self.ollama.interrupt_event.set()
        self.append_message("System", "LLM processing has been interrupted.")
        self.stop_typing_animation()

    def toggle_server(self):
        if self.ollama.is_server_running():
            reply = QMessageBox.question(self, 'Stop Server',
                                         'Are you sure you want to stop the Ollama server?',
                                         QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.ollama.stop_server()
        else:
            self.ollama.start_server()

    def open_settings(self):
        settings_dialog = SettingsDialog(self.current_parameters, self)
        settings_dialog.settings_updated.connect(self.update_parameters)
        settings_dialog.exec_()

    def update_parameters(self, params):
        self.current_parameters = params
        QMessageBox.information(self, "Settings Updated", "LLM parameters have been updated.")

    def on_task_completed_step(self, result):
        self.append_message("LLM", f"Result: {result}")
        self.update_plan_list()
        self.stop_typing_animation()

    def on_task_failed_step(self, error):
        self.append_message("System", f"Error: {error}", message_type="error")
        self.update_plan_list()
        self.stop_typing_animation()

    def update_progress(self, completed, total):
        if total == 0:
            self.task_progress.setValue(0)
        else:
            progress = int((completed / total) * 100)
            self.task_progress.setValue(progress)
        self.update_plan_list()
        
    def update_plan_list(self):
        self.plan_list.clear()
        for i, task in enumerate(self.task_manager.completed_tasks, start=1):
            item = QListWidgetItem(f"{i}. {task}")
            item.setCheckState(Qt.Checked)
            self.plan_list.addItem(item)
        if self.task_manager.current_task:
            current_index = len(self.task_manager.completed_tasks) + 1
            item = QListWidgetItem(f"{current_index}. {self.task_manager.current_task} (running)")
            item.setForeground(QColor("blue"))
            self.plan_list.addItem(item)
        for i, task in enumerate(self.task_manager.task_queue, start=len(self.task_manager.completed_tasks) + 2):
            item = QListWidgetItem(f"{i}. {task}")
            self.plan_list.addItem(item)
        for i, task in enumerate(self.task_manager.failed_tasks, start=len(self.task_manager.completed_tasks) + len(self.task_manager.task_queue) + 2):
            item = QListWidgetItem(f"{i}. {task} (error)")
            item.setForeground(QColor("red"))
            self.plan_list.addItem(item)

    def edit_task(self):
        if self.task_manager.current_task:
            text, ok = QInputDialog.getText(self, "Edit Task", "Edit the current task:", QLineEdit.Normal, self.task_manager.current_task)
            if ok and text.strip():
                self.task_manager.current_task = text.strip()
                self.append_message("System", f"Task edited: {self.task_manager.current_task}")

    def skip_task(self):
        if self.task_manager.current_task:
            self.append_message("System", f"Task skipped: {self.task_manager.current_task}", message_type="warning")
            self.task_manager.current_task = None
            self.task_manager.process_next_task()

    def display_final_answer(self, answer):
        self.append_message("LLM", f"Final Answer: {answer}")
        self.stop_typing_animation()

class SettingsDialog(QDialog):
    
    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)

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

        # Additional options
        self.additional_options_box = QLabel("Additional Options (JSON):")
        self.additional_options_input = QLineEdit()
        self.additional_options_input.setPlaceholderText('e.g., "top_p": 0.9, "frequency_penalty": 1.0')
        layout.addRow(self.additional_options_box, self.additional_options_input)

        # 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_output_label(self, value):
        self.output_label.setText(str(value))

    def save_settings(self):
        try:
            additional_text = self.additional_options_input.text().strip()
            additional_options = {}
            if additional_text:
                additional_options = json.loads("{" + additional_text + "}")
            temperature = self.temperature_slider.value() / 100
            output_length = self.output_slider.value()
            settings = {
                "temperature": temperature,
                "max_new_tokens": output_length,
                "additional_options": additional_options
            }
            self.settings_updated.emit(settings)
            self.accept()
        except json.JSONDecodeError:
            QMessageBox.warning(self, "Invalid JSON", "Please enter valid JSON for additional options.")

    if __name__ == "__main__":
        app = QApplication(sys.argv)
        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;
            }
            QTextBrowser {
                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;
            }
            QListWidget {
                background-color: #2b2b2b;
                border: 1px solid #5c5c5c;
                color: #ffffff;
            }
            QProgressBar {
                border: 1px solid #5c5c5c;
                text-align: center;
                color: #ffffff;
            }
            QProgressBar::chunk {
                background-color: #4caf50;
            }
        """)
        window = AutoLLMApp()
        window.show()
        sys.exit(app.exec_())