Skip to content

The ai_runs table

Every provider call creates a row in ai_runs (the name is set by table in the config). Model — Fomvasss\AiTasks\Models\AiRun, UUID primary key.

A sync call with fallback leaves one row per tried driver; a queued run is one row whatever driver answered.

ColumnDescription
idUUID; returned by AI::queue()
tenant_idTenant the run is billed to
taskTask name
driverDriver that answered
user_idWho started the run, see User
modelModel used
modalitytext, image, embed, audio, transcription
subject_type, subject_idRecord the run concerns, see Subject
dispatchsync or queue
statusSee below
errorError message
idempotency_keyUnique; queued runs only, see Idempotency
requestModality, options, meta, task_class, execution_context (when the task has one); with store_request also messages, system prompt and task_args; for queued runs dispatch_id and available_at (due time of a delayed run)
responseResponse content and metadata
tokens_in, tokens_out, cache_read_tokens, cache_write_tokensSee Tokens
costUSD
cost_ratesRates snapshot, see Cost tracking
started_at, finished_at, duration_msTiming
created_at, updated_at
StatusMeaning
queuedDispatched, waiting for a worker
runningProvider call in progress; a queued run also stays here between retry attempts
waitingParked until a provider webhook, see Webhooks
okFinished with a result
pausedThe call finished and waits for a tool approval decision, see Resuming
errorFailed and not retried by the queue: a sync attempt failed, the driver returned ok: false, or the post-call budget check rejected the response
deadQueued run failed after all retries, or closed by hand
skippedshouldRun() returned false, or a sync call skipped a driver without an API key
stateDiagram-v2
    [*] --> queued: queue
    [*] --> running: send or stream
    queued --> running: worker picked it up
    queued --> skipped: shouldRun is false
    running --> ok
    running --> paused: tool needs approval
    paused --> ok: resumed
    running --> error: failed, not retried
    running --> dead: queue gave up
    running --> skipped: driver without API key
    running --> waiting: markWaiting
    waiting --> ok: webhook succeeded
    waiting --> error: webhook failed
    error --> queued: Retry
    dead --> queued: Retry

stuck is not a status but a condition: queued/running without progress for longer than dashboard.stuck_after_minutes.

MethodDescription
AiRun::stuck(?int $minutes = null)Scope: stuck runs
isStuck(?int $minutes = null): boolWhether the run is stuck
canRetry(): boolWhether the dashboard / ai:retry can re-dispatch it
isSuperseded(): boolA failed sync attempt whose fallback driver answered; response.superseded_by holds the id of that row
isPaused(): bool, pauseExpired(): boolWaiting for an approval decision; past approvals.ttl_minutes
dismissPause(): bool, expirePause()Close a pause the app won’t continue / that ran out of time
isResume(): boolA continuation started by AI::resume(); never retried
executionContext(): arrayThe context captured at dispatch, see Acting as a user
markWaiting(array $extra = [])Park the run until a webhook; $extra goes to response, e.g. provider_run_id
abandon(string $reason)Mark dead without firing AiRunFailed
use Fomvasss\AiTasks\Models\AiRun;
AiRun::where('task', 'summarize')->where('status', 'dead')->latest()->get();
AiRun::where('subject_type', 'order')->where('subject_id', $order->id)->get();
AiRun::stuck()->count();