Verbesser das skript damit es fehlerfrei und problemlos ausführbar ist und die verwendung der ollama api und die kommunikation mit dem ollama server korrekt abläuft.

Ollama api information:

# Ollama API Summary

## Endpoints

### Generate a completion
- **Endpoint**: `POST /api/generate`
- **Description**: Generate a response for a given prompt with a provided model.
- **Parameters**:
  - `model` (required): the model name
  - `prompt`: the prompt to generate a response for
  - `suffix`: the text after the model response
  - `images` (optional): a list of base64-encoded images (for multimodal models)
  - Advanced parameters: `format`, `options`, `system`, `template`, `context`, `stream`, `raw`, `keep_alive`
- **Example**:
  ```shell
  curl http://localhost:11434/api/generate -d '{
    "model": "llama3.2",
    "prompt": "Why is the sky blue?"
  }'
  ```

### Generate a chat completion
- **Endpoint**: `POST /api/chat`
- **Description**: Generate a chat response for a given prompt with a provided model.
- **Parameters**: Similar to `/api/generate`
- **Example**:
  ```shell
  curl http://localhost:11434/api/chat -d '{
    "model": "llama3.2",
    "prompt": "Hello, how are you?"
  }'
  ```

### Create a Model
- **Endpoint**: `POST /api/models`
- **Description**: Create a new model.
- **Parameters**: Model-specific parameters
- **Example**:
  ```shell
  curl http://localhost:11434/api/models -d '{
    "name": "new_model",
    "parameters": {...}
  }'
  ```

### List Local Models
- **Endpoint**: `GET /api/models`
- **Description**: List all local models.
- **Example**:
  ```shell
  curl http://localhost:11434/api/models
  ```

### Show Model Information
- **Endpoint**: `GET /api/models/{model}`
- **Description**: Show information about a specific model.
- **Example**:
  ```shell
  curl http://localhost:11434/api/models/llama3.2
  ```

### Copy a Model
- **Endpoint**: `POST /api/models/{model}/copy`
- **Description**: Copy a model to a new location.
- **Example**:
  ```shell
  curl http://localhost:11434/api/models/llama3.2/copy -d '{
    "destination": "new_location"
  }'
  ```

### Delete a Model
- **Endpoint**: `DELETE /api/models/{model}`
- **Description**: Delete a specific model.
- **Example**:
  ```shell
  curl -X DELETE http://localhost:11434/api/models/llama3.2
  ```

### Pull a Model
- **Endpoint**: `POST /api/pull`
- **Description**: Pull a model from a remote repository.
- **Parameters**: `name`, `insecure`, `stream`
- **Example**:
  ```shell
  curl http://localhost:11434/api/pull -d '{
    "name": "remote_model"
  }'
  ```

### Push a Model
- **Endpoint**: `POST /api/push`
- **Description**: Upload a model to a model library.
- **Parameters**: `name`, `insecure`, `stream`
- **Example**:
  ```shell
  curl http://localhost:11434/api/push -d '{
    "name": "local_model"
  }'
  ```

### Generate Embeddings
- **Endpoint**: `POST /api/embed`
- **Description**: Generate embeddings from a model.
- **Parameters**: `model`, `input`, `truncate`, `options`, `keep_alive`
- **Example**:
  ```shell
  curl http://localhost:11434/api/embed -d '{
    "model": "all-minilm",
    "input": "Why is the sky blue?"
  }'
  ```

### List Running Models
- **Endpoint**: `GET /api/ps`
- **Description**: List models that are currently loaded into memory.
- **Example**:
  ```shell
  curl http://localhost:11434/api/ps
  ```

## Conventions

### Model names
- Model names follow a `model:tag` format, where `model` can have an optional namespace such as `example/model`.
- Examples: `orca-mini:3b-q4_1`, `llama3:70b`

### Durations
- All durations are returned in nanoseconds.

### Streaming responses
- Certain endpoints stream responses as JSON objects. Streaming can be disabled by providing `{"stream": false}` for these endpoints.

