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
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), ;
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 ;
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 ;
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
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 ) ;
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 );
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 );
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.