Skip to main content
OrbitPilot

Виджет чата OrbitPilot — потоковый, перемещаемый, мобильный и голосовой

Что это такое

OrbitPilot теперь является глобальным плавающим виджетом чата для вошедших пользователей: кнопка 🛰 в углу открывает панель, которую вы можете использовать с любой страницы. Панель не блокирует (продолжайте просматривать сайт, пока она открыта), перетаскиваемая, сворачиваемая и она запоминает свое место и ваш открытый разговор, пока вы перемещаетесь по сайту.

Новые возможности

  • Потоковые ответы — ответ появляется по слову, а не после ожидания.
  • Стоп — остановите ИИ во время ответа. Остановка сохраняет все, что было написано до сих пор, и не расходует запрос из вашего месячного лимита.
  • Скопировать / скачать весь чат — кнопки вверху и внизу панели (скачивание — это текстовый файл .txt).
  • Скопировать одно сообщение — наведите на любое облачко, чтобы увидеть значок копирования.
  • Развернуть окно, просматривать вашу историю чата и начать Новый разговор.
  • Голосовой ввод — нажмите на микрофон и говорите; ваши слова будут напечатаны за вас.

Гости (не вошедшие в систему) сохраняют оригинальное всплывающее окно чата на главной странице.

На вашем телефоне (мобильный)

  • Панель прикрепляется к нижней части экрана и никогда не выходит за края, в любом положении — поворачивая телефон, она автоматически подстраивается.
  • Она учитывает вырез и индикатор домашней страницы (безопасные зоны) и использует текущую высоту экрана, чтобы адресная строка браузера не перекрывала кнопку Отправить.
  • После того как вы говорите и отправляете, коробка очищается для вашего следующего вопроса — снова нажмите на микрофон, чтобы говорить для следующего поворота.

Для разработчиков

Потоковая передача требует асинхронного представления под ASGI (синхронный генератор буферизует перед первым байтом). orbitpilot.views.orbitpilot_ask_stream — это async def, возвращающий StreamingHttpResponse через асинхронный генератор, который генерирует события, отправленные сервером (meta → token… → done). Вся ORM/управление выполняется в двух СИНХРОННЫХ помощниках (_prepare_ask_stream / _finalize_ask_stream) через sync_to_async(thread_sensitive=True). Стоп = клиент прекращает запрос → асинхронный генератор отменяется → частичный ответ сохраняется, и строка квоты НЕ docx. Фронтенд находится в templates/_orbitpilot_widget.html + static/css/orbitpilot-widget.css + static/js/orbitpilot-widget.js, включен в base.html только для аутентифицированных пользователей.

Мобильная адаптивность (Workstream E): весь виджет имеет box-sizing: border-box, так что граница в 1 пиксель учитывается внутри ширины (предыдущая переполнение iPhone было width:100vw + border). Правило @media (max-width:575px) отбрасывает 100vw для left/right: env(safe-area-inset-*) + width:auto, размеры с помощью dvh (с vh запасом) и добавляет отступ к композиторской панели путем env(safe-area-inset-bottom). В JS сохраненное положение для перетаскивания на десктопе не применяется на мобильной области просмотра, и слушатель resize/orientationchange повторно фиксирует положение через applyPos(). Очистка голоса: модуль речи сохраняет конечный saved буфер отдельно от ввода; send() теперь вызывает общий resetVoice() (остановка распознавания, сброс таймера тишины, пустой saved) прямо где он очищает input.value, так что следующий поворот начинается с пустым.

Размер шрифта чата + исправление отправки на мобильных (2026-07-27)

  • A− / A+ в заголовке виджета уменьшают/увеличивают текст чата. Ваш выбор запоминается (сохраняется в браузере через localStorage ключ op_widget_font) на различных страницах и сессиях. Размер ограничен 12–22пкс.
  • Кнопка отправки на телефонах: строка композитора теперь оставляет кнопки Отправить (и микрофон) видимыми рядом с текстовым полем на узких экранах — ранее текстовое поле могло сдвинуть Отправить за правый край.

