Обработайте отклонённую трату
PHPОшибки, которые говорят, что бюджет, лимит или кредиты клиента отклонили запуск, две формы такого отказа и что делать с каждой.
3 мин чтения
Slen проверяет вызов модели во флоу по лимитам, которые вы задали: по бюджетам проекта и по кредитам ваших клиентов. Когда один из них говорит «нет», вызов отклоняется до обращения к провайдеру. Эта страница — о том, как поймать такой отказ и что с ним делать.
Семейство отклонённой траты
На такой отказ отвечают статусом 402. Оба SDK выбрасывают такие отказы как одно семейство ошибок: SpendRefusedException в PHP и SpendRefusedError в Node.js, и у каждого кода есть свой тип, который расширяет тип семейства. 402 с кодом, для которого у SDK нет типа, выбрасывается как сам тип семейства.
| Код | HTTP-статус | Тип ошибки в SDK | Что это значит | Что делать |
|---|---|---|---|---|
budget_exceeded | 402 | BudgetExceededException PHPBudgetExceededError Node.js | Вызов вывел бы бюджет за его лимит. То, что бюджет уже держит и потратил в текущем окне, вместе с холдом, который просит этот вызов, больше лимита. | Дождитесь следующего окна бюджета — его resets_at говорит когда; бюджет типа total заново не начинается. Или поднимите лимит бюджета. |
operation_limit_exceeded | 402 | OperationLimitExceededException PHPOperationLimitExceededError Node.js | Бюджет на операцию ограничивает, сколько может зарезервировать один вызов операции, а холд, который просит этот вызов, выше этого ограничения. | Просите меньше — меньше выходных токенов, модель дешевле — или поднимите ограничение. Тот же запрос будет отклонён снова, пока одно из них не изменится. |
concurrency_limit_exceeded | 402 | ConcurrencyLimitException PHPConcurrencyLimitError Node.js | Бюджет типа concurrency — в консоли «Одновременные резервы» — разрешает держать открытыми заданное число холдов одновременно, и все они заняты вызовами, которые выполняются. | Отправьте запрос снова, когда закончится один из выполняющихся вызовов, или поднимите лимит бюджета. Запрос к флоу отправляйте снова с новым ключом, потому что его отклонённое выполнение сохраняет ключ, который у него был. Это не лимит запросов ключа. |
insufficient_credits | 402 | InsufficientCreditsException PHPInsufficientCreditsError Node.js | Счёт клиента не может покрыть цену вызова в кредитах, или у клиента нет счёта этого типа кредитов. Списание или корректировка отклоняются так же, когда оставили бы баланс ниже того, что держит счёт. | Пополните кредиты клиента. Счёт, в настройках которого разрешён отрицательный баланс, не отклоняет. |
overrun_stopped | 402 | OverrunStoppedException PHPOverrunStoppedError Node.js | Оценка вызова модели во флоу, сделанная заново после того, как взят холд, и до вызова провайдера, превысила холд больше, чем позволяет допуск превышения узла, а политика перерасхода узла модели — остановить. Холд возвращён, провайдер не вызывался. Тот же код завершает выполнение, когда человек отклонил согласование, которое запросил перерасход, или никто не ответил вовремя. У нового узла модели политики перерасхода нет, а политика, которая у узла есть, практически не срабатывает, потому что обе оценки считаются по одному и тому же запросу с разницей в мгновение. | Отправьте запрос снова с новым ключом — вызов будет оценён заново и возьмёт собственный холд. Чтобы таких отказов не было, выключите политику перерасхода узла модели и опубликуйте новую версию. |
overrun_approval_required | 402 | OverrunApprovalRequiredException PHPOverrunApprovalRequiredError Node.js | Оценка вызова модели во флоу, сделанная заново после того, как взят холд, и до вызова провайдера, превысила холд больше, чем позволяет допуск превышения узла, а политика перерасхода узла модели просит человека одобрить разницу. Выполнение ждёт этого решения, поэтому на запрос отвечает ждущее выполнение, а не этот код. У нового узла модели политики перерасхода нет, а политика, которая у узла есть, практически не срабатывает, потому что обе оценки считаются по одному и тому же запросу с разницей в мгновение. | Примите решение по согласованию в консоли. Согласование, которое отклонили или по которому никто не решил вовремя, завершает выполнение с кодом overrun_stopped. |
Политика, которая отказывает, в семейство не входит. На policy_denied и model_not_allowed отвечают 403 и выбрасывают их как PolicyDeniedException или PolicyDeniedError — как проверяется политика, говорит статья Политики. Запрос для заблокированного клиента отклоняется так же, с customer_blocked, как CustomerBlockedException или CustomerBlockedError, которые расширяют тип отказа политики.
Два кода семейства, overrun_stopped и overrun_approval_required, приходят от политики перерасхода узла модели во флоу, а такая политика практически не срабатывает — почему, говорят их строки в таблице выше.
Поймать его
use Slen\Sdk\Exceptions\OperationUncertainException;
use Slen\Sdk\Exceptions\SlenException;
use Slen\Sdk\Exceptions\SpendRefusedException;
use Slen\Sdk\Modules\Operations\Options\ExecuteOptions;
$input = ['messages' => [['role' => 'user', 'content' => 'Write a short follow-up email.']]];
try {
$execution = $slen->operations()->execute('generate_email', $input, new ExecuteOptions(idempotencyKey: 'order-42:email'));
echo $execution->status->value, PHP_EOL;
} catch (SpendRefusedException $error) {
// The spend was refused, and the code says what refused it.
echo 'refused: ', $error->errorCode, PHP_EOL;
} catch (OperationUncertainException $error) {
// A provider was called and its answer was lost: the call may have been paid for.
echo 'uncertain, request ', $error->requestId, PHP_EOL;
} catch (SlenException $error) {
echo $error->statusCode, ' ', $error->errorCode, ': ', $error->getMessage(), PHP_EOL;
}Код говорит, какой лимит отклонил вызов, а таблица выше — что делать с каждым.
Отклонённый вызов ничего не стоил: его провайдер не вызывался, и за него не остаётся зарезервированных денег. Во флоу вызовы моделей в узлах до него оплачены.
Тот же запрос отклоняется снова, пока что-то не изменится: бюджет начнёт следующее окно, лимит поднимут, клиента пополнят. Исключение —
concurrency_limit_exceeded: он проходит, как только идущий вызов закончится.Не отправляйте отклонённый запрос снова в цикле.
Вторая форма: выполнение, которое закончилось отказом
На запуск отвечают ошибкой один раз. Выполнение, которое он открыл, закрывается со статусом rejected и хранит код. Его cost.estimated — оценка отклонённого вызова, то есть трата, которую отказ предотвратил, а не потраченные деньги, — или null, когда отказ не назвал оценки, как для клиента без кредитов. Тот, кто читает его позже, получает выполнение, а не ошибку: повтор запроса под его ключом, ожидание, чтение статуса, запуск, отправленный в асинхронном режиме. Поэтому запуск, который вы отправляете снова после отказа, идёт под новым ключом.
$again = $slen->operations()->execute('generate_email', $input, new ExecuteOptions(idempotencyKey: 'order-42:email'));
if ($again->isRejected()) {
echo 'rejected: ', $again->errorCode, PHP_EOL;
}Когда у узла модели есть ветка на порту Отказ, отказ не заканчивает выполнение. Флоу продолжает путь по этой ветке и решает, что услышит вызывающий.
Узел Отклонить запрос может закончить выполнение с любым кодом, в том числе с кодом этого семейства, и тогда ошибка тоже не выбрасывается. Код, который реагирует на отказ, обрабатывает обе формы: catch и статус того, что вернулось.
Где лимиты
Бюджеты проекта — на странице Бюджеты.
Кредиты ваших клиентов — на странице Клиенты, а пополняет их ваше приложение через
customers.topUp()— Клиенты и кредиты.
Что читать дальше
Обработка ошибок — каждая ошибка SDK и стоила ли она денег.
Коды ошибок — что значит каждый код и что с ним делать.
Асинхронные запуски и ожидание — как прочитать, чем закончилось выполнение.
Собрано из openapi b070e1f, sdk contract 6a2e0e0, php 4089db3, node 0fa87be