Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
17 changes: 15 additions & 2 deletions sdk/python/feast/infra/online_stores/sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from feast.infra.online_stores.helpers import compute_table_id
from feast.infra.online_stores.online_store import OnlineStore
from feast.infra.online_stores.vector_store import VectorStoreConfig
from feast.labeling.label_view import LabelView
from feast.protos.feast.core.InfraObject_pb2 import InfraObject as InfraObjectProto
from feast.protos.feast.core.Registry_pb2 import Registry as RegistryProto
from feast.protos.feast.core.SqliteTable_pb2 import SqliteTable as SqliteTableProto
Expand Down Expand Up @@ -317,21 +318,33 @@ def plan(
self, config: RepoConfig, desired_registry_proto: RegistryProto
) -> List[InfraObject]:
project = config.project
versioning = config.registry.enable_online_feature_view_versioning

infra_objects: List[InfraObject] = [
SqliteTable(
path=self._get_db_path(config),
name=_table_id(
project,
FeatureView.from_proto(view),
config.registry.enable_online_feature_view_versioning,
versioning,
),
)
for view in [
*desired_registry_proto.feature_views,
*desired_registry_proto.stream_feature_views,
]
]

for lv_proto in desired_registry_proto.label_views:
if lv_proto.spec.online:
lv = LabelView.from_proto(lv_proto)
infra_objects.append(
SqliteTable(
path=self._get_db_path(config),
name=_table_id(project, lv, versioning),
)
)

return infra_objects

def teardown(
Expand Down Expand Up @@ -716,7 +729,7 @@ def _initialize_conn(
return db


def _table_id(project: str, table: FeatureView, enable_versioning: bool = False) -> str:
def _table_id(project: str, table: Any, enable_versioning: bool = False) -> str:
return compute_table_id(project, table, enable_versioning)


Expand Down
13 changes: 10 additions & 3 deletions sdk/python/feast/infra/registry/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -1598,8 +1598,15 @@ def _list_objects(
skip_udf: If True, call from_proto() but skip deserializing UDFs
(dill.loads). Returns Python objects suitable for filtering and
display without requiring the UDF's source module to be installed.
Only relevant for feature view types.
Only relevant for feature view types that contain UDFs.
"""
import inspect

supports_skip_udf = (
skip_udf
and "skip_udf" in inspect.signature(python_class.from_proto).parameters
)

with self.read_engine.begin() as conn:
stmt = select(table).where(table.c.project_id == project)
rows = conn.execute(stmt).all()
Expand All @@ -1611,8 +1618,8 @@ def _list_objects(
objects.append(proto)
else:
obj = (
python_class.from_proto(proto, skip_udf=skip_udf)
if skip_udf
python_class.from_proto(proto, skip_udf=True)
if supports_skip_udf
else python_class.from_proto(proto)
)
if utils.has_all_tags(obj.tags, tags):
Expand Down
27 changes: 27 additions & 0 deletions sdk/python/feast/templates/local/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ def bootstrap():
import pathlib
from datetime import datetime, timedelta

import pyarrow as pa
import pyarrow.parquet as pq

from feast.driver_test_data import create_driver_hourly_stats_df

repo_path = pathlib.Path(__file__).parent.absolute() / "feature_repo"
Expand All @@ -23,6 +26,25 @@ def bootstrap():
driver_stats_path = data_path / "driver_stats.parquet"
driver_df.to_parquet(path=str(driver_stats_path), allow_truncated_timestamps=True)

# Create an empty parquet file for the label view batch source
# Use explicit pyarrow schema to ensure proper column types even with zero rows
label_schema = pa.schema(
[
("driver_id", pa.int64()),
("is_reliable", pa.int64()),
("quality_score", pa.float32()),
("reviewer_notes", pa.string()),
("labeler", pa.string()),
("event_timestamp", pa.timestamp("ns")),
]
)
label_table = pa.table(
{field.name: pa.array([], type=field.type) for field in label_schema},
schema=label_schema,
)
label_data_path = data_path / "driver_quality_labels.parquet"
pq.write_table(label_table, str(label_data_path))

example_py_file = repo_path / "feature_definitions.py"
replace_str_in_file(example_py_file, "%PROJECT_NAME%", str(project_name))
replace_str_in_file(
Expand All @@ -31,6 +53,11 @@ def bootstrap():
replace_str_in_file(
example_py_file, "%LOGGING_PATH%", str(data_path.relative_to(repo_path))
)
replace_str_in_file(
example_py_file,
"%LABEL_DATA_PATH%",
str(label_data_path.relative_to(repo_path)),
)


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import pandas as pd

from feast import (
ConflictPolicy,
Entity,
FeatureService,
FeatureView,
Field,
FileSource,
LabelView,
Project,
PushSource,
RequestSource,
Expand Down Expand Up @@ -165,3 +167,41 @@ def transformed_conv_rate_fresh(inputs: pd.DataFrame) -> pd.DataFrame:
name="driver_activity_v3",
features=[driver_stats_fresh_fv, transformed_conv_rate_fresh],
)

# --- Label Views ---
# Label views manage mutable human labels for training data, RLHF, and evaluation.
# They use PushSources so labels can be submitted from the UI or external tools.

driver_quality_labels_source = PushSource(
name="driver_quality_labels_push",
batch_source=FileSource(
name="driver_quality_labels_batch",
path="%LABEL_DATA_PATH%",
timestamp_field="event_timestamp",
),
)

driver_quality_labels = LabelView(
name="driver_quality_labels",
entities=[driver],
schema=[
Field(name="is_reliable", dtype=Int64),
Field(name="quality_score", dtype=Float32),
Field(name="reviewer_notes", dtype=String),
Field(name="labeler", dtype=String),
],
source=driver_quality_labels_source,
labeler_field="labeler",
conflict_policy=ConflictPolicy.LAST_WRITE_WINS,
description="Human quality labels for drivers - used for model training and evaluation",
tags={
"feast.io/labeling-method": "table",
"feast.io/field-role:is_reliable": "label",
"feast.io/field-role:quality_score": "label",
"feast.io/field-role:reviewer_notes": "metadata",
"feast.io/label-values:is_reliable": "1,0",
"feast.io/label-widget:is_reliable": "binary",
"feast.io/label-widget:quality_score": "number",
"feast.io/label-widget:reviewer_notes": "text",
},
)
Loading
Loading