## Error Handling
- Common error codes:
  - `400 Bad Request`: The request was invalid or cannot be otherwise served.
  - `401 Unauthorized`: Authentication credentials were missing or incorrect.
  - `403 Forbidden`: The request is understood, but it has been refused or access is not allowed.
  - `404 Not Found`: The URI requested is invalid or the resource requested does not exist.
  - `500 Internal Server Error`: An error occurred on the server.

## Managing the Ollama Server

### Installation
- **Install Ollama**:
  ```shell
  curl -fsSL https://ollama.com/install.sh | sh
  ```
- **Manual Install**:
  ```shell
  curl -L https://ollama.com/download/ollama-linux-amd64.tgz -o ollama-linux-amd64.tgz
  sudo tar -C /usr -xzf ollama-linux-amd64.tgz
  ```

### Starting the Server
- **Start Ollama**:
  ```shell
  ollama serve
  ```
- **Verify Ollama is running**:
  ```shell
  ollama -v
  ```

### Adding Ollama as a Startup Service
- **Create a user and group for Ollama**:
  ```shell
  sudo useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama
  sudo usermod -a -G ollama $(whoami)
  ```
- **Create a service file**:
  ```ini
  [Unit]
  Description=Ollama Service
  After=network-online.target

  [Service]
  ExecStart=/usr/bin/ollama serve
  User=ollama
  Group=ollama
  Restart=always
  RestartSec=3
  Environment="PATH=$PATH"

  [Install]
  WantedBy=default.target
  ```
- **Start the service**:
  ```shell
  sudo systemctl daemon-reload
  sudo systemctl enable ollama
  sudo systemctl start ollama
  sudo systemctl status ollama
  ```

### Viewing Logs
- **View logs**:
  ```shell
  journalctl -e -u ollama
  ```

### Uninstalling
- **Remove the Ollama service**:
  ```shell
  sudo systemctl stop ollama
  sudo systemctl disable ollama
  sudo rm /etc/systemd/system/ollama.service
  ```
- **Remove the Ollama binary**:
  ```shell
  sudo rm $(which ollama)
  ```
- **Remove downloaded models and service user/group**:
  ```shell
  sudo rm -r /usr/share/ollama
  sudo userdel ollama
  sudo groupdel ollama
  ```

## Advanced Parameters for Communication
- **format**: The format to return a response in. Currently, the only accepted value is `json`.
- **options**: Additional model parameters such as `temperature`, `max_tokens`, etc.
- **system**: System message to override what is defined in the `Modelfile`.
- **template**: The prompt template to use, overriding what is defined in the `Modelfile`.
- **context**: The context parameter returned from a previous request to `/generate`, used to keep a short conversational memory.
- **stream**: If `false`, the response will be returned as a single response object, rather than a stream of objects.
- **raw**: If `true`, no formatting will be applied to the prompt. Use this if specifying a full templated prompt in the request.
- **keep_alive**: Controls how long the model will stay loaded into memory following the request (default: `5m`).

## Checking if the LLM has Finished a Message
- The `done` field in the response indicates whether the LLM has finished generating the message.
- Example:
  ```json
  {
    "model": "llama3.2",
    "created_at": "2023-08-04T08:52:19.385406455-07:00",
    "response": "The",
    "done": false
  }
  ```

## Examples of Advanced Communication
- **Generate request with JSON format**:
  ```shell
  curl http://localhost:11434/api/generate -d '{
    "model": "llama3.2",
    "prompt": "Why is the sky blue?",
    "format": "json"
  }'
  ```
- **Generate request with streaming disabled**:
  ```shell
  curl http://localhost:11434/api/generate -d '{
    "model": "llama3.2",
    "prompt": "Why is the sky blue?",
    "stream": false
  }'
  ```



Skript:

