Запустите флоу в фоне и дождитесь его результата
PHPОтправьте запуск, не дожидаясь его, дождитесь выполнения, которое ответило queued или waiting, и прочитайте его статус и трассировку.
6 мин чтения
Запуск не всегда возвращается законченным. Флоу может остановиться на узле, который ждёт, а ваше приложение может попросить Slen выполнить флоу в фоне. В обоих случаях ответ несёт неокончательный статус, а результат читают позже.
Когда ответ не окончательный
Три статуса значат, что выполнение ещё продолжится.
| Статус | Что он значит |
|---|---|
queued | Запуск отправлен в асинхронном режиме. Он принят, и ни один воркер ещё не начал флоу |
running | Флоу выполняется прямо сейчас |
waiting | Флоу остановился на узле, который ждёт. Он продолжится сам, когда случится то, чего он ждёт |
На запрос отвечают 202, когда его выполнение не закончилось: queued в асинхронном режиме, waiting на узле, который ждёт, и running, когда повторный запрос застаёт выполнение, которое ещё идёт. Ответ несёт идентификатор выполнения и retry_after_seconds — сколько Slen советует подождать, прежде чем читать статус.
Выполнение со статусом waiting говорит, чего оно ждёт, в waiting_on. node_key называет узел, resume_at — момент, когда ожидание закончится, если он есть, а type — причина.
type | Чего ждёт выполнение |
|---|---|
queue_node | Узел Очередь держит его до resume_at |
approval | Решение принимает человек на странице Согласования. resume_at нет |
rate_limit_queue | Ни одно подключение пула провайдеров не может сейчас принять вызов, а AI-модель или его пул настроены ждать, а не отказывать. Флоу пробует вызов снова в resume_at |
provider_background | Провайдер выполняет вызов как фоновую задачу, и Slen спрашивает у него ответ. Когда провайдера не удаётся спросить даже после многих попыток, выполнение заканчивается со статусом uncertain. Вызов, который провайдер продолжает выполнять, ждут столько же, сколько любое ожидание, — по умолчанию 72 часа в сумме, — а затем выполнение заканчивается со статусом failed и кодом parked_expired |
У выполнения со статусом queued поле waiting_on равно null.
- Запрос — По умолчанию → Очередь
- Очередь — По умолчанию → AI-модель
- AI-модель — Успех → Вернуть ответ
На запуск этого флоу отвечают waiting, как только выполнение доходит до Очередь.
Отправить запуск, не дожидаясь его
operations.executeAsync() принимает те же аргументы, что и operations.execute(). Флоу выполняется в фоне, а вызов сразу возвращается со статусом queued.
use Slen\Sdk\Modules\Operations\Options\ExecuteOptions;
$queued = $slen->operations()->executeAsync(
'summarize_report',
['messages' => [['role' => 'user', 'content' => 'Summarize the third-quarter report.']]],
new ExecuteOptions(idempotencyKey: 'report-2026-q3:summary'),
);
echo $queued->status->value, PHP_EOL;Ключ идемпотентности работает так же, как в синхронном запуске. Запуск, повторённый под тем же ключом с тем же телом, ничего не начинает. Ответом на него будет выполнение, которое начал первый запрос, — в том состоянии, в каком оно находится в этот момент: queued, пока его не взял воркер, и его результат, когда оно закончилось.
У выполнения, которое идёт в фоне, нет запроса, на который можно ответить ошибкой. Отказ или сбой читают из его статуса.
Дождаться результата
executions.wait() читает статус выполнения снова и снова, пока выполнение не закончится.
use Slen\Sdk\Exceptions\SlenException;
use Slen\Sdk\Modules\Executions\Options\WaitOptions;
// In a queue job or a script, not in an HTTP handler: the wait sleeps in the process.
$finished = $slen->executions()->wait($queued->executionId, new WaitOptions(
timeoutSeconds: 60,
onPollError: static function (SlenException $error, int $failuresInARow): void {
echo sprintf('Status read failed (%d in a row): %s', $failuresInARow, $error->getMessage()), PHP_EOL;
},
));Пауза между двумя чтениями — та, о которой просит Slen: неокончательный статус несёт
retry_after_seconds, и следующее чтение происходит через столько секунд. Статус без него получает паузу, которая начинается с одной секунды и удваивается до 30. Каждая пауза сдвигается до четверти в любую сторону, чтобы выполнения, отправленные вместе, не читались в одно мгновение.Таймаут —
timeoutSecondsопций,WaitOptionsв обоих SDK, — сколько ждёт вызов: 300 секунд, если вы не передали другой, и не меньше 1. Пауза сокращается до того, что осталось от таймаута, и когда таймаут истёк, статус читается ещё один раз.Таймаут — не ошибка. Вызов возвращает последний прочитанный статус, и этот статус всё ещё не окончательный. Вызовите
executions.wait()ещё раз, чтобы ждать дальше.
Чтению отводится то, что осталось от таймаута, — не меньше пяти секунд и не больше таймаута конфигурации, — поэтому вызов возвращается не позже чем через пять секунд после своего таймаута. Guzzle и Symfony HttpClient принимают таймаут чтения. Любой другой HTTP-клиент читает со своими таймаутами, и с ним вызов может вернуться на это время позже.
Ожидание читает статус тем же API-ключом, а для чтения нужно разрешение read. У ключа с gateway:write оно есть.
Прочитать, чем всё закончилось
echo match (true) {
$finished->isCompleted() => $finished->output['content'],
$finished->isPending() => 'Not over yet: ' . $finished->status->value,
default => sprintf('Ended as %s: %s', $finished->status->value, $finished->errorCode),
}, PHP_EOL;Ответ ожидания заканчивается одним из трёх способов.
completed— вoutputлежит то, что вернул флоу.queued,waitingилиrunning— таймаут наступил раньше. Подождите ещё.rejected,failedилиuncertain— выполнение не завершилось.error_codeговорит почему, аerror_messageговорит то же фразой для человека. У выполнения со статусомfailed, закрытого ошибкой, для которой у API нет кода, нет ни того ни другого: оба равныnull.
Ожидание возвращает ExecutionStatusView: поля ExecutionResult без error и replayed.
Когда чтение статуса не удаётся
Чтение, которое не удалось по временной причине, не заканчивает ожидание. Клиент сам не отправляет это чтение ещё раз: ожидание читает статус заново после паузы, которая начинается с одной секунды и удваивается до 30.
Временная причина — запрос без ответа,
429и любой5xx. Чтение статуса учитывается в лимите запросов ключа, поэтому429— одна из них.Любая другая ошибка —
404,401— сразу заканчивает ожидание.Когда наступает таймаут, вызов возвращает последний прочитанный статус. Последнюю ошибку он выбрасывает, только если не удалось ни одно чтение.
Поэтому статус, который всё ещё не окончательный, не говорит, медленное ли выполнение или API нельзя было спросить. Это говорит onPollError. Ожидание вызывает его при каждом неудачном чтении, после которого оно продолжается, и передаёт число неудачных чтений подряд. Ошибка, которая заканчивает ожидание, выбрасывается и в него не передаётся, а ошибка, которую выбрасывает onPollError, заканчивает ожидание.
Прочитать статус самому
executions.get() читает статус один раз. Это запрос GET /v1/executions/{id}/status — тот, который повторяет ожидание.
executions.trace() читает всю историю выполнения: каждый узел, через который оно прошло, и каждое событие. Это запрос GET /v1/executions/{id} — его поля описывает статья Выполнения.
В консоли те же выполнения — на вкладке Выполнения своего флоу, со статусами В очереди, Выполняется и Ожидает.
В сервисе используйте вебхук
Ожидание длится столько, сколько идёт выполнение, и спит между своими чтениями. Это подходит задаче вашей очереди или скрипту, но не обработчику HTTP-запроса: тот, кто его вызвал, ждёт всё это время, а воркер PHP в это время не обслуживает другие запросы. Сервис отправляет запуск через operations.executeAsync() и сразу отвечает тому, кто его вызвал. Результат он читает, когда приходит вебхук execution.finished, — Вебхуки, — или в задаче своей очереди, которая его ждёт.
Что читать дальше
Очередь — узел, который заставляет выполнение ждать заданное время.
Паузы, очереди и возобновление — какие узлы заставляют выполнение ждать и что оно держит в это время.
Вебхуки — узнайте, что запуск закончился, не дожидаясь его.
Тестирование интеграции — запустите код, который ждёт, без сети.