Вы создали задание Развёртывания и уже через миллисекунды получили ID задания pjb_ и ID движка eng_. Движок уже можно использовать, но он ещё продолжает наполняться: AI-агент сканирует ваши источники и записывает в него тональности бренда, элементы глоссария и инструкции. И пока всё это происходит, хочется показывать пользователю, что именно идёт работа — строкой вроде «Сканируем ваш гайд по стилю… настраиваем движок… готово», как в мастере установки, а не молчаливым спиннером.
WebSocket даёт именно такой поток событий. Подключитесь к заданию, и сервер отправит снимок текущего состояния, а затем событие provisioning.progress каждый раз, когда Процесс переходит на новый шаг. И поскольку сервер отправляет текущее состояние в момент подключения, а завершённое задание сразу после этого закрывает, подключиться можно в любой момент, даже после завершения — ловить какое-то специальное окно не нужно.
GET /jobs/provisioning/:jobId/wsjobId — это значение pjb_ из вызова create. Если вы ещё не работали с асинхронным Развёртыванием, начните с раздела Обзор, чтобы понять общую модель.
На этой странице
- Типы сообщений
- Снимок состояния при подключении
- События прогресса
- Подключение после завершения задания
- Как встроить это в ваш UI
- Храните API-ключ на стороне сервера
Типы сообщений#
По сокету передаются два типа сообщений. Первое приходит один раз — при подключении. Второе приходит многократно, по мере продвижения задания.
| Тип | Когда | Ключевые поля |
|---|---|---|
provisioning.snapshot | При первом подключении | jobId, status, errorMessage |
provisioning.progress | Когда каждый шаг Процесса начинается или завершается | jobId, step, detail |
Это поток для отслеживания активности, а не результатов: он показывает, на каком этапе находится задание и завершилось ли оно ошибкой, но не сообщает, какие именно записи создал AI. Сводка обо всём, что было развёрнуто, — ID тональности бренда, глоссария и инструкций — приходит отдельно: в completion webhook или при чтении задания после его завершения. Сокет используйте для индикатора прогресса, webhook — для итоговой полезной нагрузки.
Снимок состояния при подключении#
Сразу после подключения сервер считывает текущее состояние задания из базы данных и отправляет его. Сначала ждать событие прогресса не нужно — снимок состояния самодостаточен.
{
"type": "provisioning.snapshot",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"status": "in_progress",
"errorMessage": null
}| Поле | Описание |
|---|---|
status | in_progress, completed или failed. |
errorMessage | Описание ошибки, если status равно failed, иначе null. |
Снимок состояния — единственное сообщение, которое вы гарантированно получите. Если задание ещё выполняется, после него придут события прогресса; если задание уже завершено, вы получите снимок состояния и больше ничего (см. ниже).
События прогресса#
Пока идёт Процесс, сервер отправляет событие provisioning.progress каждый раз, когда переходит к новому шагу. В каждом событии указан step и передаётся человекочитаемый detail.
{
"type": "provisioning.progress",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"step": "crawling",
"detail": "Crawling source URLs..."
}step | Когда | Пример detail |
|---|---|---|
crawling | Загружаются URL источников | "Crawling source URLs..." или "Retrying crawl (attempt 2)..." |
configuring | AI-агент анализирует контент и записывает конфигурацию движка | "AI agent analyzing content and configuring engine..." или "Retrying configuration (attempt 2)..." |
completed | Задание успешно завершено | "Provisioning complete" |
failed | Задание завершилось с ошибкой | Сообщение об ошибке с описанием сбоя |
Повторная попытка — не ошибка
Шаги crawling и configuring могут срабатывать несколько раз: временная ошибка загрузки или анализа запускает повторную попытку, и она приходит как событие прогресса с detail вроде "Retrying crawl (attempt 2)...". Это значит, что задание восстанавливается, а не падает. Терминальным считайте только шаг failed; его detail и содержит настоящую причину.
Обрабатывайте незнакомые шаги
Со временем могут появляться новые значения step. Обрабатывайте те шаги, которые вам известны, считайте completed и failed двумя значениями, после которых сокет закрывается, а всё остальное воспринимайте как информационные сообщения — клиент с заделом на будущее продолжит работать и без обновления.
Подключение после завершения задания#
Главный вопрос для любого сокета прогресса — что будет, если подключиться поздно: после завершения сканирования, после переподключения вкладки из-за деплоя, после того как задание уже завершилось с ошибкой. Здесь ответ заложен в том, как работает снимок состояния.
Если задание уже дошло до completed или failed, сервер отправит снимок состояния с этим финальным status (и errorMessage, если была ошибка) и сразу закроет соединение. Проигрывать события прогресса задним числом не нужно, потому что итоговое состояние уже содержится в снимке. Если задание всё ещё в работе, соединение остаётся открытым и транслирует прогресс; если задание завершено, вы сразу получаете результат, и соединение закрывается.
В любом случае первое сообщение показывает, что происходит прямо сейчас. Подключайтесь когда угодно, даже после завершения — ни слишком рано, ни слишком поздно подключиться здесь нельзя.
Как встроить это в ваш UI#
Откройте сокет для задания с ID pjb_, прочитайте снимок состояния, чтобы задать начальное состояние, затем обновляйте его с каждым событием прогресса и закрывайте соединение, когда задание доходит до completed или failed:
import WebSocket from "ws";
const jobId = "pjb_A1b2C3d4E5f6G7h8";
const ws = new WebSocket(
`wss://api.lingo.dev/jobs/provisioning/${jobId}/ws`,
{ headers: { "X-API-Key": process.env.LINGO_API_KEY } }
);
ws.on("message", (raw) => {
const event = JSON.parse(raw);
switch (event.type) {
case "provisioning.snapshot":
console.log(`status: ${event.status}`);
break;
case "provisioning.progress":
console.log(`${event.step}: ${event.detail}`);
if (event.step === "completed" || event.step === "failed") {
ws.close();
}
break;
}
});Запустите это на задании, которое проходит сканирование без ошибок и пошагово показывает, как идёт настройка:
status: in_progress
crawling: Crawling source URLs...
configuring: AI agent analyzing content and configuring engine...
completed: Provisioning completeНа экране это выглядит так: задание переходит в in_progress, вы видите, как оно сначала сканирует, а затем настраивает, а completed сообщает, что движок полностью развёрнут. Тот же цикл корректно работает и при позднем подключении: завершённое задание отправляет один снимок состояния со своим финальным status, после чего сокет закрывается, поэтому код для live-сценария без специальных условий отрабатывает и этот случай.
Храните API-ключ на стороне сервера#
Сокет аутентифицируется вашим API-ключом — тем же organization-scoped key, который используют REST-эндпоинты. Этот ключ даёт доступ ко всем движкам в вашей организации, поэтому открывать такое соединение из браузера не стоит: любой, кто посмотрит исходный код, сможет его увидеть.
Подключайтесь с бэкенда, а не из браузера
Открывайте WebSocket со своего сервера, где ключ уже хранится, а затем пересылайте прогресс в браузер по собственному каналу — через WebSocket или поток server-sent events, который вы контролируете. Фронтенд показывает, как настраивается движок, а ключ не покидает вашу инфраструктуру.
Это повторяет модель webhook: соединение с Lingo.dev работает на стороне сервера, а до пользователя доходит только то, что ваше приложение решит переслать дальше.
Как это вписывается в общую схему#
WebSocket — это live-представление: он привязан к одному заданию и закрывается, когда это задание завершено. Если вам нужна надёжная server-to-server запись результата, которая переживёт закрытую вкладку или деплой, используйте его вместе с completion webhook: сокет управляет индикатором прогресса, пока задание видно на экране, а webhook доставляет сводку обо всём, что создал AI, сразу после завершения. Подключайте оба из одного вызова create.
