Как сделать так чтобы у меня был каждый клиент. То есть чтобы у каждого пользователя был отдельный клиент?





app.py: [from flask import Flask, render_template, request, jsonify
from gradio_client import Client
import bleach
import markdown2
import re

app = Flask(__name__)

# Инициализируем клиента Gradio
client = Client("yuntian-deng/o1")

# Разрешенные теги и атрибуты для bleach
allowed_tags = bleach.sanitizer.ALLOWED_TAGS.union({'br', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'hr', 'br'})
allowed_attrs = {'code': ['class'], 'pre': ['class']}


@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')


@app.route('/chat', methods=['POST'])
def chat():
    data = request.get_json()
    user_input = data.get('message')
    chat_counter = data.get('chat_counter', 0)
    chatbot = data.get('chatbot', [])

    # Отправляем сообщение ИИ
    result = client.predict(
        inputs=user_input,
        top_p=1,
        temperature=1,
        chat_counter=chat_counter,
        chatbot=chatbot,
        api_name="/predict"
    )

    # Извлекаем ответ ИИ
    ai_response = result[0][-1][1]
    print(ai_response)

    # Заменяем теги <br/> на символы новой строки
    ai_response_processed = re.sub(r'<br\s*/?>', '\n', ai_response)

    # Обрабатываем markdown в HTML
    ai_response_html = markdown2.markdown(ai_response_processed, extras=["fenced-code-blocks"])

    # Очищаем HTML от потенциально опасных тегов и атрибутов
    ai_response_clean = bleach.clean(
        ai_response_html,
        tags=allowed_tags,
        attributes=allowed_attrs
    )

    # Обновляем chatbot и chat_counter
    updated_chatbot = result[0]
    updated_chat_counter = chat_counter + 1

    # Возвращаем ответ в формате JSON
    return jsonify({
        'response': ai_response_clean,
        'chatbot': updated_chatbot,
        'chat_counter': updated_chat_counter
    })


@app.route('/reset', methods=['POST'])
def reset():
    # Сбрасываем состояние ИИ
    client.predict(api_name="/reset_textbox")
    return jsonify({'status': 'reset'})


if __name__ == '__main__':
    app.run(debug=True)
]







