Project structure:

ui_develop/
├── static/
│   ├── css/
│   │   └── styles.css
├── templates/
│   └── index.html
├── ai_engineer.py
├── ai_tools.py
├── models.py
├── project_structure_manager.py
├── requirements.txt
└── run.py


Project files:

ai_engineer.py
```py
import json
from openai import OpenAI
import httpx

from ai_tools import AIEngineer_tools
from project_structure_manager import ProjectStructureManagement

# ai_engineer.py
class AIEngineer:
    def __init__(self, OPENAI_API_KEY, proxy=None):
        self.client = self.loadOpenAI_Client(OPENAI_API_KEY, proxy)
        self.project_manager = ProjectStructureManagement("ui_develop")
        self.SYSTEM_PROMPT = self.get_system_prompt()
        self.messages = [{"role": "system", "content": self.SYSTEM_PROMPT}]

    def loadOpenAI_Client(self, OPENAI_API_KEY, proxy):
        httpx_Client_proxy = None
        if proxy:
            # Разбиваем строку с прокси на части
            proxy_parts = proxy.split(":")
            proxy_host = proxy_parts[1][2:]  # Убираем "//" из начала хоста
            proxy_port = int(proxy_parts[2])

            httpx_Client_proxy = httpx.Client(
                proxies="socks5://" + proxy_host + ":" + str(
                    proxy_port),
                verify=False  # Добавляем проверку сертификата
            )
        print(f"OPENAI_API_KEY = {OPENAI_API_KEY}")
        # Создаем HTTP-клиент с прокси
        client = OpenAI(
            api_key=OPENAI_API_KEY,
            http_client=httpx_Client_proxy
        )
        return client

    def get_system_prompt(self):
        with open("system_prompt.txt", 'r', encoding='utf-8') as file:
            return file.read()

    def ai_mind_generator(self, tools, role, messages):
        response = self.client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )
        response_message = response.choices[0].message
        messages.append(response_message)
        #print(f"messages after response: {messages}\n")

        yield {"type": "message", "role": role, "message": response_message.content}

        # Обработка вызовов функций
        tool_calls = response_message.tool_calls
        if tool_calls:
            for tool_call in tool_calls:
                #print(f"tool_call = {tool_call}")
                tool_call_id = tool_call.id
                tool_function_name = tool_call.function.name

                _arguments = json.loads(tool_call.function.arguments)

                yield {"type": "tool_call", "role": role, "tool_function_name": tool_function_name,
                       "arguments": _arguments}

                function_result = ""

                if tool_function_name == "get_project_structure":
                    function_result = self.project_manager.get_project_structure()
                elif tool_function_name == "read_file":
                    function_result = self.project_manager.read_file(_arguments["file_path"])
                elif tool_function_name == "update_file":
                    function_result = self.project_manager.update_file(_arguments["file_path"], _arguments["content"])
                elif tool_function_name == "rename_file":
                    function_result = self.project_manager.rename_file(_arguments["old_path"], _arguments["new_name"])
                elif tool_function_name == "create_file":
                    function_result = self.project_manager.create_file(_arguments["path"], _arguments["content"])
                else:
                    print(f"Error: function {tool_function_name} does not exist")

                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call_id,
                    "name": tool_function_name,
                    "content": function_result
                })

            # Все tools_calls модели обработаны и нужно вернуть ей результат через эту же функцию в рекурсии
            for event in self.ai_mind_generator(tools, role, messages):
                yield event
        else:
            print(f"!! {role} has no tool calls! Даааа")

    def ask(self, text):
        print(f"ask_response")
        # Общение клиента с начальником разработчиков
        self.messages.append({"role": "user", "content": text})
        #print(f"self.AI_Boss_messages = {self.messages}")

        for event in self.ai_mind_generator(AIEngineer_tools, "DEVELOPER", self.messages):
            #print(f"RECEIVED EVENT: {event}")
            if event:
                if event["type"] == "message":
                    role = event["role"]
                    message = event["message"]
                    # тут нужно реализовать возвращение сообщения message
                elif event["type"] == "tool_call":
                    role = event["role"]
                    tool_function_name = event["tool_function_name"]
                    arguments = event["arguments"]
                    # тут нужно реализовать возвращение сообщения tool_function_name c arguments
                    print(
                        f"RECEIVED TOOL CALL by {role}, tool_function_name: {tool_function_name}, arguments: {arguments}")

        print("FULL DONE!!!!!!!!!!!")
```


