API и разработка

LLM API на JavaScript: от каталога до подтверждённого ответа

Рабочий путь для Node.js: ключ на сервере, выбор API ID, сохранение тела, quote, запрос с Idempotency-Key и различение результата и ожидания.

Редакция KvantoraТематический выпуск: Около 5 минут чтения
Обложка статьи «LLM API на JavaScript: от каталога до подтверждённого ответа»

В Node.js интеграция LLM API начинается с безопасного хранения ключа и проверки ID модели. Затем подготовьте неизменный запрос, оцените расход и отправьте его с сохранённым Idempotency-Key; текст показывайте как окончательный только после подтверждения завершения.

Ключ остаётся на сервере

Не вызывайте платный LLM API прямо из React-компонента с ключом в NEXT_PUBLIC переменной. Браузерный код и сетевые запросы доступны пользователю. Вместо этого браузер отправляет собственный запрос вашему серверу, а сервер проверяет пользователя, бюджет и допустимую задачу. Только после этого он обращается к модели. Такой слой нужен даже для небольшого прототипа.

Для Kvantora задайте KVANTORA_API_KEY и KVANTORA_BASE_URL=https://api.kvantora.ai в серверном окружении. Публичный ID модели возьмите из /models. Не пишите секрет в файл примера или в команду, которую сохранит история терминала. Логируйте свой ID операции и статус, но не значение ключа.

Пример начинается с безопасных команд

Скачиваемый quickstart.mjs для Node.js 22+ позволяет пройти последовательность catalog, prepare и quote до платного вызова. Prepare создаёт файл тела с UUID без секрета и не перезаписывает существующий. Quote оценивает расход этого тела. Запуск send выполняйте только после проверки модели, доступа и бюджета. Для следующего вопроса задайте новое имя файла, чтобы не спутать операции.

Команды ниже предполагают уже заданные серверные переменные. Они показывают важное свойство рабочего клиента: после потери соединения вы можете снова открыть тот же файл, а не собирать новый запрос по памяти. В веб-сервисе таким хранилищем станет запись в базе или надёжной очереди.

bash
node quickstart.mjs catalog
node quickstart.mjs prepare request.json
node quickstart.mjs quote request.json
# Только после проверки оценки и разрешения:
node quickstart.mjs send request.json

Если у вас уже есть OpenAI SDK

Документированный вариант Kvantora использует клиент OpenAI с baseURL, заканчивающимся на /v1, и maxRetries: 0. Установите совместимую с вашим проектом версию SDK по документации. Автоматические повторы лучше отключить до проработки идемпотентности: потерянный ответ после платного вызова нельзя считать гарантированным отказом. Заголовок Idempotency-Key должен оставаться тем же для того же логического запроса.

Сначала проверьте используемый метод по матрице совместимости. SDK предоставляет широкий интерфейс, но выбранный маршрут поддерживает лишь конкретное подмножество. Если приложение применяет Responses, инструменты или особый формат JSON, отдельный тест обязателен. Простая смена base URL подтверждает только то, что первый совместимый запрос прошёл.

Разберите завершённое и промежуточное состояния

Обычный завершённый ответ содержит object=chat.completion, текст в choices[0].message.content и подтверждённый billing.status=settled по контракту Kvantora. HTTP 202 означает, что результат ещё не готов. Если серверный обработчик при 202 возвращает клиенту пустую строку с кодом успеха, интерфейс побудит человека нажать кнопку ещё раз. Это ошибка интеграции.

Сделайте собственные состояния: ожидает, завершён, отклонён, требует восстановления. Для каждого решите, что показывать человеку. Например, «ответ ещё обрабатывается» лучше бесконечного спиннера после таймаута браузера. Сохраните ID запроса и внутренний trace ID отдельно; они решают разные задачи диагностики.

Серверный маршрут должен отвечать своему UI

Промежуточный сервер в Node.js не обязан ждать модель бесконечно. Он может вернуть собственный ID операции, а интерфейс будет читать её состояние. При этом статус вашего HTTP-ответа не должен стирать статус upstream-запроса: «ожидает» остаётся ожиданием, даже если ваш сервер успешно принял задачу. Запишите это соответствие до реализации компонента чата.

Если пользователь закрывает вкладку, серверная работа может продолжиться. Не удаляйте её запись только потому, что браузер отменил fetch. При повторном открытии страницы пользователь должен увидеть результат той же операции или честное состояние ожидания. Такой путь чуть сложнее прямого вызова из браузера, зато не раскрывает ключ и сохраняет контроль над платным запросом.

Ошибки должны менять поведение клиента

Неверный ключ требует исправить конфигурацию, отсутствие права: полномочия, неверная модель: каталог, превышенный бюджет: параметры запроса. Слепой повтор не поможет. Для сетевого обрыва нужна та же идентичность запроса, а для временной перегрузки: ограниченная политика ожидания после выяснения исхода. Не создавайте цикл, который тратит баланс быстрее, чем пользователь замечает ошибку.

Проверяйте и структуру ответа. Если отсутствует ожидаемый content, не объявляйте это нормальным пустым сообщением без анализа статуса и операции. Лог должен помогать поддержке понять, где сломался путь, не раскрывая текст клиентского документа в общих системных записях.

Проверьте, что сервер не отдаёт пользователю сырой upstream-ответ целиком. В нём могут быть технические поля, не предназначенные для интерфейса. Сформируйте собственный ответ: ID операции, состояние и разрешённый текст. При ошибке покажите понятное действие, например исправить документ или подождать. Технические подробности оставьте в защищённом журнале для расследования.

Прежде чем подключить кнопку в интерфейсе

На локальном синтетическом сервере воспроизведите 200, 202, 401, 429 и разрыв соединения после принятия тела. Проверьте, что ваше приложение не теряет сохранённый UUID при перезапуске процесса. Затем проведите отдельно разрешённый реальный запрос с минимальным бюджетом и сверкой истории. Протокольный локальный тест не доказывает доступность конкретной модели.

После успеха добавьте продуктовую валидацию: ответ должен соответствовать задаче и проходить проверяемые критерии. Для извлечения данных нужны типы и проверки арифметики; для текста нужны контроль запретных обещаний и возможность правки. Интеграция закончена только тогда, когда результат можно безопасно использовать.

Источники