Для разработчиков: разметка templates/_orbitpilot_widget.html, поведение static/js/orbitpilot-widget.js (applyFont()/bumpFont(), восстановленные при инициализации), стилизация static/css/orbitpilot-widget.css. Кнопки увеличивают --op-chat-font CSS переменную, читаемую телом сообщения + вводом; композитор использует min-width:0 на текстовом поле и flex:0 0 auto на кнопках, так что Отправить никогда не оказывается за пределами экрана.

Меню-гамбургер + полный экран (2026-07-27)

В заголовке виджета чата теперь есть ☰ меню и ⤢ полный экран кнопка:

  • Ваши инструкции — постоянные заметки, которые ИИ всегда учитывает (как пользовательские инструкции ChatGPT). Сохраняется в OrbitPilotSettings.custom_user_prompt через orbitpilot_custom_instructions; ограничено плане через функцию custom_prompt (блокированное сообщение + ссылка на обновление появляется, если план этого не позволяет; сервер тоже накладывает это с помощью ошибки 403).
  • Загрузки — теперь четко два варианта: "Этот разговор только" (orbitpilot_conversation_download) против "Вся моя история чата" (новое orbitpilot_history_download). Кнопка на панели инструментов переименована в "⭧ Этот чат".
  • Память → Восстановить мою память сейчас — полное восстановление (orbitpilot_rebuild_memory) под подтверждением с предупреждением о затратах (перечитывает все и переинтегрирует, что использует кредит ИИ); он не запустит восстановление, пока одно уже по-настоящему не выполняется. (BL-404, 2026-09-07) Если это "уже выполняемое" восстановление на самом деле вылетело или было убито перезапуском сервера — застряло в запланированном или выполняемом далеко за безопасное время (10 минут с фоновым работником включенным, 3 минуты без него) — нажатие кнопки теперь автоматически очищает его и запускает новое восстановление, используя тот же обработчик, который всегда использовал инспектор буклетов (orbitpilot.services.reap_stuck_builds). Ответ говорит, что зависшее восстановление было очищено, вместо того чтобы делать вид, что ничего не произошло. До этого исправления мертвое восстановление в таком состоянии блокировало обновление навсегда, без возможности его "разблокировать", кроме как администратору, открывающему инспектор.

Режим полного экрана (⤢) открывает /orbitpilot/chat/ в новой вкладке, чат в стиле ChatGPT/Gemini dля полного экрана, который повторно использует ТОТ ЖЕ виджет (через класс тела op-fullpage) — так что переключение между маленьким виджетом и полным экраном сохраняет тот же разговор (общая op_widget_conv localStorage + событие storage слушатель). Также: A− / A+ изменяют шрифт чата (запоминаются), и на телефоне кнопка отправки остается видимой рядом с микрофоном.

Для разработчиков: разметка templates/_orbitpilot_widget.html, поведение static/js/orbitpilot-widget.js, стилизация static/css/orbitpilot-widget.css; просмотр в полный экран orbitpilot.views.orbitpilot_chat → orbitpilot/templates/orbitpilot/chat_full.html.

Быстрое обновление против полного восстановления (мягкая/жесткая память, 2026-07-27)

Меню 🧠 Память виджета теперь предлагает два восстановления:

  • ⚡ Быстрое обновление (мягкое) — перечитывает и переинтегрирует только те элементы, которые вы изменили с последнего восстановления; неизмененные элементы сохраняют свои существующие интеграции, удаленные элементы отбрасываются. Гораздо дешевле, потому что стоимость интеграции OpenAI зависит от делты, а не от всего корпуса.
  • 🔄 Полное восстановление (жесткое) — перечитывает и переинтегрирует все с нуля (использует больше кредитов ИИ). Используйте его только в том случае, если что-то выглядит неправильно.