ai_tools.py
```py

# AI Engineer Tools

AIEngineer_tools = [
    {
        "type": "function",
        "function": {
            "name": "get_project_structure",
            "description": "Returns the project's file structure.",
            "parameters": {
                "type": "object",
                "properties": {},
                "required": []
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "Returns the content of a file.",
            "parameters": {
                "type": "object",
                "properties": {
                    "file_path": {
                        "type": "string",
                        "description": "Local path to the file in the project (e.g., project/main.py)."
                    }
                },
                "required": ["file_path"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "update_file",
            "description": "Updates the content of a file.",
            "parameters": {
                "type": "object",
                "properties": {
                    "file_path": {
                        "type": "string",
                        "description": "Local path to the file in the project (e.g., project/main.py)."
                    },
                    "content": {
                        "type": "string",
                        "description": "New content of the file (code)."
                    }
                },
                "required": ["file_path", "content"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "rename_file",
            "description": "Renames a file.",
            "parameters": {
                "type": "object",
                "properties": {
                    "old_path": {
                        "type": "string",
                        "description": "Local path to the existing file in the project (e.g., project/main.py)."
                    },
                    "new_name": {
                        "type": "string",
                        "description": "New name for the file."
                    }
                },
                "required": ["old_path", "new_name"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "create_file",
            "description": "Creates a new file in the specified directory with the given content.",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "Local path to the file in the project (e.g., project/main.py)."
                    },
                    "content": {
                        "type": "string",
                        "description": "Content of the file (code)."
                    }
                },
                "required": ["path", "content"]
            }
        }
    }
]

```


models.py
```py
# app/models.py
import json
import os

class ChatDatabase:
    def __init__(self, db_path='chats.json'):
        self.db_path = db_path
        self.chats = {}
        self.load_chats()

    def load_chats(self):
        if os.path.exists(self.db_path):
            with open(self.db_path, 'r', encoding='utf-8') as f:
                self.chats = json.load(f)
        else:
            self.chats = {}

    def save_chats(self):
        with open(self.db_path, 'w', encoding='utf-8') as f:
            json.dump(self.chats, f, ensure_ascii=False, indent=4)

    def create_chat(self, chat_name):
        chat_id = str(len(self.chats) + 1)
        self.chats[chat_id] = {
            'name': chat_name,
            'messages': []
        }
        self.save_chats()
        return chat_id

    def rename_chat(self, chat_id, new_name):
        if chat_id in self.chats:
            self.chats[chat_id]['name'] = new_name
            self.save_chats()

    def add_message(self, chat_id, role, content):
        if chat_id in self.chats:
            self.chats[chat_id]['messages'].append({
                'role': role,
                'content': content
            })
            self.save_chats()

    def get_chats(self):
        return self.chats

    def get_chat(self, chat_id):
        return self.chats.get(chat_id, None)
```


project_structure_manager.py
```py

import os
import os.path

class ProjectStructureManagement:
    def __init__(self, current_project_path="test_project"):
        self.current_project_path = current_project_path
        os.makedirs(self.current_project_path, exist_ok=True)

    def process_path(self, path):
        full_path = os.path.join(self.current_project_path, path)
        return os.path.abspath(full_path)

    def get_project_structure(self):
        if not self.current_project_path:
            return "No project has been created or set."

        root_dir = self.current_project_path
        root_dir_display = os.path.basename(root_dir)

        def walk_dir(dir_path, prefix=""):
            contents = sorted(os.listdir(dir_path))
            paths = [os.path.join(dir_path, p) for p in contents]
            dirs = [p for p in paths if os.path.isdir(p)]
            files = [p for p in paths if os.path.isfile(p)]

            lines = []
            for d in dirs:
                lines.append(f"{prefix}├── {os.path.basename(d)}/")
                lines.extend(walk_dir(d, prefix + "│   "))
            for f in files:
                if f != files[-1]:
                    lines.append(f"{prefix}├── {os.path.basename(f)}")
                else:
                    lines.append(f"{prefix}└── {os.path.basename(f)}")
            return lines

        return "\n".join([root_dir_display + "/"] + walk_dir(root_dir))

    def read_file(self, file_path):
        full_file_path = self.process_path(file_path)
        if not os.path.exists(full_file_path):
            return f"File not found: {file_path}"
        try:
            with open(full_file_path, 'r', encoding='utf-8') as file:
                return file.read()
        except Exception as e:
            return f"Error reading file: {str(e)}"

    def update_file(self, file_path, content):
        full_path = self.process_path(file_path)
        try:
            with open(full_path, 'w', encoding='utf-8') as file:
                file.write(content)
            return f"File updated successfully: {file_path}"
        except Exception as e:
            return f"Error updating file: {str(e)}"

    def rename_file(self, old_path, new_name):
        old_full_path = self.process_path(old_path)
        new_full_path = os.path.join(os.path.dirname(old_full_path), new_name)
        try:
            os.rename(old_full_path, new_full_path)
            return f"File renamed successfully from {old_path} to {new_name}"
        except Exception as e:
            return f"Error renaming file: {str(e)}"

    def create_file(self, path, content):
        if not self.current_project_path:
            return "No project has been created or set."

        full_file_path = self.process_path(path)
        os.makedirs(os.path.dirname(full_file_path), exist_ok=True)

        try:
            with open(full_file_path, 'w', encoding='utf-8') as file:
                file.write(content)
            return f"File created successfully: {path}"
        except Exception as e:
            return f"Error creating file: {str(e)}"

```


