Skip to main content

Agentic Spark — declarative, self-healing Apache Spark blueprints.

Project description

Aqueduct Logo

Aqueduct

Self-healing Spark pipelines. Declarative. Observable. Autonomous.

PyPI Python License
Test Suite Compatibility Matrix Project status: beta Stars


Why Aqueduct

A Spark job fails at 3 a.m. on a column rename upstream. Today that means a paged engineer scrolling a four-kilobyte JVM stack trace to find a one-line fix. Aqueduct turns it into a Git-diffable patch waiting for review in the morning.

Figure 1: Aqueduct at a glance



  • Declarative, not DAG code. Pipelines are YAML Blueprints — no PySpark boilerplate, no scheduler glue, no operator classes. Bring your own scheduler; Aqueduct is the control plane on top of Spark.
  • Self-healing, not just alerting. On failure an LLM agent diagnoses the root cause and emits a structured patch that passes guardrail, lineage, and sandbox gates before it touches your pipeline. No codegen, no shell access, no silent mutation — and a failure it has solved before heals from memory with zero tokens.
  • Observable by construction. Every run, every heal attempt, every column-lineage edge lands in a queryable store — at zero extra Spark actions on the hot path.
  • Model-agnostic. Any LLM — a local 7B on Ollama up to a frontier API. The constrained patch grammar (14 deterministic operations, no code generation) is what keeps small models reliable; multi-model cascades escalate to bigger models only when needed.

Wake up to a pending patch — not a wall of Spark errors.


Table of Contents


What You Get

Capability What it does Details
Self-healing LLM diagnoses failures, emits gated, Git-diffable patches — human, CI, or auto approval Spec §8
Heal memory Failure signatures cache validated fixes — repeat failures heal with zero LLM tokens Spec §8.2
Observability store Runs, failures, heal attempts, metrics in queryable DuckDB/Postgres Observability Guide
Column lineage Compile-time, zero Spark actions; powers the patch lineage gate Spec §7
Data quality Inline Assert rules + Spillway quarantine — bad rows routed, not dropped, typed like catch blocks Spec §4.4
Module tests aqueduct test runs transforms against inline fixtures — no I/O, no cluster CLI Reference
LLM benchmark aqueduct benchmark scores models against simulated failures — pick the cheapest model that heals your pipelines CLI Reference
Safety rails Guardrails, multi-axis budgets, hourly heal caps, sandbox replay before any live write Spec §8.3

Core Concepts

Concept Purpose
Blueprint Your pipeline definition
Ingress Reads sources (CSV, Parquet, Delta, JDBC)
Channel Transformations (SQL or native ops)
Egress Writes sinks (overwrite, append, Delta merge)
Junction Fan-out (conditional, broadcast, partition)
Funnel Fan-in (unions, coalesce, zip)
Spillway Routes bad rows to error sink
Probe Non-blocking observability taps
Assert Inline quality gates
Depot Cross-run state & watermarks
Arcade Reusable sub-pipelines

Full details in the References.

The Healing Flow

When a pipeline fails, Aqueduct does not throw a stack trace at an LLM and hope. Healing is a staged, auditable pipeline, and the model works inside a constrained grammar — it cannot write code, mutate files, or run shell commands.

📽️ Demo coming soon — a live heal, start to green, in under 20 seconds.

Figure 2: The Healing Flow

Approval modes

Who applies a generated patch. Deterministic guardrails — allowed paths, forbidden operations, minimum confidence — bound every patch regardless of mode.

Mode Who applies the patch When the Blueprint changes Use when
disabled LLM never fires Never Healing is intentionally off.
human Engineer reviews and applies Only after human accepts Production. Default behind CI/CD.
ci External CI receives patch, opens a PR Only after merge Production with code review.
auto Aqueduct applies in-memory, re-validates, writes only if the re-run succeeds Only on a successful re-run Trusted environments — dev, scoped pipelines.

Low-confidence patches and any guardrail violation auto-escalate to human review.

What a patch looks like (click to expand)

Every patch is a PatchSpec — a structured, Git-diffable JSON document staged under patches/pending/. No code, no shell, just declarative operations against the Blueprint:

// patches/pending/hello-pipeline-20260611T031412.json (abridged)
{
  "patch_id": "hello-pipeline-20260611T031412",
  "run_id": "9f3c2e1a",
  "category": "config_error",
  "root_cause": "Ingress 'load' reads data/in.csv, but the upstream job renamed the file to data/input.csv.",
  "confidence": 0.92,
  "rationale": "PATH_NOT_FOUND on data/in.csv; a sibling data/input.csv exists with a matching schema, so the path is stale rather than the data missing.",
  "operations": [
    { "op": "set_module_config_key", "module_id": "load", "key": "path", "value": "data/input.csv" }
  ]
}

Review it, then aqueduct patch apply — or let auto mode validate and apply it for you.

Why it holds up

  • No silent mutations. Every patch is a structured diff with a rationale and a confidence score; low confidence escalates.
  • No production data corruption. The sandbox validates patches against representative data before any live write.
  • No runaway loops. A multi-axis budget bounds wall-clock, tokens, reprompt count, and stuck-signature windows; a rolling rate-limit caps heals per hour per blueprint.
  • No black-box decisions. Every LLM turn persists with the gate that rejected it, a stable error signature, and the prompt version. One run id joins every iteration of a heal.
  • Efficient. Healing stops on the first successful patch. Structured error extraction replaces multi-kilobyte traces with a short root-cause block, and cheap lineage/sandbox checks reject bad patches in seconds before any full-pipeline replay.

For the stage-by-stage detail, see the Blueprint & Engine Spec.

Architecture

Aqueduct is a single CLI that runs on the Spark driver — no servers, no daemons. Logic flows through four immutable layers:

