sidequery/sidemantic/modeler

Claude Code and agent skill by sidequery/sidemantic

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Build, validate, and manage semantic models using Sidemantic. Use when asked to create a semantic layer, define metrics/dimensions, model a database schema, generate models from SQL queries, import from Cube/dbt/LookML, or set up analytics definitions. Prioritizes CLI-first workflows, with YAML and optional Python API usage for advanced automation.

Install

npx vskill@latest install sidequery/sidemantic/modeler

SKILL.md

Sidemantic Modeler

Build semantic layers that map physical database tables to business-friendly dimensions and metrics. Sidemantic generates SQL from these definitions, handling joins, aggregations, granularity, and dialect differences automatically.

Quick Start

2-Minute First Success (CLI-first onboarding path)
bash
uv add sidemantic duckdb
mkdir -p models
cat > models/orders.yml <<'YAML'
models:
- name: orders
table: orders
primary_key: order_id
dimensions:
- name: status
type: categorical
metrics:
- name: revenue
agg: sum
sql: order_amount
- name: order_count
agg: count
YAML
uv run sidemantic validate models/ --verbose
uv run sidemantic info models/
uv run sidemantic query \
"SELECT revenue, status FROM orders ORDER BY revenue DESC LIMIT 5" \
--models models/ --connection duckdb:///data.duckdb --format json

Assumes an orders table already exists in data.duckdb with status and order_amount columns. Use --format json or --format jsonl when consuming CLI results in automation; use --plain for stable tab-separated text. Primary data is stdout and status is stderr, so do not merge the streams before parsing.

YAML (preferred for file-based models)
yaml
models:
- name: orders
table: orders
primary_key: order_id
dimensions:
- name: status
type: categorical
- name: order_date
type: time
sql: created_at
granularity: day
metrics:
- name: revenue
agg: sum
sql: order_amount
- name: order_count
agg: count

Load and query:

python
from sidemantic import SemanticLayer
layer = SemanticLayer.from_yaml("models.yml", connection="duckdb:///data.duckdb")
result = layer.sql("SELECT revenue, status FROM orders")
Python API (advanced, optional)
python
from sidemantic import Model, Dimension, Metric, SemanticLayer
layer = SemanticLayer(connection="duckdb:///data.duckdb")
Model(
name="orders",
table="orders",
primary_key="order_id",
dimensions=[
Dimension(name="status", type="categorical"),
Dimension(name="order_date", type="time", sql="created_at", granularity="day"),
],
metrics=[
Metric(name="revenue", agg="sum", sql="order_amount"),
Metric(name="order_count", agg="count"),
],
)
result = layer.sql("SELECT revenue, status FROM orders")

Generate Models from SQL Queries

The fastest path when existing queries are available. The Migrator reverse-engineers semantic models by analyzing SQL: it extracts tables, columns, aggregations, joins, time dimensions, derived metrics, and window functions automatically.

CLI (bootstrap from a folder of .sql files)
bash
# Generate model YAML + rewritten queries from raw SQL
sidemantic migrate generate queries/ --output output/
# Check coverage: how well do existing models handle these queries?
sidemantic migrate check queries/ --models models/ --verbose
Python API (advanced/automation only)
python
from sidemantic import SemanticLayer
from sidemantic.core.migrator import Migrator
# Connect to your database (optional but improves inference via information_schema)
layer = SemanticLayer(connection="duckdb:///data.duckdb", auto_register=False)
migrator = Migrator(layer, connection=layer.conn)
# Feed it SQL queries (strings, not files)
queries = [
"SELECT status, SUM(amount) AS revenue, COUNT(*) AS orders FROM orders GROUP BY status",
"SELECT DATE_TRUNC('month', created_at), SUM(amount) FROM orders GROUP BY 1",
"SELECT c.region, SUM(o.amount) / COUNT(DISTINCT c.id) AS rev_per_customer "
"FROM orders o JOIN customers c ON o.customer_id = c.id GROUP BY 1",
]
report = migrator.analyze_queries(queries)
models = migrator.generate_models(report) # YAML-ready model dicts
graph_metrics = migrator.generate_graph_metrics(report, models) # cross-model metrics
rewritten = migrator.generate_rewritten_queries(report) # semantic SQL
# Write to disk
migrator.write_model_files(models, "output/models/")
migrator.write_rewritten_queries(rewritten, "output/rewritten_queries/")
# Print coverage report
migrator.print_report(report, verbose=True)
What the Migrator auto-detects
Pattern in SQLWhat it generates
SUM(amount) / COUNT(*) / AVG(price)Metric with matching agg
COUNT(DISTINCT user_id)Metric with agg: count_distinct
SUM(amount) AS revenueMetric named revenue (preserves aliases)
GROUP BY statusDimension type: categorical
DATE_TRUNC('month', created_at)Dimension type: time, granularity extracted from SQL (here: month)
JOIN customers ON o.customer_id = c.idRelationship many_to_one, foreign_key: customer_id
SUM(a) / NULLIF(COUNT(b), 0)Derived metric with formula
SUM(x) OVER (ORDER BY date ROWS ...)Cumulative metric with window
SUM(x) OVER (PARTITION BY DATE_TRUNC(...))Cumulative metric with grain_to_date
Cross-model expressionsGraph-level derived metrics
Workflow: queries first, then refine
  1. Collect existing SQL queries (dashboards, reports, ad-hoc analyses)
  2. Run migrator.analyze_queries(queries) to generate a first pass
  3. Review generated models: rename metrics, add descriptions, fix types
  4. Run coverage check to verify queries can be rewritten through the semantic layer
  5. Iterate until coverage is high