index.html: [<!doctype html>
<html lang="ru">
<head>
    <meta charset="utf-8">
    <title>Чат с ИИ</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Подключаем шрифты Google Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;700&display=swap" rel="stylesheet">
    <!-- Подключаем Font Awesome -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <!-- Подключаем свои стили -->
    <style>
        :root {
            --primary-color: #4F46E5;
            --secondary-color: #6366F1;
            --accent-color: #FF6B6B;
            --background-color: #F3F4F6;
            --white-color: #FFFFFF;
            --user-message-bg: #DCFCE7;
            --ai-message-bg: #E0E7FF;
            --text-color: #111827;
            --border-color: #E5E7EB;
            --gradient-bg: linear-gradient(135deg, #667EEA 0%, #764BA2 100%);
        }

        body {
            font-family: 'Poppins', sans-serif;
            background-color: var(--background-color);
            display: flex;
            flex-direction: column;
            height: 100vh;
            margin: 0;
            color: var(--text-color);
            overflow: hidden;
        }

        .chat-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            max-width: 768px;
            margin: 20px auto;
            width: 95%;
            background-color: var(--white-color);
            border-radius: 15px;
            overflow: hidden;
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
        }

        .chat-header {
            background: var(--gradient-bg);
            color: var(--white-color);
            padding: 20px;
            text-align: center;
            position: relative;
        }

        .chat-header h1 {
            margin: 0;
            font-size: 28px;
            font-weight: 700;
            text-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }

        #chat {
            flex: 1;
            padding: 20px;
            overflow-y: auto;
            background-color: var(--background-color);
            position: relative;
        }

        .message {
            display: flex;
            margin-bottom: 20px;
            align-items: flex-start;
            animation: fadeIn 0.3s ease-in-out;
        }

        .message.user {
            flex-direction: row-reverse;
        }

        .message .avatar {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            background-color: var(--secondary-color);
            margin: 0 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 22px;
            color: var(--white-color);
            box-shadow: 0 4px 10px rgba(0,0,0,0.15);
        }

        .message.ai .avatar {
            background-color: var(--primary-color);
        }

        .message-content {
            max-width: 75%;
            padding: 15px 20px;
            border-radius: 25px;
            position: relative;
            word-wrap: break-word;
            font-size: 17px;
            background-color: var(--white-color);
            box-shadow: 0 4px 10px rgba(0,0,0,0.1);
        }

        .message.ai .message-content {
            background-color: var(--ai-message-bg);
        }

        .message.user .message-content {
            background-color: var(--user-message-bg);
        }

        .message-content p {
            margin: 0;
            line-height: 1.5;
        }

        .chat-form {
            display: flex;
            padding: 15px;
            background-color: var(--white-color);
            border-top: 1px solid var(--border-color);
            align-items: center;
        }

        .chat-form textarea {
            flex: 1;
            resize: vertical;
            border: none;
            border-radius: 25px;
            padding: 12px 20px;
            font-size: 16px;
            margin-right: 10px;
            outline: none;
            background-color: var(--background-color);
            box-shadow: inset 0 2px 4px rgba(0,0,0,0.05);
            transition: all 0.3s ease;
            min-height: 50px;
            max-height: 200px;
            overflow-y: auto;
        }

        .chat-form textarea:focus {
            background-color: var(--white-color);
            box-shadow: 0 0 0 2px var(--primary-color);
        }

        .chat-form button {
            padding: 12px;
            font-size: 22px;
            border: none;
            background-color: var(--primary-color);
            color: var(--white-color);
            cursor: pointer;
            border-radius: 50%;
            width: 50px;
            height: 50px;
            transition: background-color 0.3s ease, transform 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .chat-form button:hover {
            background-color: #3B82F6;
            transform: scale(1.05);
        }

        .reset-button {
            text-align: center;
            padding: 15px;
            background-color: var(--white-color);
            border-top: 1px solid var(--border-color);
        }

        .reset-button button {
            padding: 12px 25px;
            font-size: 16px;
            border: none;
            background-color: var(--accent-color);
            color: var(--white-color);
            cursor: pointer;
            border-radius: 25px;
            transition: background-color 0.3s ease, transform 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .reset-button button i {
            margin-right: 8px;
        }

        .reset-button button:hover {
            background-color: #FF4B4B;
            transform: scale(1.05);
        }

        /* Стили для кода */
        pre {
            position: relative;
            background-color: #2d2d2d;
            padding: 15px;
            border-radius: 10px;
            overflow-x: auto;
            max-width: 100%;
            margin-top: 20px;
            font-family: 'Fira Code', monospace;
        }

        pre code {
            font-size: 14px;
            color: #ccc;
        }

        /* Кнопка копирования */
        .copy-button {
            position: absolute;
            top: 10px;
            right: 10px;
            background-color: transparent;
            border: none;
            color: #ccc;
            cursor: pointer;
            font-size: 16px;
        }

        .copy-button:hover {
            color: #fff;
        }

        /* Заголовок кода */
        .code-header {
            position: absolute;
            top: 10px;
            left: 15px;
            font-size: 14px;
            color: #ccc;
        }

        /* Подсветка синтаксиса */
        .hljs {
            background: none;
            color: #ccc;
        }

        /* Анимация появления сообщений */
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }

        /* Эффект печати ИИ */
        .typing-indicator {
            display: flex;
            align-items: center;
            margin-bottom: 15px;
            animation: fadeIn 0.3s ease-in-out;
        }

        .typing-indicator .avatar {
            background-color: var(--primary-color);
        }

        .typing-indicator .dots {
            display: inline-block;
            position: relative;
            width: 40px;
            height: 20px;
            margin-left: 10px;
        }

        .typing-indicator .dots span {
            display: block;
            position: absolute;
            bottom: 0;
            width: 8px;
            height: 8px;
            background-color: var(--primary-color);
            border-radius: 50%;
            opacity: 0;
            animation: typing 1s infinite;
        }

        .typing-indicator .dots span:nth-child(1) {
            left: 0;
            animation-delay: 0s;
        }

        .typing-indicator .dots span:nth-child(2) {
            left: 12px;
            animation-delay: 0.2s;
        }

        .typing-indicator .dots span:nth-child(3) {
            left: 24px;
            animation-delay: 0.4s;
        }

        @keyframes typing {
            0%, 60%, 100% { transform: translateY(0); opacity: 0.2; }
            30% { transform: translateY(-8px); opacity: 1; }
        }

        /* Адаптивность для мобильных устройств */
        @media (max-width: 600px) {
            .message-content {
                max-width: 100%;
            }

            .chat-form textarea {
                font-size: 15px;
            }

            .chat-form button {
                font-size: 20px;
                padding: 10px;
                width: 45px;
                height: 45px;
            }

            .reset-button button {
                font-size: 15px;
                padding: 10px 20px;
            }

            .chat-header h1 {
                font-size: 24px;
            }

            .avatar {
                width: 40px;
                height: 40px;
                font-size: 20px;
            }

            pre {
                padding: 10px;
            }

            .copy-button {
                top: 5px;
                right: 5px;
            }

            .code-header {
                top: 5px;
                left: 10px;
            }
        }
    </style>
    <!-- Подключаем тему подсветки синтаксиса -->
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/styles/atom-one-dark.min.css">
    <!-- Подключаем библиотеку Highlight.js -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/highlight.min.js"></script>
    <!-- Подключаем языковые модули для Highlight.js (опционально) -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/languages/javascript.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/languages/python.min.js"></script>
</head>
<body>
    <div class="chat-container" id="chat-container">
        <div class="chat-header">
            <h1><i class="fas fa-comments"></i> Чат с ИИ</h1>
        </div>
        <div id="chat">
            <!-- Здесь будут отображаться сообщения -->
        </div>
        <!-- Спиннер загрузки -->
        <div id="spinner" class="spinner"></div>
        <form class="chat-form" id="chat-form">
            <textarea id="message-input" placeholder="Введите сообщение..." autofocus required></textarea>
            <button type="submit" id="send-btn"><i class="fas fa-paper-plane"></i></button>
        </form>
        <div class="reset-button">
            <button type="button" id="reset-btn"><i class="fas fa-trash-alt"></i> Очистить чат</button>
        </div>
    </div>

    <!-- Подключаем библиотеку для анимации текста (опционально) -->
    <!-- <script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.12"></script> -->

    <script>
        let chatCounter = 0;
        let chatbot = [];

        const chatContainer = document.getElementById('chat');
        const chatWrapper = document.getElementById('chat-container');
        const spinner = document.getElementById('spinner');
        const sendBtn = document.getElementById('send-btn');
        const resetBtn = document.getElementById('reset-btn');
        const messageInput = document.getElementById('message-input');

        // Функция для автоматического изменения высоты textarea
        function autoResizeTextarea() {
            messageInput.style.height = 'auto';
            messageInput.style.height = (messageInput.scrollHeight) + 'px';
        }

        messageInput.addEventListener('input', autoResizeTextarea);

        // Загрузка истории чата из localStorage
        if (localStorage.getItem('chatHistory')) {
            chatContainer.innerHTML = localStorage.getItem('chatHistory');
            chatCounter = parseInt(localStorage.getItem('chatCounter')) || 0;
            chatbot = JSON.parse(localStorage.getItem('chatbot')) || [];
            // Инициализируем подсветку в загруженных сообщениях
            hljs.highlightAll();
            // Прокручиваем чат вниз при загрузке
            chatContainer.scrollTop = chatContainer.scrollHeight;
        }

        document.getElementById('chat-form').addEventListener('submit', function (event) {
            event.preventDefault();
            const userMessage = messageInput.value.trim();
            if (userMessage === '') return;

            // Отображаем сообщение пользователя
            addMessage('user', userMessage);
            messageInput.value = '';
            messageInput.style.height = 'auto'; // Сбрасываем высоту поля ввода

            // Показываем индикатор ввода ИИ
            showTypingIndicator();

            // Показываем спиннер и блокируем кнопки
            toggleLoadingState(true);

            // Отправляем сообщение на сервер
            fetch('/chat', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    message: userMessage,
                    chat_counter: chatCounter,
                    chatbot: chatbot
                }),
            })
                .then(response => response.json())
                .then(data => {
                    // Скрываем спиннер и разблокируем кнопки
                    toggleLoadingState(false);

                    // Прячем индикатор ввода ИИ
                    hideTypingIndicator();

                    // Отображаем ответ ИИ
                    addMessage('ai', data.response);

                    // Обновляем chatCounter и chatbot
                    chatCounter = data.chat_counter;
                    chatbot = data.chatbot;

                    // Сохраняем историю чата и состояние в localStorage
                    localStorage.setItem('chatHistory', chatContainer.innerHTML);
                    localStorage.setItem('chatCounter', chatCounter);
                    localStorage.setItem('chatbot', JSON.stringify(chatbot));

                    // Инициализируем подсветку в новых сообщениях
                    hljs.highlightAll();
                })
                .catch((error) => {
                    console.error('Ошибка:', error);
                    // Скрываем спиннер и разблокируем кнопки в случае ошибки
                    toggleLoadingState(false);
                    alert('Произошла ошибка при получении ответа от сервера.');
                });
        });

        resetBtn.addEventListener('click', function () {
            if (!confirm('Вы действительно хотите очистить чат?')) return;

            // Показываем спиннер и блокируем кнопки
            toggleLoadingState(true);

            // Отправляем запрос на сброс состояния на сервер
            fetch('/reset', {
                method: 'POST',
            })
                .then(response => response.json())
                .then(data => {
                    // Скрываем спиннер и разблокируем кнопки
                    toggleLoadingState(false);

                    // Очищаем историю чата и состояние
                    chatContainer.innerHTML = '';
                    chatCounter = 0;
                    chatbot = [];

                    // Удаляем данные из localStorage
                    localStorage.removeItem('chatHistory');
                    localStorage.removeItem('chatCounter');
                    localStorage.removeItem('chatbot');

                    // Прячем индикатор ввода ИИ
                    hideTypingIndicator();

                })
                .catch((error) => {
                    console.error('Ошибка:', error);
                    // Скрываем спиннер и разблокируем кнопки в случае ошибки
                    toggleLoadingState(false);
                    alert('Произошла ошибка при сбросе чата.');
                });
        });

        function addMessage(sender, text) {
            const messageDiv = document.createElement('div');
            messageDiv.classList.add('message', sender);

            const avatarDiv = document.createElement('div');
            avatarDiv.classList.add('avatar');

            const avatarIcon = document.createElement('i');
            avatarIcon.classList.add('fas', sender === 'user' ? 'fa-user' : 'fa-robot');
            avatarDiv.appendChild(avatarIcon);

            const messageContent = document.createElement('div');
            messageContent.classList.add('message-content');

            // Обработка текста как HTML
            messageContent.innerHTML = text;

            // Добавляем обработку кодовых блоков
            processCodeBlocks(messageContent);

            messageDiv.appendChild(avatarDiv);
            messageDiv.appendChild(messageContent);

            chatContainer.appendChild(messageDiv);

            hljs.highlightAll();

            chatContainer.scrollTop = chatContainer.scrollHeight;

            localStorage.setItem('chatHistory', chatContainer.innerHTML);
        }

        function toggleLoadingState(isLoading) {
            if (isLoading) {
                spinner.style.display = 'none'; // Не показываем спиннер
                sendBtn.classList.add('disabled');
                resetBtn.classList.add('disabled');
                sendBtn.disabled = true;
                resetBtn.disabled = true;
                messageInput.disabled = true;
            } else {
                spinner.style.display = 'none';
                sendBtn.classList.remove('disabled');
                resetBtn.classList.remove('disabled');
                sendBtn.disabled = false;
                resetBtn.disabled = false;
                messageInput.disabled = false;
                messageInput.focus();
            }
        }

        // Функции для отображения индикатора печати ИИ
        function showTypingIndicator() {
            const typingDiv = document.createElement('div');
            typingDiv.classList.add('typing-indicator', 'ai');
            typingDiv.id = 'typing-indicator';

            const avatarDiv = document.createElement('div');
            avatarDiv.classList.add('avatar');

            const avatarIcon = document.createElement('i');
            avatarIcon.classList.add('fas', 'fa-robot');
            avatarDiv.appendChild(avatarIcon);

            const dotsDiv = document.createElement('div');
            dotsDiv.classList.add('dots');

            for (let i = 0; i < 3; i++) {
                const dot = document.createElement('span');
                dotsDiv.appendChild(dot);
            }

            typingDiv.appendChild(avatarDiv);
            typingDiv.appendChild(dotsDiv);

            chatContainer.appendChild(typingDiv);
            chatContainer.scrollTop = chatContainer.scrollHeight;
        }

        function hideTypingIndicator() {
            const typingDiv = document.getElementById('typing-indicator');
            if (typingDiv) {
                typingDiv.remove();
            }
        }

        // Обработка кодовых блоков
        function processCodeBlocks(container) {
            const codeBlocks = container.querySelectorAll('pre');

            codeBlocks.forEach((block) => {
                // Создаем контейнер для копирования и заголовка
                const codeHeader = document.createElement('div');
                codeHeader.classList.add('code-header');

                // Получаем язык программирования из первой строки, если есть
                const codeContent = block.innerText;
                let language = '';
                const firstLine = codeContent.split('\n')[0];
                const languageRegex = /^<!\-\-language:\s*(\w+)\-\->/;

                if (languageRegex.test(firstLine)) {
                    language = firstLine.match(languageRegex)[1];
                    block.innerText = codeContent.split('\n').slice(1).join('\n');
                } else {
                    language = '';
                }

                codeHeader.textContent = language;

                // Кнопка копирования
                const copyButton = document.createElement('button');
                copyButton.classList.add('copy-button');
                copyButton.innerHTML = '<i class="fas fa-copy"></i>';

                copyButton.addEventListener('click', () => {
                    navigator.clipboard.writeText(block.innerText).then(() => {
                        copyButton.innerHTML = '<i class="fas fa-check"></i>';
                        setTimeout(() => {
                            copyButton.innerHTML = '<i class="fas fa-copy"></i>';
                        }, 2000);
                    });
                });

                block.appendChild(codeHeader);
                block.appendChild(copyButton);
            });
        }

        // Автофокус на поле ввода при загрузке страницы
        window.onload = function() {
            messageInput.focus();
        };
    </script>
</body>
</html>
]