y83 code / инструмент

Чат WebAI

Мультипровайдерный чат с веб-ИИ-аккаунтами: не агентский чат с файлами и терминалом, а обычный разговор с моделью — с картинками, документами, озвучкой ответов и встроенным веб-поиском.

раздел реестра
AI и агенты
код
общий модуль с аккаунтами WebAI
документ
11-webai-chat.md
Раздел: AI & Agents
Идентификатор: webai_chat · Экран: ui/webai/ (17 файлов, ~5400 строк)
Снимок: 2026-10-11

1. Зачем и что это

Отличие от других чатов проекта:

ЧатАгентЧто умеет
ui/opencodeopencodeсессии, файлы проекта, инструменты
ui/codexCodex app-serverMCP, скиллы, планы, кэш ленты
WebAI (ui/webai)модель напрямую через backendкартинки/документы, речь, поиск, шаблоны

Весь чат живёт на backend: аккаунты, диалоги, сообщения, группы и шаблоны хранятся там, поэтому чат синхронизируется и переживает переустановку приложения. По умолчанию выбрана модель deepseek-v4-flash.

2. Как попасть

Хаб инструментов → секция AI & Agents → карточка «Чат WebAI» (hub_webai_chat_title, подпись «Мультипровайдерный чат с картинками и документами», тайл «AI-чат»).

Откуда ещёПуть
Список чатовScreen.WebAiChats → WebAiChatsScreen.kt
Мостapp_navigate с screen: "webai_chat"

Маршрут: Screen.kt:129 ("webai_chat" -> Screen.WebAiChat) → MainRouter.kt:282.

3. Экран: элементы и действия

ЭлементЧто делаетГде в коде
ШапкаНазвание чата, кнопки: группы, шаблоны, экспорт, переименованиеui/webai/WebAiHeader.kt
ЛентаСообщения пользователя и модели, «размышления», источники, вложенияWebAiChatRow.kt, WebAiBubbles.kt
Поле вводаТекст + кнопка отправки/остановкиui/webai/WebAiInputBar.kt
Чипы моделиВыбор модели из providersWithModelsui/webai/ModelPicker.kt
Тумблеры Thinking и SearchРассуждения и веб-поиск; состояние запоминаетсяtoggleThinking, toggleSearch, toggle*Persist
Кнопка вложенияПикер файлов/картинок, до 4 штукaddAttachment, ui/webai/WebAiFiles.kt
Кнопка озвучкиОзвучить сообщение моделиtoggleSpeech, ui/webai/WebAiSpeech.kt
ДиалогиПереименование, удаление, восстановление после ошибкиui/webai/WebAiChatDialogs.kt
Лист шаблоновCRUD шаблонов промптовui/webai/WebAiTemplatesDialog.kt
Лист сессийПереключение между диалогамиui/webai/WebAiSessionsSheet.kt

Действия над сообщениями: regenerate() — переспросить у модели, retry() — повторить после ошибки, editMessage(index) — вернуть текст пользовательского сообщения в поле ввода для правки, stop() — прервать стриминг.

4. Возможности подробно

Стриминг

send() собирает вложения, sendInternal() добавляет сообщение пользователя и пустой «pending» ответ, выставляет streaming = true и запускает streamJob. Текст ответа и текст рассуждений копятся в отдельные StringBuilder, а updateAssistant точечно обновляет элемент ленты. Пока идёт стриминг, отправка нового сообщения блокируется (if (ui.streaming) return), а кнопка ввода превращается в «стоп».

Поля состояния стриминга: streaming, streamingStartedAt, searching — последнее показывает отдельную фазу поиска, чтобы лента могла показать «ищу…» независимо от генерации.

Модели и провайдеры

Модель выбирается через ModelPicker из карты providersWithModels, пришедшей с сервера. Провайдер определяется функцией webAiProviderForModel — это зеркало webai.ProviderFor на бэкенде:

Префикс моделиПровайдер
qwen…qwen
kimi…, moonshot…kimi
glm…, zai-…zai
остальноеdeepseek