For the full Migrator API (all methods, outputs, edge cases), load references/generation.md.

Core Workflow

Follow these steps when building a semantic model from a database schema.

Step 1: Analyze the database schema

Inspect tables, columns, data types, and foreign key relationships. Identify which tables hold transactional/event data (fact tables) and which hold descriptive attributes (dimension tables).

Step 2: Create Model definitions

For each table, create a Model with:

  • name: a short, snake_case identifier
  • table: schema-qualified table name (e.g., public.orders)
  • primary_key: the table's primary key column (default: id)

Use sql instead of table for derived/virtual tables built from a SQL expression.

Step 3: Define Dimensions

Add dimensions for columns used in GROUP BY or WHERE clauses. Choose the correct type:

TypeWhen to useExample
categoricalStrings, enums, IDs for groupingstatus, region
timeDates/timestamps (enables granularity)created_at, order_date
booleanComputed true/false from SQL expressionsql: "amount > 100"
numericNumbers used for grouping, not aggregationquantity_bucket

Time dimensions require granularity (one of: second, minute, hour, day, week, month, quarter, year). Queries use double-underscore syntax: orders.order_date__month.

Use sql when the dimension maps to a different column name or a computed expression. If omitted, defaults to a column matching name.

Set parent on dimensions to create drill-down hierarchies (e.g., country > state > city).

Step 4: Define Metrics

Add metrics for columns that should be aggregated.

Simple aggregations (model-level):

aggSQL generatedNotes
sumSUM(col)Revenue, quantities
countCOUNT(*)Row counts (no sql needed)
count_distinctCOUNT(DISTINCT col)Unique values
avgAVG(col)Averages
min / maxMIN(col) / MAX(col)Extremes
medianMEDIAN(col)Median

Model-level simple metrics currently validate against: sum, count, count_distinct, avg, min, max, median.

Use filters on a metric to create filtered aggregations (e.g., filters: ["status = 'completed'"]). These become CASE WHEN expressions, not WHERE clauses.

Complex metrics (usually graph-level, in top-level metrics: section):

typePurposeRequired fields
ratioDivision of two measuresnumerator, denominator
derivedArbitrary SQL formulasql (references other metrics)
cumulativeRolling/running totalssql, optional window or grain_to_date
time_comparisonPeriod-over-periodbase_metric, comparison_type (yoy/mom/wow/dod/qoq)
conversionFunnel analysisentity, base_event, conversion_event

Graph-level metrics sit in the top-level metrics: section (outside models:). They reference model-level measures using model.metric syntax.

Step 5: Define Relationships

Connect models with relationships so Sidemantic can auto-generate JOINs.

TypeDirectionExample
many_to_oneThis model has FK to otherorders -> customers
one_to_oneUnique FKuser -> user_profile
one_to_manyOther model has FK to thiscustomer -> orders
many_to_manyThrough junction tablestudents <-> courses

Declare relationships on the model that owns the foreign key. For many_to_one, foreign_key defaults to {related_model}_id.

For many_to_many, specify through (junction model), through_foreign_key, and related_foreign_key.

Step 6: Validate and inspect
bash
# Validate definitions (checks for errors and warnings)
sidemantic validate models/ --verbose
# Quick summary of what's defined
sidemantic info models/
Step 7: Test with queries
bash
# Validate and inspect without writing code
uv run sidemantic validate models/ --verbose
uv run sidemantic info models/
# Execute semantic SQL through CLI
uv run sidemantic query \
"SELECT revenue, status FROM orders WHERE status = 'completed'" \
--models models/ --connection duckdb:///data.duckdb

Python API (optional):

python
# Structured query API
result = layer.query(
metrics=["orders.revenue"],
dimensions=["orders.status", "orders.order_date__month"],
filters=["orders.status = 'completed'"],
order_by=["orders.revenue DESC"],
limit=10,
)
# SQL interface (auto-rewrites through semantic layer)
result = layer.sql("SELECT revenue, status FROM orders WHERE status = 'completed'")
# Compile to SQL without executing
sql = layer.compile(metrics=["orders.revenue"], dimensions=["customers.region"])

Segments

Reusable named WHERE filters applied at query time. Unlike metric filters, segments affect all metrics in the query.

yaml
models:
- name: orders
table: orders
segments:
- name: completed_orders
sql: "status = 'completed'"
- name: us_only
sql: "{model}.region = 'US'"

Segments are model-scoped and used as model.segment references at query time:

bash
uv run sidemantic query \
"SELECT revenue, status FROM orders WHERE completed_orders" \
--models models/ --connection duckdb:///data.duckdb

Python API (optional):

python
result = layer.query(
metrics=["orders.revenue"],
dimensions=["orders.status"],
segments=["orders.completed_orders"],
)

Loading from Other Formats

CLI-first:

bash
uv run sidemantic info path/to/models/
uv run sidemantic validate path/to/models/ --verbose

Python API (optional):

python
from sidemantic import SemanticLayer, load_from_directory
layer = SemanticLayer(connection="duckdb:///data.duckdb")
load_from_directory(layer, "path/to/models/")

Auto-detects: Cube (.yml with cubes:), dbt MetricFlow (.yml with semantic_models:), LookML (.lkml), Malloy (.malloy), Rill, Hex, Snowflake Cortex, and more.

For detailed field mappings from each format, load references/migration.md.

Auto-Registration

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/sidequery/sidemantic/modeler/badge)](https://verified-skill.com/skills/sidequery/sidemantic/modeler)

Something wrong with this skill or its page? Tell us.