Visitar URL original
feat: Enable static artifacts for feature server that can be used in Feature Transformations by franciscojavierarceo · Pull Request #5787 · feast-dev/feast · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
108 changes: 108 additions & 0 deletions docs/reference/feature-servers/python-feature-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,114 @@ To start the feature server in TLS mode, you need to provide the private and pub
feast serve --key /path/to/key.pem --cert /path/to/cert.pem
```

# Static Artifacts Loading

## Overview

Static artifacts loading allows you to load models, lookup tables, and other static resources once during feature server startup instead of loading them on each request. These artifacts are cached in memory and accessible to on-demand feature views.

## Usage

### Create Static Artifacts Module

Create a `static_artifacts.py` file in your feature repository:

```python
# static_artifacts.py
from fastapi import FastAPI
from transformers import pipeline

def load_sentiment_model():
"""Load sentiment analysis model."""
return pipeline(
"sentiment-analysis",
model="cardiffnlp/twitter-roberta-base-sentiment-latest",
device="cpu"
)

def load_lookup_tables():
"""Load static lookup tables."""
return {
"sentiment_labels": {"LABEL_0": "negative", "LABEL_1": "neutral", "LABEL_2": "positive"},
"domain_categories": {"twitter.com": "social", "news.com": "news"},
}

def load_artifacts(app: FastAPI):
"""Load static artifacts into app.state."""
app.state.sentiment_model = load_sentiment_model()
app.state.lookup_tables = load_lookup_tables()

# Update global references for access from feature views
import example_repo
example_repo._sentiment_model = app.state.sentiment_model
example_repo._lookup_tables = app.state.lookup_tables
```

### Use Artifacts in Feature Views

Access pre-loaded artifacts in your on-demand feature views:

```python
# example_repo.py
import pandas as pd
from feast.on_demand_feature_view import on_demand_feature_view

# Global references for static artifacts
_sentiment_model = None
_lookup_tables: dict = {}

@on_demand_feature_view(
sources=[text_input_request],
schema=[
Field(name="predicted_sentiment", dtype=String),
Field(name="sentiment_confidence", dtype=Float32),
],
)
def sentiment_prediction(inputs: pd.DataFrame) -> pd.DataFrame:
"""Sentiment prediction using pre-loaded artifacts."""
global _sentiment_model, _lookup_tables

results = []
for text in inputs["input_text"]:
predictions = _sentiment_model(text)
label_map = _lookup_tables["sentiment_labels"]
best_pred = max(predictions, key=lambda x: x["score"])
predicted_sentiment = label_map[best_pred["label"]]
confidence = best_pred["score"]

results.append({
"predicted_sentiment": predicted_sentiment,
"sentiment_confidence": confidence,
})

return pd.DataFrame(results)
```

### Start Feature Server

```bash
feast serve
```

The server will automatically load static artifacts during startup.

## Supported Artifact Types

- Small to medium ML models
- Lookup tables and reference data
- Configuration parameters
- Pre-computed embeddings

## Example Template

The PyTorch NLP template demonstrates static artifacts loading:

```bash
feast init my-nlp-project -t pytorch_nlp
cd my-nlp-project/feature_repo
feast serve
```

# Online Feature Server Permissions and Access Control

## API Endpoints and Permissions
Expand Down
49 changes: 49 additions & 0 deletions sdk/python/feast/feature_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,52 @@ async def _get_features(
return features


async def load_static_artifacts(app: FastAPI, store):
"""
Load static artifacts (models, lookup tables, etc.) into app.state.

This function can be extended to load various types of static artifacts:
- Small ML models (scikit-learn, small neural networks)
- Lookup tables and reference data
- Configuration parameters
- Pre-computed embeddings

Note: Not recommended for large language models - use dedicated
model serving solutions (vLLM, TGI, etc.) for those.
"""
try:
# Import here to avoid loading heavy dependencies unless needed
import importlib.util
import inspect
from pathlib import Path

# Look for static artifacts loading in the feature repository
# This allows templates and users to define their own artifact loading
repo_path = Path(store.repo_path) if store.repo_path else Path.cwd()
artifacts_file = repo_path / "static_artifacts.py"

if artifacts_file.exists():
# Load and execute custom static artifacts loading
spec = importlib.util.spec_from_file_location(
"static_artifacts", artifacts_file
)
if spec and spec.loader:
artifacts_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(artifacts_module)

# Look for load_artifacts function
if hasattr(artifacts_module, "load_artifacts"):
load_func = artifacts_module.load_artifacts
if inspect.iscoroutinefunction(load_func):
await load_func(app)
else:
load_func(app)
logger.info("Loaded static artifacts from static_artifacts.py")
except Exception as e:
# Non-fatal error - feature server should still start
logger.warning(f"Failed to load static artifacts: {e}")


def get_app(
store: "feast.FeatureStore",
registry_ttl_sec: int = DEFAULT_FEATURE_SERVER_REGISTRY_TTL,
Expand Down Expand Up @@ -217,6 +263,9 @@ def async_refresh():

@asynccontextmanager
async def lifespan(app: FastAPI):
# Load static artifacts before initializing store
await load_static_artifacts(app, store)

await store.initialize()
async_refresh()
yield
Expand Down
127 changes: 116 additions & 11 deletions sdk/python/feast/templates/pytorch_nlp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ my-sentiment-project/
├── README.md # This file
└── feature_repo/
├── feature_store.yaml # Feast configuration
├── example_repo.py # Feature definitions
├── example_repo.py # Feature definitions (uses pre-loaded artifacts)
├── static_artifacts.py # Static artifacts loading (models, lookup tables)
├── test_workflow.py # Complete demo workflow
└── data/ # Generated sample data
└── sentiment_data.parquet
Expand Down Expand Up @@ -205,6 +206,109 @@ offline_store:
- ✅ **No external services** - No Redis/cloud required
- ✅ **Perfect for demos** - Easy to share and understand

## 🚀 Static Artifacts Loading

This template demonstrates **static artifacts loading** - a performance optimization that loads models, lookup tables, and other artifacts once at feature server startup instead of on each request.

### What are Static Artifacts?

Static artifacts are pre-loaded resources that remain constant during server operation:
- **Small ML models** (sentiment analysis, classification, small neural networks)
- **Lookup tables and mappings** (label encoders, category mappings)
- **Configuration data** (model parameters, feature mappings)
- **Pre-computed embeddings** (user embeddings, item features)

### Performance Benefits

**Before (Per-Request Loading):**
```python
def sentiment_prediction(inputs):
# ❌ Model loads on every request - slow!
model = pipeline("sentiment-analysis", model="...")
return model(inputs["text"])
```

**After (Startup Loading):**
```python
# ✅ Model loads once at server startup
def sentiment_prediction(inputs):
global _sentiment_model # Pre-loaded model
return _sentiment_model(inputs["text"])
```

**Performance Impact:**
- 🚀 **10-100x faster** inference (no model loading overhead)
- 💾 **Lower memory usage** (shared model across requests)
- ⚡ **Better scalability** (consistent response times)

### How It Works

1. **Startup**: Feast server loads `static_artifacts.py` during initialization
2. **Loading**: `load_artifacts(app)` function stores models in `app.state`
3. **Access**: On-demand feature views access pre-loaded artifacts via global references

```python
# static_artifacts.py - Define what to load
def load_artifacts(app: FastAPI):
app.state.sentiment_model = load_sentiment_model()
app.state.lookup_tables = load_lookup_tables()