Для разработчиков: run_memory_build(user, settings, build, incremental=True) сравнивает новые части с сохраненными по ключу (source_key, source_id, chunk_index, content_hash) до вызова embed_chunks, так что только новые/измененные части интегрируются; устаревшие строки удаляются в том же атомарном блоке; неизмененные строки остаются нетронутыми. Режим сохраняется в OrbitPilotMemoryBuild.incremental (миграция orbitpilot/0025) и задается orbitpilot_rebuild_memory из mode=soft|hard. Если incremental опущен, run_memory_build читает его из записи восстановления. Полный/жесткий путь сохраняет точное предыдущее атомарное удаление + переинтеграцию.

Исправления, основанные на чате (2026-07-28)

Анализируя реальные (с удалением PII) разговоры OrbitPilot, выявились четыре исправления:

  • Сломанные ссылки ИИ (your-app-link): модель придумала домен, когда ей передавали относительные пути из устаревшей буклеты. Теперь (a) проверка состояния системы предупреждает, если SITE_URL является localhost в рабочем окружении, и (b) orbitpilot.services.heal_links() переписывает любую заменитель/localhost ссылку на реальный хост сайта в каждом ответе (потоковом/блокирующем/гостевом) — так что ссылки работают даже до восстановления. Перманентное исправление (установите SITE_URL + восстановите) находится в Списке настройки администратора .
  • “Список моих Blink'ов” теперь работает: pipeline/retrieve.py add_list_intent_sources() вводит ВСЕ части источника, когда запрос является запросом на перечисление (“список/показать/все мои blink'и/задачи/события”), и моментальные снимки рабочего пространства образца названий Blink — так что ИИ списывает реальные элементы, а не просто количество.
  • Кликабельные следующие действия: действия теперь {label, url}. Реальная навигационная ссылка отображается как кликаемая кнопка (открывается в новой вкладке); в противном случае это чип предзаполнения. Исправлено по всему виджету, модальному окну домашней страницы гостей (который ранее сбрасывал действия) и результату в полном экране. Встроенные ссылки на ответы также открываются в новой вкладке теперь.
  • Полировка подсказок: основная подсказка ответа перестает чрезмерно повторять заголовок цели пользователя и обрабатывает “какие изменения с тех пор, как вчера” грациозно (это работает от текущего снимка, а не по истории день за днем).

Для разработчиков: orbitpilot/services.py (heal_links, request_base, split_answer_actions теперь возвращает словарь, слой next_actions для гостей), pipeline/retrieve.py (add_list_intent_sources), adminapp/diagnostics.py (_check_site_url), JS виджета + home.html (отображение действий + DOMPurify target=_blank хук), aihub/prompt_registry.py (основной + стандартные настройки next_actions; обновлено на нетронутых сеянцах по стандартам через aihub/0007).

Одна панель инструментов, а не две (2026-08-28)

Что изменилось. Виджет раньше рисовал одну и ту же строку кнопок — Новый · История · Копировать · Этот чат — дважды: однажды прямо под фиолетовым заголовком, и снова под текстовым полем. Теперь одна, под текстовым полем.

Почему. Отчет #136, с телефона. Две строки инструментария внутри уже короткого окна оставили разговор с тем, что было завершено. Отчет был о том, сколько мало места имел разговор.

Почему нижняя строка — это та, что осталась. Это то, до чего дотягивается палец на телефоне, она находится рядом с полем, в которое вы печатаете — что и следовало бы принадлежать "начать новый чат" и "скопировать этот чат" — и она уже несла каждую кнопку, которую имела другая. Подсказки на верхней строке были более четкой формулировкой, так что они спустились вместе с ней.

Ничего не стало недоступным. Заголовок сохраняет свой собственный ×, так что свернутый виджет все еще закрывается; и Закрыть остается вне ворота входа, так что гость все еще может закрыть виджет. Под охраной orbitpilot/test_widget_toolbar.py.

Микрофон: одно намерение, одна сессия (BL-447, 2026-09-09)

Владелец сообщил, что чат микрофон "сначала работает, затем во второй раз не работает, а иногда зависает". В рабочей версии уже была предыдущая поправка, когда он это сказал, так что это было то, что оставалось под ней, а не пропущенный деплой. Три вещи оказались неправильными, и они объясняют три разные симптомы.

