Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
85a18dc
feat: Add enabled/disabled toggle for feature views
rpathade May 13, 2026
5e98c7f
feat: Add demo noteboooks for users
ntkathole May 8, 2026
f735cd2
feat: Add CLI enable/disable commands and registry metadata support
rpathade May 15, 2026
426ae09
Added features
rpathade May 16, 2026
365ecb4
fix(compute-engine/local): Honor field_mapping on join keys in dedup …
1fanwang May 13, 2026
5b94d4d
feat: Add Prometheus gauges for FeatureStore installation telemetry (…
ntkathole May 13, 2026
b5297b8
docs: Rename Atlas Vector Search to MongoDB Vector Search and fix cod…
jvincent-mongodb May 13, 2026
04d500c
feat(dynamodb): Use ProjectionExpression when requested_features is set
Jwrede May 3, 2026
c9e368b
fix(dynamodb): Fix mypy type for _build_projection_expression return
Jwrede May 3, 2026
de050ab
fix(bigquery): Enable list inference for parquet loads in offline_wri…
Jwrede May 3, 2026
78fcd72
fix(trino): Clean up temporary entity tables after retrieval (#6381)
Jwrede May 14, 2026
2014acd
feat(bigquery): Support DATE-type event timestamp columns (#6362)
Jwrede May 14, 2026
5e69e5f
fix: Fixes for ray source
ntkathole May 14, 2026
53e4a92
feat: Expose registry endpoints on feature server for MCP access
patelchaitany Apr 21, 2026
e9791b0
fix: Revert state propagation to always update in _update_metadata_fi…
rpathade May 16, 2026
f7fd0f8
fix: Recompile protos for protobuf 4.x compatibility and fix state ma…
rpathade May 18, 2026
a4f7ab8
feat: Add unit tests for state machine and clean up lazy imports in r…
rpathade May 18, 2026
3aa8778
fix: Address review comments for feature view state management
rpathade May 20, 2026
e626433
fix: Resolve integration test failures in apply loop
rpathade May 20, 2026
8130b63
fix: Resolve integration test failures in apply loop
rpathade May 20, 2026
2b54bd0
Apply suggestion from @ntkathole
rpathade May 21, 2026
cb33b6d
fix: Resolve review comments for feature_store
rpathade May 21, 2026
f3b94eb
fix: Resolve review comments for feature_views.py
rpathade May 21, 2026
a4c85dc
Merge branch 'master' into feat/feature-view-enabled-disabled-v2
rpathade May 21, 2026
abb6a41
Merge remote-tracking branch 'origin/master' into feat/feature-view-e…
rpathade May 21, 2026
f4dd0a4
feat: Add FeatureStore methods and update describe for enabled/state
rpathade May 22, 2026
a3f65b0
Merge branch 'master' into feat/feature-view-enabled-disabled-v2
rpathade May 22, 2026
efd41a6
fix: Add type: ignore comments for mypy on BaseFeatureView attr access
rpathade May 22, 2026
436a712
fix: Remove REST API endpoints for enable/disable/set-state (deferred…
rpathade May 22, 2026
7110346
Merge remote-tracking branch 'origin/master' into feat/feature-view-e…
rpathade May 22, 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
Prev Previous commit
Next Next commit
fix: Add type: ignore comments for mypy on BaseFeatureView attr access
Signed-off-by: RutujaPathade <73137503+RutujaPathade@users.noreply.github.com>
  • Loading branch information
rpathade committed May 22, 2026
commit efd41a6f4d15240b4e23b51ac5b335312e405b2a
154 changes: 154 additions & 0 deletions sdk/python/feast/api/registry/rest/feature_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,160 @@ def apply_feature_view(body: ApplyFeatureViewRequestBody):
},
)

@router.put("/feature_views/{name}/enable")
def enable_feature_view(
name: str,
project: str = Query(...),
):
req = RegistryServer_pb2.GetAnyFeatureViewRequest(
name=name,
project=project,
)
resp = grpc_call(grpc_handler.GetAnyFeatureView, req)
any_fv = resp.any_feature_view

from feast.feature_view import FeatureView
from feast.on_demand_feature_view import OnDemandFeatureView
from feast.stream_feature_view import StreamFeatureView

if any_fv.HasField("feature_view"):
fv = FeatureView.from_proto(any_fv.feature_view)
elif any_fv.HasField("on_demand_feature_view"):
fv = OnDemandFeatureView.from_proto(any_fv.on_demand_feature_view)
elif any_fv.HasField("stream_feature_view"):
fv = StreamFeatureView.from_proto(any_fv.stream_feature_view)
else:
return JSONResponse(
status_code=404,
content={"error": f"Feature view '{name}' not found."},
)

fv.enabled = True
apply_req = RegistryServer_pb2.ApplyFeatureViewRequest(
project=project, commit=True
)
if isinstance(fv, StreamFeatureView):
apply_req.stream_feature_view.CopyFrom(fv.to_proto())
elif isinstance(fv, OnDemandFeatureView):
apply_req.on_demand_feature_view.CopyFrom(fv.to_proto())
else:
apply_req.feature_view.CopyFrom(fv.to_proto())
grpc_call(grpc_handler.ApplyFeatureView, apply_req)

return {"name": name, "project": project, "enabled": True}

@router.put("/feature_views/{name}/disable")
def disable_feature_view(
name: str,
project: str = Query(...),
):
req = RegistryServer_pb2.GetAnyFeatureViewRequest(
name=name,
project=project,
)
resp = grpc_call(grpc_handler.GetAnyFeatureView, req)
any_fv = resp.any_feature_view

from feast.feature_view import FeatureView
from feast.on_demand_feature_view import OnDemandFeatureView
from feast.stream_feature_view import StreamFeatureView

if any_fv.HasField("feature_view"):
fv = FeatureView.from_proto(any_fv.feature_view)
elif any_fv.HasField("on_demand_feature_view"):
fv = OnDemandFeatureView.from_proto(any_fv.on_demand_feature_view)
elif any_fv.HasField("stream_feature_view"):
fv = StreamFeatureView.from_proto(any_fv.stream_feature_view)
else:
return JSONResponse(
status_code=404,
content={"error": f"Feature view '{name}' not found."},
)

fv.enabled = False
apply_req = RegistryServer_pb2.ApplyFeatureViewRequest(
project=project, commit=True
)
if isinstance(fv, StreamFeatureView):
apply_req.stream_feature_view.CopyFrom(fv.to_proto())
elif isinstance(fv, OnDemandFeatureView):
apply_req.on_demand_feature_view.CopyFrom(fv.to_proto())
else:
apply_req.feature_view.CopyFrom(fv.to_proto())
grpc_call(grpc_handler.ApplyFeatureView, apply_req)

return {"name": name, "project": project, "enabled": False}

@router.put("/feature_views/{name}/set-state")
def set_feature_view_state(
name: str,
state: str = Query(...),
project: str = Query(...),
):
from feast.feature_view import (
_VALID_STATE_TRANSITIONS,
FeatureView,
FeatureViewState,
)
from feast.on_demand_feature_view import OnDemandFeatureView
from feast.stream_feature_view import StreamFeatureView

try:
new_state = FeatureViewState[state.upper()]
except KeyError:
return JSONResponse(
status_code=400,
content={
"error": f"Invalid state '{state}'. "
f"Valid states: CREATED, GENERATED, MATERIALIZING, AVAILABLE_ONLINE."
},
)

req = RegistryServer_pb2.GetAnyFeatureViewRequest(
name=name,
project=project,
)
resp = grpc_call(grpc_handler.GetAnyFeatureView, req)
any_fv = resp.any_feature_view

if any_fv.HasField("feature_view"):
fv = FeatureView.from_proto(any_fv.feature_view)
elif any_fv.HasField("on_demand_feature_view"):
fv = OnDemandFeatureView.from_proto(any_fv.on_demand_feature_view)
elif any_fv.HasField("stream_feature_view"):
fv = StreamFeatureView.from_proto(any_fv.stream_feature_view)
else:
return JSONResponse(
status_code=404,
content={"error": f"Feature view '{name}' not found."},
)

if not fv.state.can_transition_to(new_state):
current = fv.state.name
allowed = _VALID_STATE_TRANSITIONS.get(fv.state, set())
allowed_names = ", ".join(sorted(s.name for s in allowed)) or "none"
return JSONResponse(
status_code=400,
content={
"error": f"Invalid state transition: {current} -> {new_state.name}. "
f"Allowed transitions from {current}: {allowed_names}."
},
)

fv.state = new_state
apply_req = RegistryServer_pb2.ApplyFeatureViewRequest(
project=project, commit=True
)
if isinstance(fv, StreamFeatureView):
apply_req.stream_feature_view.CopyFrom(fv.to_proto())
elif isinstance(fv, OnDemandFeatureView):
apply_req.on_demand_feature_view.CopyFrom(fv.to_proto())
else:
apply_req.feature_view.CopyFrom(fv.to_proto())
grpc_call(grpc_handler.ApplyFeatureView, apply_req)

return {"name": name, "project": project, "state": new_state.name}

@router.delete("/feature_views/{name}")
def delete_feature_view(
name: str,
Expand Down
10 changes: 5 additions & 5 deletions sdk/python/feast/feature_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ def enable_feature_view(self, name: str):
name: Name of feature view.
"""
fv = self.registry.get_any_feature_view(name, self.project)
fv.enabled = True
fv.enabled = True # type: ignore[attr-defined]
self.registry.apply_feature_view(fv, self.project)

def disable_feature_view(self, name: str):
Expand All @@ -817,7 +817,7 @@ def disable_feature_view(self, name: str):
name: Name of feature view.
"""
fv = self.registry.get_any_feature_view(name, self.project)
fv.enabled = False
fv.enabled = False # type: ignore[attr-defined]
self.registry.apply_feature_view(fv, self.project)

def set_feature_view_state(self, name: str, state: FeatureViewState):
Expand All @@ -829,11 +829,11 @@ def set_feature_view_state(self, name: str, state: FeatureViewState):
state: Target state.
"""
fv = self.registry.get_any_feature_view(name, self.project)
if not fv.state.can_transition_to(state):
if not fv.state.can_transition_to(state): # type: ignore[attr-defined]
raise ValueError(
f"Invalid state transition: {fv.state.name} -> {state.name}."
f"Invalid state transition: {fv.state.name} -> {state.name}." # type: ignore[attr-defined]
)
fv.state = state
fv.state = state # type: ignore[attr-defined]
self.registry.apply_feature_view(fv, self.project)

def delete_feature_service(self, name: str):
Expand Down
Loading