diff --git a/protos/feast/core/FeatureView.proto b/protos/feast/core/FeatureView.proto index 60cf5d28d3..a4fca76df9 100644 --- a/protos/feast/core/FeatureView.proto +++ b/protos/feast/core/FeatureView.proto @@ -35,6 +35,7 @@ message FeatureView { FeatureViewMeta meta = 2; } +// Next available id: 12 // TODO(adchia): refactor common fields from this and ODFV into separate metadata proto message FeatureViewSpec { // Name of the feature view. Must be unique. Not updated. @@ -50,9 +51,15 @@ message FeatureViewSpec { // List of features specifications for each feature defined with this feature view. repeated FeatureSpecV2 features = 4; + // Description of the feature view. + string description = 10; + // User defined metadata map tags = 5; + // Owner of the feature view. + string owner = 11; + // Features in this feature view can only be retrieved from online serving // younger than ttl. Ttl is measured as the duration of time between // the feature's event timestamp and when the feature is retrieved diff --git a/protos/feast/core/OnDemandFeatureView.proto b/protos/feast/core/OnDemandFeatureView.proto index 5c373ab6f6..b265f73966 100644 --- a/protos/feast/core/OnDemandFeatureView.proto +++ b/protos/feast/core/OnDemandFeatureView.proto @@ -34,6 +34,7 @@ message OnDemandFeatureView { OnDemandFeatureViewMeta meta = 2; } +// Next available id: 9 message OnDemandFeatureViewSpec { // Name of the feature view. Must be unique. Not updated. string name = 1; @@ -48,6 +49,15 @@ message OnDemandFeatureViewSpec { map inputs = 4; UserDefinedFunction user_defined_function = 5; + + // Description of the on demand feature view. + string description = 6; + + // User defined metadata. + map tags = 7; + + // Owner of the on demand feature view. + string owner = 8; } message OnDemandFeatureViewMeta { diff --git a/protos/feast/core/RequestFeatureView.proto b/protos/feast/core/RequestFeatureView.proto index 541ad788dc..4049053c2b 100644 --- a/protos/feast/core/RequestFeatureView.proto +++ b/protos/feast/core/RequestFeatureView.proto @@ -29,6 +29,7 @@ message RequestFeatureView { RequestFeatureViewSpec spec = 1; } +// Next available id: 7 message RequestFeatureViewSpec { // Name of the feature view. Must be unique. Not updated. string name = 1; @@ -38,4 +39,13 @@ message RequestFeatureViewSpec { // Request data which contains the underlying data schema and list of associated features DataSource request_data_source = 3; + + // Description of the request feature view. + string description = 4; + + // User defined metadata. + map tags = 5; + + // Owner of the request feature view. + string owner = 6; } diff --git a/sdk/python/feast/base_feature_view.py b/sdk/python/feast/base_feature_view.py index b0cd70617f..a42d9e0963 100644 --- a/sdk/python/feast/base_feature_view.py +++ b/sdk/python/feast/base_feature_view.py @@ -11,10 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import warnings from abc import ABC, abstractmethod from datetime import datetime -from typing import List, Optional, Type +from typing import Dict, List, Optional, Type from google.protobuf.json_format import MessageToJson from proto import Message @@ -22,22 +21,65 @@ from feast.feature import Feature from feast.feature_view_projection import FeatureViewProjection -warnings.simplefilter("once", DeprecationWarning) - class BaseFeatureView(ABC): - """A FeatureView defines a logical grouping of features to be served.""" - + """ + A BaseFeatureView defines a logical group of features. + + Attributes: + name: The unique name of the base feature view. + features: The list of features defined as part of this base feature view. + description: A human-readable description. + tags: A dictionary of key-value pairs to store arbitrary metadata. + owner: The owner of the base feature view, typically the email of the primary + maintainer. + projection: The feature view projection to be applied to this base feature view + at retrieval time. + created_timestamp (optional): The time when the base feature view was created. + last_updated_timestamp (optional): The time when the base feature view was last + updated. + """ + + name: str + features: List[Feature] + description: str + tags: Dict[str, str] + owner: str + projection: FeatureViewProjection created_timestamp: Optional[datetime] last_updated_timestamp: Optional[datetime] @abstractmethod - def __init__(self, name: str, features: List[Feature]): + def __init__( + self, + name: str, + features: List[Feature], + description: str = "", + tags: Optional[Dict[str, str]] = None, + owner: str = "", + ): + """ + Creates a BaseFeatureView object. + + Args: + name: The unique name of the base feature view. + features: The list of features defined as part of this base feature view. + description (optional): A human-readable description. + tags (optional): A dictionary of key-value pairs to store arbitrary metadata. + owner (optional): The owner of the base feature view, typically the email of the + primary maintainer. + + Raises: + ValueError: A field mapping conflicts with an Entity or a Feature. + """ self.name = name self.features = features + self.description = description + self.tags = tags or {} + self.owner = owner self.projection = FeatureViewProjection.from_definition(self) - self.created_timestamp: Optional[datetime] = None - self.last_updated_timestamp: Optional[datetime] = None + self.created_timestamp = None + self.last_updated_timestamp = None @property @abstractmethod @@ -55,12 +97,7 @@ def from_proto(cls, feature_view_proto): @abstractmethod def __copy__(self): - """ - Generates a deep copy of this feature view - - Returns: - A copy of this FeatureView - """ + """Returns a deep copy of this base feature view.""" pass def __repr__(self): @@ -92,10 +129,13 @@ def __eq__(self, other): "Comparisons should only involve BaseFeatureView class objects." ) - if self.name != other.name: - return False - - if sorted(self.features) != sorted(other.features): + if ( + self.name != other.name + or sorted(self.features) != sorted(other.features) + or self.description != other.description + or self.tags != other.tags + or self.owner != other.owner + ): return False return True diff --git a/sdk/python/feast/entity.py b/sdk/python/feast/entity.py index b14997e979..817d884a65 100644 --- a/sdk/python/feast/entity.py +++ b/sdk/python/feast/entity.py @@ -36,8 +36,7 @@ class Entity: with their associated features. If not specified, defaults to the name. description: A human-readable description. tags: A dictionary of key-value pairs to store arbitrary metadata. - owner: The owner of the feature service, typically the email of the primary - maintainer. + owner: The owner of the entity, typically the email of the primary maintainer. created_timestamp: The time when the entity was created. last_updated_timestamp: The time when the entity was last updated. """ diff --git a/sdk/python/feast/feature_view.py b/sdk/python/feast/feature_view.py index 3e7561d338..e327d4f311 100644 --- a/sdk/python/feast/feature_view.py +++ b/sdk/python/feast/feature_view.py @@ -50,28 +50,36 @@ class FeatureView(BaseFeatureView): """ - A FeatureView defines a logical grouping of serveable features. + A FeatureView defines a logical group of features. - Args: - name: Name of the group of features. - entities: The entities to which this group of features is associated. + Attributes: + name: The unique name of the feature view. + entities: The list of entities with which this group of features is associated. ttl: The amount of time this group of features lives. A ttl of 0 indicates that this group of features lives forever. Note that large ttl's or a ttl of 0 can result in extremely computationally intensive queries. batch_source: The batch source of data where this group of features is stored. stream_source (optional): The stream source of data where this group of features is stored. - features (optional): The set of features defined as part of this FeatureView. - tags (optional): A dictionary of key-value pairs used for organizing - FeatureViews. + features: The list of features defined as part of this feature view. + online: A boolean indicating whether online retrieval is enabled for this feature + view. + description: A human-readable description. + tags: A dictionary of key-value pairs to store arbitrary metadata. + owner: The owner of the feature view, typically the email of the primary + maintainer. """ + name: str entities: List[str] - tags: Optional[Dict[str, str]] ttl: timedelta - online: bool batch_source: DataSource stream_source: Optional[DataSource] + features: List[Feature] + online: bool + description: str + tags: Dict[str, str] + owner: str materialization_intervals: List[Tuple[datetime, datetime]] @log_exceptions @@ -83,12 +91,31 @@ def __init__( batch_source: DataSource, stream_source: Optional[DataSource] = None, features: Optional[List[Feature]] = None, - tags: Optional[Dict[str, str]] = None, online: bool = True, + description: str = "", + tags: Optional[Dict[str, str]] = None, + owner: str = "", ): """ Creates a FeatureView object. + Args: + name: The unique name of the feature view. + entities: The list of entities with which this group of features is associated. + ttl: The amount of time this group of features lives. A ttl of 0 indicates that + this group of features lives forever. Note that large ttl's or a ttl of 0 + can result in extremely computationally intensive queries. + batch_source: The batch source of data where this group of features is stored. + stream_source (optional): The stream source of data where this group of features + is stored. + features (optional): The list of features defined as part of this feature view. + online (optional): A boolean indicating whether online retrieval is enabled for + this feature view. + description (optional): A human-readable description. + tags (optional): A dictionary of key-value pairs to store arbitrary metadata. + owner (optional): The owner of the feature view, typically the email of the + primary maintainer. + Raises: ValueError: A field mapping conflicts with an Entity or a Feature. """ @@ -106,9 +133,8 @@ def __init__( f"Entity or Feature name." ) - super().__init__(name, _features) + super().__init__(name, _features, description, tags, owner) self.entities = entities if entities else [DUMMY_ENTITY_NAME] - self.tags = tags if tags is not None else {} if isinstance(ttl, Duration): self.ttl = timedelta(seconds=int(ttl.seconds)) @@ -123,10 +149,9 @@ def __init__( else: self.ttl = ttl - self.online = online self.batch_source = batch_source self.stream_source = stream_source - + self.online = online self.materialization_intervals = [] # Note: Python requires redefining hash in child classes that override __eq__ @@ -312,7 +337,9 @@ def to_proto(self) -> FeatureViewProto: name=self.name, entities=self.entities, features=[feature.to_proto() for feature in self.features], + description=self.description, tags=self.tags, + owner=self.owner, ttl=(ttl_duration if ttl_duration is not None else None), online=self.online, batch_source=batch_source_proto, @@ -349,7 +376,9 @@ def from_proto(cls, feature_view_proto: FeatureViewProto): ) for feature in feature_view_proto.spec.features ], + description=feature_view_proto.spec.description, tags=dict(feature_view_proto.spec.tags), + owner=feature_view_proto.spec.owner, online=feature_view_proto.spec.online, ttl=( None diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 04b7f33cc6..f0eaf987ef 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -1,7 +1,7 @@ import copy import functools from types import MethodType -from typing import Dict, List, Type, Union +from typing import Dict, List, Optional, Type, Union import dill import pandas as pd @@ -33,20 +33,31 @@ class OnDemandFeatureView(BaseFeatureView): """ - [Experimental] An OnDemandFeatureView defines on demand transformations on existing feature view values and request - data. - - Args: - name: Name of the group of features. - features: Output schema of transformation with feature names - inputs: The input feature views passed into the transform. - udf: User defined transformation function that takes as input pandas dataframes + [Experimental] An OnDemandFeatureView defines a logical group of features, along with + transformations to be applied on those features and additional request data. + + Attributes: + name: The unique name of the on demand feature view. + features: The list of features in the output of the on demand feature view, after + the transformation has been applied. + inputs: The feature views and request data sources passed into the transformation. + udf: The user defined transformation function, which must take pandas dataframes + as inputs. + description: A human-readable description. + tags: A dictionary of key-value pairs to store arbitrary metadata. + owner: The owner of the on demand feature view, typically the email of the primary + maintainer. """ # TODO(adchia): remove inputs from proto and declaration + name: str + features: List[Feature] input_feature_view_projections: Dict[str, FeatureViewProjection] input_request_data_sources: Dict[str, RequestDataSource] udf: MethodType + description: str + tags: Dict[str, str] + owner: str @log_exceptions def __init__( @@ -55,11 +66,26 @@ def __init__( features: List[Feature], inputs: Dict[str, Union[FeatureView, FeatureViewProjection, RequestDataSource]], udf: MethodType, + description: str = "", + tags: Optional[Dict[str, str]] = None, + owner: str = "", ): """ Creates an OnDemandFeatureView object. + + Args: + name: The unique name of the on demand feature view. + features: The list of features in the output of the on demand feature view, after + the transformation has been applied. + inputs: The feature views and request data sources passed into the transformation. + udf: The user defined transformation function, which must take pandas dataframes + as inputs. + description (optional): A human-readable description. + tags (optional): A dictionary of key-value pairs to store arbitrary metadata. + owner (optional): The owner of the on demand feature view, typically the email + of the primary maintainer. """ - super().__init__(name, features) + super().__init__(name, features, description, tags, owner) self.input_feature_view_projections: Dict[str, FeatureViewProjection] = {} self.input_request_data_sources: Dict[str, RequestDataSource] = {} for input_ref, odfv_input in inputs.items(): @@ -138,6 +164,9 @@ def to_proto(self) -> OnDemandFeatureViewProto: user_defined_function=UserDefinedFunctionProto( name=self.udf.__name__, body=dill.dumps(self.udf, recurse=True), ), + description=self.description, + tags=self.tags, + owner=self.owner, ) return OnDemandFeatureViewProto(spec=spec, meta=meta) @@ -184,6 +213,9 @@ def from_proto(cls, on_demand_feature_view_proto: OnDemandFeatureViewProto): udf=dill.loads( on_demand_feature_view_proto.spec.user_defined_function.body ), + description=on_demand_feature_view_proto.spec.description, + tags=dict(on_demand_feature_view_proto.spec.tags), + owner=on_demand_feature_view_proto.spec.owner, ) # FeatureViewProjections are not saved in the OnDemandFeatureView proto. diff --git a/sdk/python/feast/request_feature_view.py b/sdk/python/feast/request_feature_view.py index 5d716f2f8d..410d692b56 100644 --- a/sdk/python/feast/request_feature_view.py +++ b/sdk/python/feast/request_feature_view.py @@ -1,5 +1,5 @@ import copy -from typing import Type +from typing import Dict, List, Optional, Type from feast.base_feature_view import BaseFeatureView from feast.data_source import RequestDataSource @@ -14,21 +14,47 @@ class RequestFeatureView(BaseFeatureView): """ - [Experimental] An RequestFeatureView defines a feature that is available from the inference request. - - Args: - name: Name of the group of features. - request_data_source: Request data source that specifies the schema and features + [Experimental] A RequestFeatureView defines a logical group of features that should + be available as an input to an on demand feature view at request time. + + Attributes: + name: The unique name of the request feature view. + request_data_source: The request data source that specifies the schema and + features of the request feature view. + features: The list of features defined as part of this request feature view. + description: A human-readable description. + tags: A dictionary of key-value pairs to store arbitrary metadata. + owner: The owner of the request feature view, typically the email of the primary + maintainer. """ + name: str request_data_source: RequestDataSource + features: List[Feature] + description: str + tags: Dict[str, str] + owner: str @log_exceptions def __init__( - self, name: str, request_data_source: RequestDataSource, + self, + name: str, + request_data_source: RequestDataSource, + description: str = "", + tags: Optional[Dict[str, str]] = None, + owner: str = "", ): """ - Creates an RequestFeatureView object. + Creates a RequestFeatureView object. + + Args: + name: The unique name of the request feature view. + request_data_source: The request data source that specifies the schema and + features of the request feature view. + description (optional): A human-readable description. + tags (optional): A dictionary of key-value pairs to store arbitrary metadata. + owner (optional): The owner of the request feature view, typically the email + of the primary maintainer. """ super().__init__( name=name, @@ -36,6 +62,9 @@ def __init__( Feature(name=name, dtype=dtype) for name, dtype in request_data_source.schema.items() ], + description=description, + tags=tags, + owner=owner, ) self.request_data_source = request_data_source @@ -51,7 +80,11 @@ def to_proto(self) -> RequestFeatureViewProto: A RequestFeatureViewProto protobuf. """ spec = RequestFeatureViewSpec( - name=self.name, request_data_source=self.request_data_source.to_proto() + name=self.name, + request_data_source=self.request_data_source.to_proto(), + description=self.description, + tags=self.tags, + owner=self.owner, ) return RequestFeatureViewProto(spec=spec) @@ -73,6 +106,9 @@ def from_proto(cls, request_feature_view_proto: RequestFeatureViewProto): request_data_source=RequestDataSource.from_proto( request_feature_view_proto.spec.request_data_source ), + description=request_feature_view_proto.spec.description, + tags=dict(request_feature_view_proto.spec.tags), + owner=request_feature_view_proto.spec.owner, ) # FeatureViewProjections are not saved in the RequestFeatureView proto.