How this demo works
Architecture of the demo — a dlt pipeline, a hand-written Boring Semantic Layer model, and dashdown as the UI layer, with Mistral narrating the data.
Three tools, one clean seam between them: dlt loads
the data and builds the star schema, the
Boring Semantic Layer
defines the metrics exactly once, and dashdown
turns that semantic model into the dashboard you're reading — with
mistral-medium-latest adding the inline commentary. (The setup follows
dltHub's Autofilling the Boring Semantic Layer
pipeline, with a UI layer where the blog has APIs and chatbots.)
flowchart LR
A["Sakila OLTP\n(SQLite seed)"] -->|"dlt: load + schema,\nFKs, PII hints"| B["DuckDB warehouse\n+ star schema"]
C["semantic/sakila.yml\n(BSL, written by hand)"] --> E["dashdown"]
B --> E
D["mistral-medium-latest\n(Ask / explain)"] --> E
E -->|"dashdown build"| F["Static site\n(Cloudflare Pages)"]
1 · dlt loads the data — and shapes the warehouse #
python -m pipeline.run reads the classic Sakila movie-rental database with
dlt's sql_database source. Because resolve_foreign_keys=True, dlt records
the foreign-key graph alongside column types, and column hints mark PII:
customer = table("customer", "customer_id") # sql_table(..., resolve_foreign_keys=True)
customer.apply_hints(columns={
"first_name": {"x-annotation-pii": True},
"email": {"x-annotation-pii": True},
})
An Ibis transform step (pipeline/transform.py) then reshapes the snowflaked
OLTP tables into the star schema we modeled — fact_rental joined one hop
from dim_film, dim_customer and dim_store — dropping every PII-hinted
column on the way. This warehouse never contains a name or an email
address.
2 · The semantic model — defined once, in YAML #
semantic/sakila.yml
is the contract between the warehouse and every widget: four BSL models
declaring the measures, dimensions and joins over the star schema. Plain,
reviewable YAML:
rentals:
connector: warehouse
table: fact_rental
dimensions:
rented_at:
expr: _.rented_at
is_time_dimension: true
measures:
revenue:
expr: _.amount.sum()
metadata: {format: currency, currency: "$"}
joins:
films: {model: films, type: one, left_on: film_id, right_on: film_id}
Because a semantic model is executable, it's also checkable:
python -m pipeline.validate builds every model against the live warehouse
and runs each measure and dimension once, so a typo'd column, an
unresolvable join, or a type slip (_.active == 1 on a text column instead
of _.active == '1') fails loudly before the dashboard ever renders. CI runs
it on every build.
3 · dashdown renders it #
dashdown's semantic backend is BSL — the YAML above is loaded natively, and a page references it with no SQL:
<BarChart metric={rentals.revenue} by={rentals.category}
title="Revenue by film category" />
Behind that one tag, BSL joins fact_rental to dim_film, handles the
fan-out, compiles to DuckDB SQL, and pushes the aggregation down to the
warehouse. Filters become semantic filters automatically, and the definition
of revenue lives in exactly one place. The proof:
4 · Mistral narrates it #
This is what MISTRAL_API_KEY is for: dashdown's <Ask /> component and the
✨ explain button on charts send a query's (capped) result to
mistral-medium-latest and render the answer inline — the ✦ commentary
you've seen on every page. It binds to semantic metrics with the same grammar
the charts use:
<Ask metric={rentals.revenue} by={rentals.category}
ask="Which film categories drive revenue?" />
Answers are cached, and dashdown build bakes them into the static
export — the published site needs no server and no API key. Without the key
everything still builds; the commentary cards simply say so.
Run it yourself #
git clone https://github.com/DirendAI/dashdown-bsl-dlt && cd dashdown-bsl-dlt
make setup # uv sync
make pipeline # load Sakila → DuckDB star schema
make serve # live dashboard on :8000
export MISTRAL_API_KEY=… # optional: AI commentary via mistral-medium-latest
make check # validate the semantic model + every page
make build # static site → dist/ (what CI deploys)