Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
483e0bc
mlflow-tracing
Vperiodt May 14, 2026
e89e0a6
cross-process trace
Vperiodt May 19, 2026
bd1a625
Merge branch 'feast-dev:master' into mcp-trace
Vperiodt May 25, 2026
4cf9f6e
distributed tracing
Vperiodt May 26, 2026
5b3c20c
Merge branch 'feast-dev:master' into mcp-trace
Vperiodt May 27, 2026
1918d3c
Resolve CI failures in MLflow tracing and MCP server tests
Vperiodt May 27, 2026
189c20d
Merge branch 'master' into mcp-trace
Vperiodt Jun 2, 2026
1b54f74
Merge branch 'master' into mcp-trace
Vperiodt Jun 3, 2026
1f6058b
Merge branch 'feast-dev:master' into mcp-trace
Vperiodt Jun 3, 2026
2c80822
Merge branch 'master' into mcp-trace
Vperiodt Jun 4, 2026
1164e84
resolve issues
Vperiodt Jun 5, 2026
596c2ab
fix-CI
Vperiodt Jun 5, 2026
31b1416
format
Vperiodt Jun 8, 2026
a72e4c5
Merge branch 'master' into mcp-trace
Vperiodt Jun 8, 2026
ef12987
add tracing.md
Vperiodt Jun 8, 2026
75c6713
Merge branch 'feast-dev:master' into mcp-trace
Vperiodt Jun 9, 2026
41c15bd
Merge branch 'feast-dev:master' into mcp-trace
Vperiodt Jun 10, 2026
65ace98
address reviews
Vperiodt Jun 10, 2026
8e3cabc
update pixi.lock
Vperiodt Jun 10, 2026
648ad53
Merge branch 'master' into mcp-trace
Vperiodt Jun 10, 2026
fb72cdc
fix-lint
Vperiodt Jun 11, 2026
cd839cd
update docs
Vperiodt Jun 11, 2026
5d66d4e
Merge branch 'master' into mcp-trace
Vperiodt Jun 15, 2026
c7f541f
fine-tuning export, dataset registry, and tracing enhancements
Vperiodt Jun 15, 2026
7f97666
update pixi
Vperiodt Jun 15, 2026
c69a73d
Merge branch 'feast-dev:master' into mcp-trace
Vperiodt Jun 16, 2026
1cb9915
Merge branch 'master' into mcp-trace
Vperiodt Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions docs/reference/mlflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mlflow:
entity_df_max_rows: 100000 # default
log_operations: false # default
ops_experiment_suffix: "-feast-ops" # default
enable_distributed_tracing: true # default
```

### Configuration options
Expand All @@ -64,6 +65,7 @@ mlflow:
| `entity_df_max_rows` | int | `100000` | Skip entity DataFrame artifact upload for DataFrames exceeding this limit |
| `log_operations` | bool | `false` | Log `feast apply` and `feast materialize` to a separate MLflow experiment |
| `ops_experiment_suffix` | string | `"-feast-ops"` | Suffix appended to project name for the operations experiment |
| `enable_distributed_tracing` | bool | `true` | When enabled, server-side API calls create MLflow trace spans. Supports parent-child linking via `traceparent` headers. See [Distributed tracing](#distributed-tracing) |

### Tracking URI resolution

Expand Down Expand Up @@ -345,3 +347,159 @@ Start the Feast UI with:
```bash
feast ui --host 127.0.0.1 --port 8888
```

## Distributed tracing

When `enable_distributed_tracing` is `true` (the default when `mlflow.enabled=true`), server-side API calls create MLflow trace spans via `mlflow.start_span()`. Spans appear in the MLflow UI **Traces** tab and support parent-child linking via W3C `traceparent` headers.

All feature server endpoints will emit MLflow traces automatically.

### Automatic server traces (MCP & HTTP clients)

When any client (Cursor, Claude Desktop, or direct HTTP) calls the Feast feature server, traces are created automatically with no client-side code changes.

Each trace includes:

| Span attribute | Description |
|----------------|-------------|
| `feast.feature_refs` | Which features were served |
| `feast.entity_count` | Number of entities in the request |
| `feast.project` | Project name |
| `feast.retrieval_type` | `online` |
| `feast.mcp_session_id` | MCP session identifier (when called via MCP) |

The `mcp_session_id` groups all tool calls from a single MCP session (e.g., one Cursor chat conversation), allowing you to filter and correlate all feature retrievals made during that session.

**MCP client configuration** (`mcp.json`):

```json
{
"mcpServers": {
"feast": {
"url": "http://127.0.0.1:6567/mcp"
}
}
}
```

**Result:** Independent traces per request in MLflow, filterable by `feast.mcp_session_id`.

### Cross-process trace linking (HTTP agents)

When your agent calls the Feast feature server over HTTP, pass the `traceparent` header to produce a unified trace tree in MLflow.

```python
import mlflow
import requests