import sys
import threading
import requests
import json
import logging
import time
from datetime import datetime
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QLabel, QPushButton, QComboBox, QAction,
    QHBoxLayout, QVBoxLayout, QWidget, QTextEdit, QFileDialog,
    QTreeWidget, QTreeWidgetItem, QTextBrowser, QProgressBar, QTabWidget,
    QListWidget, QStatusBar, QMenuBar, QToolBar, QDialog, QGroupBox, QFormLayout,
    QSpinBox, QPlainTextEdit, QMessageBox, QInputDialog, QLineEdit
)
from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal
from PyQt5.QtGui import QIcon, QColor

# Configure logging
logging.basicConfig(level=logging.INFO, filename='app.log', filemode='w',
                    format='%(asctime)s - %(levelname)s - %(message)s')


class LLMClient:
    def __init__(self, base_url='http://localhost:11434'):
        self.base_url = 'http://localhost:11434/api'
        self.cache = {}
        self.memory = {}  # Context buffer for tasks

    def get_models(self):
        try:
            response = requests.get(f'{self.base_url}/api/models')
            if response.status_code == 200:
                models = response.json()
                model_names = [model['name'] for model in models]
                return model_names
            else:
                logging.error(f"Error fetching models: {response.text}")
                return []
        except requests.RequestException as e:
            logging.error(f"Error fetching models: {e}")
            return []

    def generate(self, model, prompt):
        cache_key = (model, prompt)
        if cache_key in self.cache:
            return self.cache[cache_key]
        payload = {
            "model": model,
            "prompt": prompt
        }
        headers = {"Content-Type": "application/json"}
        try:
            response = requests.post(f'{self.base_url}/generate', json=payload, headers=headers)
            if response.status_code == 200:
                result = response.json().get('response', '')
                self.cache[cache_key] = result
                return result
            else:
                logging.error(f"Error generating response: {response.text}")
                return f"Error generating response: {response.text}"
        except requests.RequestException as e:
            logging.error(f"Error communicating with LLM: {e}")
            return f"Error communicating with LLM: {e}"

    def add_to_memory(self, key, value):
        self.memory[key] = value

    def get_from_memory(self, key):
        return self.memory.get(key, '')


class ServerStatusChecker(QThread):
    status_updated = pyqtSignal(bool)

    def __init__(self, base_url, parent=None):
        super().__init__(parent)
        self.base_url = base_url
        self._running = True

    def run(self):
        while self._running:
            try:
                response = requests.get(f'{self.base_url}/api/status', timeout=2)
                is_running = response.status_code == 200
            except requests.RequestException:
                is_running = False
            self.status_updated.emit(is_running)
            self.sleep(5)  # Check every 5 seconds

    def stop(self):
        self._running = False