requirements.txt
```txt
Flask==2.0.1
PyWebview==3.5
```


run.py
```py
# app/run.py
import threading

import webview
from flask import Blueprint, render_template, request, jsonify, Flask
from models import ChatDatabase
from SIMPLE.ui_develop.ai_engineer import AIEngineer
from flask_socketio import SocketIO, emit

app = Flask(__name__)
socketio = SocketIO(app)

db = ChatDatabase()
ai_engineer = AIEngineer()  # Используем затычку

@app.route('/')
def index():
    chats = db.get_chats()
    return render_template('index.html', chats=chats)

@app.route('/create_chat', methods=['POST'])
def create_chat():
    chat_name = request.form.get('chat_name', 'Новый чат')
    chat_id = db.create_chat(chat_name)
    return jsonify({'chat_id': chat_id, 'chat_name': chat_name})

@app.route('/rename_chat', methods=['POST'])
def rename_chat():
    chat_id = request.form.get('chat_id')
    new_name = request.form.get('new_name')
    db.rename_chat(chat_id, new_name)
    return jsonify({'success': True})

@app.route('/send_message', methods=['POST'])
def send_message():
    chat_id = request.form.get('chat_id')
    message = request.form.get('message')
    db.add_message(chat_id, 'user', message)

    # Ответ от AIEngineer
    response = ai_engineer.ask(message)
    db.add_message(chat_id, 'bot', response)

    return jsonify({'response': response})

@app.route('/get_messages', methods=['GET'])
def get_messages():
    chat_id = request.args.get('chat_id')
    chat = db.get_chat(chat_id)
    if chat:
        return jsonify({'messages': chat['messages']})
    else:
        return jsonify({'messages': []})

def run_flask_app():
    socketio.run(app, port=5002, allow_unsafe_werkzeug=True)


def run_webview():
    window = webview.create_window(
        'DarkmatterMC',
        'http://localhost:5002',
        background_color="#111111", ##111111
        frameless=True,
        easy_drag=False,
        resizable=True,
        width=1000,  # ширина окна
        height=600  # высота окна
    )
    webview.start(debug=True)
    socketio.stop()


if __name__ == '__main__':
    flask_thread = threading.Thread(target=run_flask_app)
    flask_thread.start()
    run_webview()
    flask_thread.join()
```


