Техническое руководство
SSE и AI API: переподключение без дублирования
SSE переподключение OpenAI-совместимого API нельзя сводить к бесконечному retry. При обрыве браузер знает только, что доставка прекратилась; исходящая операция могла завершиться, быть отменена или остаться в неизвестном состоянии. RussiaAPI как независимый gateway не обязан поддерживать Last-Event-ID или возобновление потока: эти возможности проверяются по текущему контракту.
RUSSIAAPI_API_KEY; не передавайте ключи других поставщиков, cookie, пароли, коды подтверждения или лишние персональные данные.Различайте доставку и операцию
Доставка SSE — соединение между клиентом и вашим backend; операция — работа, которую backend разрешил отправить дальше. Создайте внутренний operation ID до сети и свяжите с ним владельца, версию адаптера, время и состояние. При закрытии вкладки не меняйте статус на completed по умолчанию. Используйте отдельные состояния streaming, cancelled, completed и unknown, чтобы интерфейс не выдавал частичный ответ за подтверждённый результат.
Клиент не должен обращаться к gateway с ключом напрямую. Он сообщает вашему backend только собственный operation ID, а backend решает, можно ли закрыть соединение, проверить известный статус или показать пользователю кнопку повторного запуска. Такая граница сохраняет секрет на сервере и не превращает потерю сети в основание повторить запрос без согласия пользователя.
Не предполагайте поддержку Last-Event-ID
Last-Event-ID полезен лишь тогда, когда ваш конкретный server-side маршрут и upstream-контракт явно описывают идентификаторы событий и их повторную доставку. Нельзя добавить заголовок наугад и считать поток восстановленным. Сначала проверьте документацию, текущий endpoint и минимальный тест. Если поддержки нет, ваш backend может хранить только собственный безопасный прогресс доставки, но не должен выдумывать недостающие chunks.
Для текстовой генерации разумный UX — показать «соединение прервано, результат не подтверждён» и предложить явное действие. Для запросов с побочным эффектом сначала требуется идемпотентность и сверка внутреннего состояния. Не повторяйте операцию автоматически: это может создать второй расход, другой ответ или дубль задачи. Принципы отмены уже разобраны в материале об отмене SSE.
Задайте deadline и cleanup
У каждого потока должны быть deadline, ограничение буфера и путь cleanup в finally. Если клиент читает медленно, не накапливайте chunks без границ в памяти. Если upstream вернул timeout или 5xx, записывайте нормализованный безопасный статус и закрывайте ответ; не маскируйте проблему бесконечным retry. Отмену пользователя, timeout и сетевую ошибку полезно различать в метриках, потому что они требуют разных продуктовых решений.
Протестируйте обрыв до первого события, после нескольких delta, закрытие вкладки и истечение deadline. В каждом случае подтверждайте три свойства: ключ не попал в браузер, активный controller освобождён, а UI не показывает ложную готовность. Для диагностики можно сохранить request ID без тела запроса; подход к безопасному журналированию описан в руководстве по request ID.
Восстанавливайте только разрешённые сценарии
Повторная доставка безопасна, когда приложение может доказать отсутствие побочного эффекта или дедуплицировать его по своему идентификатору. Для чтения статуса допускается ограниченный повтор с backoff и общим deadline. Для создания задачи сначала найдите operation ID в своём хранилище; если состояние неизвестно, покажите проверку, а не новую отправку. Это особенно важно для видео и других асинхронных операций.
Собирайте метрики возраста потока, доли unknown, отмен пользователей и числа повторов. Они помогают заметить деградацию, но не являются SLA RussiaAPI или любого производителя модели. При устойчивой ошибке остановите feature flag, проверьте каталог и контракт, затем выполните контролируемый rollback. Не подменяйте модель и не обходите ограничения ради «успешного» результата.
Server-side пример
Пример ниже показывает форму минимального теста. Он использует только собственный ключ из окружения, не передаёт секрет в браузер и не гарантирует поддержку неописанной функции. Перед запуском подтвердите маршрут и model ID в текущем каталоге.
export async function streamWithDeadline({ operationId, signal, write }) {
if (!operationId || !process.env.RUSSIAAPI_API_KEY) throw new Error('invalid_operation');
const controller = new AbortController();
const stop = () => controller.abort(new Error('client_disconnected'));
signal.addEventListener('abort', stop, { once: true });
try {
const response = await fetch('https://russiaapi.com/v1/chat/completions', {
method: 'POST', headers: { authorization: `Bearer ${process.env.RUSSIAAPI_API_KEY}`, 'content-type': 'application/json' },
body: JSON.stringify({ model: process.env.RUSSIAAPI_TEXT_MODEL, stream: true, messages: [{ role: 'user', content: 'test' }] }),
signal: AbortSignal.any([controller.signal, AbortSignal.timeout(20_000)])
});
if (!response.ok || !response.body) return { operationId, state: 'unknown' };
for await (const chunk of response.body) write(chunk);
return { operationId, state: 'completed' };
} finally { signal.removeEventListener('abort', stop); }
}Проверьте синтаксис командой node --check, добавьте аутентификацию своего маршрута, rate limit, ограничение входа и тесты ошибок. Не логируйте тело запроса или заголовки только ради отладки.
Что фиксировать в рабочем контуре
Перед изменением назначьте владельца, внутренний идентификатор операции, версию адаптера, разрешённый модельный ID и критерий успеха. В безопасный журнал обычно достаточно записать время, HTTP-класс, нормализованный код, latency и request ID, если он предоставлен. Не записывайте Authorization, полный prompt, ответ пользователя, временные URL или экспорт заголовков. Эти данные редко нужны для базовой диагностики и повышают риск утечки.
Проверяйте изменения на обезличенном наборе и отдельном собственном ключе с небольшим бюджетом. Один удачный вызов не доказывает поддержку всех параметров, стабильность цены или доступность модели. Не используйте интеграцию для обхода законов, санкций, региональных, платёжных или платформенных ограничений. При неопределённом результате сначала сверяйте своё хранилище и текущую документацию, затем выполняйте только явно разрешённое действие.
Проверьте сценарий в RussiaAPI
Создайте собственный тестовый ключ в консоли, сверьте текущий каталог моделей и выполните обезличенный server-side smoke test. Расширяйте нагрузку и доступ только после измеримой проверки.
FAQ
Можно ли всегда использовать Last-Event-ID?
Нет. Используйте его только при документированной и проверенной поддержке вашего маршрута. Наличие SSE само по себе не обещает идентификаторы, буферизацию или повторную доставку. При отсутствии поддержки отображайте неопределённый результат честно.
Когда retry безопасен?
Ограниченный retry допустим для разрешённого read-only статуса с общим deadline. Создание или иная операция с побочным эффектом требует собственного operation ID, дедупликации и сверки состояния; не запускайте её автоматически после обрыва.
Что сохранять после обрыва?
Минимально необходимое: operation ID, время, нормализованное состояние, версию адаптера и безопасный request ID. Не сохраняйте Authorization, ключи, полный prompt, ответ пользователя или временные ссылки только для отладки.