mlflow.set_tracking_uri("http://localhost:5000")
mlflow.openai.autolog()

@mlflow.trace
def my_agent(entity_id: int):
headers = mlflow.tracing.get_tracing_context_headers_for_http_request()

resp = requests.post(
"http://feast-server:6567/get-online-features",
json={"features": [...], "entities": {...}},
headers=headers,
)
features = resp.json()

response = llm.chat.completions.create(...)
return response.choices[0].message.content
```

**Result:** A single MLflow trace containing both agent spans and Feast server spans.

### In-process feature context tagging (SDK usage)

When using Feast as a Python library, use `feast_trace_scope` to capture which features were retrieved and attach that metadata to the LLM span.

```python
import mlflow
from feast import FeatureStore
from feast.tracing_context import feast_trace_scope

mlflow.set_tracking_uri("http://localhost:5000")
mlflow.openai.autolog()

store = FeatureStore(repo_path=".")

@mlflow.trace
def my_agent(entity_id: int):
feature_refs = ["my_view:feature_a", "my_view:feature_b"]

with feast_trace_scope() as ctx:
features = store.get_online_features(
features=feature_refs,
entity_rows=[{"entity_id": entity_id}],
)
ctx.push_retrieval(feature_refs)
context_attrs = ctx.get_context_attributes()

with mlflow.start_span(name="llm_call") as span:
span.set_attributes(context_attrs)
response = llm.chat.completions.create(...)

return response.choices[0].message.content
```

**Result:** The LLM span in MLflow contains:

- `feast.context_features` — which features were retrieved
- `feast.context_feature_views` — which feature views were queried
- `feast.context_feature_count` — number of features

### Automatic LLM span tagging

Instead of manually calling `ctx.get_context_attributes()` and `span.set_attributes(...)`, you can install the Feast span processor once at agent startup. It automatically tags any `LLM` / `CHAT_MODEL` span with Feast feature context when a `FeastTraceContext` is active:

```python
from feast.tracing_hooks import install_feast_span_processor

install_feast_span_processor()
```

With this installed, the agent code simplifies to:

```python
import mlflow
from feast import FeatureStore
from feast.tracing_context import feast_trace_scope
from feast.tracing_hooks import install_feast_span_processor

mlflow.set_tracking_uri("http://localhost:5000")
mlflow.openai.autolog()
install_feast_span_processor()

store = FeatureStore(repo_path=".")

@mlflow.trace
def my_agent(entity_id: int):
feature_refs = ["my_view:feature_a", "my_view:feature_b"]

with feast_trace_scope() as ctx:
features = store.get_online_features(
features=feature_refs,
entity_rows=[{"entity_id": entity_id}],
)
ctx.push_retrieval(feature_refs)

