Skip to main content

tai42-backend-arq

License: Apache 2.0

An arq execution backend for the TAI ecosystem. It implements the tai42_contract.backend.Backend surface — the worker runtime (launch) — and layers the platform's background-execution features over arq: queued and awaited tool runs, recurring schedules (interval or crontab) with export/import backup, and result-chaining callbacks. Fleet propagation of config changes is not this backend's concern: a backend-runtime process receives fleet ops through the app's own internal worker bus, exactly like a serving HTTP worker.

The TAI ecosystem

TAI is an open-source runtime for MCP tools, agents, and workflows. An execution Backend is "how work runs beyond the request" — a pluggable strategy the runtime uses to queue tool executions on a worker fleet. Fanning control operations (manifest updates, config/tool/MCP reloads) out across every worker is the skeleton's worker bus, not the backend — a backend-runtime process just joins that bus like any other worker. This package is one such backend (arq over Redis); any package can back the same contract, so this repo is this provider's own full doc home, and the documentation site covers the platform-level story:

Its only tai-* dependencies are tai42-contract (the Backend ABC, CallbackSchema, Manifest, and the tai42_app handle) and tai42-kit[jq] (settings base, schedule normalization, signature helpers, jq). Beyond those it depends on the broker stack — arq, croniter, orjson, makefun, click — plus fastmcp (the platform's tool substrate) and pydantic / pydantic-settings / pydantic-core.

Install

Requires Python 3.13+. Install from PyPI into the environment that runs the server:

uv add tai42-backend-arq

Or from source — clone this repo and add it as an editable dependency; the tai42-* dependencies resolve in-tree from the workspace.

git clone https://github.com/tai42ai/tai42   # next to your app checkout
cd /path/to/your/app
uv add --editable ../tai42/plugins/backend-arq

Discovery

The host discovers this backend by importing its package — importing tai42_backend_arq registers everything through the global tai42_app handle as a side-effect (there is no entry-point): the ArqBackend (@tai42_app.backends.register_backend), the backend_* tool surface, the sync_task / schedule_task / async_task BACKEND-kind tool extensions. The enqueue side (enqueues, job status and results, schedule hashes) runs over the platform's pooled Redis client, so its connections are pooled per configuration generation and closed when that generation retires. Name the package in your manifest's backend_module field:

backend_module: tai42_backend_arq

Start a worker through the host's backend CLI; everything after worker is this backend's own option surface (see tai42_backend_arq.worker.main):

tai backend worker --max-jobs 10

Configuration

Settings are read from the ARQ_ environment group (see ArqSettings):

Env var Default Purpose
ARQ_REDIS_URL redis://localhost:6379/0 Redis connection for the queue and schedules
ARQ_REDIS_MAX_CONNECTIONS — Optional cap on the pool's connections
ARQ_CALLBACK_TIMEOUT 5 Seconds a callback job waits for its predecessor to complete
ARQ_MANIFEST_KEY MANIFEST_KEY Name of the env var the host exports the manifest under for worker processes
ARQ_TASK_TIMEOUT 300 The dispatch job budget in seconds: the longest a job may run on the worker (the --job-timeout default), and how long a synchronous branch tool waits for its queued job's result
ARQ_TOOL_NAME_ARG backend_tool_name kwargs key carrying the target tool name into a queued execution

ARQ_MANIFEST_KEY, ARQ_TASK_TIMEOUT, and ARQ_TOOL_NAME_ARG mirror the host's backend settings defaults — both sides must agree on these values without sharing code, so override them only in lockstep with the host.

Worker CLI options (after the worker subcommand): --redis-url (defaults to ARQ_REDIS_URL), --burst, --keep-result (default 3600; 0 disables result retention), --queue-name, --max-jobs (default 10), --job-timeout (defaults to ARQ_TASK_TIMEOUT), --poll-delay (default 0.5), --max-tries (default 5), --health-check-interval (default 60). The worker runs with arq's allow_abort_jobs enabled — task cancellation and schedule replace/delete rely on abort processing. A dispatched tool runs once: the backend never retries a tool body (a tool that wants retries declares a ToolRetryPolicy). arq itself re-runs a job cut by a cold stop (CancelledError), bounded by --max-tries — arq's own delivery semantics.

Fleet ops

Config-change propagation across the worker fleet (manifest updates, config/tool/MCP reloads) is the app's own internal worker bus, not a backend concern — a backend-runtime process receives those ops through the app's bus subscription exactly like a serving HTTP worker. See the skeleton's worker-bus concept: https://tai42.ai/concepts/worker-bus

Schedules

Recurring runs live in per-schedule Redis hashes (arq:schedule:{name}) driven by a self-rescheduling task_scheduler job; a startup watchdog restarts schedules whose pending job was lost. The schedule tools (backend_list_schedules — the contract's ScheduleListRow: name / tool / cadence / enabled / next_run_at_ts / next_run_at_iso / error, never the stored arguments —, backend_get_schedule, backend_delete_schedule, backend_enable_schedule, backend_disable_schedule, backend_run_schedule_now, backend_schedule_exists, backend_update_schedule) operate on those hashes, and backend_export_schedules / backend_import_schedules round-trip the contract's ScheduleRecord rows for backup (per-row import errors surfaced as {"index", "name", "error"}, never swallowed). A schedule's cadence is the contract's typed ScheduleCadence (tai42_contract.backend.scheduling); the hash stores its JSON dump in the schedule field. arq cannot honour a relative interval, so relative: true is refused on create, update and import.

Upgrading: reset the schedule store

The schedule field's stored format is the typed cadence. A hash written in the earlier format makes the schedule tools (list, export, get, update) raise ScheduleStoreFormatError naming the schedule; arq keeps firing such a hash from its cron_or_interval field. Reset the store and re-create the schedules:

redis-cli --scan --pattern 'arq:schedule:*' | xargs redis-cli del

The pattern is ArqSettings.arq_schedule_pattern (arq:schedule:*).

Task execution

The sync_task / async_task extension branches dispatch the wrapped tool to the queue via the tool_execution job; a sync_task wait re-raises a failed job's stored failure as TaskFailedError (aborted jobs included). Task options exposed on every branch: countdown (defer by seconds), eta (ISO datetime; defer until then), expires (seconds the queued job stays runnable), and callback_kwargs — a callback schema chained via a callback_job that runs over the primary job's result (jq condition gate, jq expression transform, optional follow-up tool). schedule_task registers a recurring schedule (see below).

Tools

Task/worker tools use arq's public Job status/result API: backend_task_status, backend_task_result (timeout-aware; a task's stored failure re-raises as TaskFailedError carrying the original exception's type, repr, and traceback text — result payloads describe an unserializable value in a tagged JSON shape the deserializer revives on read), backend_cancel_task (a stored abort replays as arq's confirmed-abort verdict; stored failures are reported with their detail), backend_active_tasks (a flat job-id-keyed map — arq has no per-worker attribution), backend_reserved_tasks (a flat list of due job ids), backend_scheduled_tasks (job id → due time in epoch milliseconds), backend_list_failed_tasks (failed/aborted results within the keep-result window, as {"task_id", "error"} rows carrying the stored failure detail). Capabilities arq has no reliable data model for raise NotImplementedError loudly: backend_registered_tasks, backend_worker_stats, backend_worker_queues, backend_ping_worker, backend_list_active_workers.

Development

uv venv --python 3.13
uv pip install --no-sources --group dev --editable .
uv run --no-sync pytest --cov --cov-report=term-missing
uv run --no-sync ruff check .
uv run --no-sync ruff format --check .
uv run --no-sync pyright

License

Apache-2.0. See LICENSE and NOTICE.

Metadata

Release files for tai42-backend-arq 5.0.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tai42-backend-arq 5.0.7
File Size Uploaded
tai42_backend_arq-5.0.7.tar.gz 81.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tai42-backend-arq 5.0.7
File Interpreter ABI Platform
tai42_backend_arq-5.0.7-py3-none-any.whl Python 3 none any Details

Total release size: 123.2 kB

Release files / tai42_backend_arq-5.0.7.tar.gz

Download URL tai42_backend_arq-5.0.7.tar.gz
Size 81.0 kB
Tags Source
SHA-256 checksum
How to use checksums
a3cd6d66af2b1d61cddce15ddc65b74354f804f2782c4ec29664dba6488fb80b
BLAKE2b-256 checksum
How to use checksums
4b6dd27838ece0df66f779797fbb1c52c2633ad544a0341b096115f6ec99708f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / tai42_backend_arq-5.0.7-py3-none-any.whl

Download URL tai42_backend_arq-5.0.7-py3-none-any.whl
Size 42.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
96aac0d67e91db4b2433b8ebc9386b8f8aed206393726c21689d9f2c2583761b
BLAKE2b-256 checksum
How to use checksums
b02fdb3f39bbe5f82ffff7ac0eec9f9b9320db7405cbf1d019a7c12ddc49c6f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

5.0.8

2 release files

This release

5.0.7 This release

2 release files

5.0.6

2 release files

5.0.5

2 release files

5.0.4

2 release files

5.0.3

2 release files

5.0.2

2 release files

5.0.1

2 release files

5.0.0

2 release files

4.3.4

2 release files

4.3.3

2 release files

4.3.2

2 release files

4.3.1

2 release files

4.3.0

2 release files

4.2.3

2 release files

4.2.2

2 release files

4.2.1

2 release files

4.2.0

2 release files

4.1.2

2 release files

4.1.1

2 release files

4.1.0

2 release files

4.0.3

2 release files

4.0.2

2 release files

4.0.1

2 release files

4.0.0

2 release files

3.0.7

2 release files

3.0.6

2 release files

3.0.5

2 release files

3.0.4

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.1.5

2 release files

2.1.4

2 release files

2.1.3

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.7

2 release files

2.0.6

2 release files

2.0.5

2 release files

2.0.4

2 release files

2.0.3

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page