Model Lineage¶
Interactive, dbt-Explorer-style view over the dbt model DAG. The backend hydrates a per-view dataset (nodes / edges / column edges) from the manifest lineage graph and the semantic registry; a React Flow front end renders it with click-to-expand, a layer toggle (physical model DAG vs. semantic join graph), and a column-lineage drawer.
- Resource URI:
ui://cerebro/model_lineage - Entry tool:
open_model_lineage(seed_model?, direction="both", depth=1, layer="model") - Backed by:
src/cerebro_mcp/tools/analytics/model_lineage_app.py+src/cerebro_mcp/loaders/column_lineage.py
When to use it¶
Use Model Lineage when you want to see how models relate rather than list them:
- "What feeds
fct_execution_transactions_daily, and what breaks if I change it?" - "Walk the DAG outward from this staging model, one hop at a time."
- "Which upstream column does
volume_usdactually come from?" - "Show me the approved semantic join relationships around this mart."
For a non-visual answer, the plain lineage tools are cheaper: get_upstream_lineage / get_downstream_impact for transitive sets, get_model_subgraph for a bounded JSON subgraph, and get_column_lineage for column edges without the UI.
Entry points¶
| Tool | What it does |
|---|---|
open_model_lineage(seed_model?, direction, depth, layer, title?) | Opens the panel. With a seed_model, loads the bounded lineage subgraph around it; without one, opens a searchable catalog of every model as a start screen. layer="model" renders the physical dbt DAG; layer="semantic" renders approved semantic-registry join relationships. |
expand_model_lineage_node(view_id, node_id, direction, depth) | Expands the graph one hop around node_id and merges the result in (deduplicated). Source nodes (source.*) are leaf inputs and are focused, not expanded. |
set_model_lineage_filters(view_id, direction?, depth?, include_kinds?, tags?, layer?) | Re-runs the subgraph from the current seed with new filters or a different layer. |
load_column_lineage(view_id, model_name, column, direction="upstream", depth=1) | Computes column-level lineage and loads it into the column drawer. |
Depth is capped at 5 per request; expansion merges into the existing view, so you can grow the graph incrementally.
Column-level lineage¶
dbt's manifest.json has model-to-model edges but no column-to-column edges. Cerebro derives them on demand by parsing each model's SQL with sqlglot (ClickHouse dialect) and resolving which source columns a target column depends on. Manifests that only carry Jinja raw_sql are best-effort rendered (ref/source/this rewritten to physical relations). Heavy macro-generated SQL can defeat the parser — in that case the drawer degrades gracefully to a model-level edge plus a warning instead of failing.
Typical flow¶
> Where does api_bridges_flows_daily get its data?
agent calls open_model_lineage(seed_model="api_bridges_flows_daily", direction="upstream", depth=2)
→ panel renders the upstream DAG
> Expand around the int_bridges_flows model
agent calls expand_model_lineage_node(view_id="…", node_id="model.gnosis_dbt.int_bridges_flows_daily")
> Trace the amount_usd column
agent calls load_column_lineage(view_id="…", model_name="api_bridges_flows_daily", column="amount_usd")
→ column drawer shows the source columns per level
Limits¶
- Depth cap: 5 hops per open/expand call.
- Semantic layer needs the registry.
layer="semantic"requires a loaded semantic registry snapshot; otherwise the view warns and stays empty. - Column lineage is per-column, not precomputed. Each drawer load parses SQL on demand; expect a warning (not a failure) on macro-heavy models.
See also¶
- Data Catalog — search-first entity profiles, including a lineage tab backed by the same subgraph
- Graph Explorer — cross-sector data graph (rows, not models)
- Hybrid Search — how model discovery ranks candidates before you visualise them