Figure 3: Architecture
  • Parser validates YAML into an immutable AST.
  • Compiler resolves context, expands Arcades and macros, extracts column lineage, and assembles a fully-resolved Manifest.
  • Executor runs the Manifest on Spark; all PySpark code is isolated under executor/spark/.
  • Surveyor records runs, failures, and lineage to pluggable stores and triggers the Agent on failure.

Getting Started

Installation

pip install aqueduct-core[spark]

Requirements: Python 3.11+ · Java 17 for the spark extra (JAVA_HOME must point to it). Every release is CI-tested against three pinned combos — LTS (Python 3.11 · Spark 4.1), Latest (Python 3.13 · Spark 4.1), Legacy (Python 3.12 · Spark 3.5). Live results in the Compatibility Matrix.

Compose extras as needed — pip install aqueduct-core[spark,airflow,aws]:

Extra Adds Install when
spark PySpark 4 + Delta Lake Running pipelines on this host.
airflow Apache Airflow operator shim Scheduler / worker host; the box submitting jobs to Spark.
secrets AWS + GCP + Azure secret-manager SDKs (or pick aws / gcp / azure individually) Resolving @aq.secret('KEY') against a cloud vault.
stores Postgres + Redis backends (or pick postgres / redis individually) Replacing single-writer DuckDB defaults for obs / lineage / depot.
llm json-repair — last-ditch recovery of malformed LLM patch JSON Healing with small local models that emit imperfect JSON.
all Everything above Single-laptop dev.

A first blueprint

aqueduct: "1.0"
id: hello.pipeline
name: Hello Pipeline

macros:
  active: "status = 'active' AND deleted_at IS NULL"

modules:
  - id: load
    type: Ingress
    label: Load orders
    config: { format: csv, path: "data/in.csv", options: { header: true } }

  - id: clean
    type: Channel
    label: Filter active
    config:
      op: sql
      query: "SELECT order_id, amount FROM load WHERE {{ macros.active }}"

  - id: save
    type: Egress
    label: Write parquet
    config: { format: parquet, path: "data/out/", mode: overwrite }

edges:
  - { from: load,  to: clean }
  - { from: clean, to: save }

agent:
  approval: human

Engine-wide defaults live in a separate aqueduct.yml (LLM provider, store backends, danger settings). Inline module tests live in *.aqtest.yml. Repeatable healing benchmarks live in *.aqscenario.yml. The Gallery has runnable examples of each.

Five commands to know

  1. aqueduct doctor blueprints/hello.yml — preflight check. Validates YAML, resolves paths, verifies LLM reachability, opens stores.
  2. aqueduct run blueprints/hello.yml — execute the pipeline. On failure, the agent generates a patch under patches/pending/.
  3. aqueduct patch apply patches/pending/<id>.json --blueprint blueprints/hello.yml — review and accept a staged patch. Moves it to patches/applied/.
  4. aqueduct test blueprints/hello.aqtest.yml — run Channel / Junction / Funnel modules against inline data. No Ingress, no Egress, no external I/O.
  5. aqueduct benchmark gallery/aqscenarios/ --model claude-sonnet-4-6 --model qwen2.5-coder:7b — compare LLM models against simulated failures. No Spark required.

Full reference in CLI Reference.

How It Compares

Aqueduct dbt Dagster / Airflow Raw PySpark
Pipeline definition Declarative YAML Blueprints SQL models Python DAG code Imperative code
Engine Apache Spark Warehouse SQL Orchestration only Apache Spark
On failure Autonomous LLM patch + gates Manual fix Retry / alert Manual fix
Column lineage Built-in, compile-time Built-in Plugin DIY
Built-in observability store Yes (DuckDB/Postgres) Partial External DIY

Aqueduct sits where a Spark transformation engine and an autonomous repair loop meet — it is not a scheduler (pair it with Airflow via the airflow extra) and not a warehouse SQL tool.

References

Contributing

Contributions are welcome! See CONTRIBUTING.md.

Aqueduct is Apache 2.0 licensed — free, open source, no telemetry, no lock-in.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aqueduct_core-1.3.2.tar.gz (407.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aqueduct_core-1.3.2-py3-none-any.whl (426.0 kB view details)

Uploaded Python 3

File details

Details for the file aqueduct_core-1.3.2.tar.gz.

File metadata

  • Download URL: aqueduct_core-1.3.2.tar.gz
  • Upload date:
  • Size: 407.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aqueduct_core-1.3.2.tar.gz
Algorithm Hash digest
SHA256 782ba9bffffd56750c2b7974956d27a94fa5cc398b017892d567f9dcb3f3359c
MD5 dc980e8078bafa8c1d8578f0c6e05eff
BLAKE2b-256 b76bbd067f83128d8d0925aeed65d43d7c2f466526e71c472f75344505527777

See more details on using hashes here.

Provenance

The following attestation bundles were made for aqueduct_core-1.3.2.tar.gz:

Publisher: release.yml on sadigaxund/Aqueduct

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aqueduct_core-1.3.2-py3-none-any.whl.

File metadata

  • Download URL: aqueduct_core-1.3.2-py3-none-any.whl
  • Upload date:
  • Size: 426.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aqueduct_core-1.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6f5b56bb59a45e1eae5da6f63e523fce45bdc2e0e9cd5b97b992ade3a781d74a
MD5 1ccd9eb48d778e8b17d49f2108d8fa7b
BLAKE2b-256 181d900cb9c954619cf8da0dae6086bb37afc9c9ac578d87d229dd64ee87d155

See more details on using hashes here.

Provenance

The following attestation bundles were made for aqueduct_core-1.3.2-py3-none-any.whl:

Publisher: release.yml on sadigaxund/Aqueduct

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page