AI Blocks and Your Data
The ai blocks call a model provider you choose, with your key. This page says exactly what leaves your pipeline when they do, how to limit it, and where the record of it lives.
The shape of every ai block🔗
An ai block writes an artifact -- a query, a chart spec, a report -- and a separate block runs it:
your tables ──► ai.sql ──► query.sql ──► duckdb.query_file ──► result
│ ▲
└──────────────────────────────────────────┘The executor (duckdb.query_file, viz.vegalite, viz.report_html) has no network access. Only the ai block talks to the provider, and it reaches your data only through a fixed set of read-only tools -- it never runs code the model wrote, and the queries it tries while drafting run on a connection that can read the connected tables and nothing else.
What is sent🔗
Always:
- your description;
- the schema of every input: table and column names, types, row counts.
Then, depending on the block's share argument:
share | Also sent | Tools the model gets |
|---|---|---|
schema (default) | nothing | describe a table; try a query (it learns only whether it ran, and the result's columns and row count) |
summary | per-column min, max, mean, null and distinct counts, the most frequent values; chart previews | column statistics, top values, rendered chart previews |
samples | up to 20 rows per request | sample rows; query results up to 20 rows |
Three things the levels do not change, stated so they are not assumed:
- Column names are data. They are always sent. If the column names themselves are sensitive, an
aiblock cannot be used on that table. summaryreveals values. A minimum, a maximum and the most common values are values. Data that must not leave -- precise plot coordinates, say -- needsschema.- A chart preview is a picture of your data. It is sent only at
summaryand above, and only to providers that accept images in tool results (Anthropic, at present).
The provenance record🔗
Every ai block writes a provenance output alongside its artifact:
{
"block": "ai.sql",
"provider": "anthropic",
"model": "claude-opus-5-5",
"endpoint_host": "api.anthropic.com",
"share": "schema",
"turns": [
{ "tool": "describe_table", "args": { "name": "t0" }, "sent": { "rows": 812, "columns": [...] } },
{ "tool": "submit_query", "args": { "sql": "SELECT ..." }, "sent": { "accepted": true, "rows": 40 } }
],
"usage": { "input_tokens": 9123, "output_tokens": 1402 },
"served_models": ["claude-opus-5-5"]
}sent is exactly what was returned to the model -- which is exactly what left the sandbox. An organisation auditing what a run disclosed reads this file. The endpoint is recorded by host only, and your key is never recorded anywhere. If a block fails, the same record is in its log.
Your key🔗
The key is a secret you store once and bind to the block's llm name. It reaches the block the way every secret does -- injected into the sandbox for that run, never written to disk, never in the pipeline file. The Spade platform holds no model key of its own.
Instructions hidden in data🔗
At summary and above, values from your data reach the model, and a cell can contain text that reads like an instruction. Spade does not try to detect that. It limits what it could achieve: every tool is read-only, the model's only output is an artifact that runs offline and stays readable, and nothing the model writes runs with network access or your key. The worst an injected instruction can do is make the artifact wrong -- the same failure a careless description produces, and the reason the artifact is something you can read.
Tracing🔗
LangChain, which the blocks use to talk to providers, can send traces to LangSmith when certain environment variables are set. The ai blocks switch that off in code whatever your environment says, so tracing you use for your own LangChain work never sends Spade pipeline data anywhere.