ai

The ai collection turns a plain-language description into an artifact you can read -- a SQL query, a chart specification, a report -- fitted to your data as it actually is when the pipeline runs. Its main use is work whose shape is only known at run time: the columns base.pivot produces, or a different table in each item of a map.

BlockWritesRun by
ai.sqla DuckDB queryduckdb.query_file
ai.viza Vega-Lite chart specviz.vegalite
ai.reportreport source: Markdown, charts, named statisticsviz.report_html

Agents write; they never run anything🔗

An ai block's output is the artifact, not its result. A separate block with no network runs it. So:

  • You can read what the model did. The query is an output of the run, like any file.
  • The model's work never touches your data with network access. The agent inspects the data only through read-only tools, and what runs on the data is the executor, offline.
  • You can keep it. Save the query, upload it, and replace ai.sql with platform.upload_file -- the executor runs it the same way. That is how you pin a generated query for good.

No ai block runs code the model wrote. Agents act only through fixed tools, and each artifact is checked by the executor's own rules before the block accepts it: a query must run against your tables, a chart must render, a report must reference only statistics and figures that exist.

Your model, your key🔗

The blocks call a model you choose -- Anthropic, OpenAI, or any OpenAI-compatible endpoint (a gateway, vLLM, a local Ollama) -- with your key, bound to the block's llm secret:

- id: "@write_query"
  name: ai.sql
  inputs: [{ block: "@pivot", output: result, as: tables }]
  args:
    description: Total biomass per plot across every species column.
    provider: anthropic
    model: claude-opus-5-5
  secrets:
    llm: my-anthropic-key

The platform holds no model key and pays for no tokens: usage is billed to your key, and the run page shows the total. Each block also has a turn budget and a token budget (max_turns, max_tokens), and a block inside a map runs once per item -- size the budgets accordingly.

model has no default: choose it, and it is recorded in the pipeline. A local server works for spade run on that machine; it is not reachable from a cloud worker.

What the model sees🔗

Column names and types are always sent. Beyond that, the share argument decides, and it defaults to the least:

shareAdds
schema (default)nothing
summaryper-column statistics -- min, max, mean, the most frequent values -- and chart previews
samplesup to 20 rows per question

A filter on a value the model has never seen -- a coded forest type, a state abbreviation -- may need summary. Data that must not leave at all needs schema, and even then column names are sent. See AI blocks and your data for exactly what is sent, and how to audit it.

Every tool result sent to the model is recorded in the block's provenance output, together with the model and endpoint used and the tokens spent.

Reruns and caching🔗

The same description on the same data can produce a different artifact on another try. Locally, a rerun with nothing changed replays the first artifact from the cache, which is usually what you want while working downstream. To ask for a fresh one with nothing else changed, change the block's nonce argument to anything new.

Installation🔗

spade install file:///path/to/blocks/ai