class TaskProcessor(QThread):
    progress_updated = pyqtSignal(int)
    task_message = pyqtSignal(str)
    task_result = pyqtSignal(str)
    task_status = pyqtSignal(int, int, str)  # task_index, goal_index, status

    def __init__(self, tasks, llm_client, llm_model, parent=None):
        super().__init__(parent)
        self.tasks = tasks
        self.llm_client = llm_client
        self.llm_model = llm_model
        self._paused = False
        self._stopped = False
        self.current_task_index = 0
        self.current_goal_index = 0
        self.context_memory = {}  # Stores results from previous steps

    def run(self):
        total_goals = sum(len(task['Goals']) for task in self.tasks)
        completed_goals = 0

        for i, task in enumerate(self.tasks):
            self.current_task_index = i
            if self._stopped:
                break
            while self._paused:
                self.msleep(100)

            self.task_message.emit(f"Assistant: Starting task '{task['Title']}'")
            self.task_status.emit(i, -1, 'processing')  # Mark task as processing

            for j, goal in enumerate(task['Goals']):
                self.current_goal_index = j
                if self._stopped:
                    break
                while self._paused:
                    self.msleep(100)

                self.task_message.emit(f"Assistant: Processing goal: {goal}")
                self.task_status.emit(i, j, 'processing')  # Mark goal as processing

                result = self.process_goal(task, goal)
                success = self.self_review(goal, result)
                if success:
                    self.task_result.emit(f"Result for goal '{goal}':\n{result}")
                    self.context_memory[goal] = result  # Store result in context
                    self.task_status.emit(i, j, 'completed')  # Mark goal as completed
                else:
                    self.task_message.emit(f"Assistant: Repeating processing for goal: {goal}")
                    self.task_status.emit(i, j, 'error')  # Mark goal as error
                    # Repeat the processing
                    result = self.process_goal(task, goal, retry=True)
                    success = self.self_review(goal, result)
                    if success:
                        self.task_result.emit(f"Result for goal '{goal}' after retry:\n{result}")
                        self.context_memory[goal] = result
                        self.task_status.emit(i, j, 'completed')
                    else:
                        self.task_message.emit(f"Assistant: Could not successfully process goal '{goal}'.")
                        self.task_status.emit(i, j, 'failed')

                completed_goals += 1
                progress = int((completed_goals / total_goals) * 100)
                self.progress_updated.emit(progress)

            self.task_status.emit(i, -1, 'completed')  # Mark task as completed

            # Task summary
            self.task_message.emit(f"Assistant: Creating summary for task: {task['Title']}")
            task_summary = self.create_task_summary(task)
            self.task_result.emit(f"Summary for task '{task['Title']}':\n{task_summary}")
            self.context_memory[task['Title']] = task_summary

        if not self._stopped:
            self.task_message.emit("Assistant: All tasks have been completed.")
            # Generate final result
            final_result = self.generate_final_result()
            self.task_result.emit(f"Final Result:\n{final_result}")

    def process_goal(self, task, goal, retry=False):
        context = self.get_relevant_context(goal)
        retry_text = "This is a retry attempt.\n" if retry else ""

        prompt = f"""
{retry_text}
You are working on a software development task.

**Application Description:**
{task.get('ApplicationDescription', '')}

**Current Goal:**
{goal}

**Relevant Context:**
{context}

Create a solution for the goal above. Use clear and precise language and provide code examples where appropriate.
"""
        result = self.llm_client.generate(self.llm_model, prompt)
        return result

    def self_review(self, goal, result):
        prompt = f"""
Critically review the following result for the goal "{goal}":

{result}

Evaluate if the result is complete, correct, and meets the requirements. Answer with "Yes" if the result is satisfactory, or "No" with a brief explanation if it is not satisfactory.
"""
        review = self.llm_client.generate(self.llm_model, prompt)
        if "Yes" in review:
            return True
        else:
            self.task_message.emit(f"Assistant: Self-review for goal '{goal}' failed. Reason: {review}")
            return False

    def get_relevant_context(self, current_goal):
        # Collect relevant results from context
        context = ""
        for key, value in self.context_memory.items():
            context += f"**{key}:**\n{value}\n\n"
        return context

    def create_task_summary(self, task):
        # Create a summary of the task based on results of the goals
        summaries = []
        for goal in task['Goals']:
            result = self.context_memory.get(goal, '')
            summaries.append(f"{goal}:\n{result}\n")
        task_summary = "\n".join(summaries)
        return task_summary

    def generate_final_result(self):
        # Generate the final result based on all tasks
        context = "\n".join(self.context_memory.values())
        prompt = f"""
Summarize all previous results and generate the complete code of the application including documentation.

**Overall Context:**
{context}

Create a structured and comprehensive summary with all relevant details.
"""
        final_result = self.llm_client.generate(self.llm_model, prompt)
        return final_result

    def pause(self):
        self._paused = True

    def resume(self):
        self._paused = False

    def stop(self):
        self._stopped = True


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Intelligent Assistant for Developers')
        self.setGeometry(100, 100, 1200, 800)
        self.llm_client = LLMClient()
        self.paused = False
        self.stopped = False
        self.plan_data = []
        self.uploaded_files = []
        self.server_status_checker = None
        self.init_ui()
        self.update_llm_list()
        self.apply_styles()

    def init_ui(self):
        self.create_menu()
        self.create_toolbar()
        self.status_bar = self.statusBar()
        self.status_bar.showMessage('Ready')

        self.tabs = QTabWidget()
        self.tabs.addTab(self.create_configuration_tab(), "Configuration")
        self.tabs.addTab(self.create_plan_tab(), "Planning")
        self.tabs.addTab(self.create_chat_tab(), "Assistant")
        self.setCentralWidget(self.tabs)

        # Start server status checker
        self.start_server_status_checker()

    def create_menu(self):
        menu_bar = self.menuBar()

        # File menu
        file_menu = menu_bar.addMenu('File')
        save_action = QAction('Save', self)
        save_action.triggered.connect(self.save_project)
        file_menu.addAction(save_action)
        load_action = QAction('Load', self)
        load_action.triggered.connect(self.load_project)
        file_menu.addAction(load_action)
        settings_action = QAction('Settings', self)
        settings_action.triggered.connect(self.open_settings_dialog)
        file_menu.addAction(settings_action)
        exit_action = QAction('Exit', self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # Help menu
        help_menu = menu_bar.addMenu('Help')
        about_action = QAction('About', self)
        about_action.triggered.connect(self.show_about_dialog)
        help_menu.addAction(about_action)

    def create_toolbar(self):
        tool_bar = self.addToolBar('Tools')
        start_action = QAction(QIcon('start.png'), 'Start', self)
        start_action.triggered.connect(self.start_processing)
        tool_bar.addAction(start_action)
        pause_action = QAction(QIcon('pause.png'), 'Pause', self)
        pause_action.triggered.connect(self.pause_processing)
        tool_bar.addAction(pause_action)
        resume_action = QAction(QIcon('resume.png'), 'Resume', self)
        resume_action.triggered.connect(self.resume_processing)
        tool_bar.addAction(resume_action)
        stop_action = QAction(QIcon('stop.png'), 'Stop', self)
        stop_action.triggered.connect(self.stop_processing)
        tool_bar.addAction(stop_action)

    def create_configuration_tab(self):
        config_widget = QWidget()
        v_layout = QVBoxLayout()

        llm_label = QLabel('Select an LLM:')
        self.llm_combo = QComboBox()

        self.server_status_label = QLabel('Server Status: Unknown')
        self.server_status_indicator = QLabel('⚪')

        self.start_server_btn = QPushButton('Start Server')
        self.stop_server_btn = QPushButton('Stop Server')

        h_layout = QHBoxLayout()
        h_layout.addWidget(llm_label)
        h_layout.addWidget(self.llm_combo)

        h_layout2 = QHBoxLayout()
        h_layout2.addWidget(self.server_status_label)
        h_layout2.addWidget(self.server_status_indicator)
        h_layout2.addWidget(self.start_server_btn)
        h_layout2.addWidget(self.stop_server_btn)

        # Application description
        description_label = QLabel('Application Description:')
        self.description_text = QTextEdit()

        # File upload
        self.upload_btn = QPushButton('Upload Files')
        self.file_list = QListWidget()

        # Settings
        settings_group = QGroupBox('Settings')
        settings_layout = QFormLayout()
        self.token_limit_spin = QSpinBox()
        self.token_limit_spin.setRange(100, 10000)
        self.token_limit_spin.setValue(1000)
        settings_layout.addRow('Token Limit:', self.token_limit_spin)
        settings_group.setLayout(settings_layout)

        v_layout.addLayout(h_layout)
        v_layout.addLayout(h_layout2)
        v_layout.addWidget(description_label)
        v_layout.addWidget(self.description_text)
        v_layout.addWidget(self.upload_btn)
        v_layout.addWidget(QLabel('Uploaded Files:'))
        v_layout.addWidget(self.file_list)
        v_layout.addWidget(settings_group)

        config_widget.setLayout(v_layout)

        # Connections
        self.start_server_btn.clicked.connect(self.start_server)
        self.stop_server_btn.clicked.connect(self.stop_server)
        self.upload_btn.clicked.connect(self.upload_files)

        return config_widget

    def create_plan_tab(self):
        plan_widget = QWidget()
        v_layout = QVBoxLayout()

        # Analysis button
        self.analyse_btn = QPushButton('Start Analysis')
        self.edit_plan_btn = QPushButton('Edit Plan')

        # Plan display
        self.plan_tree = QTreeWidget()
        self.plan_tree.setHeaderLabels(['Task', 'Description/Goals'])
        self.plan_tree.setColumnWidth(0, 300)

        h_button_layout = QHBoxLayout()
        h_button_layout.addWidget(self.analyse_btn)
        h_button_layout.addWidget(self.edit_plan_btn)

        v_layout.addLayout(h_button_layout)
        v_layout.addWidget(self.plan_tree)

        plan_widget.setLayout(v_layout)

        # Connections
        self.analyse_btn.clicked.connect(self.start_analysis)
        self.edit_plan_btn.clicked.connect(self.edit_plan)

        return plan_widget

    def create_chat_tab(self):
        chat_widget = QWidget()
        chat_layout = QVBoxLayout()

        # Chat area
        self.chat_area = QTextBrowser()

        # History area
        self.history_area = QTextEdit()
        self.history_area.setReadOnly(True)

        # Progress bar
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        self.progress_bar.setAlignment(Qt.AlignCenter)

        chat_layout.addWidget(QLabel('Chat Area:'))
        chat_layout.addWidget(self.chat_area)
        chat_layout.addWidget(QLabel('History:'))
        chat_layout.addWidget(self.history_area)
        chat_layout.addWidget(self.progress_bar)

        chat_widget.setLayout(chat_layout)

        return chat_widget

    def apply_styles(self):
        # Loading a dark mode stylesheet
        try:
            with open("style.qss", "r") as f:
                self.setStyleSheet(f.read())
        except Exception as e:
            logging.error(f"Error loading stylesheet: {e}")

    def update_llm_list(self):
        models = self.llm_client.get_models()
        self.llm_combo.clear()
        self.llm_combo.addItems(models)

    def start_server_status_checker(self):
        self.server_status_checker = ServerStatusChecker(base_url=self.llm_client.base_url)
        self.server_status_checker.status_updated.connect(self.update_server_status)
        self.server_status_checker.status_updated.connect(self.check_initial_server_status)
        self.server_status_checker.start()

    def check_initial_server_status(self):
        try:
            response = requests.get(f'{self.llm_client.base_url}/status', timeout=2)
            if response.status_code == 200:
                self.update_server_status(True)
            else:
                self.update_server_status(False)
        except requests.RequestException:
            self.update_server_status(False)

    def update_server_status(self, is_running):
        if is_running:
            self.server_status_label.setText('Server Status: Running')
            self.server_status_indicator.setText('🟢')
        else:
            self.server_status_label.setText('Server Status: Stopped')
            self.server_status_indicator.setText('🔴')

    def start_server(self):
        try:
            response = requests.post('http://localhost:11434/v1/start')
            if response.status_code == 200:
                self.chat_area.append("<b>Assistant:</b> Server started successfully.")
                self.update_llm_list()
            else:
                logging.error(f"Error starting the server: {response.text}")
                QMessageBox.critical(self, "Error", f"Error starting the server: {response.text}")
        except Exception as e:
            logging.error(f"Error starting the server: {e}")
            QMessageBox.critical(self, "Error", f"Error starting the server: {e}")

    def stop_server(self):
        try:
            response = requests.post('http://localhost:11434/v1/stop')
            if response.status_code == 200:
                self.chat_area.append("<b>Assistant:</b> Server stopped successfully.")
            else:
                logging.error(f"Error stopping the server: {response.text}")
                QMessageBox.critical(self, "Error", f"Error stopping the server: {response.text}")
        except Exception as e:
            logging.error(f"Error stopping the server: {e}")
            QMessageBox.critical(self, "Error", f"Error stopping the server: {e}")

    def upload_files(self):
        options = QFileDialog.Options()
        files, _ = QFileDialog.getOpenFileNames(
            self, "Select Files", "",
            "All Files (*)", options=options
        )
        if files:
            self.uploaded_files.extend(files)
            self.display_uploaded_files()

    def display_uploaded_files(self):
        self.file_list.clear()
        for file in self.uploaded_files:
            self.file_list.addItem(file)

    def start_analysis(self):
        self.chat_area.append("<b>Assistant:</b> Starting analysis of the application description...")
        thread = threading.Thread(target=self.perform_analysis)
        thread.start()

    def perform_analysis(self):
        description = self.description_text.toPlainText()
        selected_llm = self.llm_combo.currentText()
        file_contents = self.get_file_contents()
        prompt = self.generate_plan_prompt(description, file_contents)
        plan_text = self.llm_client.generate(selected_llm, prompt)
        self.plan_data = self.parse_plan(plan_text)
        if self.plan_data:
            self.display_plan(self.plan_data)
            self.chat_area.append("<b>Assistant:</b> Analysis completed. The plan has been created.")
        else:
            self.chat_area.append("<b>Assistant:</b> Error during analysis.")
            logging.error("Plan data is empty after analysis.")

    def get_file_contents(self):
        content = ""
        for file_path in self.uploaded_files:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content += f"\nContent of {file_path}:\n{f.read()}"
            except Exception as e:
                logging.error(f"Error reading file {file_path}: {e}")
        return content

    def generate_plan_prompt(self, description, file_contents):
        return f"""
As an experienced software architect, please create a detailed development plan based on the following application description.

**Application Description:**
{description}

**Additional Files:**
{file_contents}

**Plan Requirements:**
- Structured JSON output.
- Includes tasks with Title, Description, and Goals.
- Use clear and precise language.

**Example Output:**
[
    {{
        "Title": "Task 1: XYZ",
        "Description": "Description of the task.",
        "Goals": ["Goal 1", "Goal 2"]
    }},
    ...
]
"""

    def parse_plan(self, plan_text):
        try:
            plan = json.loads(plan_text)
            return plan
        except json.JSONDecodeError as e:
            logging.error(f"Error parsing the plan: {e}")
            return []

    def display_plan(self, plan):
        self.plan_tree.clear()
        for i, task in enumerate(plan):
            task_item = QTreeWidgetItem([task['Title']])
            task_item.setData(0, Qt.UserRole, (i, None))  # Store index for identification
            task_item.setBackground(0, QColor('#444'))  # Darker background for dark mode

            description_item = QTreeWidgetItem(['Description', task['Description']])
            task_item.addChild(description_item)

            goals_item = QTreeWidgetItem(['Goals'])
            for j, goal in enumerate(task['Goals']):
                goal_item = QTreeWidgetItem([goal])
                goal_item.setData(0, Qt.UserRole, (i, j))
                goal_item.setBackground(0, QColor('#555'))
                goals_item.addChild(goal_item)
            task_item.addChild(goals_item)

            self.plan_tree.addTopLevelItem(task_item)

    def edit_plan(self):
        selected_item = self.plan_tree.currentItem()
        if selected_item:
            if selected_item.parent():
                # Editing goals or descriptions
                text, ok = QInputDialog.getText(self, 'Edit', 'Enter new text:', QLineEdit.Normal, selected_item.text(0))
                if ok and text:
                    selected_item.setText(0, text)
            else:
                # Editing tasks
                text, ok = QInputDialog.getText(self, 'Edit', 'Enter new task title:', QLineEdit.Normal, selected_item.text(0))
                if ok and text:
                    selected_item.setText(0, text)
        else:
            QMessageBox.warning(self, 'Warning', 'Please select an item to edit.')

    def update_task_status(self, task_index, goal_index, status):
        if goal_index == -1:
            # Update task
            task_item = self.plan_tree.topLevelItem(task_index)
            color = self.get_status_color(status)
            task_item.setBackground(0, color)
        else:
            # Update goal
            task_item = self.plan_tree.topLevelItem(task_index)
            goals_item = task_item.child(1)  # Assuming Goals are the second child
            goal_item = goals_item.child(goal_index)
            color = self.get_status_color(status)
            goal_item.setBackground(0, color)

    def get_status_color(self, status):
        if status == 'processing':
            return QColor('#FFD700')  # Gold
        elif status == 'completed':
            return QColor('#32CD32')  # Lime Green
        elif status == 'error':
            return QColor('#FF4500')  # Orange Red
        elif status == 'failed':
            return QColor('#DC143C')  # Crimson
        else:
            return QColor('#555')  # Default dark

    def start_processing(self):
        if not self.plan_data:
            QMessageBox.warning(self, "Warning", "Please create a plan first.")
            return
        self.chat_area.append("<b>Assistant:</b> Starting the processing of tasks.")
        self.task_processor = TaskProcessor(
            tasks=self.plan_data,
            llm_client=self.llm_client,
            llm_model=self.llm_combo.currentText()
        )
        self.task_processor.progress_updated.connect(self.update_progress)
        self.task_processor.task_message.connect(self.append_chat_message)
        self.task_processor.task_result.connect(self.append_chat_message)
        self.task_processor.task_status.connect(self.update_task_status)
        self.task_processor.start()

    def append_chat_message(self, message):
        self.chat_area.append(f"<b>{message}</b>")
        self.history_area.append(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}")

    def update_progress(self, value):
        self.progress_bar.setValue(value)
        self.status_bar.showMessage(f'Progress: {value}%')

    def pause_processing(self):
        if hasattr(self, 'task_processor'):
            self.task_processor.pause()
            self.chat_area.append("<b>System:</b> The process has been paused.")

    def resume_processing(self):
        if hasattr(self, 'task_processor'):
            self.task_processor.resume()
            self.chat_area.append("<b>System:</b> The process will continue.")

    def stop_processing(self):
        if hasattr(self, 'task_processor'):
            self.task_processor.stop()
            self.chat_area.append("<b>System:</b> The process has been stopped.")
            self.progress_bar.setValue(0)

    def save_project(self):
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getSaveFileName(self, "Save Project", "", "JSON Files (*.json)", options=options)
        if file_name:
            project_data = {
                'description': self.description_text.toPlainText(),
                'uploaded_files': self.uploaded_files,
                'plan_data': self.plan_data
            }
            try:
                with open(file_name, 'w', encoding='utf-8') as f:
                    json.dump(project_data, f, ensure_ascii=False, indent=4)
                QMessageBox.information(self, "Success", "Project successfully saved.")
            except Exception as e:
                logging.error(f"Error saving project: {e}")
                QMessageBox.critical(self, "Error", f"Error saving project: {e}")

    def load_project(self):
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getOpenFileName(self, "Load Project", "", "JSON Files (*.json)", options=options)
        if file_name:
            try:
                with open(file_name, 'r', encoding='utf-8') as f:
                    project_data = json.load(f)
                self.description_text.setPlainText(project_data.get('description', ''))
                self.uploaded_files = project_data.get('uploaded_files', [])
                self.display_uploaded_files()
                self.plan_data = project_data.get('plan_data', [])
                if self.plan_data:
                    self.display_plan(self.plan_data)
                QMessageBox.information(self, "Success", "Project successfully loaded.")
            except Exception as e:
                logging.error(f"Error loading project: {e}")
                QMessageBox.critical(self, "Error", f"Error loading project: {e}")

    def open_settings_dialog(self):
        # Open settings dialog
        settings_dialog = QDialog(self)
        settings_dialog.setWindowTitle('Settings')
        layout = QFormLayout()
        # You can add additional settings here
        layout.addRow('Token Limit:', self.token_limit_spin)
        settings_dialog.setLayout(layout)
        settings_dialog.exec_()

    def show_about_dialog(self):
        QMessageBox.about(self, "About", "Intelligent Assistant for Developers\nVersion 2.2")

    def closeEvent(self, event):
        # Stop the server status checker thread when the application is closed
        if self.server_status_checker is not None:
            self.server_status_checker.stop()
            self.server_status_checker.wait()
        event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())