Metadata-Version: 2.4
Name: featuremesh
Version: 0.3.0
Summary: FeatureQL local transpiler and client for managed FeatureMesh infrastructure.
Project-URL: Homepage, https://featuremesh.com
Project-URL: Documentation, https://featuremesh.com/docs
Author-email: FeatureMesh <info@featuremesh.com>
License-Expression: LicenseRef-Proprietary
License-File: src/featuremesh/LICENSE
Keywords: bigquery,data,duckdb,feature-store,featuremesh,featureql,local,query,sql,transpiler,trino
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: <4.0,>=3.12
Requires-Dist: antlr4-python3-runtime<5,>=4.12.0
Requires-Dist: duckdb<2,>=1.3.2
Requires-Dist: ipython>=7.0.0
Requires-Dist: jinja2<4,>=3.1.2
Requires-Dist: jmespath<2,>=1.0.1
Requires-Dist: numpy>=1.26.0
Requires-Dist: opentelemetry-api>=1.0.0
Requires-Dist: pandas>=2.1.0
Requires-Dist: pydantic-settings>=2.13.1
Requires-Dist: pydantic>=2.10.2
Requires-Dist: requests>=2.31.0
Requires-Dist: returns>=0.26.0
Requires-Dist: sqlalchemy>=2.0.43
Requires-Dist: sqlglot>=30.11.0
Requires-Dist: sqlmodel>=0.0.22
Requires-Dist: structlog>=25.4.0
Provides-Extra: all
Requires-Dist: fastapi>=0.115.0; extra == 'all'
Requires-Dist: featuremesh-serving-runtime==0.1.0; extra == 'all'
Requires-Dist: google-cloud-bigquery>=3.0.0; extra == 'all'
Requires-Dist: mcp>=1.6.0; extra == 'all'
Requires-Dist: trino>=0.320.0; extra == 'all'
Requires-Dist: uvicorn[standard]>=0.32.0; extra == 'all'
Provides-Extra: bigquery
Requires-Dist: google-cloud-bigquery>=3.0.0; extra == 'bigquery'
Provides-Extra: dev
Requires-Dist: cython>=3.0.0; extra == 'dev'
Requires-Dist: fastapi>=0.115.0; extra == 'dev'
Requires-Dist: mcp>=1.6.0; extra == 'dev'
Requires-Dist: mypy>=1.11.0; extra == 'dev'
Requires-Dist: pre-commit>=4.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Requires-Dist: setuptools>=75.0.0; extra == 'dev'
Requires-Dist: types-requests>=2.31.0; extra == 'dev'
Provides-Extra: server
Requires-Dist: fastapi>=0.115.0; extra == 'server'
Requires-Dist: mcp>=1.6.0; extra == 'server'
Requires-Dist: uvicorn[standard]>=0.32.0; extra == 'server'
Provides-Extra: serving
Requires-Dist: featuremesh-serving-runtime==0.1.0; extra == 'serving'
Provides-Extra: trino
Requires-Dist: trino>=0.320.0; extra == 'trino'
Description-Content-Type: text/markdown

# FeatureMesh

