Your business logic,
deterministically executable by AI.

FeatureMesh is a registry of named, typed, composable business logic
that humans write and AI agents compose. Dashboards, production APIs,
and LLM queries all read the same definitions.

Or use locally with no account: pip install featuremesh

The definitions are written in FeatureQL, a formula language for business logic.
FeatureMesh compiles them to SQL for your warehouse, serves them at request latency, and rejects any change that would break them.

Why configuration is not enough

Semantic layers declare metrics in YAML and expose them to LLMs through MCP.
That covers flat metrics. Composition, typing, and variants are where it stops.

YAML is configuration, not a language.

You can declare revenue = sum(amount) but you can't compose it. You can't say, yes take that but "remove refunds, and do not count shipping for Italy" for my query. It needs to be defined as a different metric. Composition stops where the YAML ends, and the LLM fills the gap by writing raw SQL against your tables, so you are back to generated SQL against raw tables.

English is ambiguous. SQL is expensive.

When the LLM falls back to writing SQL, you pay the cost twice. First in tokens: the LLM needs the full schema in context to generate one query. A 100 tables warehouse is 50k+ tokens per question. Second in review: a human has to read and verify a 200 lines SQL query to trust the answer. This review is expensive enough that it usually gets skipped.

LLM progress will not fix this.

Even a perfect LLM cannot resolve ambiguity that exists in the request. "Active customers" often means several different things inside the same company. Without a canonical definition, the LLM guesses from different bits of english coming from md files. The guess can silently change across prompts, schema changes, and model updates.

Evaluation becomes the bottleneck.

So teams running LLMs on data tend to end up with the same homework: build eval harnesses, grade responses, debug regressions. This work of always catching up does not scale.

What a language gives you
that configuration cannot

FeatureQL is a functional dataflow language.
Every feature is a named, typed, composable pure function.

Composition

Every feature can reference every other feature. customer_ltv can build on order_total, which builds on order_items_price. Deep composition is the unit of reuse, not flat metric declarations.

Entity types

BIGINT#CUSTOMERS is not the same type as BIGINT#ORDERS. An LLM cannot accidentally join two unrelated tables because the type system rejects it at compile time.

Deterministic execution

A feature definition expresses a universal business intent and transpiles to executable SQL on your warehouse of choice (DuckDB, Trino, BigQuery). Same feature, same answer, every backend.

Small context

An LLM asking a question doesn't need the warehouse schema or outdated markdown docs. It just needs to identify the right features reading their formula and compose them. No overblown context window. No explosion of cost.

Human verifiable

Reviewing "does customer_ltv match the business definition" is a one time, one place conversation. Reviewing a 200 lines generated SQL query every time an agent answers a question is not.

Variants without duplication

Want to test a different discount threshold, a new churn model, or a country specific rule? VARIANT() swaps one dependency in any feature without touching the original. Run the old and the new side by side, compare the answers, ship the winner. No branching, no copy paste, no drift.

Analytics and serving
from the same definitions

The same definitions run in batch on your warehouse and at request latency in production.

Analytics

FeatureQL transpiles to SQL and runs on your warehouse (DuckDB, Trino, BigQuery). Dashboards, BI tools, ad hoc queries, LLM analytical questions.

Serving

The same features run on DataFusion for milliseconds real time inference. Connect Redis, JDBC, HTTP sources. Compile to prepared statements. Serve at production latency.

A reactive agent analyzes history and proposes a change.
A proactive agent tests that change in production: eligibility, pricing, fraud, personalization.
Both need the same definitions offline and online, which is what VARIANT() and prepared statements give you.

How it actually works

1. Analytics / Training

1

Define entities and keys

Business objects first — typed keys so invalid joins fail at compile time.

CREATE FEATURES IN fm.home AS
SELECT
    customers := ENTITY(),
    orders := ENTITY(),
    customer_id := INPUT(BIGINT#customers),
    order_id := INPUT(BIGINT#orders),
;
2

Map features to columns

Warehouse columns become source features — business names, not table schemas.

CREATE FEATURES IN fm.home AS
SELECT
    tables.fct_orders := EXTERNAL_COLUMNS(
        order_id BIGINT#orders BIND TO order_id,
        order_customer_id BIGINT#customers,
        price DECIMAL,
        created_at TIMESTAMP
        FROM TABLE(home.fct_orders)
    ),
    order_price := tables.fct_orders[price],
    -- …customers, OBT, keysets
;
3

Write transformations

One promo rule — LTV, recency, and the decision as named features.

CREATE FEATURES IN fm.home AS
SELECT
    customer_ltv := customer_id.RELATED(
        SUM(order_price)
        GROUP BY order_customer_id
    ),
    customer_ltv_cents := CAST(customer_ltv * 100 AS BIGINT),
    recency := DATE_SUBTRACT(
        TIMESTAMP '2026-02-01',
        last_order_id.RELATED(order_created_at),
        'day'
    ),
    show_promocode_offline := recency > 30
        AND customer_ltv_cents > 100000
;
4

Compute features in batch

FeatureQL owns the definitions; outer SQL aggregates as usual.

/* SQL */
SELECT
    show_promocode_offline,
    COUNT(1) AS num_customers
FROM FEATUREQL(
    SELECT
        customer_id,
        show_promocode_offline := fm.home.show_promocode_offline
    FROM fm.home
    FOR
        customer_id := @BIND_KEYSET(all, customers),
        order_id := @BIND_KEYSET(all, orders)
)
GROUP BY show_promocode_offline

2. Real-time / Serving

1

Define online sources

Same types as offline — recency and LTV cents, read from Redis.

CREATE FEATURES IN fm.home AS
SELECT
    redis_source := SOURCE_REDIS(
        'redis://…'
        WITH (timeout='500ms')
    ),
    redis_key := 'tutorial:featuremesh:'
        || UNSAFE_CAST(customer_id AS VARCHAR),
    recency_online := CAST(
        EXTERNAL_REDIS(KEY redis_key FIELD 'days_since_order' FROM redis_source)
        AS BIGINT
    ),
    customer_ltv_cents_online := CAST(
        EXTERNAL_REDIS(KEY redis_key FIELD 'lifetime_value_cents' FROM redis_source)
        AS BIGINT
    )
;
2

Re-use features online

Swap warehouse dependencies for Redis — keep the same promo rule.

CREATE FEATURE fm.home.show_promocode_online AS
VARIANT(
    fm.home.show_promocode_offline
    REPLACING fm.home.recency, fm.home.customer_ltv_cents
    WITH fm.home.recency_online, fm.home.customer_ltv_cents_online
);
3

Compile as prepared statement

Bind a customer id, get the boolean at serving latency.

CREATE FEATURE fm.home.show_promocode_online_ps AS
PREPARED_STATEMENT(
    fm.home.show_promocode_online
    USING fm.home.customer_id
);
4

Integrate anywhere

Evaluation of the prepared statement is just an API call away.

POST /api/evaluate
Content-Type: application/json

{
    "id": "fm.home.show_promocode_online_ps",
    "inputs": [
        ["100"]
    ]
}

Who this is for

FeatureMesh is designed for teams where:

Business logic is high value and reused across systems.

Experimentation is a key part of the business.

AI agents are in production or about to be, and unstable behavior is unacceptable.