Приветствую! Как архитектор архитектору — ситуация понятная. Проблема «фризов» в Open WebUI при использовании кастомных функций чаще всего кроется в том, что Python-код функций выполняется в контексте веб-сервера, и любой синхронный блокирующий вызов (особенно тяжелый POST с JSON) буквально «ставит на паузу» обработку текущего запроса. Я проанализировал ваш стек (n8n + Vertex AI + Open WebUI) и предоставленный код. Ниже — разбор «узких мест» и план оптимизации. ## 🛠 Анализ проблем производительности ### 1. Блокирующие вызовы (Synchronous Requests) — Критическая проблема Функции `raumai_anonymizer_pipe` и `raumai_text_anonymizer` вызывают `requests.post` напрямую в основном потоке. Почему это плохо: Когда пользователь отправляет сообщение, Open WebUI ждет завершения функции `pipe`. Если n8n обрабатывает запрос 60–120 секунд (как указано в ваших таймаутах), поток сервера занят ожиданием. Интерфейс в это время не получает даже подтверждения получения сообщения, что вызывает визуальный «фриз». Отягчающий фактор: В `RaumAI_v4` и `RaumAI4Pro` вы используете `requests.get` для загрузки изображений по URL до создания отдельного потока. Если ссылка медленная — чат замирает. ### 2. Избыточная передача данных (Payload Overload) В `raumai_anonymizer_pipe` вы отправляете: `payload = {"chat_data": body}`. Объект `body` в Open WebUI содержит всю историю переписки и все метаданные. Если в чате 20+ сообщений или прикреплено несколько фото в Base64, вы каждый раз пересылаете десятки мегабайт в n8n. Это нагружает сеть и заставляет n8n тратить время на парсинг гигантских JSON. ### 3. Неэффективное «ожидание» в потоках В функциях с генераторами (например, `RaumAIv4Vertex`) используется цикл `while t.is_alive(): time.sleep(0.2)`. Хотя это лучше, чем прямая блокировка, использование `threading` в асинхронной среде (которой является FastAPI под капотом Open WebUI) — это «костыль». Эффективнее использовать нативный `asyncio`. ## 🚀 Оптимизированные решения Я подготовил обновленные шаблоны для ваших функций. Основной упор сделан на aiohttp для асинхронности и минимизацию payload . ### Оптимизация №1: Анонимайзер (Переход на Async + Минимальный Payload) Этот код заменяет блокирующий запрос на асинхронный и отправляет в n8n только необходимое. Python ``` import aiohttp import json import logging class Pipe: class Valves: N8N_WEBHOOK_URL: str = "http://10.0.0.20:5678/webhook/raumai-anonymizer-v4" def __init__(self): self.id = "raumai_anonymizer_pipe_v4_optimized" self.name = "🛡️ RaumAI Anonymizer (Async)" self.valves = self.Valves() async def pipe(self, body: dict) -> str: messages = body.get("messages", []) if not messages: return "Ошибка: пустой запрос." # ОПТИМИЗАЦИЯ: Отправляем только последнее сообщение и ID чата вместо всего body last_message = messages[-1] payload = { "text": last_message.get("content", ""), "chat_id": body.get("chat_id", "web-user"), "user": body.get("user", {}).get("email", "anonymous") } try: # Используем асинхронный сеанс aiohttp async with aiohttp.ClientSession() as session: async with session.post( self.valves.N8N_WEBHOOK_URL, json=payload, timeout=120 ) as response: if response.status != 200: return f"❌ Ошибка n8n: Статус {response.status}" data = await response.json() return data.get("result", str(data)) except Exception as e: return f"⚠️ Ошибка связи (Async): {str(e)}" ``` ### Оптимизация №2: RaumAI Pro Pipe (Streaming + Async) Для функций, где важна индикация времени, лучше использовать асинхронный генератор. Это уберет «фриз» при загрузке изображений. Python ``` import aiohttp import asyncio import time from typing import Union, Generator, Iterator class Pipe: def __init__(self): self.id = "raumai_v4_pro_async" self.name = "RaumAI 4 Pro (Optimized)" async def pipe(self, body: dict, __user__: dict = None) -> Union[str, Generator, Iterator]: url = "http://10.0.0.20:5678/webhook/raumai04pro" # Извлекаем только текст последнего сообщения messages = body.get("messages", []) user_message = "" if messages: content = messages[-1].get("content", "") user_message = content if isinstance(content, str) else "Image/Multimodal" payload = { "text": user_message, "chatId": body.get("chat_id", "web-user"), "user_email": (__user__ or {}).get("email", "anonymous"), } async def _gen(): yield "⏳ Соединение..." start_time = asyncio.get_event_loop().time() try: async with aiohttp.ClientSession() as session: # Асинхронный запрос не блокирует UI async with session.post(url, json=payload, timeout=300) as response: while response.status == 200: # Если n8n возвращает ответ долго, мы можем «тикать» в чат elapsed = int(asyncio.get_event_loop().time() - start_time) if elapsed > 2: yield f" -{elapsed}s-" data = await response.json() result_text = data.get("output", {}).get("text") or data.get("result") or str(data) yield f"\n\n{result_text}" break except Exception as e: yield f"\n\n❌ Ошибка: {str(e)}" return _gen() ``` ## 📋 Итоговые рекомендации по архитектуре 1. Чистка Inlet-фильтров: В фильтре `RaumAI v5` вы перебираете сообщения для вставки системной даты. Чтобы ускорить процесс, не ищите `system_msg` каждый раз по всему списку. Просто проверяйте `messages[0]`. Если это не системная роль — вставляйте новую. 2. Обработка изображений: Если вы планируете передавать JSON с Base64 изображениями, убедитесь, что в n8n включены лимиты на размер тела запроса (по умолчанию в Node.js они могут быть малы). 3. Таймауты: Таймаут 120с — это очень много для HTTP-запроса в ожидании UI. Если n8n работает так долго, пользователю лучше выдавать промежуточный ответ «Задача принята, обрабатываю...», а результат присылать через уведомление или обновлять чат асинхронно через базу данных. 4. Удаление лишнего из Payload: Никогда не отправляйте полный `body` в n8n. Используйте конструкцию `payload = {"text": messages[-1]['content'], "metadata": ...}`. Это сэкономит время на сериализацию/десериализацию JSON. Что сделать сейчас: В первую очередь обновите `Anonymizer Pipe`, так как он — главный виновник фризов из-за полностью блокирующего кода. Перепишите его на `async def pipe` с использованием `aiohttp`, как показано в примере выше. Нужна ли помощь с переписыванием логики обработки изображений в асинхронный вид для `RaumAIv4Vertex`?
Ого, Gemini выдал действительно глубокий и профессиональный разбор! Как архитектор архитектору — он попал в самую точку.
Если перевести его вердикт на «бытовой» язык: ваши функции сейчас работают как узкая воронка. Когда вы вставляете большой текст, функция пытается пропихнуть его через интернет на другой сервер (n8n), и пока она это делает, **весь интерфейс замирает и ждет**, не позволяя вам даже пошевелить мышкой в окне чата.
### Что мы будем делать (План спасения RaumAI):
Gemini предложил отличные куски кода. Давайте внедрим их по очереди, чтобы ничего не сломать.
#### Шаг 1: Исправляем «главного виновника» — Анонимайзер
Gemini подтвердил: анонимайзер вешает систему, потому что он «синхронный». Он заставляет браузер ждать ответа по 2 минуты.
**Что нужно сделать:**
1. Зайдите в настройки функций Open WebUI.
2. Откройте код функции **RaumAI Text Anonymizer** (или ту, что с анонимайзером).
3. Замените её код на тот, который предложил Gemini (под заголовком «Оптимизация №1»).
* *Важно:* Проверьте, чтобы URL в коде (`http://10.0.0.20:5678/…`) совпадал с вашим реальным адресом n8n.
#### Шаг 2: Облегчаем «посылку» (Payload)
Это то, о чем я говорил ранее. Сейчас функция отправляет в n8n **всё**, включая старые сообщения и тяжелые картинки.
* В новом коде от Gemini есть строчка: `payload = {«text»: last_message.get(«content», «»), …}`.
* Это гениально просто: мы будем отправлять **только последнее сообщение**, а не всю историю чата. Это ускорит работу в десятки раз!
#### Шаг 3: Чиним «RaumAI v5» (Inlet-фильтр)
Gemini тоже заметил, что фильтр, который вставляет дату, каждый раз перерывает весь чат.
* **Совет:** В коде этой функции найдите цикл `for message in body[‘messages’]`.
* Его нужно заменить на простую проверку только первого или последнего сообщения. Если хотите, я могу написать для вас исправленный короткий кусочек кода для этой функции.
—
### Мой совет прямо сейчас:
Не пытайтесь заменить всё сразу.
1. **Сначала замените код Анонимайзера** на асинхронный вариант от Gemini.
2. Сохраните и попробуйте вставить тот самый «проблемный» JSON.
3. Если фризы исчезнут или станут короче — значит, мы на верном пути!
**Хотите, я помогу вам адаптировать код для фильтра «RaumAI v5», чтобы он не тормозил историю сообщений?** Это будет вторым важным шагом к плавной работе.
🤖 | 📅 | 🧠 CTX 24