### Setup the development environment Source: https://motley-slayer.readthedocs.io/en/latest/development Clone the repository and install all dependencies using Poetry. ```bash git clone https://github.com/motleyai/slayer.git cd slayer poetry install -E all ``` -------------------------------- ### Install and start SLayer Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Install the tool using uv and start the server. ```bash uv tool install motley-slayer slayer serve ``` -------------------------------- ### Setup Diamond Join Example Source: https://motley-slayer.readthedocs.io/en/latest/examples/05_joins/joins_nb Initializes a diamond join schema and inspects the join paths for orders, customers, and warehouses models. ```python from joins_utils import setup_diamond_example diamond_engine, diamond_storage, diamond_models, diamond_db_path, diamond_work_dir = setup_diamond_example() diamond_orders = next(m for m in diamond_models if m.name == "orders") diamond_customers = next(m for m in diamond_models if m.name == "customers") diamond_warehouses = next(m for m in diamond_models if m.name == "warehouses") print("orders joins (direct FKs only):") for j in diamond_orders.joins: src, tgt = j.join_pairs[0] print(f" -> {j.target_model:<12} ON {src:<25} = {tgt}") print("\ncustomers joins:") for j in diamond_customers.joins: src, tgt = j.join_pairs[0] print(f" -> {j.target_model:<12} ON {src:<25} = {tgt}") print("\nwarehouses joins:") for j in diamond_warehouses.joins: src, tgt = j.join_pairs[0] print(f" -> {j.target_model:<12} ON {src:<25} = {tgt}") print("\nDiamond: orders reaches regions via customers.joins AND warehouses.joins") print("Each path gets a unique alias: customers__regions vs warehouses__regions") ``` -------------------------------- ### Start Demo Server Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/cli Launch the API or MCP server with the demo dataset preloaded. ```bash slayer serve --demo # API server on :5143 with the demo ready to query slayer mcp --demo # MCP server with the demo ready for your agent ``` -------------------------------- ### Save a memory with an example query Source: https://motley-slayer.readthedocs.io/en/latest/examples/09_lightning_talk/lightning_talk_nb Include an example query within the memory to provide context for specific entity configurations. ```python # You can save an example query with annotation, too, and it will be linked to the entities it contains top_customers = run_sync(client.save_memory( learning=( 'Top-5 customers by lifetime spend - known-good query pattern ' 'used in the weekly CRM-ops dashboard.' ), linked_entities={ 'source_model': 'orders', 'measures': [{'formula': 'order_total:sum', 'name': 'lifetime_spend'}], 'dimensions': ['customers.name'], 'order': [{'column': 'lifetime_spend', 'direction': 'desc'}], 'limit': 5, }, id='lightning.top_customers', )) print(f'Saved query-bearing memory id={top_customers.memory_id}') print(f'Auto-extracted entities: {top_customers.resolved_entities}') ``` -------------------------------- ### Install motley-slayer CLI Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/cli Install the tool using uv. Add driver extras for non-SQLite databases. ```bash uv tool install motley-slayer ``` ```bash uv tool install 'motley-slayer[postgres]' ``` -------------------------------- ### Install Motley Slayer Source: https://motley-slayer.readthedocs.io/en/latest/interfaces/pg-facade Install the package using pip. ```bash pip install motley-slayer ``` -------------------------------- ### Install SLayer SDK Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/python Commands to install the base package or specific extras like PostgreSQL support and client utilities. ```bash pip install motley-slayer # Base (SQLite works out of the box) pip install motley-slayer[all] # Everything ``` ```bash pip install motley-slayer[postgres] # PostgreSQL driver pip install motley-slayer[client] # httpx + pandas for remote mode ``` -------------------------------- ### Install with database driver Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Install SLayer with specific database driver support. ```bash uv tool install 'motley-slayer[postgres]' ``` -------------------------------- ### Start server with auto-ingestion Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Run the server with auto-ingestion enabled to process datasources on startup. ```bash slayer serve --ingest-on-startup ``` -------------------------------- ### Start SLayer and Metabase container Source: https://motley-slayer.readthedocs.io/en/latest/interfaces/pg-facade Commands to launch the SLayer PostgreSQL facade and a Metabase container instance. ```bash # 1. Start SLayer speaking Postgres, with the Jaffle Shop demo preloaded. # The BI tool connects over the network (e.g. from a Docker container), so # bind all interfaces — a non-loopback bind requires a token. slayer pg-serve --demo --host 0.0.0.0 --token pick-a-secret # 2. Run Metabase (any BI tool works — Superset, Tableau, Power BI, Grafana, …). # --add-host makes `host.docker.internal` resolve to the Docker host on # every platform (built into Docker Desktop; required on Linux, Docker ≥ 20.10). # The volume keeps Metabase's own settings/dashboards across container re-creates. docker run -d -p 3000:3000 --name metabase \ --add-host=host.docker.internal:host-gateway \ -e MB_DB_FILE=/metabase.data/metabase.db \ -v metabase-data:/metabase.data \ metabase/metabase ``` -------------------------------- ### Install dbt Extra Source: https://motley-slayer.readthedocs.io/en/latest/dbt/dbt_import Commands to install the required dbt extra for SLayer. ```bash pip install 'motley-slayer[dbt]' # or, with Poetry: poetry install -E dbt ``` -------------------------------- ### Install Slayer Python Client Source: https://motley-slayer.readthedocs.io/en/latest/reference/python-client Install the client package including dependencies for HTTP and data frame support. ```bash pip install motley-slayer[client] # httpx + pandas ``` -------------------------------- ### Jinja Filter Syntax Examples Source: https://motley-slayer.readthedocs.io/en/latest/dbt/dbt_import Examples of dbt Jinja templates used for filter references. ```jinja {{ Dimension('claim_amount__has_loss_payment') }} = 1 {{ TimeDimension('metric_time', 'day') }} >= '2024-01-01' {{ Entity('customer_id') }} IS NOT NULL ``` -------------------------------- ### YAMLStorage Directory Structure Source: https://motley-slayer.readthedocs.io/en/latest/configuration/storage Example of the expected file layout for the YAML storage backend. ```text slayer_data/ models/ my_postgres/ orders.yaml customers.yaml other_db/ orders.yaml # same name, different datasource — coexists datasources/ my_postgres.yaml other_db.yaml priority.yaml # datasource priority list (optional) memories.yaml # agent memories (optional) embeddings.db # SQLite sidecar for embedding rows (DEV-1405) ``` -------------------------------- ### Start the Slayer server Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/cli Launches the REST API or MCP endpoint with optional storage and ingestion configurations. ```bash slayer serve # REST API at http://localhost:5143 slayer serve --storage slayer.db # Using SQLite storage slayer serve --ingest-on-startup # Run idempotent ingest over every configured datasource before opening the port — pairs well with YAML-drop datasource configs ``` -------------------------------- ### Start the SLayer Postgres server Source: https://motley-slayer.readthedocs.io/en/latest/interfaces/pg-facade Commands to launch the Postgres server in local development or production modes. ```bash # Local dev — loopback, no auth needed slayer pg-serve --demo # Production-ish — non-loopback bind requires a password token slayer pg-serve --host 0.0.0.0 --token "$(pass slayer-token)" ``` -------------------------------- ### Install and configure Motley Slayer MCP Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/mcp Use these commands to perform a permanent installation of the tool and register it with the Claude MCP configuration. ```bash uv tool install 'motley-slayer[postgres,advanced_search]' claude mcp add slayer -- slayer mcp --ingest-on-startup ``` -------------------------------- ### Aggregation Windowing Example Source: https://motley-slayer.readthedocs.io/en/latest/examples/07_aggregations/aggregations Example of using sum and avg aggregations with a specified time window in a query definition. ```APIDOC ## Aggregation Windowing ### Description Aggregations like `sum` and `avg` can accept a trailing time window when a time dimension is present in the query. The window is calculated over raw source rows using units: y, m, w, d, h, min, and s. ### Request Example { "measures": [ {"formula": "revenue:sum(window='30d')", "name": "revenue_30d"}, {"formula": "revenue:avg(window='1y2m3w5d6h7min8s')", "name": "avg_window"} ], "time_dimensions": [{"dimension": "created_at", "granularity": "month"}] } ``` -------------------------------- ### Colon Syntax Examples Source: https://motley-slayer.readthedocs.io/en/latest/concepts/formulas Examples of using colon syntax to define aggregations on measures. ```text revenue:sum — SUM the "revenue" measure *:count — COUNT(*), always available, no measure definition needed revenue:avg — AVG the "revenue" measure revenue:sum(window='90d') — trailing 90-day SUM ending at each output bucket price:weighted_avg(weight=quantity) — weighted average with kwargs latency:stddev_samp — sample standard deviation latency:var_pop — population variance price:corr(other=quantity) — Pearson correlation between two columns (named `other` kwarg) price:covar_samp(other=quantity) — sample covariance (Bessel-corrected) price:covar_pop(other=quantity) — population covariance customers.score:avg — cross-model: AVG of "score" from the joined "customers" model ``` -------------------------------- ### Configure Windowed Aggregations Source: https://motley-slayer.readthedocs.io/en/latest/examples/07_aggregations/aggregations Example showing how to apply trailing time windows to sum and avg measures. ```json { "measures": [ {"formula": "revenue:sum(window='30d')", "name": "revenue_30d"}, {"formula": "revenue:avg(window='1y2m3w5d6h7min8s')", "name": "avg_window"} ], "time_dimensions": [{"dimension": "created_at", "granularity": "month"}] } ``` -------------------------------- ### Start the SLayer Server Source: https://motley-slayer.readthedocs.io/en/latest/reference/rest-api Commands to launch the server with various configurations for storage, host, port, and startup ingestion. ```bash slayer serve --storage ./slayer_data slayer serve --host 0.0.0.0 --port 8080 --storage ./slayer_data slayer serve --ingest-on-startup --storage ./slayer_data # idempotent ingest across every configured datasource before the port opens ``` -------------------------------- ### Connect to a datasource via psql Source: https://motley-slayer.readthedocs.io/en/latest/interfaces/pg-facade Example of connecting to a specific SLayer datasource using the psql client. ```bash # `dbname` picks the SLayer datasource: psql "host=127.0.0.1 port=5145 dbname=jaffle_shop" ``` -------------------------------- ### Initialize dbt MetricFlow Data and Environment Source: https://motley-slayer.readthedocs.io/en/latest/examples/11_dbt_metricflow/dbt_metricflow_nb Imports necessary setup helpers and initializes the dbt project by cloning the repository and loading CSV data into DuckDB. ```python import os import sys import pandas as pd # The setup helper lives next to this notebook. sys.path.insert(0, os.getcwd()) from setup_metricflow import ( ensure_dbt_data, convert_dbt_to_slayer, fetch_gold, DB_PATH, MODELS_DIR, DBT_PIN_SHA, ) from slayer.client.slayer_client import SlayerClient from slayer.storage.yaml_storage import YAMLStorage dbt_project = ensure_dbt_data() print(f"Cloned dbt project @ {DBT_PIN_SHA[:12]} and loaded ACME CSVs into DuckDB.") ``` -------------------------------- ### Querying Claims Count Source: https://motley-slayer.readthedocs.io/en/latest/examples/11_dbt_metricflow/dbt_metricflow Example of a basic SLayer query to count claims. ```json {"source_model": "claim", "measures": ["*:count"]} ``` -------------------------------- ### Search for a memory Source: https://motley-slayer.readthedocs.io/en/latest/concepts/search Example of querying for a specific memory by its ID. ```json { "call": {"entities": ["memory:42"], "max_results": 5}, "response": { "results": [{"id": "42", "kind": "memory", "matched_entities": ["memory:42"]}] } } ``` -------------------------------- ### Search for an entity Source: https://motley-slayer.readthedocs.io/en/latest/concepts/search Example of querying for a specific column entity using the search tool. ```json { "call": {"entities": ["mydb.orders.amount"], "max_results": 5}, "response": { "results": [{"id": "mydb.orders.amount", "kind": "column", "score": 0.0}] } } ``` -------------------------------- ### Verify SLayer installation and storage Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/python Validates the storage configuration and engine execution by listing models and running a sample query. ```python from slayer.storage.base import resolve_storage from slayer.engine.query_engine import SlayerQueryEngine storage = resolve_storage("./slayer_data") engine = SlayerQueryEngine(storage=storage) # Should list your ingested models print(storage.list_models()) # Should return data result = engine.execute_sync(query={"source_model": "orders", "measures": ["*:count"]}) print(f"{result.row_count} row(s), columns: {result.columns}") ``` -------------------------------- ### Compute Transitive Closure Source: https://motley-slayer.readthedocs.io/en/latest/examples/03_auto_ingest/auto_ingest_nb Demonstrates how to determine reachable tables from a starting point using the foreign key graph. ```python # Transitive closure: what tables are reachable from items? for table in ["orders", "items"]: reachable = _compute_transitive_closure(fk_graph, table) print(f"{table} can reach: {sorted(reachable)}") sa_engine.dispose() ``` -------------------------------- ### SlayerQuery.filters DSL mode configurations Source: https://motley-slayer.readthedocs.io/en/latest/concepts/references Examples of valid and invalid filter configurations for SlayerQuery construction and enrichment. ```json {"source_model": "orders", "filters": ["revenue:sum > 100"]} {"source_model": "orders", "filters": ["change(revenue:sum) > 0"]} {"source_model": "orders", "filters": ["customers.region == 'EU'"]} {"source_model": "orders", "filters": ["status = '{val}'"], "variables": {"val": "active"}} ``` ```json {"source_model": "orders", "filters": ["row_number() over (...)"]} // raw OVER ``` ```json {"source_model": "orders", "dimensions": ["id"], "filters": ["json_extract(data, '$.x') > 5"]} // ↑ ReferenceError: raw SQL function calls in DSL mode {"source_model": "orders", "dimensions": ["id"], "filters": ["unknown_col > 0"]} // ↑ ReferenceError: 'unknown_col' is not a Column / ModelMeasure on 'orders' {"source_model": "orders", "dimensions": ["id"], "filters": ["customers__region = 'EU'"]} // ↑ ReferenceError: 'customers__region' doesn't resolve to any virtual-model column // (use single-dot DSL: 'customers.region') ``` -------------------------------- ### Run SLayer with SSE Transport Source: https://motley-slayer.readthedocs.io/en/latest/reference/mcp Start the SLayer server manually to expose the REST API and MCP SSE endpoint. ```bash uvx --from 'motley-slayer[postgres]' slayer serve --ingest-on-startup # REST API at http://localhost:5143/ # MCP SSE at http://localhost:5143/mcp/sse ``` -------------------------------- ### Initialize Jaffle Shop demo Source: https://motley-slayer.readthedocs.io/en/latest/examples/schema-drift Sets up the required demo datasource for the tutorial. ```bash slayer datasources create demo --ingest --yes ``` -------------------------------- ### Column.filter SQL mode configurations Source: https://motley-slayer.readthedocs.io/en/latest/concepts/references Examples of valid and invalid filter strings for Column construction in SQL mode. ```json {"name": "active_amount", "sql": "amount", "filter": "json_extract(metadata, '$.active') = 1", "type": "DOUBLE"} {"name": "amt", "sql": "amount", "filter": "CASE WHEN status = 'active' THEN 1 ELSE 0 END = 1", "type": "DOUBLE"} {"name": "amt", "sql": "amount", "filter": "customers__regions.name = 'US'", "type": "DOUBLE"} ``` ```json {"name": "x", "sql": "amount", "filter": "revenue:sum > 100"} // DSL agg colon syntax {"name": "x", "sql": "amount", "filter": "cumsum(amount) > 0"} // DSL transform call {"name": "x", "sql": "amount", "filter": "row_number() over (...)"} // raw OVER ``` -------------------------------- ### Run Jaffle Shop Demo Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/cli Initialize the demo dataset and execute a sample query. ```bash slayer datasources create demo --ingest slayer query '{"source_model": "orders", "measures": ["*:count"]}' ``` -------------------------------- ### Pre-populate and Run Demo Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/cli Build the demo dataset upfront to ensure subsequent runs are instantaneous. ```bash slayer datasources create demo --ingest --yes # builds and ingests upfront slayer mcp --demo # subsequent runs reuse the DuckDB ``` -------------------------------- ### Create and Test Data Sources Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/cli Commands to initialize database connections and verify their status. ```bash # Postgres (use ${ENV_VAR} to keep secrets out of shell history) slayer datasources create postgresql://analyst:${DB_PASSWORD}@localhost/myapp --ingest # SQLite / DuckDB — name comes from the filename stem slayer datasources create sqlite:///path/to/app.db --ingest # Override the auto-derived name slayer datasources create duckdb:///tmp/data.duckdb --name warehouse --ingest ``` ```bash slayer datasources test myapp # OK — connected to 'myapp' (postgres). ``` -------------------------------- ### Initialize SLayer Environment Source: https://motley-slayer.readthedocs.io/en/latest/examples/05_joined_measures/joined_measures_nb Sets up the environment and loads the Jaffle Shop models for demonstration. ```python import os import sys sys.path.insert(0, os.path.join(os.getcwd(), "..", "..", "..")) sys.path.insert(0, os.path.join(os.getcwd(), "..", "jaffle_data")) from setup_jaffle import ensure_jaffle_shop engine, storage, models = ensure_jaffle_shop() ``` -------------------------------- ### Convert dbt entities to SLayer joins Source: https://motley-slayer.readthedocs.io/en/latest/dbt/dbt_import Example showing how foreign entities in dbt are mapped to explicit joins in SLayer. ```yaml # dbt: orders model has foreign entity customer_id # dbt: customers model has primary entity customer_id (expr: id) # SLayer output: orders model gets a join joins: - target_model: customers join_pairs: [["customer_id", "id"]] ``` -------------------------------- ### Reference Saved Measures in Queries Source: https://motley-slayer.readthedocs.io/en/latest/concepts/formulas Example of using bare names, transforms, and arithmetic operations to reference saved measures in a query. ```json "measures": [ {"formula": "aov"}, {"formula": "cumsum(aov)"}, {"formula": "aov * 1.1", "name": "aov_with_markup"} ] ``` -------------------------------- ### Configure and ingest via API Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Create a datasource and trigger ingestion using HTTP POST requests. ```bash # Create datasource curl -X POST http://localhost:5143/datasources \ -H "Content-Type: application/json" \ -d '{"name": "my_pg", "type": "postgres", "host": "localhost", "database": "myapp", "username": "analyst", "password": "secret"}' # Ingest models curl -X POST http://localhost:5143/ingest \ -H "Content-Type: application/json" \ -d '{"datasource": "my_pg"}' ``` -------------------------------- ### Query Result Column Formatting Source: https://motley-slayer.readthedocs.io/en/latest/concepts/models Examples of how query results map to model_name.column_name keys, including aggregation syntax and multi-hop joined dimensions. ```json {"orders.status": "completed", "orders._count": 42, "orders.revenue_sum": 1500} {"orders.customers.regions.name": "US", "orders._count": 3} ``` -------------------------------- ### Define a multi-stage DAG query Source: https://motley-slayer.readthedocs.io/en/latest/examples/06_multistage_queries/multistage_queries This example demonstrates a three-stage query where the second stage joins a prior aggregation result back to a base model, and the final stage computes a maximum value. ```json [ { "name": "kpis", "source_model": "orders", "dimensions": ["customer_id"], "measures": ["order_total:sum"] }, { "name": "tagged", "source_model": { "source_name": "customers", "joins": [{"target_model": "kpis", "join_pairs": [["id", "customer_id"]]}] }, "dimensions": ["name", "kpis.order_total_sum"] }, { "source_model": "tagged", "measures": ["kpis__order_total_sum:max"] } ] ``` -------------------------------- ### Initialize the Lightning Talk Environment Source: https://motley-slayer.readthedocs.io/en/latest/examples/09_lightning_talk/lightning_talk_nb Configures the system path and initializes the Jaffle Shop dataset demo environment. ```python import os, sys sys.path.insert(0, os.path.join(os.getcwd(), '..', '..', '..')) sys.path.insert(0, os.getcwd()) from setup_talk import ensure_lightning_talk_demo from slayer.async_utils import run_sync from slayer.core.errors import MemoryNotFoundError engine, storage, client, models = ensure_lightning_talk_demo() ``` -------------------------------- ### Initialize Jaffle Shop Environment Source: https://motley-slayer.readthedocs.io/en/latest/examples/03_auto_ingest/auto_ingest_nb Sets up the necessary environment, imports, and database engine for the Jaffle Shop dataset. ```python import os import sys import duckdb import sqlalchemy as sa sys.path.insert(0, os.path.join(os.getcwd(), "..", "..", "..")) sys.path.insert(0, os.path.join(os.getcwd(), "..", "jaffle_data")) from setup_jaffle import ensure_jaffle_shop, DB_PATH engine, storage, models = ensure_jaffle_shop() ``` -------------------------------- ### Cypher Filter Examples Source: https://motley-slayer.readthedocs.io/en/latest/reference/mcp Examples of Cypher queries used for filtering search results, ranging from naive label-based filtering to advanced graph traversal. ```cypher MATCH (n:Memory) RETURN n.id AS id # memory hits only MATCH (n:Column:Measure) RETURN n.id AS id # column + named-measure hits only ``` ```cypher MATCH (d:Datasource {name: 'mydb'})-[:CONTAINS]->(m:Model)-[:CONTAINS]->(c:ModelColumn) RETURN c.id AS id ``` -------------------------------- ### Initialize SLayer Client Source: https://motley-slayer.readthedocs.io/en/latest/examples/11_dbt_metricflow/dbt_metricflow_nb Configures a SLayer client using a YAML storage backend. ```python client = SlayerClient(storage=YAMLStorage(base_dir=str(MODELS_DIR))) ``` -------------------------------- ### GET /health Source: https://motley-slayer.readthedocs.io/en/latest/reference/rest-api Performs a health check on the SLayer server. ```APIDOC ## GET /health ### Description Checks the status of the SLayer server. ### Method GET ### Endpoint /health ### Response #### Success Response (200) - **status** (string) - The status of the server, typically 'ok'. ``` -------------------------------- ### List and show models and datasources Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/cli Commands to inspect available models and datasources in the system. ```bash slayer models list slayer models show orders slayer datasources list ``` -------------------------------- ### GET /models Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Lists all ingested models or retrieves details for a specific model. ```APIDOC ## GET /models ### Description Retrieves a list of all available models or details for a specific model by name. ### Method GET ### Endpoint http://localhost:5143/models/{model_name} ### Parameters #### Path Parameters - **model_name** (string) - Optional - The name of the model to retrieve details for. ``` -------------------------------- ### Naive Cypher Query Syntax Source: https://motley-slayer.readthedocs.io/en/latest/concepts/search The basic syntax supported when the advanced_search extra is not installed. ```cypher MATCH (var:Label1:Label2:...) RETURN var.id AS id ``` -------------------------------- ### Inspect API JSON response Source: https://motley-slayer.readthedocs.io/en/latest/reference/rest-api Example of the JSON-formatted response returned by the /inspect endpoint. ```json {"result": "# `orders`\nOne row per placed order.\nColumns: id, order_total, ...\nMeasures: ...\nAggregations: _(none)_\nJoins to: stores"} ``` -------------------------------- ### API response format Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Example JSON response structure returned by the query endpoint. ```json { "data": [ {"orders.status": "completed", "orders._count": 42}, {"orders.status": "pending", "orders._count": 15} ], "columns": ["orders.status", "orders._count"], "row_count": 2 } ``` -------------------------------- ### Implement custom storage backend Source: https://motley-slayer.readthedocs.io/en/latest/configuration/storage Create a custom backend by implementing the StorageBackend protocol. ```python from slayer.storage.base import StorageBackend from slayer.core.models import SlayerModel, DatasourceConfig class MyCustomStorage(StorageBackend): # Models are keyed by (data_source, name). def save_model(self, model: SlayerModel) -> None: ... def _list_all_model_identities(self) -> list[tuple[str, str]]: ... def get_model(self, name: str, data_source: str | None = None) -> SlayerModel | None: ... def delete_model(self, name: str, data_source: str | None = None) -> bool: ... # ``StorageBackend`` provides default implementations of ``list_models`` # and ``resolve_model_identity`` on top of ``_list_all_model_identities``. def save_datasource(self, datasource: DatasourceConfig) -> None: ... def get_datasource(self, name: str) -> DatasourceConfig | None: ... def list_datasources(self) -> list[str]: ... def delete_datasource(self, name: str) -> bool: ... ``` -------------------------------- ### Filter Columns in Datasource Source: https://motley-slayer.readthedocs.io/en/latest/concepts/search Example query to surface all columns within a specific datasource. ```cypher MATCH (d:Datasource {id: 'shop'})-[:CONTAINS*1..3]->(c:ModelColumn) RETURN c.id AS id ``` -------------------------------- ### Define Model-Level Filters Source: https://motley-slayer.readthedocs.io/en/latest/examples/02_sql_vs_dsl/sql_vs_dsl_nb Example of raw SQL filters applied at the model level. ```yaml filters: - "deleted_at IS NULL" - "status <> 'test'" ``` -------------------------------- ### Ingestion Status Output Source: https://motley-slayer.readthedocs.io/en/latest/concepts/ingestion Example output formats for ingestion status sent to stderr. ```text Ingest-on-startup: N/M datasources ingested ``` ```text Ingest-on-startup: N/M datasources ingested (K failed: name1, name2) ``` -------------------------------- ### CLI storage configuration Source: https://motley-slayer.readthedocs.io/en/latest/configuration/storage Specify the storage backend via the --storage flag in the CLI. ```bash slayer serve --storage ./slayer_data # YAML slayer serve --storage slayer.db # SQLite ``` -------------------------------- ### Search Response Format Source: https://motley-slayer.readthedocs.io/en/latest/reference/rest-api Example of a successful search response containing memory and entity hits. ```json { "results": [ {"kind": "memory", "id": "42", "score": 0.13, "text": "Brooklyn switched POS in late 2024 …", "matched_entities": [], "query": null}, {"kind": "column", "id": "jaffle_shop.orders.order_total", "score": 0.11, "text": "...\nSample values: [\"100.00\", \"42.50\", …]\nDistinct count: 4382", "matched_entities": [], "query": null}, {"kind": "model", "id": "jaffle_shop.stores", "score": 0.09, "text": "...", "matched_entities": [], "query": null} ], "resolved_input_entities": [], "warnings": [] } ``` -------------------------------- ### Initialize YAMLStorage Source: https://motley-slayer.readthedocs.io/en/latest/configuration/storage Programmatically initialize the YAML storage backend by specifying a base directory. ```python from slayer.storage.yaml_storage import YAMLStorage storage = YAMLStorage(base_dir="./slayer_data") ``` -------------------------------- ### Filter Memories by Model Mention Source: https://motley-slayer.readthedocs.io/en/latest/concepts/search Example query to surface only memories that mention a specific model. ```cypher MATCH (m:Memory)-[:MENTIONS]->(n:Model {id: 'shop.orders'}) RETURN m.id AS id ``` -------------------------------- ### Execute 1-hop and Multi-hop Queries Source: https://motley-slayer.readthedocs.io/en/latest/examples/03_auto_ingest/auto_ingest_nb Demonstrates querying the semantic layer for 1-hop relationships between items and products, and multi-hop relationships between items, orders, and customers. ```python # 1-hop: items -> products result = engine.execute_sync( query={ "source_model": "items", "measures": ["*:count"], "dimensions": ["products.name"], "order": [{"column": {"name": "_count"}, "direction": "desc"}], "limit": 5, } ) print("=== 1-hop: items -> products ===") for row in result.data: print( f" {row['items.products.name']}: {row['items._count']} items" ) # Multi-hop: items -> orders -> customers result = engine.execute_sync( query={ "source_model": "items", "measures": ["*:count"], "dimensions": ["orders.customers.name"], "order": [{"column": {"name": "_count"}, "direction": "desc"}], "limit": 5, } ) print("\n=== Multi-hop: items -> orders -> customers ===") for row in result.data: print( f" {row['items.orders.customers.name']}: {row['items._count']} items" ) ``` -------------------------------- ### SlayerResponse Structure Source: https://motley-slayer.readthedocs.io/en/latest/concepts/queries Example of the response object returned by queries, including data rows and metadata. ```json { "data": [ {"orders.status": "completed", "orders._count": 42}, {"orders.status": "pending", "orders._count": 15} ], "columns": ["orders.status", "orders._count"], "row_count": 2, "sql": "SELECT ..." } ``` -------------------------------- ### Initialize Local Slayer Client Source: https://motley-slayer.readthedocs.io/en/latest/reference/python-client Configure the client to operate directly against a local YAML storage backend without a server. ```python from slayer.client.slayer_client import SlayerClient from slayer.storage.yaml_storage import YAMLStorage client = SlayerClient(storage=YAMLStorage(base_dir="./slayer_data")) # Same query API as remote mode data = client.query(query) df = client.query_df(query) ``` -------------------------------- ### Define Query Dimensions Source: https://motley-slayer.readthedocs.io/en/latest/concepts/queries Example of defining dimensions using both bare strings and ColumnRef dictionaries. ```json "dimensions": [ "status", "customers.name", "customers.regions.name", {"name": "status", "label": "Order Status"} ] ``` -------------------------------- ### Scalar math functions in Column.sql Source: https://motley-slayer.readthedocs.io/en/latest/concepts/formulas Examples of using math functions within Column definitions. ```python # Examples (in Column.sql): Column(name="ln_amount", sql="ln(amount)", type=DataType.DOUBLE) Column(name="rms", sql="sqrt(pow(x, 2) + pow(y, 2))", type=DataType.DOUBLE) ``` -------------------------------- ### Create SLayer datasources Source: https://motley-slayer.readthedocs.io/en/latest/reference/cli Create new datasources from connection strings, with optional auto-ingestion and naming. ```bash slayer datasources create postgresql://user:${DB_PW}@localhost/analytics slayer datasources create postgresql://localhost/analytics --ingest slayer datasources create sqlite:///path/to/app.db --name analytics --ingest slayer datasources create demo --ingest # bundled Jaffle Shop demo ``` -------------------------------- ### Execute a multi-stage SLayer query Source: https://motley-slayer.readthedocs.io/en/latest/examples/09_lightning_talk/lightning_talk_nb Demonstrates chaining two query stages where the first aggregates monthly revenue by store and the second calculates the average of those monthly sums. ```python from IPython.display import Markdown result = engine.execute_sync(query=[ { 'name': 'monthly_store_revenue', 'source_model': 'orders', 'measures': ['order_total:sum'], 'dimensions': ['stores.name'], 'time_dimensions': [{'dimension': 'ordered_at', 'granularity': 'month'}], }, { 'source_model': 'monthly_store_revenue', 'measures': ['order_total_sum:avg'], 'dimensions': ['stores__name'], 'order': [{'column': 'order_total_sum_avg', 'direction': 'desc'}], }, ]) assert len(result.data) > 0, 'multistage query returned no rows' # Note the pretty formatting of the numbers if you export them as markdown Markdown(result.to_markdown()) ``` -------------------------------- ### Initialize SQLiteStorage Source: https://motley-slayer.readthedocs.io/en/latest/configuration/storage Programmatically initialize the SQLite storage backend by specifying the database file path. ```python from slayer.storage.sqlite_storage import SQLiteStorage storage = SQLiteStorage(db_path="./slayer.db") ``` -------------------------------- ### Save and Search with SlayerClient Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/python Demonstrates saving a memory and performing a search using the synchronous wrapper run_sync. ```python from slayer.async_utils import run_sync # Save a learning so the next session inherits it run_sync(client.save_memory( learning="orders.is_returned in {0,1,NULL}; treat NULL as not returned", linked_entities=["mydb.orders.is_returned"], id="kb.returns.null-handling", # optional; auto-allocated if omitted )) # Three retrieval channels (BM25 + Tantivy + optional dense embeddings) # fused into one flat ranked list: resp = run_sync(client.search( question="What should I know about returns?", max_results=10, )) for hit in resp.results: # kind: "memory" | "datasource" | "model" | "column" | "measure" | "aggregation" # hit.query is not None marks a saved example query print(hit.kind, hit.id, round(hit.score, 3), hit.text[:80]) ``` -------------------------------- ### Define a PostgreSQL datasource using a connection string Source: https://motley-slayer.readthedocs.io/en/latest/configuration/datasources Alternative configuration format using a single connection string URI. ```yaml name: my_db type: postgres connection_string: postgresql://user:pass@host:5432/dbname ``` -------------------------------- ### Create datasource configuration Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Define a datasource in a YAML file for database connection. ```yaml # slayer_data/datasources/my_pg.yaml name: my_pg type: postgres host: localhost port: 5432 database: myapp username: analyst password: ${DB_PASSWORD} ``` -------------------------------- ### Define Saved Named Measures Source: https://motley-slayer.readthedocs.io/en/latest/concepts/formulas Example of defining a library of reusable formulas within a model definition. ```yaml # model definition measures: - {name: aov, formula: "revenue:sum / *:count", label: "Average Order Value"} - {name: aov_pct_change, formula: "change_pct(aov)"} ``` -------------------------------- ### Run linting Source: https://motley-slayer.readthedocs.io/en/latest/development Check code quality using ruff. ```bash poetry run ruff check slayer/ tests/ ``` -------------------------------- ### Applying transforms to cross-model measures Source: https://motley-slayer.readthedocs.io/en/latest/examples/05_joined_measures/joined_measures Example of applying a cumulative sum transform to a measure from a joined model. ```json { "source_model": "orders", "time_dimensions": [{"dimension": "ordered_at", "granularity": "month"}], "measures": [ "customers.*:count", {"formula": "cumsum(customers.*:count)", "name": "cumulative_customers"} ] } ``` -------------------------------- ### Import dbt project Source: https://motley-slayer.readthedocs.io/en/latest/dbt/dbt_import Run the import command to process dbt YAML files and generate SLayer model definitions. ```bash slayer import-dbt ./my_dbt_project --datasource my_postgres --storage ./slayer_data ``` -------------------------------- ### Convert dbt metrics with filters Source: https://motley-slayer.readthedocs.io/en/latest/dbt/dbt_import Example of converting a dbt metric with a filter into an SLayer column and measure. ```yaml # dbt metric: loss_payment_amount (filter: has_loss_payment = 1) columns: - name: loss_payment_amount_col sql: claim_amount type: number format: {type: float} filter: "has_loss_payment = 1" measures: - name: loss_payment_amount formula: loss_payment_amount_col:sum ``` ```sql SUM(CASE WHEN has_loss_payment = 1 THEN claim_amount END) ``` -------------------------------- ### ModelMeasure.formula DSL mode configurations Source: https://motley-slayer.readthedocs.io/en/latest/concepts/references Examples of valid and invalid formula strings for ModelMeasure construction and enrichment. ```json {"name": "aov", "formula": "revenue:sum / *:count"} {"name": "cust_rev", "formula": "customers.revenue:sum"} // cross-model dotted path {"name": "growth", "formula": "change(revenue:sum)"} // transform on agg ref ``` ```json {"name": "bad", "formula": "json_extract(data, '$.x')"} // raw SQL fn ``` -------------------------------- ### Execute queries with SlayerQueryEngine Source: https://motley-slayer.readthedocs.io/en/latest/reference/python-client Initializes the query engine with YAML storage and executes a query. Access result data, columns, and metadata through the returned SlayerResponse object. ```python from slayer.engine.query_engine import SlayerQueryEngine from slayer.storage.yaml_storage import YAMLStorage storage = YAMLStorage(base_dir="./slayer_data") engine = SlayerQueryEngine(storage=storage) result = engine.execute(query=query) # result.data — list of row dicts # result.columns — list of column names # result.attributes — ResponseAttributes with .dimensions and .measures dicts (column → FieldMetadata) # # client.query() returns SlayerResponse with all fields above # client.sql(query) returns just the generated SQL string # client.explain(query) returns SlayerResponse with EXPLAIN ANALYZE output # result.row_count — number of rows # result.sql — generated SQL string ``` -------------------------------- ### Enable Ingestion via CLI Source: https://motley-slayer.readthedocs.io/en/latest/concepts/ingestion Use these commands to trigger ingestion during server or MCP startup. ```bash slayer serve --ingest-on-startup slayer mcp --ingest-on-startup slayer serve --demo --ingest-on-startup # demo first, then ingest all DSes ``` -------------------------------- ### Execute SLayer Queries Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/python Demonstrates how to run a synchronous query against the engine and access the resulting data structure. ```python from slayer.core.query import SlayerQuery engine = SlayerQueryEngine(storage=storage) result = engine.execute_sync(query=SlayerQuery( source_model="orders", measures=["*:count", "revenue:sum"], dimensions=["status"], )) for row in result.data: print(row) # {"orders.status": "completed", "orders._count": 42, "orders.revenue_sum": 12345.67} ``` ```python result.data # list of row dicts result.columns # list of column names result.row_count # number of rows result.sql # generated SQL (when dry_run or explain is set) result.attributes # ResponseAttributes with .dimensions and .measures dicts ``` -------------------------------- ### Connect and explore a new database Source: https://motley-slayer.readthedocs.io/en/latest/reference/mcp Workflow for establishing a database connection and automatically generating models. ```python 1. create_datasource(name="mydb", type="postgres", host="localhost", database="app", username="user", password="pass") # auto_ingest=true by default — models are generated automatically 2. models_summary(datasource_name="mydb") # see what was generated 3. inspect_model(model_name="orders") # see schema + sample data ``` -------------------------------- ### Configure Ingestion Programmatically Source: https://motley-slayer.readthedocs.io/en/latest/concepts/ingestion Enable ingestion at startup when initializing the application or MCP server. ```python from slayer.api.server import create_app from slayer.mcp.server import create_mcp_server app = create_app(storage=storage, ingest_on_startup=True) mcp = create_mcp_server(storage=storage, ingest_on_startup=True) ``` -------------------------------- ### Define Column for Aggregation Source: https://motley-slayer.readthedocs.io/en/latest/concepts/models Example of defining a column in a model that can be used with time-ordered aggregations like first or last. ```yaml columns: - {name: balance, sql: balance, type: number} ``` -------------------------------- ### Querying joined model measures Source: https://motley-slayer.readthedocs.io/en/latest/examples/05_joined_measures/joined_measures Examples of querying measures from a joined model compared to querying the model directly. ```json {"source_model": "orders", "measures": ["customers.*:count"], "dimensions": ["customers.name"]} ``` ```json {"source_model": "customers", "measures": ["*:count"], "dimensions": ["name"]} ``` -------------------------------- ### Configure SQLite storage Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/python Initializes SQLite storage for single-file data management. ```python from slayer.storage.sqlite_storage import SQLiteStorage storage = SQLiteStorage(db_path="slayer.db") # Use exactly like YAMLStorage ``` -------------------------------- ### Monthly Revenue Comparison Query Source: https://motley-slayer.readthedocs.io/en/latest/examples/04_time/time Demonstrates a complex query using time_shift and change transforms to compare revenue and filter by MoM website visit trends. ```json { "source_model": "my_model", "measures": [ "revenue:sum", "time_shift(revenue:sum, -1, 'year')" ], "dimensions": ["region"], "time_dimensions": [{ "dimension": "created_at", "granularity": "month", "date_range": ["2025-01-01", "2025-12-31"] }], "filters": ["last(change(website_visits:sum)) < 0"] } ``` -------------------------------- ### Rendered column sample values Source: https://motley-slayer.readthedocs.io/en/latest/concepts/search Example of how column sample values are displayed in search results when structured data is available. ```text Column: warehouse.orders.status Type: TEXT Description: Order status. Sample values: ["paid", "refunded", "cancelled", "pending", …] ← JSON-encoded, all 50 Distinct count: 12345 ← only when distinct_count > len(sampled_values) ``` -------------------------------- ### Disable Compact Mode Source: https://motley-slayer.readthedocs.io/en/latest/concepts/search Example of a JSON request body to disable compact mode, restoring full per-hit rendering. ```json {"entities": ["memory:42"], "max_results": 1, "compact": false} ``` -------------------------------- ### Run dbt Import Source: https://motley-slayer.readthedocs.io/en/latest/dbt/dbt_import Command to import a dbt project including hidden models. ```bash slayer import-dbt ./my_dbt_project \ --datasource my_warehouse \ --include-hidden-models ``` -------------------------------- ### Define Measure Formulas Source: https://motley-slayer.readthedocs.io/en/latest/concepts/formulas Example of defining measures using raw formulas, auto-generated names, and labels within a query. ```json "measures": [ "*:count", {"formula": "revenue:sum / *:count", "name": "aov", "label": "Average Order Value"}, "cumsum(revenue:sum)", ... ] ``` -------------------------------- ### Querying SLayer with Go Source: https://motley-slayer.readthedocs.io/en/latest/getting-started/rest-api Uses the net/http package to perform a POST request with a raw JSON string body. ```go body := `{"source_model": "orders", "measures": ["*:count"]}` resp, _ := http.Post("http://localhost:5143/query", "application/json", strings.NewReader(body)) ``` -------------------------------- ### Ingest Data via CLI Source: https://motley-slayer.readthedocs.io/en/latest/concepts/ingestion Execute data ingestion from a specified datasource and schema into local storage. ```bash slayer ingest --datasource my_postgres --schema public --storage ./slayer_data ``` -------------------------------- ### Managing Memories and Performing Searches Source: https://motley-slayer.readthedocs.io/en/latest/reference/python-client Demonstrates saving memories, executing semantic searches, and removing memories by ID using the SlayerClient. ```python from slayer.async_utils import run_sync # Save a learning run_sync(client.save_memory( learning="orders.is_returned in {0,1,NULL}; treat NULL as not returned", linked_entities=["mydb.orders.is_returned"], id="kb.returns.null-handling", # optional; auto-allocated if omitted )) # Save a query-bearing memory — pass a SlayerQuery / dict for linked_entities run_sync(client.save_memory( learning="Top customers by lifetime spend", linked_entities={ "source_model": "orders", "measures": [{"formula": "amount:sum", "name": "lifetime_spend"}], "dimensions": ["customers.name"], "order": [{"column": "lifetime_spend", "direction": "desc"}], "limit": 5, }, id="kb.top-customers", )) # Search resp = run_sync(client.search( question="What should I know before comparing Brooklyn revenue to other stores?", max_results=10, )) for hit in resp.results: if hit.kind == "memory": kind = "example_query" if hit.query is not None else "learning" print(f"[{kind}] {hit.id} score={hit.score:.3f} {hit.text[:80]}") else: print(f"[{hit.kind}] {hit.id} score={hit.score:.3f}") # Forget by id (cascade-strips memory: refs from other memories) run_sync(client.forget_memory("kb.returns.null-handling")) ``` -------------------------------- ### Compute Transitive Closure Source: https://motley-slayer.readthedocs.io/en/latest/examples/03_auto_ingest/auto_ingest_nb Calculates the set of reachable tables from a given starting table using the previously built foreign key graph. ```python # Transitive closure: what tables are reachable from items? for table in ["orders", "items"]: reachable = _compute_transitive_closure(fk_graph, table) print(f"{table} can reach: {sorted(reachable)}") sa_engine.dispose() ``` -------------------------------- ### Define an SLayer Query Source: https://motley-slayer.readthedocs.io/en/latest Example of a JSON query object defining measures, dimensions, time dimensions, filters, and ordering for an orders model. ```json { "source_model": "orders", "measures": [ "revenue:sum", {"formula": "change_pct(revenue:sum)", "name": "mom_growth"}, {"formula": "revenue:sum / time_shift(revenue:sum, -1, 'year') - 1", "name": "yoy_growth"}, "customers.score:last(changed_at)" ], "dimensions": ["customers.regions.name"], "time_dimensions": [{ "dimension": "created_at", "granularity": "month", "date_range": ["2025-01-01", "2025-12-31"] }], "filters": ["status = 'completed'", "change(revenue:sum) > 0"], "order": [{"column": "revenue_sum", "direction": "desc"}] } ``` -------------------------------- ### Using last() to compare revenue with the latest quarter Source: https://motley-slayer.readthedocs.io/en/latest/examples/04_time/time_nb Demonstrates how to include a 'last' formula in a query to compare historical revenue against the most recent quarter's revenue. ```python result = engine.execute_sync( query={ "source_model": "orders", "time_dimensions": [{"dimension": "ordered_at", "granularity": "quarter"}], "measures": [ "order_total:sum", {"formula": "last(order_total:sum)", "name": "latest_quarter"}, ], "dimensions": ["stores.name"], "order": [{"column": "ordered_at", "direction": "desc"}], "limit": 10, } ) print(f"{'Quarter':<12} {'Store':<20} {'Revenue':>14} {'Latest Qtr':>14}") print("-" * 62) for row in result.data: qtr = str(row["orders.ordered_at"])[:7] store = row["orders.stores.name"] rev = row["orders.order_total_sum"] latest = row["orders.latest_quarter"] print(f"{qtr:<12} {store:<20} ${rev:>13,.2f} ${latest:>13,.2f}") ``` -------------------------------- ### Querying Auto-Ingested Models Source: https://motley-slayer.readthedocs.io/en/latest/examples/03_auto_ingest/auto_ingest Example of a JSON query structure for an auto-ingested model, demonstrating the use of dot syntax to traverse the join graph. ```json { "source_model": "items", "measures": ["*:count"], "dimensions": ["orders.customers.name"], "order": [{"column": "_count", "direction": "desc"}], "limit": 5 } ```