При этом webAiVisibleProviders = setOf("deepseek"): в интерфейсе виден только DeepSeek, а webAiVisibleModel тихо сводит любую чужую модель к deepseek-v4-flash. Остальные провайдеры существуют на backend (аккаунты, ProviderFor), но пока скрыты из UI — это осознанный переключатель.

Вложения

Правила целиком в ui/webai/WebAiFiles.kt:

  • Лимит — 4 файла на сообщение (MAX_CHAT_ATTACHMENTS), тот же предел продублирован на бэкенде;
  • Размер одного файла — до 10 МиБ (MAX_WEBAI_UPLOAD_BYTES в util/ImagePrep.kt), чтение ограничивается readBounded, а не чтением в память целиком;
  • картинки: jpg, jpeg, png, gif, webp, bmp (по MIME или расширению);
  • документы: pdf, doc, docx, txt, md, csv, xls, xlsx;
  • вложения принимает только DeepSeek (webAiAttachmentAllowed), остальные провайдеры — отказ с подсказкой «does not accept files»;
  • картинки перед отправкой готовятся (webAiPreparedUploadName, webAiMimeForExtension), расширение итогового файла приводится в соответствие с реальными байтами: webp остаётся webp, всё остальное по умолчанию становится jpeg;
  • документу без расширения оно добавляется по MIME (webAiUploadNameForMime);
  • байты уходят в uploads бэкенда, в ленте остаётся path;
  • текст сообщения может быть пустым, если приложены картинки.

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

Диалоги, группы, шаблоны

  • loadChats(openLast) — список диалогов, опционально открыть последний;
  • newSession(), openChat(chat), renameCurrentChat, renameChat, deleteChat, togglePinned (закрепление);
  • группы: createGroup, renameGroup, deleteGroup, moveChatToGroup;
  • шаблоны промптов: createTemplate, updateTemplate, deleteTemplate, loadTemplates;
  • авто-заголовок: generateTitle(chatId) просит backend назвать диалог по первому сообщению (POST …/auto-title) и подставляет результат в список и в шапку. Ошибка уходит в лог, а не в UI.

Озвучка

toggleSpeech(item) озвучивает сообщение модели и запоминает speakingMessageId, чтобы кнопка у нужной реплики показывала активное состояние; stopSpeech() прерывает. Голос — через ui/webai/WebAiSpeech.kt.

Экспорт

ui/webai/WebAiChatExport.kt — выгрузка диалога в файл; кнопка в шапке.

5. Данные: БД, файлы, настройки

ЧтоГде лежитКак меняетсяКто читает
Аккаунты~/.y83-ws/ws-backend.db (backend)POST/PUT /api/v1/webai/accountsfilesEnabled, модели
Диалоги и сообщенията же БД backend/api/v1/webai/chats*loadChats, лента
Группы и шаблоныта же БД/chat-groups, /templatesЛисты секций
Загруженные файлыuploads бэкендаuploadAttachmentМодель при ответе
Состояние чиповprefs приложенияtoggleThinkingPersist, toggleSearchPersistСледующий запуск
Логи ошибокfilesDir/log.txtFileLog.log("webai …")dev_log {source:"app"}

6. Backend: REST/роуты

Все пути — backend/internal/api/v1/routes.go, обработчики wa (webai).

Метод и путьНазначение
GET /api/v1/webai/accountsсписок аккаунтов (для проверки filesEnabled)
GET /api/v1/webai/providersпровайдеры и модели для ModelPicker
GET /api/v1/webai/chatsсписок диалогов
POST /api/v1/webai/chatsсоздать диалог
GET /api/v1/webai/chats/{id}/messagesсообщения диалога
POST /api/v1/webai/chats/{id}/streamстриминг ответа существующего диалога
POST /api/v1/webai/chats/new/streamстриминг в новом диалоге
POST /api/v1/webai/chats/{id}/auto-titleавто-заголовок
PUT /api/v1/webai/chats/{id}переименование/закрепление
DELETE /api/v1/webai/chats/{id}удалить диалог
GET/POST /api/v1/webai/chat-groups, PUT/DELETE …/{id}группы диалогов
GET/POST /api/v1/webai/templates, PUT/DELETE …/{id}шаблоны промптов
GET /api/v1/webai/chats/{id}/messages/{messageId}/files/{index}/bytesбайты вложения для показа