1. Кнопка могла означать противоположное тому, что показывала

Обработчик клика решал, что делать, читая одну переменную (wantListening, запрос пользователя), в то время как красное пульсирующее состояние рисовалось из другой (listening, фактическое состояние двигателя). Вызов stop() браузера асинхронен, так что после шести секунд времени тишины и после каждой Отправки есть окно, в котором запрос уже отключен, в то время как движок по-прежнему работает — и кнопка все еще выглядит активной. Нажатие, предназначенное для остановки, было, поэтому воспринято как начало, и предыдущая поправка (правильно, для своего собственного случая) удерживала это нажатие активным, так что движок перезапускался. Нажатие на остановку снова включало микрофон. Отражение той же разницы сделало второе нажатие отменой первого нажатия пользователя, так что микрофон вообще не начинал — это и есть "второй раз, когда он не работает".

Теперь есть одна переменная. micOn — это то, что запросил пользователь; кнопка рисовалась из нее, и клик считывает ее, так что они не могут противоречить друг другу.

2. Один объект распознавания обслуживал всю страницу

Один SpeechRecognition создавался, когда страница загружалась, и использовался навсегда. Это единственная причина, по которой запуск мог завершиться с ошибкой "недопустимое состояние", и объект, зажатый всплеском сети или микрофоном, удерживаемым другим приложением, оставался зажатым до перезагрузки страницы — "работает один раз, потом никогда снова". Каждый другой микрофон на сайте (захватный блок на главной странице, сообщение об ошибке, администраторский диктант) уже создает новый при каждом нажатии. Виджет чата теперь делает то же самое, так что каждое нажатие получает чистый движок.

3. Закрытие окна не освобождало микрофон

close() никогда не прекращал распознавание, и ничего его не останавливает, когда страница покидается, так что закрытие чата в середине диктовки оставляло движок работающим за скрытой панелью: индикатор записи браузера оставался включенным, цикл перезапуска продолжал перезаряжаться, и слова продолжали попадать в коробку, которую никто не мог видеть. Это и было "зависанием". Закрытие окна и покидание страницы теперь освобождают микрофон.

Что еще изменилось, что вы можете увидеть

  • Ошибки сообщают, что произошло. Заблокированное разрешение, микрофон не найден, и "это нажатие ничего не начало" раньше выглядели одинаково — серой кнопкой и обычным заполнителем. Теперь каждая записывает свое собственное короткое сообщение в окне сообщения.
  • Сломанный движок сдается вместо того, чтобы вертеться. Диктовка все еще перезапускается сама через паузу на размышления, что является поведением каждого микрофона здесь, но сессия, которая продолжает отключаться, не услышав никогда речи, останавливается после нескольких попыток и сообщает об этом. Реальная речь восстанавливает этот бюджет, так что обычная диктовка никогда не иссякает.
  • Чат-бокс принимает клавиатуру только тогда, когда он на экране. Завершение диктовки раньше безусловно фокусировало ввод, что могло вытащить клавиатуру в окно, которое только что было закрыто.

Для разработчиков. Умирающая сессия становится сиротой — ссылка на текущую сессию устанавливается в null, и каждый обработчик проверяет свою собственную идентичность, прежде чем прикасаться к чему-либо — так что поздний окончательный результат после Отправки игнорируется, потому что он принадлежит сессии, которой больше никто не владеет. Это сохраняет гарантии, к которым стремились два предыдущих исправления, без флага, который может остаться активным. Логика живет в static/js/orbitpilot-widget.js (блок initVoice) и форма фиксирована adminapp/test_widget_mic.py и adminapp/test_voice_parity.py. Голосовое распознавание в тестовом наборе не может работать; тест дымом на реальном Chromium является отдельной предложенной задачей.

This guide was translated automatically. Read the English original.

🪐 OrbitPilot

Create a free account and I'll remember your conversations — sign up

🧠 Memory status