3939from feast .protos .feast .core .Transformation_pb2 import (
4040 FeatureTransformationV2 as FeatureTransformationProto ,
4141)
42+ from feast .transformation .mode import TransformationMode
4243from feast .types import from_value_type
4344from feast .value_type import ValueType
4445
@@ -102,6 +103,7 @@ class FeatureView(BaseFeatureView):
102103 tags : Dict [str , str ]
103104 owner : str
104105 materialization_intervals : List [Tuple [datetime , datetime ]]
106+ mode : Optional [Union ["TransformationMode" , str ]]
105107
106108 def __init__ (
107109 self ,
@@ -117,6 +119,7 @@ def __init__(
117119 description : str = "" ,
118120 tags : Optional [Dict [str , str ]] = None ,
119121 owner : str = "" ,
122+ mode : Optional [Union ["TransformationMode" , str ]] = None ,
120123 ):
121124 """
122125 Creates a FeatureView object.
@@ -140,6 +143,7 @@ def __init__(
140143 tags (optional): A dictionary of key-value pairs to store arbitrary metadata.
141144 owner (optional): The owner of the feature view, typically the email of the
142145 primary maintainer.
146+ mode (optional): The transformation mode to use (e.g., python, pandas, spark, sql).
143147
144148 Raises:
145149 ValueError: A field mapping conflicts with an Entity or a Feature.
@@ -252,6 +256,7 @@ def __init__(
252256 )
253257 self .online = online
254258 self .offline = offline
259+ self .mode = mode
255260 self .materialization_intervals = []
256261
257262 def __hash__ (self ):
@@ -439,6 +444,14 @@ def to_proto_spec(
439444 substrait_transformation = transformation_proto ,
440445 )
441446
447+ mode_str = ""
448+ if self .mode :
449+ mode_str = (
450+ self .mode .value
451+ if isinstance (self .mode , TransformationMode )
452+ else self .mode
453+ )
454+
442455 return FeatureViewSpecProto (
443456 name = self .name ,
444457 entities = self .entities ,
@@ -454,6 +467,7 @@ def to_proto_spec(
454467 stream_source = stream_source_proto ,
455468 source_views = source_view_protos ,
456469 feature_transformation = feature_transformation_proto ,
470+ mode = mode_str ,
457471 )
458472
459473 def to_proto_meta (self ):
@@ -527,6 +541,7 @@ def _from_proto_internal(
527541
528542 if has_transformation and cls == FeatureView :
529543 from feast .batch_feature_view import BatchFeatureView
544+ from feast .transformation .factory import get_transformation_class_from_type
530545 from feast .transformation .python_transformation import PythonTransformation
531546 from feast .transformation .substrait_transformation import (
532547 SubstraitTransformation ,
@@ -538,14 +553,30 @@ def _from_proto_internal(
538553 transformation = None
539554
540555 if feature_transformation_proto .HasField ("user_defined_function" ):
541- transformation = PythonTransformation .from_proto (
542- feature_transformation_proto .user_defined_function
543- )
556+ udf_proto = feature_transformation_proto .user_defined_function
557+ if udf_proto .mode :
558+ try :
559+ transformation_class = get_transformation_class_from_type (
560+ udf_proto .mode
561+ )
562+ transformation = transformation_class .from_proto (udf_proto )
563+ except (ValueError , KeyError ):
564+ transformation = PythonTransformation .from_proto (udf_proto )
565+ else :
566+ transformation = PythonTransformation .from_proto (udf_proto )
544567 elif feature_transformation_proto .HasField ("substrait_transformation" ):
545568 transformation = SubstraitTransformation .from_proto (
546569 feature_transformation_proto .substrait_transformation
547570 )
548571
572+ mode : Union [TransformationMode , str ]
573+ if feature_view_proto .spec .mode :
574+ mode = feature_view_proto .spec .mode
575+ elif transformation and hasattr (transformation , "mode" ):
576+ mode = transformation .mode
577+ else :
578+ mode = TransformationMode .PYTHON
579+
549580 feature_view : FeatureView = BatchFeatureView ( # type: ignore[assignment]
550581 name = feature_view_proto .spec .name ,
551582 description = feature_view_proto .spec .description ,
@@ -560,9 +591,14 @@ def _from_proto_internal(
560591 ),
561592 source = source_views if source_views else batch_source , # type: ignore[arg-type]
562593 sink_source = batch_source if source_views else None ,
594+ mode = mode ,
563595 feature_transformation = transformation ,
564596 )
565597 else :
598+ mode_from_spec = (
599+ feature_view_proto .spec .mode if feature_view_proto .spec .mode else None
600+ )
601+
566602 feature_view = cls ( # type: ignore[assignment]
567603 name = feature_view_proto .spec .name ,
568604 description = feature_view_proto .spec .description ,
@@ -577,6 +613,7 @@ def _from_proto_internal(
577613 ),
578614 source = source_views if source_views else batch_source ,
579615 sink_source = batch_source if source_views else None ,
616+ mode = mode_from_spec ,
580617 )
581618 if stream_source :
582619 feature_view .stream_source = stream_source
0 commit comments