7. Мост и MCP

ЧтоКакПримечание
Открыть экранapp_navigate с screen: "webai_chat"MainScreen.kt:190
Список чатовapp_navigate с screen: "webai_chats"MainScreen.kt:189
Спросить модельMCP ai_ask {message, model, provider, thinking, template, search}Скилл y83-webai
Статус аккаунтовMCP ai_statusквоты, ошибки, PoW

Важно: ai_ask — это не интерфейс этого чата, а прямой вызов модели. Через него не видно ни групп, ни шаблонов, ни вложений из UI; наоборот, диалоги, созданные в чате, и вызовы ai_ask живут в разных слоях backend.

8. Настройки, конфиг, квоты и лимиты

ПараметрЗначениеГде задаётся
Модель по умолчаниюdeepseek-v4-flashWebAiChatUiState.model
Вложений на сообщение4MAX_CHAT_ATTACHMENTS
Размер вложения10 МиБMAX_WEBAI_UPLOAD_BYTES
Видимые провайдерытолько deepseekwebAiVisibleProviders
Thinking/Searchвключены по умолчаниюWebAiChatUiState
Название нового чата«New chat»То же

Квота аккаунтов (100/день, retry 3, backoff 30 с при 429) относится к инструменту «Аккаунты WebAI» и общему пути ai_ask.

9. Ловушки и ограничения

  • Только DeepSeek в UI. Выбрать qwen/kimi/zai в чате нельзя: ModelPicker показывает только модели видимых провайдеров, а чужая модель из ответа сервера сводится к deepseek-v4-flash.
  • Вложения требуют активного аккаунта. Проверка filesEnabled учитывает не только провайдера, но и наличие рабочего аккаунта — при мёртвых аккаунтах кнопка не работает даже при «правильной» модели.
  • Лимит 4 и 10 МиБ дублируются на сервере. Обойти клиентский лимит нельзя, но и наоборот: если backend ужесточит предел, приложение об этом не узнает — придёт ошибка в ленту.
  • Формат картинок нормализуется. Расширение после подготовки может не совпасть с исходным (кроме webp), и MIME меняется на image/jpeg — файлы, чувствительные к расширению, лучше слать PNG осознанно.
  • Рассуждения и поиск — отдельные фазы. Пока идёт searching, контент может ещё не появляться: это нормально, не «зависло».
  • Название диалога появляется не сразу: авто-заголовок генерируется сервером и подставляется позже, поэтому в списке сначала будет «New chat».
  • Правка сообщения пользователя не переспрашивает модель сама — editMessage только возвращает текст в поле; повтор нужно отправить руками.

10. Источники правды

  • app/src/main/java/com/y83/ws/ui/webai/WebAiChatViewModel.kt — состояние, стриминг, вложения.
  • app/src/main/java/com/y83/ws/ui/webai/WebAiFiles.kt — правила провайдеров, форматов, лимитов.
  • app/src/main/java/com/y83/ws/ui/webai/WebAiChatScreen.kt, WebAiHeader.kt, WebAiInputBar.kt, ModelPicker.kt — UI.
  • app/src/main/java/com/y83/ws/ui/webai/WebAiBubbles.kt, WebAiChatRow.kt, WebAiThinking.kt — лента.
  • app/src/main/java/com/y83/ws/ui/webai/WebAiChatsScreen.kt, WebAiSessionsSheet.kt, WebAiTemplatesDialog.kt, WebAiChatDialogs.kt — списки и диалоги.
  • app/src/main/java/com/y83/ws/ui/webai/WebAiSpeech.kt, WebAiChatExport.kt, WebAiHeader.kt — озвучка и экспорт.
  • app/src/main/java/com/y83/ws/data/api/WebAiDtos.kt, BackendApiWebAi.kt — клиент REST.
  • backend/internal/api/v1/routes.go:109–140 — роуты webai.
  • app/src/main/res/values/strings_hub.xml — подписи карточки хаба.

Источник: docs/project/hub/11-webai-chat.md · AI и агенты · общий модуль с аккаунтами WebAI. Страница собирается из документации проекта при сборке сайта.