| queryA | Query data from a semantic model. Call inspect(reference=".", entity_type="model") first to see available columns and measures. Args:
source_model: One of three forms:
- Model name (string) — name of a saved model from models_summary, e.g. "orders".
- Inline ModelExtension (dict) — extend an existing model with extra columns/joins/measures
for this one query: {"source_name": "orders", "columns": [{"name": "double_amount", "sql": "amount * 2", "type": "DOUBLE"}]}.
- Inline SlayerModel (dict) — define a model ad-hoc:
{"name": "ad_hoc", "sql_table": "things", "data_source": "test", "columns": [...]}.
measures: Aggregated values to return. Each is a formula: {"formula": "*:count"},
{"formula": "revenue:sum / *:count", "name": "aov"} (arithmetic),
{"formula": "cumsum(revenue:sum)"} (cumulative sum),
{"formula": "change(revenue:sum)"} (period-over-period difference),
{"formula": "change_pct(revenue:sum)"} (period-over-period % change, e.g. month-over-month growth),
{"formula": "time_shift(revenue:sum, -1)"} (the shifted value itself, one time bucket back),
{"formula": "time_shift(revenue:sum, -1, 'year')"} (value from one year earlier, for custom arithmetic),
{"formula": "lag(revenue:sum, 1)"} (previous row via window function; shifts by row position, NULL at edges),
{"formula": "lead(revenue:sum, 1)"} (next row via window function), {"formula": "last(revenue:sum)"} (most recent),
{"formula": "rank(revenue:sum)"} (ranking). A bare name like {"formula": "aov"} resolves to a saved ModelMeasure on the model.
change / change_pct / time_shift are calendar-aware and partition-safe: change and change_pct compare
each row against the prior time bucket (one step back at the query's own granularity), while time_shift
compares at its explicitly requested offset and granularity. All three join on the same non-time
dimension values, so per-group series reset cleanly — safe for grouped queries like month-over-month
revenue by store.
For period-over-period growth, prefer change_pct (or change for the absolute delta); use time_shift
only when you need the shifted value itself as a term in your own arithmetic.
dimensions: List of dimension names to group by, e.g. ["status", "region"].
filters: Filter conditions as formula strings. Examples: "status == 'completed'",
"amount > 100", "status in ('a', 'b')", "status is None",
"name like '%acme%'". Filters on measures are automatically routed to HAVING.
Supports and/or: "status == 'a' or status == 'b'".
Filters can also reference computed measure names or contain inline transforms:
"change(revenue:sum) > 0", "last(change(revenue:sum)) < 0".
time_dimensions: Time grouping. Format: {"dimension": "created_at", "granularity": "day|week|month|quarter|year", "date_range": ["2024-01-01", "2024-12-31"]}.
order: Sorting. Format: {"column": "measure_or_dim_name", "direction": "asc|desc"}.
limit: Max rows to return.
offset: Number of rows to skip.
whole_periods_only: When true, snap date filters to time bucket boundaries based on granularity, exclude the current incomplete time bucket.
show_sql: When true, include the generated SQL in the response for debugging.
strict: Error instead of warn when a cross-model measure would broadcast or a producer filter would be dropped. Rejected with run-by-name execution — declare it on the stored query instead.
dry_run: When true, generate and return the SQL without executing it.
explain: When true, run EXPLAIN ANALYZE and return the query plan.
format: Output format — "markdown" (default, compact and LLM-friendly), "json" (structured), or "csv" (most compact). Case-insensitive.
distinct_dimension_values: Default True (Cube.js-style auto-dedup for dim-only queries — emits GROUP BY ). Set False to emit raw rows: no top-level GROUP BY, just SELECT <dimensions/time_dimensions> with the usual WHERE/ORDER BY/LIMIT. Any measure reference (in measures, filters, or order) raises an error in this mode. Example: query(source_model="orders", measures=[{"formula": "*:count"}], dimensions=["status"], filters=["status == 'completed'"]) Before calling this tool, run search first, supplying the entities you're thinking of using (and/or the query itself via the query arg, or a free-text question). Read the returned memories and consider any matching example queries before formulating the final query. |
| query_nestedA | Run a multi-stage query as a DAG. Use this when one stage depends on the output of another. queries is a list of query dicts forming a DAG. Each entry has the
same shape as the regular query tool's arguments
(source_model, measures, dimensions, filters,
time_dimensions, order, limit, offset,
whole_periods_only) plus an optional name. Stages reference
each other by name via source_model: "<sibling_name>" or
joins.target_model.
Order doesn't matter — the engine auto-sorts so every stage
appears after the siblings it references. The last entry of
the input is always the entry point / DAG root (its result is
what's returned); only the non-final entries are reordered.
Every non-final entry must have a name. Cycles,
self-references, and a non-final stage referencing the root are
rejected with a clear error. Stages that aren't reachable from
the root are accepted as utility sub-queries — they're silently
dropped from the emitted SQL. Args:
queries: Ordered list of stage dicts. Earlier stages must be
named; the last stage is the one whose rows return.
variables: Variable values for {var} placeholder
substitution in filters. Runtime kwarg precedence:
runtime > stage.variables > outer query.variables > model.query_variables.
show_sql: When true, include the generated SQL in the response.
dry_run: When true, generate the SQL without executing it.
explain: When true, run EXPLAIN ANALYZE and return the plan.
format: markdown (default), json, or csv. Example:
queries=[
{"name": "monthly", "source_model": "orders",
"measures": [{"formula": ":count"}, {"formula": "revenue:sum"}],
"time_dimensions": [{"dimension": "created_at", "granularity": "month"}]},
{"source_model": "monthly", "measures": [{"formula": ":count"}]}
] For a single-stage query, prefer the regular query tool — its
typed arguments give a more discoverable schema. |
| models_summaryA | Brief summary of all (non-hidden) models in a datasource. DEV-1549: compact-by-default rendering. Under compact=True
each model section emits its name, description, the column count
(Columns: N), the comma-separated measure NAMES
(Measures: a, b, c) and the Joins to: list — no
per-column table, no per-measure formula block. Pass
compact=False to restore the verbose markdown / JSON shape
with full column and measure payloads. Args:
datasource_name: Name of the datasource (from list_datasources).
format: Output format — "markdown" (default, compact and
LLM-friendly) or "json" (structured array of model summaries).
Case-insensitive.
compact: Default True — drop per-column / per-measure detail.
Set False to surface the full per-model tables. |
| inspect_modelA | DEPRECATED: use the inspect tool. Return a complete-yet-compact view of a semantic model. Always emitted (regardless of sections): model header + description,
metadata bullets (data_source, sql_table, default_time_dimension,
hidden, row_count), backing-query structure for query-backed models,
and — when show_sql=True — the custom SQL block, model-level
filters, and the cached backing-query SQL. Section-gated parts (subset selectable via sections): columns — unified row-level columns table with a sampled
column (distinct values for string/boolean, min .. max for
number/date/time, or top20 ... (N distinct) for high-
cardinality categoricals).
measures — named-formula library.
aggregations — custom aggregation definitions. The formula
column and the sql field of each params[] entry are gated
by show_sql.
joins — join definitions.
samples — live sample-data query (COUNT(*) plus one
aggregation per column).
learnings — learning-only memories whose canonical entities
reference this model.
When a section is omitted from sections: columns, measures,
aggregations and joins collapse to a one-line backticked CSV
of names; samples and learnings are dropped entirely.
A footer at the end of the response lists what was trimmed and how
to fetch more. Args:
model_name: Name of the model to inspect.
num_rows: Max sample-data rows (default: 3).
show_sql: When true, include the generated SQL for the sample-data
query, the custom SQL block, model-level filters, the cached
backing-query SQL, and aggregation formulas/param SQL.
format: Output format — "markdown" (default) or "json".
Case-insensitive.
sections: Subset of ["columns", "measures", "aggregations", "joins", "samples", "learnings"]. Default (None
or empty list) renders all six. Unknown names are ignored
with a warning line at the end of the response. A non-empty
list of only unknown names resolves to no sections (not
all six) — "all sections" is reserved for None/[] so
a typo can't silently trigger the full expensive payload.
descriptions_max_chars: When set, every description field (model,
column, measure, aggregation) longer than this is truncated
with a ... [truncated] suffix. Must be >= 0. None
(default) means no truncation. |
| inspectA | Inspect EXACTLY one entity by reference and kind, a homogeneous
BATCH when reference is a list — or the whole COLLECTION at a kind
when reference is omitted / None. A clean point-lookup: no fusion / ranking / cypher, and no bundled
memories. Use search instead when you want an entity surfaced in
context (with related memories and ranked neighbours). Collection (DEV-1667): omit reference (or pass None / [])
to list a whole kind. entity_type="model" lists all models grouped
by datasource (compact=True: one terse line per model; compact=False:
the full per-model tables). entity_type="datasource" lists all
datasources. Only model / datasource support the collection
view; other kinds raise. This subsumes models_summary /
list_datasources. Batch (DEV-1612): pass a list of references that all share the one
entity_type. Returns one rendered block per id, in input order,
each echoing its resolved canonical id (a ## <canonical> header in
markdown; a JSON array under format="json"). Per-id resolution
errors are isolated — one bad id does not sink the batch (in JSON it
becomes a {"reference": ..., "error": ...} element). A single
str keeps its byte-for-byte single output; a one-element list is
still batch-framed. Args:
reference: The entity reference, or a list of references (batch).
Accepts canonical forms (mydb, mydb.orders,
mydb.orders.amount), bare names, join paths
(orders.customers.region → resolved to the owning model),
and memory:<id> for memories. Normalised via the shared
resolver; the normalised canonical id is echoed in the JSON
shape.
entity_type: REQUIRED. One of datasource, model,
column, measure, aggregation, memory.
Disambiguates the 3-part canonical collision (a name
shared by, e.g., a column and an aggregation) and asserts
the resolved kind — a mismatch returns a detailed error.
compact: When true (default): description-only for
column/measure/aggregation/datasource/memory; for
entity_type="model" a cheap schema skeleton (column /
measure / aggregation names + join targets, zero DB calls).
False returns the full render (and, for the datasource kind,
a per-model skeleton for each visible model).
format: "markdown" (default) or "json".
num_rows: Sample-data rows for entity_type="model". Ignored
(with a warning) for other kinds.
show_sql: Include generated SQL for entity_type="model".
Ignored (with a warning) for datasource/memory; a silent
no-op for column/measure/aggregation.
sections: Section subset for entity_type="model". Ignored
(with a warning) for other kinds.
descriptions_max_chars: Truncate description fields to this many
characters. Applies to every kind. |
| create_modelA | Create a new semantic model, either from a database table or from a query. From a table (provide sql_table or sql):
create_model(name="orders", sql_table="public.orders", data_source="mydb",
columns=[...], measures=[...]) From a query (provide query):
create_model(name="monthly_summary", query={"source_model": "orders",
"measures": ["*:count", "amount:sum"],
"time_dimensions": [{"dimension": "created_at", "granularity": "month"}]})
Columns are auto-introspected from the query result. Args:
name: Unique model name (lowercase, underscores).
sql_table: Database table name, e.g. "public.orders".
sql: Alternative to sql_table — a custom SQL expression for the model's source.
data_source: Name of the datasource (from list_datasources).
description: What this model represents.
columns: List of column definitions. Each: {"name": "col", "sql": "col", "type": "string"}.
Types: string, number, time, date, boolean. Optional fields: primary_key,
unique (single-column uniqueness that is not the PK; primary_key
already implies it), allowed_aggregations (whitelist), filter
(CASE WHEN inside aggregation), label, description, hidden,
meta.
measures: List of named formula definitions on the model. Each:
{"name": "aov", "formula": "revenue:sum / *:count", "label": "...",
"description": "...", "meta": {...}}.
Queries can reference these by bare name (e.g. {"formula": "aov"}).
meta is an optional opaque dict for caller bookkeeping
(e.g. linking the formula back to a source identifier).
query: A SLayer query dict (or list of stage dicts for a multi-stage backing
query). When provided, the query is saved as the model's source_queries
and the model becomes query-backed. Mutually exclusive with sql_table, sql,
columns, and measures.
variables: Default values for {var} placeholders in the backing query.
Saved as query_variables on the model. Only meaningful when query
is provided. |
| edit_modelA | Edit an existing model in a single call — update metadata, upsert columns/measures/aggregations/joins,
manage filters, and remove entities. Args:
model_name: Name of the model to edit.
description: New model description.
data_source: Lookup key — the datasource the model belongs to.
Required when the same name exists in multiple datasources
(otherwise the priority list / single-match rules apply).
new_data_source: Move the model to a different datasource (rare;
renames its storage location). Pass None (default) to
leave the data_source unchanged.
default_time_dimension: Default time dimension (a column of type date/time) for
time-dependent transforms.
sql_table: Database table name. Setting this clears sql and source_queries.
sql: Custom SQL expression for the model source. Setting this clears sql_table and source_queries.
source_queries: Replace the model's backing query with this list of stages.
Each stage is a SlayerQuery dict; non-final stages must have a name.
Setting this clears sql_table and sql, makes the model query-backed,
and refreshes the cached columns and backing_query_sql.
query_variables: Replace the model's default {var} placeholder values for
its backing query. Pass null/None to clear. Only meaningful for
query-backed models.
hidden: Whether this model is hidden from discovery.
meta: Arbitrary JSON metadata for the model (replaces existing meta). Pass null/None to clear.
columns: Columns to create or update (upsert by name). Each dict:
{"name": "col", "type": "string", "sql": "col", "description": "...",
"primary_key": false, "unique": false, "hidden": false,
"allowed_aggregations": ["sum", "avg"],
"filter": "status = 'active'", "label": "..."}.
If a column with this name exists, only the provided fields are updated.
Types: string, number, time, date, boolean.
unique marks single-column uniqueness that is not the primary key
(primary_key already implies it); it is used to infer join
cardinality.
measures: Named formula measures to create or update (upsert by name). Each dict:
{"name": "aov", "formula": "revenue:sum / *:count", "label": "...",
"description": "...", "meta": {...}}.
Queries can reference these by bare name (e.g. {"formula": "aov"}).
meta is an optional opaque dict for caller bookkeeping.
aggregations: Aggregations to create or update (upsert by name). Each dict:
{"name": "weighted_avg", "formula": "SUM({value} * {weight}) / NULLIF(SUM({weight}), 0)",
"params": [{"name": "weight", "sql": "quantity"}], "description": "...",
"meta": {...}}.
meta is an optional opaque dict for caller bookkeeping.
joins: Joins to create or update (upsert by target_model). Each dict:
{"target_model": "customers", "join_pairs": [["customer_id", "id"]],
"cardinality": "many_to_one", "description": "...", "meta": {...}}.
A composite key is one join with several join_pairs entries, not
one join per column. cardinality is the join's arity read
source->target, one of one_to_one / one_to_many /
many_to_one / many_to_many; omit it when undetermined. It is
descriptive metadata only — it changes neither join_type nor
query results.
add_filters: SQL filter strings to add (e.g. ["deleted_at IS NULL"]). Duplicates ignored.
remove_filters: SQL filter strings to remove (exact match).
remove: Named entities to delete, keyed by type:
{"columns": ["col_name"], "measures": ["measure_name"],
"aggregations": ["agg_name"], "joins": ["target_model_name"]}.
Removals are processed before upserts. Example — update a column and add a named measure:
edit_model(model_name="orders",
columns=[{"name": "status", "type": "string"}],
measures=[{"name": "aov", "formula": "revenue:sum / *:count"}])
Example — remove a measure:
edit_model(model_name="orders", remove={"measures": ["old_metric"]}) |
| create_datasourceA | Create a database connection, verify it, and auto-ingest models. Use ${ENV_VAR} syntax in credentials to reference environment variables. Args:
name: Unique datasource name.
type: Database type — postgres, mysql, sqlite, bigquery, or snowflake.
host: Database host (default: localhost).
port: Database port (e.g. 5432 for Postgres).
database: Database name.
username: Database username.
password: Database password.
connection_string: Full connection string as alternative to individual fields.
schema_name: Default schema name. Also used as the single schema for auto-ingestion.
schemas: Comma-separated schemas to ingest. Mutually exclusive with schema_name / all_schemas.
all_schemas: Ingest every non-system schema. Mutually exclusive with schema_name / schemas.
auto_ingest: Automatically ingest models from the database schema (default: true). Set to false to skip. Example: create_datasource(name="mydb", type="postgres", host="localhost", port=5432, database="app", username="user", password="pass") |
| list_datasourcesA | List all configured database connections (names and types only, credentials are not shown). Use describe_datasource for connection details and status. |
| describe_datasourceA | Show datasource details: connection status, available schemas, and (by default) the tables in the given or default schema. Use this after create_datasource to verify the connection and explore
what's queryable before calling ingest_datasource_models. Args:
name: Datasource name (from list_datasources).
list_tables: If True (default), append a list of tables from the
schema named by schema_name (or the dialect's default
schema when empty).
schema_name: Database schema to list tables from (e.g. "public").
Empty uses the dialect default. Ignored when list_tables=False. |
| edit_datasourceB | Update a datasource's metadata. Args:
name: Datasource name to update.
description: New description for the datasource. |
| delete_modelA | Delete a semantic model. Args:
name: Model name to delete.
data_source: Datasource the model belongs to. Required when the
same name exists in multiple datasources (otherwise the
priority list / single-match rules apply). |
| validate_modelsA | Diff persisted SLayer models against the live database schema(s). Returns a JSON-serialized list of pending delete operations
(column drops, measure drops, join drops, filter removals, whole
models) needed to keep stored models valid against the current
live state. Read-only — does not modify storage. Args:
data_source: Datasource name to validate. When omitted, every
datasource is validated concurrently and results are
concatenated. |
| recommend_root_modelA | Recommend the root model (query source_model) for a set of
model.column / model.metric items, and give each item's
join-qualified reference path from that root. Introspects the join graph and picks the model from which every
requested item is reachable (LEFT joins are directional; INNER
joins traverse both ways), minimizing total join hops. The returned
paths are ready to drop into a query whose source_model is the
recommended root — e.g. a joined column comes back as
customers.regions.name and a root-owned one as status;
aggregation suffixes (:sum) are preserved. When no single model reaches everything, root_model is null and
coverage lists the best partial roots so you can split the
request into a multi-stage query. Args:
items: entity references (orders.revenue, customers.name,
orders.revenue:sum, bare aov for a saved metric...).
data_source: optional datasource scope; when omitted, names
resolve via the datasource-priority list. All items must
resolve to a single datasource.
root_hint: optional intended root — a bare model name or
<data_source>.<model> within the resolved datasource.
Honored when it reaches every item (overriding the min-hops
pick, so you can force a bridge model that owns none of the
items); otherwise the auto-pick is used and a warning
explains why. Resolved after the datasource is determined,
so it cannot pick the datasource.
format: "markdown" (default) or "json". |
| delete_datasourceB | Delete a datasource configuration. Args:
name: Datasource name to delete. |
| ingest_datasource_modelsA | Auto-discover tables in a database and create / additively update semantic models from them. Idempotent (DEV-1356): re-runs are additive only. New columns and joins
are appended to existing models; existing column / join definitions
are never overwritten. After the additive pass, returns the pending
validate_models deletes alongside the additions. Args:
datasource_name: Name of an existing datasource (from list_datasources).
include_tables: Comma-separated list of table names to include. If empty, all tables are ingested.
schema_name: A single database schema to inspect (e.g. "public"). Empty uses the default schema.
schemas: Comma-separated schemas to inspect. Mutually exclusive with schema_name / all_schemas.
all_schemas: Ingest every non-system schema. Mutually exclusive with schema_name / schemas. |
| set_datasource_priorityA | Configure how SLayer disambiguates bare model names that exist in
multiple datasources. When two datasources both define a model named users, calling
edit_model("users") (no data_source=) is ambiguous. SLayer
walks this priority list and picks the first datasource that has
the requested name. If none of the candidates appear in the list,
an AmbiguousModelError is raised. Args:
priority: Datasource names, most-preferred first. Each entry
must already exist (run list_datasources first). Pass
an empty list to clear the priority. |
| get_datasource_priorityA | Return the configured datasource priority list (most-preferred
first), or [] if none is set. |
| save_memoryA | Save an agent memory: a free-form note plus the SLayer
entities it concerns. linked_entities accepts either:
a list of entity reference strings — each item is resolved to
the canonical <datasource>.<model>[.<leaf>] form. Bare
names use the datasource priority list; ambiguous bare-column
matches are rejected. memory:<id> is also valid here
(cross-memory references; the target memory must exist). a SlayerQuery (dict) — entities are auto-extracted from
source_model, dimensions, time_dimensions,
measures, and filters; resolution warnings are
non-fatal. The query itself is stored alongside the
learning, so the memory surfaces in search's
example_queries list (vs the memories list for
entity-list memories).
DEV-1428: id is an optional canonical memory id. Omit to
auto-allocate a monotonic int-shaped id ("1", "2", ...);
supply a string for a stable user-controlled id
("kb.policy.42"). Charset excludes :, /, ?,
#, whitespace. Duplicate id → unconditional upsert,
created_at preserved. Returns the assigned memory_id (string), the canonical
entities stored, and any non-fatal warnings. Cascade-on-delete: when a model / datasource / measure is
deleted, every memory:<id> and <ds>.<model>[.<leaf>]
reference under it is automatically stripped from every other
memory's entities list. Memories with zero entities after
the strip are kept (the learning text stands alone). Search is lenient: stale entity tags in saved memories are
filtered out at retrieval time rather than raising. Args:
learning: The note text. Required, non-empty.
linked_entities: List of entity strings, or an inline
SlayerQuery payload.
id: Optional canonical memory id (see above). Examples:
save_memory(
learning="orders.is_returned in {0,1,NULL}; treat NULL as not returned",
linked_entities=["orders.is_returned"],
) save_memory(
learning="Paid revenue by status",
linked_entities={
"source_model": "orders",
"measures": [{"formula": "amount:sum"}],
"filters": ["status = 'paid'"],
},
id="kb.paid-revenue",
)
|
| forget_memoryA | Delete a memory by id. Cascades: every other memory's memory:<id> reference to
this id is automatically stripped from its entities list. Args:
id: The memory_id returned by save_memory. Accepts
strings (the canonical form, including user-supplied
"kb.policy"-style ids) as well as legacy ints
(coerced to their decimal string form). Raises a friendly error if the id is invalid or the memory does
not exist. |
| searchA | Up to three-channel semantic search over memories + canonical entities. Call this BEFORE query to surface any notes or example
queries previously saved against the entities you're
considering. Channel 1 (entity-overlap BM25 over memories): runs when
entities and/or query is supplied. Memories whose
canonical entity tags overlap the resolved input are ranked. Channel 2 (tantivy full-text over memories ∪ entities): runs
when question is supplied. The in-memory index covers every
memory + every searchable entity (datasource / non-hidden model /
non-hidden column / named measure / aggregation). Channel 3 (dense embedding similarity, optional): runs when
question is supplied AND the advanced_search extra is
installed AND a provider API key is configured for the active
embedding model. Cosine similarity between the question
embedding and persisted entity/memory embeddings. Skipped with
a single warning into SearchResponse.warnings when any
precondition fails — tantivy + BM25 continue to work. All hits (memories, example queries, entities) are fused via
Reciprocal Rank Fusion (k=60) into a single ranked
results list capped at max_results. Empty input (no entities, no query, no question) returns the
newest memories capped at max_results, with a warning. Args:
entities: Canonical entity reference strings.
query: Optional SlayerQuery (dict). Entities are
auto-extracted to broaden channel-1 input.
question: Free-text query for the tantivy full-text channel.
datasource: Optional datasource name. When set, scope all
three channels to that one datasource. Entity hits are
limited to docs rooted at the datasource (exact match
or dotted-path descendant). Memories surface when any
of their tagged entities is rooted at the datasource —
a memory spanning multiple datasources surfaces from
each. BM25 / IDF stats reflect only the filtered subset.
Unknown datasource raises ValueError.
max_results: Maximum total number of hits to return (default 10).
cypher_filter: Optional openCypher MATCH query returning
… AS id that pre-filters all three channels to the
returned canonical IDs. When advanced_search is not
installed, only simple
MATCH (n:Label1:Label2) RETURN n.id AS id patterns are
supported as a kind filter (multi-label uses union
semantics; allowed labels: Memory, Datasource, Model,
Column, Measure, Aggregation). |