Запустіть флоу у фоні й дочекайтеся його результату

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.

PHP
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() читає статус виконання знову й знову, поки виконання не закінчиться.

PHP
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 його має.

Прочитати, чим усе закінчилося

PHP
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.

Очікування повертає ExecutionStatusDTO: поля ExecutionDTO без 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, — Вебхуки, — або в задачі своєї черги, яка на нього чекає.

Що читати далі