response = llm.chat.completions.create(...)
return response.choices[0].message.content
```

The LLM span is automatically tagged with `feast.context_*` attributes — no manual `set_attributes` call needed.

### Tracing API reference

| Function | Description |
|----------|-------------|
| `mlflow.tracing.get_tracing_context_headers_for_http_request()` | Returns `dict` with `traceparent` header for cross-process linking |
| `feast_trace_scope()` | Context manager that creates a `FeastTraceContext` (clears on exit) |
| `ctx.push_retrieval(feature_refs)` | Records retrieved feature references |
| `ctx.get_context_attributes()` | Returns span-ready `dict` of accumulated metadata |
| `install_feast_span_processor()` | Registers a span processor that auto-tags LLM spans with Feast context |

**Important:** Call `ctx.get_context_attributes()` inside the `with feast_trace_scope()` block. The context is cleared on scope exit.
10 changes: 7 additions & 3 deletions pixi.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ mssql = ["ibis-framework[mssql]>=10.0.0"]
oracle = ["ibis-framework[oracle]>=10.0.0"]
mysql = ["pymysql", "types-PyMySQL"]
openlineage = ["openlineage-python>=1.40.0"]
mlflow = [
"mlflow>=2.14.0",
"opentelemetry-api>=1.28.0",
"opentelemetry-sdk>=1.28.0",
"opentelemetry-instrumentation-fastapi>=0.49b0",
"opentelemetry-instrumentation-httpx>=0.49b0",
Comment on lines 107 to +115

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Consider adding opentelemetry-exporter-otlp-proto-http dependency

The mlflow extra includes several OpenTelemetry packages but is missing opentelemetry-exporter-otlp-proto-http which was mentioned in the pixi.lock file. This could cause issues if OTLP export is needed.

Suggested:

Suggested change
oracle = ["ibis-framework[oracle]>=10.0.0"]
mysql = ["pymysql", "types-PyMySQL"]
openlineage = ["openlineage-python>=1.40.0"]
mlflow = [
"mlflow>=2.14.0",
"opentelemetry-api>=1.28.0",
"opentelemetry-sdk>=1.28.0",
"opentelemetry-instrumentation-fastapi>=0.49b0",
"opentelemetry-instrumentation-httpx>=0.49b0",
mlflow = [
"mlflow>=2.14.0",
"opentelemetry-api>=1.28.0",
"opentelemetry-sdk>=1.28.0",
"opentelemetry-instrumentation-fastapi>=0.49b0",
"opentelemetry-instrumentation-httpx>=0.49b0",
"opentelemetry-exporter-otlp-proto-http>=1.28.0",
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added that before it was causing grpcio conflicts and
opentelemetry-exporter-otlp-proto-http is already a transitive dep of mlflow>=2.14.0

]
opentelemetry = ["prometheus_client", "psutil"]
spark = ["pyspark>=4.0.0"]
trino = ["trino>=0.305.0,<0.400.0", "regex"]
Expand Down Expand Up @@ -136,7 +143,6 @@ snowflake = [
]
sqlite_vec = ["sqlite-vec==v0.1.6"]
mcp = ["fastapi_mcp"]
mlflow = ["mlflow>=2.10.0"]

dbt = ["dbt-artifacts-parser"]

Expand Down
2 changes: 2 additions & 0 deletions sdk/python/feast/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
get_historical_features,
get_online_features,
)
from feast.cli.finetuning import finetuning_cmd
from feast.cli.label_views import label_views_cmd
from feast.cli.monitor import monitor_cmd
from feast.cli.on_demand_feature_views import on_demand_feature_views_cmd
Expand Down Expand Up @@ -653,6 +654,7 @@ def demo_notebooks_command(ctx: click.Context, output_dir: str, overwrite: bool)
cli.add_command(serve_registry_command)
cli.add_command(serve_transformations_command)
cli.add_command(dbt_cmd)
cli.add_command(finetuning_cmd)
cli.add_command(monitor_cmd)

if __name__ == "__main__":
Expand Down
Loading
Loading