# Update global references for easy access
import example_repo
example_repo._sentiment_model = app.state.sentiment_model
example_repo._lookup_tables = app.state.lookup_tables

# example_repo.py - Use pre-loaded artifacts
_sentiment_model = None # Set by static_artifacts.py

def sentiment_prediction(inputs):
global _sentiment_model
if _sentiment_model is not None:
return _sentiment_model(inputs["text"])
else:
return fallback_predictions()
```

### Scope and Limitations

**✅ Great for:**
- Small to medium models (< 1GB)
- Fast-loading models (sentiment analysis, classification)
- Lookup tables and reference data
- Configuration parameters
- Pre-computed embeddings

**❌ Not recommended for:**
- **Large Language Models (LLMs)** - Use dedicated serving solutions like vLLM, TGI, or TensorRT-LLM
- Models requiring GPU clusters
- Frequently updated models
- Models with complex initialization dependencies

**Note:** Feast is optimized for feature serving, not large model inference. For production LLM workloads, use specialized model serving platforms.

### Customizing Static Artifacts

To add your own artifacts, modify `static_artifacts.py`:

```python
def load_custom_embeddings():
"""Load pre-computed user embeddings."""
embeddings_file = Path(__file__).parent / "data" / "user_embeddings.npy"
if embeddings_file.exists():
import numpy as np
return {"embeddings": np.load(embeddings_file)}
return None

def load_artifacts(app: FastAPI):
# Load your custom artifacts
app.state.custom_embeddings = load_custom_embeddings()
app.state.config_params = {"threshold": 0.7, "top_k": 10}

# Make them available to feature views
import example_repo
example_repo._custom_embeddings = app.state.custom_embeddings
```

## 📚 Detailed Usage

### 1. Feature Store Setup
Expand Down Expand Up @@ -409,20 +513,21 @@ def toxicity_detection(inputs: pd.DataFrame) -> pd.DataFrame:
### Performance Optimization

**Current Architecture:**
- Models load on each request (see `sentiment_prediction` function)
- ✅ **Static artifacts loading** at server startup (see `static_artifacts.py`)
- ✅ **Pre-loaded models** cached in memory for fast inference
- CPU-only operation to avoid multiprocessing issues
- SQLite-based storage for fast local access

**TODO: Optimization Opportunities:**
- **Startup-time Model Loading**: Load models once at server startup instead of per-request
- **Custom Provider**: Implement model caching via custom Feast provider
- **Model Serving Layer**: Use dedicated model servers (TorchServe, MLflow) for heavy models
**Implemented Optimizations:**
- **Startup-time Model Loading**: ✅ Models load once at server startup via `static_artifacts.py`
- **Memory-efficient Caching**: ✅ Models stored in `app.state` and accessed via global references
- **Fallback Handling**: ✅ Graceful degradation when artifacts fail to load

**Production Optimizations:**
1. **Model Caching**: Cache loaded models in memory to avoid repeated loading
2. **Batch Inference**: Process multiple texts together for efficiency
3. **Feature Materialization**: Pre-compute expensive features offline
4. **Async Processing**: Use async patterns for real-time serving
**Additional Production Optimizations:**
1. **Batch Inference**: Process multiple texts together for efficiency
2. **Feature Materialization**: Pre-compute expensive features offline
3. **Async Processing**: Use async patterns for real-time serving
4. **Model Serving Layer**: Use dedicated model servers (TorchServe, vLLM) for large models

### Production Configuration Examples

Expand Down
Loading
Loading