FeatureQL transpiler and client. Translate and execute [FeatureQL](https://featuremesh.com/docs) queries locally or via managed FeatureMesh infrastructure.

## Quick Start

Works immediately after install -- no server, no account, no config:

```bash
pip install featuremesh
```

```python
from featuremesh import BatchClient

client = BatchClient()

result = client.query("""
    SELECT
        F1 := 1,
        F2 := 2,
        F3 := F1 + F2;
""")

print(result.dataframe)
#    F1  F2  F3
# 0   1   2   3
```

For common patterns and discovery commands, run `featuremesh.help()` in a Python shell.

## How It Works

FeatureMesh has two independent axes:

**Transpilation + Persistence** -- where FeatureQL is translated and feature definitions are stored:
- **Local** (default): bundled engine + SQLite. No network, no account.
- **Managed**: FeatureMesh infrastructure. Requires an access token.

**Execution** -- who runs the final SQL:
- **BatchClient**: your database runs it (DuckDB, Trino, BigQuery). For analytics, ETL, experimentation.
- **ServingClient**: the ``Backend.SERVING`` stack runs either embedded in Python or in your customer-operated featuremeshd deployment. FeatureMesh never operates a serving plane that receives your row data.

## Managed Mode

For team collaboration with shared feature definitions and access control:

```python
from featuremesh import BatchClient, RegistryDeployment, set_default

set_default("registry", RegistryDeployment.MANAGED)

client = BatchClient(
    access_token="your_access_token",  # from https://console.featuremesh.com
    sql_executor=your_sql_executor,
)
```

## Real-Time Serving

``ServingClient`` exposes the same productivity APIs as ``BatchClient``
(``query``, ``translate``, ``validate``, ``describe``, ``help``, ``diagnose``,
``sltest``, ``sltest_stream``).

### ``query`` vs documentation SLT (important)

``client.query(...)``, HTTP ``POST /query``, and MCP ``featuremesh_query`` take a
**FeatureQL string only** (the body under the SLT directive line). They do **not**
parse authored SLT harness syntax.

| Input | Use |
|-------|-----|
| FeatureQL (`SELECT …`, `CREATE FEATURES …`, `REFRESH FEATURES …`, …) | ``query`` / ``POST /query`` / ``featuremesh_query`` (use ``backend="serving"`` for realtime) |
| Full SLT block (`query …`, `----`, expected rows, ``# depends:``, ``# match:``, ``client validate|help|…``, …) | ``sltest`` / ``POST /sltest`` / ``featuremesh_sltest`` |
| Fixture SQL/Redis (`statement ok using postgres`, `using redis`, …) | ``sltest`` only (named / server ``serving_executors``) |
| Prepared-statement **calls** (`prepared FM.…` + NDJSON inputs) | ``sltest`` only, or ``ServingClient.execute_prepared_statement(...)`` — **not** ``query`` |

The default HTTP/MCP response field named ``slt`` is an **output** format (a
website-style snippet built from the query result). It does **not** mean you can
POST a full `.slt` file block as the request body.

For realtime tutorial chains (Postgres/Redis fixtures → `EXTERNAL_*` →
`PREPARED_STATEMENT` → `prepared` calls), run the documentation suite with
``backend="serving"`` via ``sltest``, or paste only the FeatureQL bodies into
``query`` after fixtures already exist in the session.

Embedded serving runs inside the Python process with the local SQLite registry:

```bash
pip install "featuremesh[serving]"
```

```python
from featuremesh import RegistryDeployment, ServingDeployment, ServingClient, set_default

set_default("registry", RegistryDeployment.LOCAL)
set_default("serving", ServingDeployment.EMBEDDED)

with ServingClient() as client:
    result = client.query("SELECT F := 1;")
    client.query("REFRESH FEATURES ALL;")
```

The first native release supports Python 3.12–3.14 on Linux x86_64/aarch64
and Python 3.13 on macOS arm64. The wheel contains DataFusion and the serving
connectors; its measured download size is published with each release.

ONPREM serving uses a customer-operated featuremeshd service over HTTP:

```python
from featuremesh import RegistryDeployment, ServingDeployment, ServingClient, set_default

set_default("registry", RegistryDeployment.MANAGED)
set_default("serving", ServingDeployment.ONPREM)
set_default("serving.host", "http://host.docker.internal:10090")  # local featuremeshd
client = ServingClient(access_token="your_access_token")
result = client.query("SELECT ...")
summary = client.sltest(where="NAME LIKE '%array%#%'")
```

`REFRESH FEATURES A.B.C, ...;` and `REFRESH FEATURES ALL;` are synchronous
serving control queries and return `QueryResult`. Embedded mode reads the local
registry and atomically swaps the loaded source/prepared-statement snapshot;
ONPREM mode asks featuremeshd to synchronize from the managed registry.

## Jupyter Notebooks

```python
%load_ext featuremesh

from featuremesh import BatchClient, set_default
set_default("client", BatchClient())
```

Then use `%%featureql` in cells. Options: `--client`, `--show-sql`, `--debug`, `--hide-dataframe`, `--show-slt`, `--hook VARIABLE`.

**`--hook`** puts a **plain `dict`** in the notebook namespace: the same shape as `QueryResult.to_dict()`. It is JSON-friendly; the `"dataframe"` key is either `None` or a **list of row dicts**, not a `pandas.DataFrame`. Use attribute access only on a real `QueryResult` from `client.query(...)`, not on the hooked dict.

**Getting a real `DataFrame` in Jupyter:**

- Call the client: `qr = client.query("""..."""); df = qr.dataframe`.
- Or rely on the magic return value: without `--hide-dataframe`, `%%featureql` returns the `DataFrame`; in the next cell use `df = _` (or `Out[n]`). With `--hide-dataframe`, the magic returns `None` by design.
- To rebuild a frame from a hook dict: `pd.DataFrame(hook["dataframe"])` when that key is not `None`.

If you do not call `set_default("client", client)`, the magic uses the only `BatchClient`/`ServingClient` in the notebook namespace when there is exactly one; otherwise set a default or pass `--client`.

For embedded serving, prefer `with ServingClient() as client:` and close a
long-lived notebook client explicitly before replacing it. Garbage collection
uses non-blocking best-effort cleanup; only `close()` guarantees deterministic
connector and runtime shutdown.

## FastAPI and process ownership

Create embedded clients in the FastAPI lifespan after worker processes have
forked, and close them on shutdown. Native calls are blocking: use synchronous
`def` endpoints so FastAPI runs them in its thread pool, or explicitly offload
them from `async def` endpoints.

```python
from contextlib import asynccontextmanager

from fastapi import FastAPI
from featuremesh import RegistryDeployment, ServingDeployment, ServingClient, set_default

@asynccontextmanager
async def lifespan(app: FastAPI):
    set_default("registry", RegistryDeployment.LOCAL)
    set_default("serving", ServingDeployment.EMBEDDED)
    app.state.serving = ServingClient()
    try:
        yield
    finally:
        app.state.serving.close()

app = FastAPI(lifespan=lifespan)
```

Do not construct the client in a preloaded Gunicorn master or at module import
before a fork. A PID check rejects inherited clients. Each Uvicorn/Gunicorn
worker owns an independent DataFusion engine, memory pool, and connector pools;
run one worker when only one engine copy is desired. Set
`serving.memory_limit_bytes` before construction to bound each engine
independently (the default is 1 GiB). A client owns its native runtime,
connectors, and local-registry connection; do not share those resources across
processes, and call `close()` in the owning process.

## Result objects

| Method | Returns | Notable fields / behavior |
|--------|---------|---------------------------|
| `client.query(fql)` | `QueryResult` | FeatureQL input only. `.dataframe`, `.sql`, `.slt` (output), `.success`, `.errors`, `.warnings`, timings, `.column_types`, `.display()`, `.to_dict()` |
| `client.translate(fql)` | `TranslateResult` | `.sql`, `.success`, `.errors`, `.warnings`, `.debug_logs`, `.display()` |
| `client.help(*terms)` | `HelpResult` | `.text`, structured row lists, `.display()` |
| `client.describe(*prefixes)` | `DescribeResult` | `.text`, `.features_list`, `.display()` |
| `client.validate(query)` | `ValidateResult` | `.text`, `.formatted_featureql`, `.output_schema`, `.display()` |
| `client.sltest(...)` | `list[dict]` | Full authored SLT (incl. `using` / `prepared`); flat rows (`status` / `backend` / `client` / `batch_id` / `depends`); `where` is FeatureQL, optional `order_by` (`ASC`/`DESC` on `NAME`) / `offset` / `limit`; `# depends:` expansion needs a **DuckDB** `fetch_client` when execute backend differs; optional `source=`, `serving_executors=`, `max_workers=`, `labels=` |
| `client.sltest_multi([c1, c2], ...)` / `sltest_multi(...)` | `list[dict]` | Same flat rows; fetch once; clients concurrent; independent advance; `labels_by_backend=` / `max_workers_by_backend=` |

Use `json.dumps(..., cls=FeatureMeshJSONEncoder)` for result objects.

## More

- **Documentation**: [featuremesh.com/docs](https://featuremesh.com/docs)
- **Quick reference**: `featuremesh.help()` in Python
- **FeatureQL discovery**: `SHOW SIGNATURES`, `SHOW DOCS`, `SHOW TESTS` (run as queries)
- **Type stubs**: Ships with `py.typed` — full type-checking support in mypy, pyright, etc.
- **Support**: info@featuremesh.com

## License

FeatureMesh Proprietary License 1.0
Copyright (c) 2026 FeatureMesh