static/css/styles.css
```css
/* app/static/css/styles.css */
body {
    margin: 0;
    padding: 0;
    font-family: Arial, sans-serif;
    background-color: #fff;
}

.container {
    display: flex;
    height: calc(100vh - 60px);
}

.sidebar {
    width: 200px;
    background-color: #f1f1f1;
    padding: 10px;
}

.sidebar button {
    width: 100%;
    padding: 10px;
    margin-bottom: 10px;
}

.sidebar ul {
    list-style: none;
    padding: 0;
}

.sidebar li {
    padding: 10px;
    cursor: pointer;
}

.sidebar li:hover {
    background-color: #ddd;
}

.chat-container {
    flex: 1;
    padding: 20px;
    overflow-y: auto;
}

.message {
    margin-bottom: 15px;
    padding: 10px;
    border-radius: 5px;
    max-width: 70%;
}

.user-message {
    background-color: #e0f7fa;
    align-self: flex-end;
    margin-left: auto;
}

.bot-message {
    background-color: #fff;
    align-self: flex-start;
    margin-right: auto;
}

.action-message {
    background-color: #ffecb3;
    align-self: center;
    text-align: center;
    font-style: italic;
}

.input-area {
    position: fixed;
    bottom: 0;
    width: 100%;
    height: 60px;
    background-color: #fff;
    border-top: 1px solid #ccc;
}

#chat-form {
    display: flex;
    height: 100%;
    padding: 10px;
}

#message-input {
    flex: 1;
    padding: 10px;
    font-size: 16px;
}

#chat-form button {
    padding: 10px 20px;
    font-size: 16px;
}
```


templates/index.html
```html
<!-- app/templates/index.html -->
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>AI Chatbot</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
</head>
<body>
    <div class="container">
        <div class="sidebar">
            <button id="new-chat-btn">Новый чат</button>
            <ul id="chat-list">
                {% for chat_id, chat in chats.items() %}
                    <li data-chat-id="{{ chat_id }}">{{ chat.name }}</li>
                {% endfor %}
            </ul>
        </div>
        <div class="chat-container" id="chat-container">
            <!-- Сообщения будут динамически добавляться сюда -->
        </div>
    </div>
    <div class="input-area">
        <form id="chat-form">
            <input type="text" id="message-input" placeholder="Введите сообщение...">
            <button type="submit">Отправить</button>
        </form>
    </div>
    <script>
        let currentChatId = '1';

        document.addEventListener('DOMContentLoaded', function() {
            loadMessages(currentChatId);
            setupChatList();
        });

        document.getElementById('chat-form').addEventListener('submit', function(e) {
            e.preventDefault();
            sendMessage();
        });

        document.getElementById('new-chat-btn').addEventListener('click', function() {
            createNewChat();
        });

        function loadMessages(chatId) {
            fetch(`/get_messages?chat_id=${chatId}`)
                .then(response => response.json())
                .then(data => {
                    const chatContainer = document.getElementById('chat-container');
                    chatContainer.innerHTML = '';
                    data.messages.forEach(msg => {
                        const messageDiv = document.createElement('div');
                        messageDiv.classList.add('message');
                        if (msg.role === 'user') {
                            messageDiv.classList.add('user-message');
                        } else if (msg.role === 'bot') {
                            messageDiv.classList.add('bot-message');
                        } else {
                            messageDiv.classList.add('action-message');
                        }
                        messageDiv.textContent = msg.content;
                        chatContainer.appendChild(messageDiv);
                    });
                    chatContainer.scrollTop = chatContainer.scrollHeight;
                });
        }

        function sendMessage() {
            const messageInput = document.getElementById('message-input');
            const message = messageInput.value;
            if (!message) return;
            fetch('/send_message', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: `chat_id=${currentChatId}&message=${encodeURIComponent(message)}`
            })
            .then(response => response.json())
            .then(data => {
                messageInput.value = '';
                loadMessages(currentChatId);
            });
        }

        function createNewChat() {
            fetch('/create_chat', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: 'chat_name=Новый чат'
            })
            .then(response => response.json())
            .then(data => {
                const chatList = document.getElementById('chat-list');
                const li = document.createElement('li');
                li.textContent = data.chat_name;
                li.dataset.chatId = data.chat_id;
                li.addEventListener('click', () => {
                    currentChatId = data.chat_id;
                    loadMessages(currentChatId);
                });
                chatList.appendChild(li);
            });
        }

        function setupChatList() {
            const chatItems = document.querySelectorAll('#chat-list li');
            chatItems.forEach(item => {
                item.addEventListener('click', () => {
                    currentChatId = item.dataset.chatId;
                    loadMessages(currentChatId);
                });
            });
        }
    </script>
</body>
</html>
```


Сделай более красивый интерфейс, а также, чтобы ответы от модели посалисть в потоке в режиме стриминга. Для этого используй socektio.
Также, сделай красивый dropout при нажатии пкм на кнопку чата(переименовать, удалить), а также пкм на сообщение(изменить, удалить, перегенерировать)
Сделай все стильным. Сейчас нет скруглений краев и все выглядит плохо.

Спасибо!