The business logic platform for AI

FeatureMesh turns your business logic into a single, executable source of truth,
so AI can reason and act on it with confidence.

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 english in a .md file is not enough

And semantic layers definitions in YAML better but still insufficient.
Composition, typing, and variants are missing.

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 scattered 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.

Semantic types

BIGINT#CUSTOMERS is not the same type as BIGINT#ORDERS. DECIMAL@CURRENCY.USD is not the same as DECIMAL@CURRENCY.EUR. An LLM cannot accidentally join two unrelated tables or add dollars to euros, because the type system rejects both at compile time.

Deterministic execution

A feature definition expresses a universal business intent and transpiles to executable SQL on your warehouse of choice. 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.

From reactive to proactive agents A reactive agent analyzes history and proposes a change.
A proactive agent tests that change in production: eligibility, pricing, fraud, personalization...
It is what VARIANT() and prepared statements in DataFusion 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(dim_customers_keyset),
        order_id := @BIND_KEYSET(fct_orders_keyset)
)
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 particularly 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.