From 6991f44f9e7433a779e8dc8f92bfd525ce4d7c0e Mon Sep 17 00:00:00 2001 From: soojin Date: Sun, 8 Feb 2026 23:26:54 +0900 Subject: [PATCH 01/15] feat: Add UUID and TIME_UUID as feature types (#5885) Signed-off-by: soojin Co-Authored-By: Claude Opus 4.6 Signed-off-by: soojin --- sdk/python/feast/feature_store.py | 18 ++++++++-- .../feast/infra/online_stores/online_store.py | 24 +++++++++++-- sdk/python/feast/on_demand_feature_view.py | 4 +++ sdk/python/feast/online_response.py | 15 ++++++-- sdk/python/feast/type_map.py | 34 ++++++++++++++++--- sdk/python/feast/types.py | 16 +++++++++ sdk/python/feast/utils.py | 3 +- sdk/python/feast/value_type.py | 4 +++ 8 files changed, 105 insertions(+), 13 deletions(-) diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index 4820c401a6c..0fe60c67955 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -2768,7 +2768,11 @@ def _doc_feature(x): online_features_response=online_features_response, data=requested_features_data, ) - return OnlineResponse(online_features_response) + feature_types = { + f.name: f.dtype.to_value_type() + for f in requested_feature_view.features + } + return OnlineResponse(online_features_response, feature_types=feature_types) def retrieve_online_documents_v2( self, @@ -3058,7 +3062,11 @@ def _retrieve_from_online_store_v2( online_features_response.metadata.feature_names.val.extend( features_to_request ) - return OnlineResponse(online_features_response) + feature_types = { + f.name: f.dtype.to_value_type() + for f in table.features + } + return OnlineResponse(online_features_response, feature_types=feature_types) table_entity_values, idxs, output_len = utils._get_unique_entities_from_values( entity_key_dict, @@ -3081,7 +3089,11 @@ def _retrieve_from_online_store_v2( data=entity_key_dict, ) - return OnlineResponse(online_features_response) + feature_types = { + f.name: f.dtype.to_value_type() + for f in table.features + } + return OnlineResponse(online_features_response, feature_types=feature_types) def serve( self, diff --git a/sdk/python/feast/infra/online_stores/online_store.py b/sdk/python/feast/infra/online_stores/online_store.py index 5bdc2fa9e46..1c2376186f2 100644 --- a/sdk/python/feast/infra/online_stores/online_store.py +++ b/sdk/python/feast/infra/online_stores/online_store.py @@ -236,18 +236,21 @@ def get_online_features( track_online_store_read(_time.monotonic() - _read_start) + feature_types = self._build_feature_types(grouped_refs) + if requested_on_demand_feature_views: utils._augment_response_with_on_demand_transforms( online_features_response, feature_refs, requested_on_demand_feature_views, full_feature_names, + feature_types=feature_types, ) utils._drop_unneeded_columns( online_features_response, requested_result_row_names ) - return OnlineResponse(online_features_response) + return OnlineResponse(online_features_response, feature_types=feature_types) def _check_versioned_read_support(self, grouped_refs): """Raise an error if versioned reads are attempted on unsupported stores.""" @@ -367,18 +370,35 @@ async def query_table(table, requested_features): track_online_store_read(_time.monotonic() - _read_start) + feature_types = self._build_feature_types(grouped_refs) + if requested_on_demand_feature_views: utils._augment_response_with_on_demand_transforms( online_features_response, feature_refs, requested_on_demand_feature_views, full_feature_names, + feature_types=feature_types, ) utils._drop_unneeded_columns( online_features_response, requested_result_row_names ) - return OnlineResponse(online_features_response) + return OnlineResponse(online_features_response, feature_types=feature_types) + + @staticmethod + def _build_feature_types( + grouped_refs: List, + ) -> Dict[str, "ValueType"]: + """Build a mapping of feature names to ValueType from grouped feature view refs.""" + from feast.value_type import ValueType as VT + + feature_types: Dict[str, VT] = {} + for table, requested_features in grouped_refs: + for field in table.features: + if field.name in requested_features: + feature_types[field.name] = field.dtype.to_value_type() + return feature_types @abstractmethod def update( diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 4dd971b13e2..d23811afccd 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -1,5 +1,6 @@ import copy import functools +import uuid import warnings from types import FunctionType from typing import Any, List, Optional, Union, cast @@ -1162,6 +1163,9 @@ def _get_sample_values_by_type(self) -> dict[ValueType, list[Any]]: # Special binary types ValueType.PDF_BYTES: [pdf_sample], ValueType.IMAGE_BYTES: [image_sample], + # UUID types + ValueType.UUID: [str(uuid.uuid4())], + ValueType.TIME_UUID: [str(uuid.uuid1())], # List types ValueType.BYTES_LIST: [[b"hello world"]], ValueType.STRING_LIST: [["hello world"]], diff --git a/sdk/python/feast/online_response.py b/sdk/python/feast/online_response.py index 2491a28badc..00accc17638 100644 --- a/sdk/python/feast/online_response.py +++ b/sdk/python/feast/online_response.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, Dict, List, TypeAlias, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, TypeAlias, Union import pandas as pd import pyarrow as pa @@ -21,6 +21,7 @@ from feast.protos.feast.serving.ServingService_pb2 import GetOnlineFeaturesResponse from feast.torch_wrapper import get_torch from feast.type_map import feast_value_type_to_python_type +from feast.value_type import ValueType if TYPE_CHECKING: import torch @@ -37,14 +38,20 @@ class OnlineResponse: Defines an online response in feast. """ - def __init__(self, online_response_proto: GetOnlineFeaturesResponse): + def __init__( + self, + online_response_proto: GetOnlineFeaturesResponse, + feature_types: Optional[Dict[str, ValueType]] = None, + ): """ Construct a native online response from its protobuf version. Args: online_response_proto: GetOnlineResponse proto object to construct from. + feature_types: Optional mapping of feature names to ValueType for type-aware deserialization. """ self.proto = online_response_proto + self._feature_types = feature_types or {} # Delete DUMMY_ENTITY_ID from proto if it exists for idx, val in enumerate(self.proto.metadata.feature_names.val): if val == DUMMY_ENTITY_ID: @@ -65,8 +72,10 @@ def to_dict(self, include_event_timestamps: bool = False) -> Dict[str, Any]: for feature_ref, feature_vector in zip( self.proto.metadata.feature_names.val, self.proto.results ): + feature_type = self._feature_types.get(feature_ref) response[feature_ref] = [ - feast_value_type_to_python_type(v) for v in feature_vector.values + feast_value_type_to_python_type(v, feature_type) + for v in feature_vector.values ] if include_event_timestamps: diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 5e77f532c9c..15006cef70f 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -15,6 +15,7 @@ import decimal import json import logging +import uuid as uuid_module from collections import defaultdict from datetime import datetime, timezone from typing import ( @@ -67,7 +68,10 @@ logger = logging.getLogger(__name__) -def feast_value_type_to_python_type(field_value_proto: ProtoValue) -> Any: +def feast_value_type_to_python_type( + field_value_proto: ProtoValue, + feature_type: Optional[ValueType] = None, +) -> Any: """ Converts field value Proto to Dict and returns each field's Feast Value Type value in their respective Python value. @@ -148,6 +152,9 @@ def feast_value_type_to_python_type(field_value_proto: ProtoValue) -> Any: elif val_attr.endswith("_set_val") and val_attr != "unix_timestamp_set_val": val = set(val) + if feature_type in (ValueType.UUID, ValueType.TIME_UUID) and isinstance(val, str): + return uuid_module.UUID(val) + return val @@ -184,6 +191,8 @@ def feast_value_type_to_pandas_type(value_type: ValueType) -> Any: ValueType.BYTES: "bytes", ValueType.BOOL: "bool", ValueType.UNIX_TIMESTAMP: "datetime64[ns]", + ValueType.UUID: "str", + ValueType.TIME_UUID: "str", } if ( value_type.name in ("MAP", "JSON", "STRUCT") @@ -247,6 +256,7 @@ def python_type_to_feast_value_type( "datetime64[ns, utc]": ValueType.UNIX_TIMESTAMP, "date": ValueType.UNIX_TIMESTAMP, "category": ValueType.STRING, + "uuid": ValueType.UUID, } if type_name in type_map: @@ -405,6 +415,10 @@ def _convert_value_type_str_to_value_type(type_str: str) -> ValueType: "JSON_LIST": ValueType.JSON_LIST, "STRUCT": ValueType.STRUCT, "STRUCT_LIST": ValueType.STRUCT_LIST, + "UUID": ValueType.UUID, + "TIME_UUID": ValueType.TIME_UUID, + "UUID_LIST": ValueType.UUID_LIST, + "TIME_UUID_LIST": ValueType.TIME_UUID_LIST, } return type_map.get(type_str, ValueType.STRING) @@ -436,6 +450,8 @@ def _type_err(item, dtype): ValueType.STRING_LIST: (StringList, "string_list_val", [np.str_, str]), ValueType.BOOL_LIST: (BoolList, "bool_list_val", [np.bool_, bool]), ValueType.BYTES_LIST: (BytesList, "bytes_list_val", [np.bytes_, bytes]), + ValueType.UUID_LIST: (StringList, "string_list_val", [np.str_, str, uuid_module.UUID]), + ValueType.TIME_UUID_LIST: (StringList, "string_list_val", [np.str_, str, uuid_module.UUID]), } PYTHON_SET_VALUE_TYPE_TO_PROTO_VALUE: Dict[ @@ -486,6 +502,8 @@ def _type_err(item, dtype): ValueType.BYTES: ("bytes_val", lambda x: x, {bytes}), ValueType.IMAGE_BYTES: ("bytes_val", lambda x: x, {bytes}), ValueType.BOOL: ("bool_val", lambda x: x, {bool, np.bool_, int, np.int_}), + ValueType.UUID: ("string_val", lambda x: str(x), {str, uuid_module.UUID}), + ValueType.TIME_UUID: ("string_val", lambda x: str(x), {str, uuid_module.UUID}), } @@ -1347,6 +1365,8 @@ def _convert_value_name_to_snowflake_udf(value_name: str, project_name: str) -> "FLOAT_LIST": f"feast_{project_name}_snowflake_array_float_to_list_double_proto", "BOOL_LIST": f"feast_{project_name}_snowflake_array_boolean_to_list_bool_proto", "UNIX_TIMESTAMP_LIST": f"feast_{project_name}_snowflake_array_timestamp_to_list_unix_timestamp_proto", + "UUID": f"feast_{project_name}_snowflake_varchar_to_string_proto", + "TIME_UUID": f"feast_{project_name}_snowflake_varchar_to_string_proto", } return name_map[value_name].upper() @@ -1550,8 +1570,8 @@ def pg_type_to_feast_value_type(type_str: str) -> ValueType: "timestamp with time zone[]": ValueType.UNIX_TIMESTAMP_LIST, "numeric[]": ValueType.DOUBLE_LIST, "numeric": ValueType.DOUBLE, - "uuid": ValueType.STRING, - "uuid[]": ValueType.STRING_LIST, + "uuid": ValueType.UUID, + "uuid[]": ValueType.UUID_LIST, "json": ValueType.MAP, "jsonb": ValueType.MAP, "json[]": ValueType.MAP_LIST, @@ -1598,6 +1618,10 @@ def feast_value_type_to_pa( ValueType.STRUCT: pyarrow.struct([]), ValueType.STRUCT_LIST: pyarrow.list_(pyarrow.struct([])), ValueType.NULL: pyarrow.null(), + ValueType.UUID: pyarrow.string(), + ValueType.TIME_UUID: pyarrow.string(), + ValueType.UUID_LIST: pyarrow.list_(pyarrow.string()), + ValueType.TIME_UUID_LIST: pyarrow.list_(pyarrow.string()), } return type_map[feast_type] @@ -1750,7 +1774,7 @@ def cb_columnar_type_to_feast_value_type(type_str: str) -> ValueType: "object": ValueType.UNKNOWN, "array": ValueType.UNKNOWN, "multiset": ValueType.UNKNOWN, - "uuid": ValueType.STRING, + "uuid": ValueType.UUID, } value = ( type_map[type_str.lower()] @@ -1776,6 +1800,8 @@ def convert_scalar_column( return series.astype("boolean") elif value_type == ValueType.STRING: return series.astype("string") + elif value_type in [ValueType.UUID, ValueType.TIME_UUID]: + return series.astype("string") elif value_type == ValueType.UNIX_TIMESTAMP: return pd.to_datetime(series, unit="s", errors="coerce") elif value_type in (ValueType.JSON, ValueType.STRUCT, ValueType.MAP): diff --git a/sdk/python/feast/types.py b/sdk/python/feast/types.py index d94c356cd1a..2a94610d42f 100644 --- a/sdk/python/feast/types.py +++ b/sdk/python/feast/types.py @@ -25,6 +25,8 @@ "BYTES": "BYTES", "PDF_BYTES": "PDF_BYTES", "IMAGE_BYTES": "IMAGE_BYTES", + "UUID": "UUID", + "TIME_UUID": "TIME_UUID", "STRING": "STRING", "INT32": "INT32", "INT64": "INT64", @@ -87,6 +89,8 @@ class PrimitiveFeastType(Enum): IMAGE_BYTES = 10 MAP = 11 JSON = 12 + UUID = 13 + TIME_UUID = 14 def to_value_type(self) -> ValueType: """ @@ -121,6 +125,8 @@ def __hash__(self): UnixTimestamp = PrimitiveFeastType.UNIX_TIMESTAMP Map = PrimitiveFeastType.MAP Json = PrimitiveFeastType.JSON +Uuid = PrimitiveFeastType.UUID +TimeUuid = PrimitiveFeastType.TIME_UUID SUPPORTED_BASE_TYPES = [ Invalid, @@ -136,6 +142,8 @@ def __hash__(self): UnixTimestamp, Map, Json, + Uuid, + TimeUuid, ] PRIMITIVE_FEAST_TYPES_TO_STRING = { @@ -152,6 +160,8 @@ def __hash__(self): "UNIX_TIMESTAMP": "UnixTimestamp", "MAP": "Map", "JSON": "Json", + "UUID": "Uuid", + "TIME_UUID": "TimeUuid", } @@ -297,6 +307,10 @@ def __hash__(self): ValueType.FLOAT_SET: Set(Float32), ValueType.BOOL_SET: Set(Bool), ValueType.UNIX_TIMESTAMP_SET: Set(UnixTimestamp), + ValueType.UUID: Uuid, + ValueType.TIME_UUID: TimeUuid, + ValueType.UUID_LIST: Array(Uuid), + ValueType.TIME_UUID_LIST: Array(TimeUuid), } FEAST_TYPES_TO_PYARROW_TYPES = { @@ -310,6 +324,8 @@ def __hash__(self): UnixTimestamp: pyarrow.timestamp("us", tz=_utc_now().tzname()), Map: pyarrow.map_(pyarrow.string(), pyarrow.string()), Json: pyarrow.large_string(), + Uuid: pyarrow.string(), + TimeUuid: pyarrow.string(), } FEAST_VECTOR_TYPES: List[Union[ValueType, PrimitiveFeastType, ComplexFeastType]] = [ diff --git a/sdk/python/feast/utils.py b/sdk/python/feast/utils.py index 809b69814ac..17fb87d375d 100644 --- a/sdk/python/feast/utils.py +++ b/sdk/python/feast/utils.py @@ -727,6 +727,7 @@ def _augment_response_with_on_demand_transforms( feature_refs: List[str], requested_on_demand_feature_views: List["OnDemandFeatureView"], full_feature_names: bool, + feature_types: Optional[Dict[str, "ValueType"]] = None, ): """Computes on demand feature values and adds them to the result rows. @@ -757,7 +758,7 @@ def _augment_response_with_on_demand_transforms( else feature_name ) - initial_response = OnlineResponse(online_features_response) + initial_response = OnlineResponse(online_features_response, feature_types=feature_types) initial_response_arrow: Optional[pyarrow.Table] = None initial_response_dict: Optional[Dict[str, List[Any]]] = None diff --git a/sdk/python/feast/value_type.py b/sdk/python/feast/value_type.py index d05691199b4..94a8e61c1fd 100644 --- a/sdk/python/feast/value_type.py +++ b/sdk/python/feast/value_type.py @@ -71,6 +71,10 @@ class ValueType(enum.Enum): JSON_LIST = 33 STRUCT = 34 STRUCT_LIST = 35 + UUID = 36 + TIME_UUID = 37 + UUID_LIST = 38 + TIME_UUID_LIST = 39 ListType = Union[ From 55065f2b851dbd795c3141f49c0f31fe7bf78531 Mon Sep 17 00:00:00 2001 From: soojin Date: Sun, 8 Feb 2026 23:27:41 +0900 Subject: [PATCH 02/15] test: Add unit tests for UUID type support Signed-off-by: soojin Co-Authored-By: Claude Opus 4.6 Signed-off-by: soojin --- sdk/python/tests/unit/test_type_map.py | 53 ++++++++++++++++++++++++++ sdk/python/tests/unit/test_types.py | 19 ++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/sdk/python/tests/unit/test_type_map.py b/sdk/python/tests/unit/test_type_map.py index 8125ab61b90..3879a4fd166 100644 --- a/sdk/python/tests/unit/test_type_map.py +++ b/sdk/python/tests/unit/test_type_map.py @@ -1,3 +1,5 @@ +import uuid + import numpy as np import pandas as pd import pyarrow @@ -1415,3 +1417,54 @@ def test_array_element_incompatibility(self): assert not _spark_types_compatible( ArrayType(StringType()), ArrayType(IntegerType()) ) + + +class TestUuidTypes: + """Test cases for UUID and TIME_UUID value types.""" + + def test_uuid_string_roundtrip(self): + """UUID string -> proto -> uuid.UUID object roundtrip.""" + test_uuid = uuid.uuid4() + protos = python_values_to_proto_values([str(test_uuid)], ValueType.UUID) + assert protos[0].string_val == str(test_uuid) + + result = feast_value_type_to_python_type(protos[0], ValueType.UUID) + assert isinstance(result, uuid.UUID) + assert result == test_uuid + + def test_uuid_object_serialization(self): + """uuid.UUID object -> proto serialization (str conversion automatic).""" + test_uuid = uuid.uuid4() + protos = python_values_to_proto_values([test_uuid], ValueType.UUID) + assert protos[0].string_val == str(test_uuid) + + def test_time_uuid_roundtrip(self): + """TIME_UUID string -> proto -> uuid.UUID object roundtrip.""" + test_uuid = uuid.uuid1() + protos = python_values_to_proto_values([str(test_uuid)], ValueType.TIME_UUID) + assert protos[0].string_val == str(test_uuid) + + result = feast_value_type_to_python_type(protos[0], ValueType.TIME_UUID) + assert isinstance(result, uuid.UUID) + assert result == test_uuid + + def test_uuid_without_feature_type_returns_string(self): + """Without feature_type, UUID stored as string_val returns plain string.""" + test_uuid = uuid.uuid4() + protos = python_values_to_proto_values([str(test_uuid)], ValueType.UUID) + + result = feast_value_type_to_python_type(protos[0]) + assert isinstance(result, str) + assert result == str(test_uuid) + + def test_uuid_list_roundtrip(self): + """UUID list -> proto -> list of strings roundtrip.""" + test_uuids = [str(uuid.uuid4()), str(uuid.uuid4())] + protos = python_values_to_proto_values([test_uuids], ValueType.UUID_LIST) + result = feast_value_type_to_python_type(protos[0]) + assert result == test_uuids + + def test_pg_uuid_type_mapping(self): + """PostgreSQL uuid type maps to ValueType.UUID.""" + assert pg_type_to_feast_value_type("uuid") == ValueType.UUID + assert pg_type_to_feast_value_type("uuid[]") == ValueType.UUID_LIST diff --git a/sdk/python/tests/unit/test_types.py b/sdk/python/tests/unit/test_types.py index 438735d213a..3b90ee70965 100644 --- a/sdk/python/tests/unit/test_types.py +++ b/sdk/python/tests/unit/test_types.py @@ -1,6 +1,6 @@ import pytest -from feast.types import Array, Float32, Set, String, from_value_type +from feast.types import Array, Float32, Set, String, TimeUuid, Uuid, from_value_type from feast.value_type import ValueType @@ -43,6 +43,23 @@ def test_set_feast_type(): _ = Set(Set(String)) +def test_uuid_feast_type(): + assert Uuid.to_value_type() == ValueType.UUID + assert from_value_type(ValueType.UUID) == Uuid + assert TimeUuid.to_value_type() == ValueType.TIME_UUID + assert from_value_type(ValueType.TIME_UUID) == TimeUuid + + +def test_uuid_array_feast_type(): + array_uuid = Array(Uuid) + assert array_uuid.to_value_type() == ValueType.UUID_LIST + assert from_value_type(array_uuid.to_value_type()) == array_uuid + + array_time_uuid = Array(TimeUuid) + assert array_time_uuid.to_value_type() == ValueType.TIME_UUID_LIST + assert from_value_type(array_time_uuid.to_value_type()) == array_time_uuid + + def test_all_value_types(): for value in ValueType: # We do not support the NULL type. From 53c25139deae2629e15ef3dcf5e734c986437ef5 Mon Sep 17 00:00:00 2001 From: soojin Date: Sun, 8 Feb 2026 23:38:02 +0900 Subject: [PATCH 03/15] style: Fix ruff lint and formatting issues Co-Authored-By: Claude Opus 4.6 Signed-off-by: soojin --- sdk/python/feast/feature_store.py | 13 +++---------- .../feast/infra/online_stores/online_store.py | 7 +++---- sdk/python/feast/type_map.py | 12 ++++++++++-- sdk/python/feast/utils.py | 4 +++- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py index 0fe60c67955..42bcd2feca9 100644 --- a/sdk/python/feast/feature_store.py +++ b/sdk/python/feast/feature_store.py @@ -2769,8 +2769,7 @@ def _doc_feature(x): data=requested_features_data, ) feature_types = { - f.name: f.dtype.to_value_type() - for f in requested_feature_view.features + f.name: f.dtype.to_value_type() for f in requested_feature_view.features } return OnlineResponse(online_features_response, feature_types=feature_types) @@ -3062,10 +3061,7 @@ def _retrieve_from_online_store_v2( online_features_response.metadata.feature_names.val.extend( features_to_request ) - feature_types = { - f.name: f.dtype.to_value_type() - for f in table.features - } + feature_types = {f.name: f.dtype.to_value_type() for f in table.features} return OnlineResponse(online_features_response, feature_types=feature_types) table_entity_values, idxs, output_len = utils._get_unique_entities_from_values( @@ -3089,10 +3085,7 @@ def _retrieve_from_online_store_v2( data=entity_key_dict, ) - feature_types = { - f.name: f.dtype.to_value_type() - for f in table.features - } + feature_types = {f.name: f.dtype.to_value_type() for f in table.features} return OnlineResponse(online_features_response, feature_types=feature_types) def serve( diff --git a/sdk/python/feast/infra/online_stores/online_store.py b/sdk/python/feast/infra/online_stores/online_store.py index 1c2376186f2..27844ab347a 100644 --- a/sdk/python/feast/infra/online_stores/online_store.py +++ b/sdk/python/feast/infra/online_stores/online_store.py @@ -31,6 +31,7 @@ from feast.protos.feast.types.Value_pb2 import Value as ValueProto from feast.repo_config import RepoConfig from feast.stream_feature_view import StreamFeatureView +from feast.value_type import ValueType class OnlineStore(ABC): @@ -389,11 +390,9 @@ async def query_table(table, requested_features): @staticmethod def _build_feature_types( grouped_refs: List, - ) -> Dict[str, "ValueType"]: + ) -> Dict[str, ValueType]: """Build a mapping of feature names to ValueType from grouped feature view refs.""" - from feast.value_type import ValueType as VT - - feature_types: Dict[str, VT] = {} + feature_types: Dict[str, ValueType] = {} for table, requested_features in grouped_refs: for field in table.features: if field.name in requested_features: diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 15006cef70f..87df710e4aa 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -450,8 +450,16 @@ def _type_err(item, dtype): ValueType.STRING_LIST: (StringList, "string_list_val", [np.str_, str]), ValueType.BOOL_LIST: (BoolList, "bool_list_val", [np.bool_, bool]), ValueType.BYTES_LIST: (BytesList, "bytes_list_val", [np.bytes_, bytes]), - ValueType.UUID_LIST: (StringList, "string_list_val", [np.str_, str, uuid_module.UUID]), - ValueType.TIME_UUID_LIST: (StringList, "string_list_val", [np.str_, str, uuid_module.UUID]), + ValueType.UUID_LIST: ( + StringList, + "string_list_val", + [np.str_, str, uuid_module.UUID], + ), + ValueType.TIME_UUID_LIST: ( + StringList, + "string_list_val", + [np.str_, str, uuid_module.UUID], + ), } PYTHON_SET_VALUE_TYPE_TO_PROTO_VALUE: Dict[ diff --git a/sdk/python/feast/utils.py b/sdk/python/feast/utils.py index 17fb87d375d..893db8119e8 100644 --- a/sdk/python/feast/utils.py +++ b/sdk/python/feast/utils.py @@ -758,7 +758,9 @@ def _augment_response_with_on_demand_transforms( else feature_name ) - initial_response = OnlineResponse(online_features_response, feature_types=feature_types) + initial_response = OnlineResponse( + online_features_response, feature_types=feature_types + ) initial_response_arrow: Optional[pyarrow.Table] = None initial_response_dict: Optional[Dict[str, List[Any]]] = None From 678dc72f9f43a23d5beb483affa50f8b28c42ce9 Mon Sep 17 00:00:00 2001 From: soojin Date: Mon, 9 Feb 2026 00:44:55 +0900 Subject: [PATCH 04/15] feat: Add dedicated UUID/TIME_UUID proto fields to Value.proto Add uuid_val, time_uuid_val, uuid_list_val, time_uuid_list_val as dedicated oneof fields in the Value proto message, replacing the previous reuse of string_val/string_list_val. This allows UUID types to be identified from the proto field alone without requiring a feature_types side-channel. Backward compatibility is maintained for data previously stored as string_val. Co-Authored-By: Claude Opus 4.6 Signed-off-by: soojin --- protos/feast/types/Value.proto | 8 ++ .../protos/feast/core/DatastoreTable_pb2.pyi | 26 +++--- .../feast/protos/feast/core/Entity_pb2.pyi | 26 +++--- .../feast/core/FeatureViewProjection_pb2.pyi | 2 +- .../feast/protos/feast/core/Project_pb2.pyi | 26 +++--- .../feast/protos/feast/core/Registry_pb2.pyi | 26 +++--- .../feast/protos/feast/types/Value_pb2.py | 86 +++++++++---------- sdk/python/feast/type_map.py | 19 +++- sdk/python/tests/unit/test_type_map.py | 42 ++++++--- 9 files changed, 150 insertions(+), 111 deletions(-) diff --git a/protos/feast/types/Value.proto b/protos/feast/types/Value.proto index ada2ba42791..e67a50b9d58 100644 --- a/protos/feast/types/Value.proto +++ b/protos/feast/types/Value.proto @@ -57,6 +57,10 @@ message ValueType { JSON_LIST = 33; STRUCT = 34; STRUCT_LIST = 35; + UUID = 36; + TIME_UUID = 37; + UUID_LIST = 38; + TIME_UUID_LIST = 39; } } @@ -96,6 +100,10 @@ message Value { StringList json_list_val = 33; Map struct_val = 34; MapList struct_list_val = 35; + string uuid_val = 36; + string time_uuid_val = 37; + StringList uuid_list_val = 38; + StringList time_uuid_list_val = 39; } } diff --git a/sdk/python/feast/protos/feast/core/DatastoreTable_pb2.pyi b/sdk/python/feast/protos/feast/core/DatastoreTable_pb2.pyi index 7b5a629eb7a..6339a97536e 100644 --- a/sdk/python/feast/protos/feast/core/DatastoreTable_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/DatastoreTable_pb2.pyi @@ -1,19 +1,19 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file - -* Copyright 2021 The Feast Authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and + +* Copyright 2021 The Feast Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* 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 builtins diff --git a/sdk/python/feast/protos/feast/core/Entity_pb2.pyi b/sdk/python/feast/protos/feast/core/Entity_pb2.pyi index a5924a13451..025817edfee 100644 --- a/sdk/python/feast/protos/feast/core/Entity_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/Entity_pb2.pyi @@ -1,19 +1,19 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file - -* Copyright 2020 The Feast Authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and + +* Copyright 2020 The Feast Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* 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 builtins diff --git a/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi b/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi index 6fd1010f2e4..a3c19938a43 100644 --- a/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi @@ -19,7 +19,7 @@ else: DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class FeatureViewProjection(google.protobuf.message.Message): - """A projection to be applied on top of a FeatureView. + """A projection to be applied on top of a FeatureView. Contains the modifications to a FeatureView such as the features subset to use. """ diff --git a/sdk/python/feast/protos/feast/core/Project_pb2.pyi b/sdk/python/feast/protos/feast/core/Project_pb2.pyi index 3196304a19b..e3cce2ec425 100644 --- a/sdk/python/feast/protos/feast/core/Project_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/Project_pb2.pyi @@ -1,19 +1,19 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file - -* Copyright 2020 The Feast Authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and + +* Copyright 2020 The Feast Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* 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 builtins diff --git a/sdk/python/feast/protos/feast/core/Registry_pb2.pyi b/sdk/python/feast/protos/feast/core/Registry_pb2.pyi index 29bd76323e3..e581fe07e5d 100644 --- a/sdk/python/feast/protos/feast/core/Registry_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/Registry_pb2.pyi @@ -1,19 +1,19 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file - -* Copyright 2020 The Feast Authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and + +* Copyright 2020 The Feast Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* 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 builtins diff --git a/sdk/python/feast/protos/feast/types/Value_pb2.py b/sdk/python/feast/protos/feast/types/Value_pb2.py index 5edd8c5bde9..5dc9084ed05 100644 --- a/sdk/python/feast/protos/feast/types/Value_pb2.py +++ b/sdk/python/feast/protos/feast/types/Value_pb2.py @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types\"\xe6\x03\n\tValueType\"\xd8\x03\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04JSON\x10 \x12\r\n\tJSON_LIST\x10!\x12\n\n\x06STRUCT\x10\"\x12\x0f\n\x0bSTRUCT_LIST\x10#\"\xff\t\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08json_val\x18 \x01(\tH\x00\x12\x30\n\rjson_list_val\x18! \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12&\n\nstruct_val\x18\" \x01(\x0b\x32\x10.feast.types.MapH\x00\x12/\n\x0fstruct_list_val\x18# \x01(\x0b\x32\x14.feast.types.MapListH\x00\x42\x05\n\x03val\"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08\"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08\"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01\"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map\"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types\"\xec\x03\n\tValueType\"\xde\x03\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04UUID\x10\x1e\x12\r\n\tTIME_UUID\x10\x1f\x12\r\n\tUUID_LIST\x10 \x12\x12\n\x0eTIME_UUID_LIST\x10!\"\xf6\t\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08uuid_val\x18\x1e \x01(\tH\x00\x12\x17\n\rtime_uuid_val\x18\x1f \x01(\tH\x00\x12\x30\n\ruuid_list_val\x18 \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x35\n\x12time_uuid_list_val\x18! \x01(\x0b\x32\x17.feast.types.StringListH\x00\x42\x05\n\x03val\"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08\"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08\"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01\"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map\"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -24,48 +24,48 @@ _globals['DESCRIPTOR']._serialized_options = b'\n\021feast.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/types' _globals['_MAP_VALENTRY']._options = None _globals['_MAP_VALENTRY']._serialized_options = b'8\001' - _globals['_NULL']._serialized_start=2373 - _globals['_NULL']._serialized_end=2389 + _globals['_NULL']._serialized_start=2370 + _globals['_NULL']._serialized_end=2386 _globals['_VALUETYPE']._serialized_start=41 - _globals['_VALUETYPE']._serialized_end=527 + _globals['_VALUETYPE']._serialized_end=533 _globals['_VALUETYPE_ENUM']._serialized_start=55 - _globals['_VALUETYPE_ENUM']._serialized_end=527 - _globals['_VALUE']._serialized_start=530 - _globals['_VALUE']._serialized_end=1809 - _globals['_BYTESLIST']._serialized_start=1811 - _globals['_BYTESLIST']._serialized_end=1835 - _globals['_STRINGLIST']._serialized_start=1837 - _globals['_STRINGLIST']._serialized_end=1862 - _globals['_INT32LIST']._serialized_start=1864 - _globals['_INT32LIST']._serialized_end=1888 - _globals['_INT64LIST']._serialized_start=1890 - _globals['_INT64LIST']._serialized_end=1914 - _globals['_DOUBLELIST']._serialized_start=1916 - _globals['_DOUBLELIST']._serialized_end=1941 - _globals['_FLOATLIST']._serialized_start=1943 - _globals['_FLOATLIST']._serialized_end=1967 - _globals['_BOOLLIST']._serialized_start=1969 - _globals['_BOOLLIST']._serialized_end=1992 - _globals['_BYTESSET']._serialized_start=1994 - _globals['_BYTESSET']._serialized_end=2017 - _globals['_STRINGSET']._serialized_start=2019 - _globals['_STRINGSET']._serialized_end=2043 - _globals['_INT32SET']._serialized_start=2045 - _globals['_INT32SET']._serialized_end=2068 - _globals['_INT64SET']._serialized_start=2070 - _globals['_INT64SET']._serialized_end=2093 - _globals['_DOUBLESET']._serialized_start=2095 - _globals['_DOUBLESET']._serialized_end=2119 - _globals['_FLOATSET']._serialized_start=2121 - _globals['_FLOATSET']._serialized_end=2144 - _globals['_BOOLSET']._serialized_start=2146 - _globals['_BOOLSET']._serialized_end=2168 - _globals['_MAP']._serialized_start=2170 - _globals['_MAP']._serialized_end=2279 - _globals['_MAP_VALENTRY']._serialized_start=2217 - _globals['_MAP_VALENTRY']._serialized_end=2279 - _globals['_MAPLIST']._serialized_start=2281 - _globals['_MAPLIST']._serialized_end=2321 - _globals['_REPEATEDVALUE']._serialized_start=2323 - _globals['_REPEATEDVALUE']._serialized_end=2371 + _globals['_VALUETYPE_ENUM']._serialized_end=533 + _globals['_VALUE']._serialized_start=536 + _globals['_VALUE']._serialized_end=1806 + _globals['_BYTESLIST']._serialized_start=1808 + _globals['_BYTESLIST']._serialized_end=1832 + _globals['_STRINGLIST']._serialized_start=1834 + _globals['_STRINGLIST']._serialized_end=1859 + _globals['_INT32LIST']._serialized_start=1861 + _globals['_INT32LIST']._serialized_end=1885 + _globals['_INT64LIST']._serialized_start=1887 + _globals['_INT64LIST']._serialized_end=1911 + _globals['_DOUBLELIST']._serialized_start=1913 + _globals['_DOUBLELIST']._serialized_end=1938 + _globals['_FLOATLIST']._serialized_start=1940 + _globals['_FLOATLIST']._serialized_end=1964 + _globals['_BOOLLIST']._serialized_start=1966 + _globals['_BOOLLIST']._serialized_end=1989 + _globals['_BYTESSET']._serialized_start=1991 + _globals['_BYTESSET']._serialized_end=2014 + _globals['_STRINGSET']._serialized_start=2016 + _globals['_STRINGSET']._serialized_end=2040 + _globals['_INT32SET']._serialized_start=2042 + _globals['_INT32SET']._serialized_end=2065 + _globals['_INT64SET']._serialized_start=2067 + _globals['_INT64SET']._serialized_end=2090 + _globals['_DOUBLESET']._serialized_start=2092 + _globals['_DOUBLESET']._serialized_end=2116 + _globals['_FLOATSET']._serialized_start=2118 + _globals['_FLOATSET']._serialized_end=2141 + _globals['_BOOLSET']._serialized_start=2143 + _globals['_BOOLSET']._serialized_end=2165 + _globals['_MAP']._serialized_start=2167 + _globals['_MAP']._serialized_end=2276 + _globals['_MAP_VALENTRY']._serialized_start=2214 + _globals['_MAP_VALENTRY']._serialized_end=2276 + _globals['_MAPLIST']._serialized_start=2278 + _globals['_MAPLIST']._serialized_end=2318 + _globals['_REPEATEDVALUE']._serialized_start=2320 + _globals['_REPEATEDVALUE']._serialized_end=2368 # @@protoc_insertion_point(module_scope) diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 87df710e4aa..66f387ead0b 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -152,6 +152,13 @@ def feast_value_type_to_python_type( elif val_attr.endswith("_set_val") and val_attr != "unix_timestamp_set_val": val = set(val) + # Convert UUID values to uuid.UUID objects + if val_attr in ("uuid_val", "time_uuid_val"): + return uuid_module.UUID(val) if isinstance(val, str) else val + if val_attr in ("uuid_list_val", "time_uuid_list_val"): + return [uuid_module.UUID(v) if isinstance(v, str) else v for v in val] + + # Backward compatibility: handle UUIDs stored as string_val with feature_type hint if feature_type in (ValueType.UUID, ValueType.TIME_UUID) and isinstance(val, str): return uuid_module.UUID(val) @@ -452,12 +459,12 @@ def _type_err(item, dtype): ValueType.BYTES_LIST: (BytesList, "bytes_list_val", [np.bytes_, bytes]), ValueType.UUID_LIST: ( StringList, - "string_list_val", + "uuid_list_val", [np.str_, str, uuid_module.UUID], ), ValueType.TIME_UUID_LIST: ( StringList, - "string_list_val", + "time_uuid_list_val", [np.str_, str, uuid_module.UUID], ), } @@ -510,8 +517,8 @@ def _type_err(item, dtype): ValueType.BYTES: ("bytes_val", lambda x: x, {bytes}), ValueType.IMAGE_BYTES: ("bytes_val", lambda x: x, {bytes}), ValueType.BOOL: ("bool_val", lambda x: x, {bool, np.bool_, int, np.int_}), - ValueType.UUID: ("string_val", lambda x: str(x), {str, uuid_module.UUID}), - ValueType.TIME_UUID: ("string_val", lambda x: str(x), {str, uuid_module.UUID}), + ValueType.UUID: ("uuid_val", lambda x: str(x), {str, uuid_module.UUID}), + ValueType.TIME_UUID: ("time_uuid_val", lambda x: str(x), {str, uuid_module.UUID}), } @@ -1084,6 +1091,10 @@ def python_values_to_proto_values( "bytes_set_val": ValueType.BYTES_SET, "bool_set_val": ValueType.BOOL_SET, "unix_timestamp_set_val": ValueType.UNIX_TIMESTAMP_SET, + "uuid_val": ValueType.UUID, + "time_uuid_val": ValueType.TIME_UUID, + "uuid_list_val": ValueType.UUID_LIST, + "time_uuid_list_val": ValueType.TIME_UUID_LIST, } VALUE_TYPE_TO_PROTO_VALUE_MAP: Dict[ValueType, str] = { diff --git a/sdk/python/tests/unit/test_type_map.py b/sdk/python/tests/unit/test_type_map.py index 3879a4fd166..5e13c67d42a 100644 --- a/sdk/python/tests/unit/test_type_map.py +++ b/sdk/python/tests/unit/test_type_map.py @@ -1426,9 +1426,9 @@ def test_uuid_string_roundtrip(self): """UUID string -> proto -> uuid.UUID object roundtrip.""" test_uuid = uuid.uuid4() protos = python_values_to_proto_values([str(test_uuid)], ValueType.UUID) - assert protos[0].string_val == str(test_uuid) + assert protos[0].uuid_val == str(test_uuid) - result = feast_value_type_to_python_type(protos[0], ValueType.UUID) + result = feast_value_type_to_python_type(protos[0]) assert isinstance(result, uuid.UUID) assert result == test_uuid @@ -1436,32 +1436,52 @@ def test_uuid_object_serialization(self): """uuid.UUID object -> proto serialization (str conversion automatic).""" test_uuid = uuid.uuid4() protos = python_values_to_proto_values([test_uuid], ValueType.UUID) - assert protos[0].string_val == str(test_uuid) + assert protos[0].uuid_val == str(test_uuid) def test_time_uuid_roundtrip(self): """TIME_UUID string -> proto -> uuid.UUID object roundtrip.""" test_uuid = uuid.uuid1() protos = python_values_to_proto_values([str(test_uuid)], ValueType.TIME_UUID) - assert protos[0].string_val == str(test_uuid) + assert protos[0].time_uuid_val == str(test_uuid) - result = feast_value_type_to_python_type(protos[0], ValueType.TIME_UUID) + result = feast_value_type_to_python_type(protos[0]) assert isinstance(result, uuid.UUID) assert result == test_uuid - def test_uuid_without_feature_type_returns_string(self): - """Without feature_type, UUID stored as string_val returns plain string.""" + def test_uuid_without_feature_type_returns_uuid(self): + """With dedicated uuid_val proto field, UUID is identified without feature_type hint.""" test_uuid = uuid.uuid4() protos = python_values_to_proto_values([str(test_uuid)], ValueType.UUID) + # No feature_type hint needed — uuid_val field identifies the type result = feast_value_type_to_python_type(protos[0]) + assert isinstance(result, uuid.UUID) + assert result == test_uuid + + def test_uuid_backward_compat_string_val(self): + """UUIDs stored as string_val (old format) still work with feature_type hint.""" + from feast.protos.feast.types.Value_pb2 import Value as ProtoValue + + test_uuid = uuid.uuid4() + # Simulate old-format proto with string_val + proto = ProtoValue(string_val=str(test_uuid)) + + # Without feature_type, returns plain string + result = feast_value_type_to_python_type(proto) assert isinstance(result, str) - assert result == str(test_uuid) + + # With feature_type hint, returns uuid.UUID + result = feast_value_type_to_python_type(proto, ValueType.UUID) + assert isinstance(result, uuid.UUID) + assert result == test_uuid def test_uuid_list_roundtrip(self): - """UUID list -> proto -> list of strings roundtrip.""" - test_uuids = [str(uuid.uuid4()), str(uuid.uuid4())] - protos = python_values_to_proto_values([test_uuids], ValueType.UUID_LIST) + """UUID list -> proto -> list of uuid.UUID objects roundtrip.""" + test_uuids = [uuid.uuid4(), uuid.uuid4()] + test_uuid_strs = [str(u) for u in test_uuids] + protos = python_values_to_proto_values([test_uuid_strs], ValueType.UUID_LIST) result = feast_value_type_to_python_type(protos[0]) + assert all(isinstance(r, uuid.UUID) for r in result) assert result == test_uuids def test_pg_uuid_type_mapping(self): From 8e15939cbe72c2a26fa19252355ae030d8823213 Mon Sep 17 00:00:00 2001 From: soojin Date: Mon, 9 Feb 2026 00:54:49 +0900 Subject: [PATCH 05/15] fix: Address review feedback for UUID type support Signed-off-by: soojin --- .../feast/infra/online_stores/online_store.py | 11 +++++++++-- sdk/python/feast/type_map.py | 13 +++++++++++++ sdk/python/tests/unit/test_type_map.py | 16 ++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/online_store.py b/sdk/python/feast/infra/online_stores/online_store.py index 27844ab347a..60bbd749cba 100644 --- a/sdk/python/feast/infra/online_stores/online_store.py +++ b/sdk/python/feast/infra/online_stores/online_store.py @@ -391,12 +391,19 @@ async def query_table(table, requested_features): def _build_feature_types( grouped_refs: List, ) -> Dict[str, ValueType]: - """Build a mapping of feature names to ValueType from grouped feature view refs.""" + """Build a mapping of feature names to ValueType from grouped feature view refs. + + Includes both bare names and prefixed names (feature_view__feature) so that + lookups succeed regardless of the full_feature_names setting. + """ feature_types: Dict[str, ValueType] = {} for table, requested_features in grouped_refs: + table_name = table.projection.name_to_use() for field in table.features: if field.name in requested_features: - feature_types[field.name] = field.dtype.to_value_type() + vtype = field.dtype.to_value_type() + feature_types[field.name] = vtype + feature_types[f"{table_name}__{field.name}"] = vtype return feature_types @abstractmethod diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 66f387ead0b..08f1b0f8eee 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -778,6 +778,19 @@ def _convert_list_values_to_proto( if feast_value_type == ValueType.BOOL_LIST: return _convert_bool_collection_to_proto(values, field_name, proto_type) + if feast_value_type in (ValueType.UUID_LIST, ValueType.TIME_UUID_LIST): + # uuid.UUID objects must be converted to str for StringList proto. + return [ + ( + ProtoValue( + **{field_name: proto_type(val=[str(e) for e in value])} # type: ignore[arg-type] + ) + if value is not None + else ProtoValue() + ) + for value in values + ] + # Generic list conversion return [ ProtoValue(**{field_name: proto_type(val=value)}) # type: ignore[arg-type] diff --git a/sdk/python/tests/unit/test_type_map.py b/sdk/python/tests/unit/test_type_map.py index 5e13c67d42a..c27958b0058 100644 --- a/sdk/python/tests/unit/test_type_map.py +++ b/sdk/python/tests/unit/test_type_map.py @@ -1484,6 +1484,22 @@ def test_uuid_list_roundtrip(self): assert all(isinstance(r, uuid.UUID) for r in result) assert result == test_uuids + def test_uuid_object_list_roundtrip(self): + """uuid.UUID objects in list -> proto -> list of uuid.UUID roundtrip.""" + test_uuids = [uuid.uuid4(), uuid.uuid4(), uuid.uuid4()] + protos = python_values_to_proto_values([test_uuids], ValueType.UUID_LIST) + result = feast_value_type_to_python_type(protos[0]) + assert all(isinstance(r, uuid.UUID) for r in result) + assert result == test_uuids + + def test_time_uuid_object_list_roundtrip(self): + """uuid.UUID objects in TIME_UUID list -> proto -> roundtrip.""" + test_uuids = [uuid.uuid1(), uuid.uuid1()] + protos = python_values_to_proto_values([test_uuids], ValueType.TIME_UUID_LIST) + result = feast_value_type_to_python_type(protos[0]) + assert all(isinstance(r, uuid.UUID) for r in result) + assert result == test_uuids + def test_pg_uuid_type_mapping(self): """PostgreSQL uuid type maps to ValueType.UUID.""" assert pg_type_to_feast_value_type("uuid") == ValueType.UUID From a098c3c808c0ebf9d6562a5cb036946e83377956 Mon Sep 17 00:00:00 2001 From: soojin Date: Mon, 9 Feb 2026 01:10:13 +0900 Subject: [PATCH 06/15] fix: Address review feedback for UUID type support Signed-off-by: soojin --- sdk/python/feast/on_demand_feature_view.py | 6 ++++-- sdk/python/feast/type_map.py | 8 +++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index d23811afccd..d710607f0b7 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -1164,8 +1164,8 @@ def _get_sample_values_by_type(self) -> dict[ValueType, list[Any]]: ValueType.PDF_BYTES: [pdf_sample], ValueType.IMAGE_BYTES: [image_sample], # UUID types - ValueType.UUID: [str(uuid.uuid4())], - ValueType.TIME_UUID: [str(uuid.uuid1())], + ValueType.UUID: [uuid.uuid4()], + ValueType.TIME_UUID: [uuid.uuid1()], # List types ValueType.BYTES_LIST: [[b"hello world"]], ValueType.STRING_LIST: [["hello world"]], @@ -1175,6 +1175,8 @@ def _get_sample_values_by_type(self) -> dict[ValueType, list[Any]]: ValueType.FLOAT_LIST: [[1.0]], ValueType.BOOL_LIST: [[True]], ValueType.UNIX_TIMESTAMP_LIST: [[_utc_now()]], + ValueType.UUID_LIST: [[uuid.uuid4(), uuid.uuid4()]], + ValueType.TIME_UUID_LIST: [[uuid.uuid1(), uuid.uuid1()]], } @staticmethod diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 08f1b0f8eee..f0e8c5374a5 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -158,9 +158,13 @@ def feast_value_type_to_python_type( if val_attr in ("uuid_list_val", "time_uuid_list_val"): return [uuid_module.UUID(v) if isinstance(v, str) else v for v in val] - # Backward compatibility: handle UUIDs stored as string_val with feature_type hint + # Backward compatibility: handle UUIDs stored as string_val/string_list_val with feature_type hint if feature_type in (ValueType.UUID, ValueType.TIME_UUID) and isinstance(val, str): return uuid_module.UUID(val) + if feature_type in (ValueType.UUID_LIST, ValueType.TIME_UUID_LIST) and isinstance( + val, list + ): + return [uuid_module.UUID(v) if isinstance(v, str) else v for v in val] return val @@ -1399,6 +1403,8 @@ def _convert_value_name_to_snowflake_udf(value_name: str, project_name: str) -> "UNIX_TIMESTAMP_LIST": f"feast_{project_name}_snowflake_array_timestamp_to_list_unix_timestamp_proto", "UUID": f"feast_{project_name}_snowflake_varchar_to_string_proto", "TIME_UUID": f"feast_{project_name}_snowflake_varchar_to_string_proto", + "UUID_LIST": f"feast_{project_name}_snowflake_array_varchar_to_list_string_proto", + "TIME_UUID_LIST": f"feast_{project_name}_snowflake_array_varchar_to_list_string_proto", } return name_map[value_name].upper() From 6c9ba6355745811523a023e26aa43aefa4269675 Mon Sep 17 00:00:00 2001 From: soojin Date: Mon, 9 Feb 2026 01:20:55 +0900 Subject: [PATCH 07/15] fix: Address review feedback Signed-off-by: soojin --- sdk/python/feast/types.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/types.py b/sdk/python/feast/types.py index 2a94610d42f..e8a82926c51 100644 --- a/sdk/python/feast/types.py +++ b/sdk/python/feast/types.py @@ -207,8 +207,10 @@ class Set(ComplexFeastType): base_type: Union[PrimitiveFeastType, ComplexFeastType] def __init__(self, base_type: Union[PrimitiveFeastType, ComplexFeastType]): - # Sets do not support MAP as a base type - supported_set_types = [t for t in SUPPORTED_BASE_TYPES if t != Map] + # Sets do not support MAP, UUID, or TimeUuid as base types + supported_set_types = [ + t for t in SUPPORTED_BASE_TYPES if t not in (Map, Uuid, TimeUuid) + ] if base_type not in supported_set_types: raise ValueError( f"Type {type(base_type)} is currently not supported as a base type for Set." From ac7a61d3589d4b9977dd90f5d78011ed75ffcb82 Mon Sep 17 00:00:00 2001 From: soojin Date: Thu, 12 Feb 2026 14:43:43 +0900 Subject: [PATCH 08/15] fix: Convert uuid.UUID to string for Arrow and JSON serialization Signed-off-by: soojin --- .../feast/infra/online_stores/remote.py | 15 +++++++++++--- sdk/python/feast/online_response.py | 20 ++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/sdk/python/feast/infra/online_stores/remote.py b/sdk/python/feast/infra/online_stores/remote.py index ed5821e18ca..12c38993405 100644 --- a/sdk/python/feast/infra/online_stores/remote.py +++ b/sdk/python/feast/infra/online_stores/remote.py @@ -13,6 +13,7 @@ # limitations under the License. import json import logging +import uuid as uuid_module from collections import defaultdict from datetime import datetime from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Tuple @@ -38,6 +39,15 @@ logger = logging.getLogger(__name__) +def _json_safe(val: Any) -> Any: + """Convert uuid.UUID objects to strings for JSON serialization.""" + if isinstance(val, uuid_module.UUID): + return str(val) + if isinstance(val, list): + return [str(v) if isinstance(v, uuid_module.UUID) else v for v in val] + return val + + class RemoteOnlineStoreConfig(FeastConfigBaseModel): """Remote Online store config for remote online store""" @@ -128,9 +138,8 @@ def online_write_batch( for join_key, entity_value_proto in zip( entity_key_proto.join_keys, entity_key_proto.entity_values ): - columnar_data[join_key].append( - feast_value_type_to_python_type(entity_value_proto) - ) + val = feast_value_type_to_python_type(entity_value_proto) + columnar_data[join_key].append(_json_safe(val)) # Populate feature values – use transport-safe conversion that # preserves JSON strings instead of parsing them into dicts. diff --git a/sdk/python/feast/online_response.py b/sdk/python/feast/online_response.py index 00accc17638..ba8294f16b9 100644 --- a/sdk/python/feast/online_response.py +++ b/sdk/python/feast/online_response.py @@ -103,8 +103,26 @@ def to_arrow(self, include_event_timestamps: bool = False) -> pa.Table: Args: include_event_timestamps: bool Optionally include feature timestamps in the table """ + import uuid as uuid_module - return pa.Table.from_pydict(self.to_dict(include_event_timestamps)) + result = self.to_dict(include_event_timestamps) + # Convert uuid.UUID objects to strings for PyArrow compatibility + for key, values in result.items(): + first_valid = next((v for v in values if v is not None), None) + if isinstance(first_valid, uuid_module.UUID): + result[key] = [ + str(v) if isinstance(v, uuid_module.UUID) else v for v in values + ] + elif isinstance(first_valid, list): + inner = next((e for e in first_valid if e is not None), None) + if isinstance(inner, uuid_module.UUID): + result[key] = [ + [str(e) if isinstance(e, uuid_module.UUID) else e for e in v] + if isinstance(v, list) + else v + for v in values + ] + return pa.Table.from_pydict(result) def to_tensor( self, From fe0bfb209de46b7d6d924b4a29fcf2fdae839257 Mon Sep 17 00:00:00 2001 From: soojin Date: Tue, 17 Feb 2026 13:19:44 +0900 Subject: [PATCH 09/15] feat: Add UUID_SET/TIME_UUID_SET support and update type system docs Add Set(Uuid) and Set(TimeUuid) as feature types with full roundtrip support, backward compatibility, and documentation for all UUID types. Signed-off-by: soojin Co-Authored-By: Claude Opus 4.6 --- docs/getting-started/concepts/feast-types.md | 3 +- docs/reference/type-system.md | 42 ++++++- protos/feast/types/Value.proto | 4 + .../feast/infra/online_stores/remote.py | 4 +- sdk/python/feast/on_demand_feature_view.py | 3 + sdk/python/feast/online_response.py | 8 ++ .../feast/protos/feast/types/Value_pb2.py | 110 +++++++++--------- sdk/python/feast/type_map.py | 31 +++++ sdk/python/feast/types.py | 6 +- sdk/python/feast/value_type.py | 2 + sdk/python/tests/unit/test_type_map.py | 49 ++++++++ sdk/python/tests/unit/test_types.py | 10 ++ 12 files changed, 213 insertions(+), 59 deletions(-) diff --git a/docs/getting-started/concepts/feast-types.md b/docs/getting-started/concepts/feast-types.md index 4463d068fe2..df95ea3bc2a 100644 --- a/docs/getting-started/concepts/feast-types.md +++ b/docs/getting-started/concepts/feast-types.md @@ -9,7 +9,8 @@ Feast supports the following categories of data types: - **Primitive types**: numerical values (`Int32`, `Int64`, `Float32`, `Float64`), `String`, `Bytes`, `Bool`, and `UnixTimestamp`. - **Domain-specific primitives**: `PdfBytes` (PDF binary data for RAG/document pipelines) and `ImageBytes` (image binary data for multimodal pipelines). These are semantic aliases over `Bytes` and must be explicitly declared in schema — no backend infers them. -- **Array types**: ordered lists of any primitive type, e.g. `Array(Int64)`, `Array(String)`. +- **UUID types**: `Uuid` and `TimeUuid` for universally unique identifiers. Stored as strings at the proto level but deserialized to `uuid.UUID` objects in Python. +- **Array types**: ordered lists of any primitive type, e.g. `Array(Int64)`, `Array(String)`, `Array(Uuid)`. - **Set types**: unordered collections of unique values for any primitive type, e.g. `Set(String)`, `Set(Int64)`. Set types are not inferred by any backend and must be explicitly declared. They are best suited for online serving use cases. - **Map types**: dictionary-like structures with string keys and values that can be any supported Feast type (including nested maps), e.g. `Map`, `Array(Map)`. - **JSON type**: opaque JSON data stored as a string at the proto level but semantically distinct from `String` — backends use native JSON types (`jsonb`, `VARIANT`, etc.), e.g. `Json`, `Array(Json)`. diff --git a/docs/reference/type-system.md b/docs/reference/type-system.md index 0eda0fac665..f0739df8cd3 100644 --- a/docs/reference/type-system.md +++ b/docs/reference/type-system.md @@ -24,6 +24,8 @@ Feast supports the following data types: | `Bytes` | `bytes` | Binary data | | `Bool` | `bool` | Boolean value | | `UnixTimestamp` | `datetime` | Unix timestamp (nullable) | +| `Uuid` | `uuid.UUID` | UUID (any version) | +| `TimeUuid` | `uuid.UUID` | Time-based UUID (version 1) | ### Domain-Specific Primitive Types @@ -52,6 +54,8 @@ All primitive types have corresponding array (list) types: | `Array(Bytes)` | `List[bytes]` | List of binary data | | `Array(Bool)` | `List[bool]` | List of booleans | | `Array(UnixTimestamp)` | `List[datetime]` | List of timestamps | +| `Array(Uuid)` | `List[uuid.UUID]` | List of UUIDs | +| `Array(TimeUuid)` | `List[uuid.UUID]` | List of time-based UUIDs | ### Set Types @@ -67,6 +71,8 @@ All primitive types (except `Map` and `Json`) have corresponding set types for s | `Set(Bytes)` | `Set[bytes]` | Set of unique binary data | | `Set(Bool)` | `Set[bool]` | Set of unique booleans | | `Set(UnixTimestamp)` | `Set[datetime]` | Set of unique timestamps | +| `Set(Uuid)` | `Set[uuid.UUID]` | Set of unique UUIDs | +| `Set(TimeUuid)` | `Set[uuid.UUID]` | Set of unique time-based UUIDs | **Note:** Set types automatically remove duplicate values. When converting from lists or other iterables to sets, duplicates are eliminated. @@ -169,7 +175,7 @@ from datetime import timedelta from feast import Entity, FeatureView, Field, FileSource from feast.types import ( Int32, Int64, Float32, Float64, String, Bytes, Bool, UnixTimestamp, - Array, Set, Map, Json, Struct + Uuid, TimeUuid, Array, Set, Map, Json, Struct ) # Define a data source @@ -199,6 +205,8 @@ user_features = FeatureView( Field(name="profile_picture", dtype=Bytes), Field(name="is_active", dtype=Bool), Field(name="last_login", dtype=UnixTimestamp), + Field(name="session_id", dtype=Uuid), + Field(name="event_id", dtype=TimeUuid), # Array types Field(name="daily_steps", dtype=Array(Int32)), @@ -209,12 +217,16 @@ user_features = FeatureView( Field(name="document_hashes", dtype=Array(Bytes)), Field(name="notification_settings", dtype=Array(Bool)), Field(name="login_timestamps", dtype=Array(UnixTimestamp)), + Field(name="related_session_ids", dtype=Array(Uuid)), + Field(name="event_chain", dtype=Array(TimeUuid)), # Set types (unique values only — see backend caveats above) Field(name="visited_pages", dtype=Set(String)), Field(name="unique_categories", dtype=Set(Int32)), Field(name="tag_ids", dtype=Set(Int64)), Field(name="preferred_languages", dtype=Set(String)), + Field(name="unique_device_ids", dtype=Set(Uuid)), + Field(name="unique_event_ids", dtype=Set(TimeUuid)), # Map types Field(name="user_preferences", dtype=Map), @@ -250,6 +262,34 @@ tag_list = [100, 200, 300, 100, 200] tag_ids = set(tag_list) # {100, 200, 300} ``` +### UUID Type Usage Examples + +UUID types store universally unique identifiers natively, with support for both random UUIDs and time-based UUIDs: + +```python +import uuid + +# Random UUID (version 4) — use Uuid type +session_id = uuid.uuid4() # e.g., UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') + +# Time-based UUID (version 1) — use TimeUuid type +event_id = uuid.uuid1() # e.g., UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') + +# UUID values are returned as uuid.UUID objects from get_online_features() +response = store.get_online_features( + features=["user_features:session_id"], + entity_rows=[{"user_id": 1}], +) +result = response.to_dict() +# result["session_id"][0] is a uuid.UUID object + +# UUID lists +related_sessions = [uuid.uuid4(), uuid.uuid4(), uuid.uuid4()] + +# UUID sets (unique values) +unique_devices = {uuid.uuid4(), uuid.uuid4()} +``` + ### Map Type Usage Examples Maps can store complex nested data structures: diff --git a/protos/feast/types/Value.proto b/protos/feast/types/Value.proto index e67a50b9d58..69922eb0e8e 100644 --- a/protos/feast/types/Value.proto +++ b/protos/feast/types/Value.proto @@ -61,6 +61,8 @@ message ValueType { TIME_UUID = 37; UUID_LIST = 38; TIME_UUID_LIST = 39; + UUID_SET = 40; + TIME_UUID_SET = 41; } } @@ -104,6 +106,8 @@ message Value { string time_uuid_val = 37; StringList uuid_list_val = 38; StringList time_uuid_list_val = 39; + StringSet uuid_set_val = 40; + StringSet time_uuid_set_val = 41; } } diff --git a/sdk/python/feast/infra/online_stores/remote.py b/sdk/python/feast/infra/online_stores/remote.py index 12c38993405..78b88363ae0 100644 --- a/sdk/python/feast/infra/online_stores/remote.py +++ b/sdk/python/feast/infra/online_stores/remote.py @@ -40,9 +40,11 @@ def _json_safe(val: Any) -> Any: - """Convert uuid.UUID objects to strings for JSON serialization.""" + """Convert uuid.UUID objects and sets to JSON-serializable form.""" if isinstance(val, uuid_module.UUID): return str(val) + if isinstance(val, set): + return [str(v) if isinstance(v, uuid_module.UUID) else v for v in val] if isinstance(val, list): return [str(v) if isinstance(v, uuid_module.UUID) else v for v in val] return val diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index d710607f0b7..0e53e3e2370 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -1177,6 +1177,9 @@ def _get_sample_values_by_type(self) -> dict[ValueType, list[Any]]: ValueType.UNIX_TIMESTAMP_LIST: [[_utc_now()]], ValueType.UUID_LIST: [[uuid.uuid4(), uuid.uuid4()]], ValueType.TIME_UUID_LIST: [[uuid.uuid1(), uuid.uuid1()]], + # Set types + ValueType.UUID_SET: [{uuid.uuid4(), uuid.uuid4()}], + ValueType.TIME_UUID_SET: [{uuid.uuid1(), uuid.uuid1()}], } @staticmethod diff --git a/sdk/python/feast/online_response.py b/sdk/python/feast/online_response.py index ba8294f16b9..2cd22969fec 100644 --- a/sdk/python/feast/online_response.py +++ b/sdk/python/feast/online_response.py @@ -122,6 +122,14 @@ def to_arrow(self, include_event_timestamps: bool = False) -> pa.Table: else v for v in values ] + elif isinstance(first_valid, set): + inner = next((e for e in first_valid if e is not None), None) + if isinstance(inner, uuid_module.UUID): + result[key] = [ + [str(e) for e in v] if isinstance(v, set) else v for v in values + ] + else: + result[key] = [list(v) if isinstance(v, set) else v for v in values] return pa.Table.from_pydict(result) def to_tensor( diff --git a/sdk/python/feast/protos/feast/types/Value_pb2.py b/sdk/python/feast/protos/feast/types/Value_pb2.py index 5dc9084ed05..79474d6b368 100644 --- a/sdk/python/feast/protos/feast/types/Value_pb2.py +++ b/sdk/python/feast/protos/feast/types/Value_pb2.py @@ -3,69 +3,73 @@ # source: feast/types/Value.proto # Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" + from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder + # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types\"\xec\x03\n\tValueType\"\xde\x03\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04UUID\x10\x1e\x12\r\n\tTIME_UUID\x10\x1f\x12\r\n\tUUID_LIST\x10 \x12\x12\n\x0eTIME_UUID_LIST\x10!\"\xf6\t\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08uuid_val\x18\x1e \x01(\tH\x00\x12\x17\n\rtime_uuid_val\x18\x1f \x01(\tH\x00\x12\x30\n\ruuid_list_val\x18 \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x35\n\x12time_uuid_list_val\x18! \x01(\x0b\x32\x17.feast.types.StringListH\x00\x42\x05\n\x03val\"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08\"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08\"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01\"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map\"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types"\x8d\x04\n\tValueType"\xff\x03\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04UUID\x10\x1e\x12\r\n\tTIME_UUID\x10\x1f\x12\r\n\tUUID_LIST\x10 \x12\x12\n\x0eTIME_UUID_LIST\x10!\x12\x0c\n\x08UUID_SET\x10"\x12\x11\n\rTIME_UUID_SET\x10#"\xdb\n\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08uuid_val\x18\x1e \x01(\tH\x00\x12\x17\n\rtime_uuid_val\x18\x1f \x01(\tH\x00\x12\x30\n\ruuid_list_val\x18 \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x35\n\x12time_uuid_list_val\x18! \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12.\n\x0cuuid_set_val\x18" \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12\x33\n\x11time_uuid_set_val\x18# \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x42\x05\n\x03val"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3' +) _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'feast.types.Value_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None - _globals['DESCRIPTOR']._serialized_options = b'\n\021feast.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/types' - _globals['_MAP_VALENTRY']._options = None - _globals['_MAP_VALENTRY']._serialized_options = b'8\001' - _globals['_NULL']._serialized_start=2370 - _globals['_NULL']._serialized_end=2386 - _globals['_VALUETYPE']._serialized_start=41 - _globals['_VALUETYPE']._serialized_end=533 - _globals['_VALUETYPE_ENUM']._serialized_start=55 - _globals['_VALUETYPE_ENUM']._serialized_end=533 - _globals['_VALUE']._serialized_start=536 - _globals['_VALUE']._serialized_end=1806 - _globals['_BYTESLIST']._serialized_start=1808 - _globals['_BYTESLIST']._serialized_end=1832 - _globals['_STRINGLIST']._serialized_start=1834 - _globals['_STRINGLIST']._serialized_end=1859 - _globals['_INT32LIST']._serialized_start=1861 - _globals['_INT32LIST']._serialized_end=1885 - _globals['_INT64LIST']._serialized_start=1887 - _globals['_INT64LIST']._serialized_end=1911 - _globals['_DOUBLELIST']._serialized_start=1913 - _globals['_DOUBLELIST']._serialized_end=1938 - _globals['_FLOATLIST']._serialized_start=1940 - _globals['_FLOATLIST']._serialized_end=1964 - _globals['_BOOLLIST']._serialized_start=1966 - _globals['_BOOLLIST']._serialized_end=1989 - _globals['_BYTESSET']._serialized_start=1991 - _globals['_BYTESSET']._serialized_end=2014 - _globals['_STRINGSET']._serialized_start=2016 - _globals['_STRINGSET']._serialized_end=2040 - _globals['_INT32SET']._serialized_start=2042 - _globals['_INT32SET']._serialized_end=2065 - _globals['_INT64SET']._serialized_start=2067 - _globals['_INT64SET']._serialized_end=2090 - _globals['_DOUBLESET']._serialized_start=2092 - _globals['_DOUBLESET']._serialized_end=2116 - _globals['_FLOATSET']._serialized_start=2118 - _globals['_FLOATSET']._serialized_end=2141 - _globals['_BOOLSET']._serialized_start=2143 - _globals['_BOOLSET']._serialized_end=2165 - _globals['_MAP']._serialized_start=2167 - _globals['_MAP']._serialized_end=2276 - _globals['_MAP_VALENTRY']._serialized_start=2214 - _globals['_MAP_VALENTRY']._serialized_end=2276 - _globals['_MAPLIST']._serialized_start=2278 - _globals['_MAPLIST']._serialized_end=2318 - _globals['_REPEATEDVALUE']._serialized_start=2320 - _globals['_REPEATEDVALUE']._serialized_end=2368 +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "feast.types.Value_pb2", _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals["DESCRIPTOR"]._options = None + _globals[ + "DESCRIPTOR" + ]._serialized_options = b"\n\021feast.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/types" + _globals["_MAP_VALENTRY"]._options = None + _globals["_MAP_VALENTRY"]._serialized_options = b"8\001" + _globals["_NULL"]._serialized_start = 2504 + _globals["_NULL"]._serialized_end = 2520 + _globals["_VALUETYPE"]._serialized_start = 41 + _globals["_VALUETYPE"]._serialized_end = 566 + _globals["_VALUETYPE_ENUM"]._serialized_start = 55 + _globals["_VALUETYPE_ENUM"]._serialized_end = 566 + _globals["_VALUE"]._serialized_start = 569 + _globals["_VALUE"]._serialized_end = 1940 + _globals["_BYTESLIST"]._serialized_start = 1942 + _globals["_BYTESLIST"]._serialized_end = 1966 + _globals["_STRINGLIST"]._serialized_start = 1968 + _globals["_STRINGLIST"]._serialized_end = 1993 + _globals["_INT32LIST"]._serialized_start = 1995 + _globals["_INT32LIST"]._serialized_end = 2019 + _globals["_INT64LIST"]._serialized_start = 2021 + _globals["_INT64LIST"]._serialized_end = 2045 + _globals["_DOUBLELIST"]._serialized_start = 2047 + _globals["_DOUBLELIST"]._serialized_end = 2072 + _globals["_FLOATLIST"]._serialized_start = 2074 + _globals["_FLOATLIST"]._serialized_end = 2098 + _globals["_BOOLLIST"]._serialized_start = 2100 + _globals["_BOOLLIST"]._serialized_end = 2123 + _globals["_BYTESSET"]._serialized_start = 2125 + _globals["_BYTESSET"]._serialized_end = 2148 + _globals["_STRINGSET"]._serialized_start = 2150 + _globals["_STRINGSET"]._serialized_end = 2174 + _globals["_INT32SET"]._serialized_start = 2176 + _globals["_INT32SET"]._serialized_end = 2199 + _globals["_INT64SET"]._serialized_start = 2201 + _globals["_INT64SET"]._serialized_end = 2224 + _globals["_DOUBLESET"]._serialized_start = 2226 + _globals["_DOUBLESET"]._serialized_end = 2250 + _globals["_FLOATSET"]._serialized_start = 2252 + _globals["_FLOATSET"]._serialized_end = 2275 + _globals["_BOOLSET"]._serialized_start = 2277 + _globals["_BOOLSET"]._serialized_end = 2299 + _globals["_MAP"]._serialized_start = 2301 + _globals["_MAP"]._serialized_end = 2410 + _globals["_MAP_VALENTRY"]._serialized_start = 2348 + _globals["_MAP_VALENTRY"]._serialized_end = 2410 + _globals["_MAPLIST"]._serialized_start = 2412 + _globals["_MAPLIST"]._serialized_end = 2452 + _globals["_REPEATEDVALUE"]._serialized_start = 2454 + _globals["_REPEATEDVALUE"]._serialized_end = 2502 # @@protoc_insertion_point(module_scope) diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index f0e8c5374a5..c911f5b0b9a 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -157,6 +157,8 @@ def feast_value_type_to_python_type( return uuid_module.UUID(val) if isinstance(val, str) else val if val_attr in ("uuid_list_val", "time_uuid_list_val"): return [uuid_module.UUID(v) if isinstance(v, str) else v for v in val] + if val_attr in ("uuid_set_val", "time_uuid_set_val"): + return {uuid_module.UUID(v) if isinstance(v, str) else v for v in val} # Backward compatibility: handle UUIDs stored as string_val/string_list_val with feature_type hint if feature_type in (ValueType.UUID, ValueType.TIME_UUID) and isinstance(val, str): @@ -165,6 +167,10 @@ def feast_value_type_to_python_type( val, list ): return [uuid_module.UUID(v) if isinstance(v, str) else v for v in val] + if feature_type in (ValueType.UUID_SET, ValueType.TIME_UUID_SET) and isinstance( + val, set + ): + return {uuid_module.UUID(v) if isinstance(v, str) else v for v in val} return val @@ -496,6 +502,12 @@ def _type_err(item, dtype): ValueType.STRING_SET: (StringSet, "string_set_val", [np.str_, str]), ValueType.BOOL_SET: (BoolSet, "bool_set_val", [np.bool_, bool]), ValueType.BYTES_SET: (BytesSet, "bytes_set_val", [np.bytes_, bytes]), + ValueType.UUID_SET: (StringSet, "uuid_set_val", [np.str_, str, uuid_module.UUID]), + ValueType.TIME_UUID_SET: ( + StringSet, + "time_uuid_set_val", + [np.str_, str, uuid_module.UUID], + ), } PYTHON_SCALAR_VALUE_TYPE_TO_PROTO_VALUE: Dict[ @@ -722,6 +734,19 @@ def convert_set_to_list(value: Any) -> Any: converted_values, set_field_name, set_proto_type ) + if feast_value_type in (ValueType.UUID_SET, ValueType.TIME_UUID_SET): + # uuid.UUID objects must be converted to str for StringSet proto. + return [ + ( + ProtoValue( + **{set_field_name: set_proto_type(val=[str(e) for e in value])} # type: ignore[arg-type] + ) + if value is not None + else ProtoValue() + ) + for value in converted_values + ] + # Generic set conversion return [ ProtoValue(**{set_field_name: set_proto_type(val=value)}) # type: ignore[arg-type] @@ -1108,6 +1133,8 @@ def python_values_to_proto_values( "bytes_set_val": ValueType.BYTES_SET, "bool_set_val": ValueType.BOOL_SET, "unix_timestamp_set_val": ValueType.UNIX_TIMESTAMP_SET, + "uuid_set_val": ValueType.UUID_SET, + "time_uuid_set_val": ValueType.TIME_UUID_SET, "uuid_val": ValueType.UUID, "time_uuid_val": ValueType.TIME_UUID, "uuid_list_val": ValueType.UUID_LIST, @@ -1405,6 +1432,8 @@ def _convert_value_name_to_snowflake_udf(value_name: str, project_name: str) -> "TIME_UUID": f"feast_{project_name}_snowflake_varchar_to_string_proto", "UUID_LIST": f"feast_{project_name}_snowflake_array_varchar_to_list_string_proto", "TIME_UUID_LIST": f"feast_{project_name}_snowflake_array_varchar_to_list_string_proto", + "UUID_SET": f"feast_{project_name}_snowflake_array_varchar_to_list_string_proto", + "TIME_UUID_SET": f"feast_{project_name}_snowflake_array_varchar_to_list_string_proto", } return name_map[value_name].upper() @@ -1660,6 +1689,8 @@ def feast_value_type_to_pa( ValueType.TIME_UUID: pyarrow.string(), ValueType.UUID_LIST: pyarrow.list_(pyarrow.string()), ValueType.TIME_UUID_LIST: pyarrow.list_(pyarrow.string()), + ValueType.UUID_SET: pyarrow.list_(pyarrow.string()), + ValueType.TIME_UUID_SET: pyarrow.list_(pyarrow.string()), } return type_map[feast_type] diff --git a/sdk/python/feast/types.py b/sdk/python/feast/types.py index e8a82926c51..a950abd0d87 100644 --- a/sdk/python/feast/types.py +++ b/sdk/python/feast/types.py @@ -208,9 +208,7 @@ class Set(ComplexFeastType): def __init__(self, base_type: Union[PrimitiveFeastType, ComplexFeastType]): # Sets do not support MAP, UUID, or TimeUuid as base types - supported_set_types = [ - t for t in SUPPORTED_BASE_TYPES if t not in (Map, Uuid, TimeUuid) - ] + supported_set_types = [t for t in SUPPORTED_BASE_TYPES if t not in (Map,)] if base_type not in supported_set_types: raise ValueError( f"Type {type(base_type)} is currently not supported as a base type for Set." @@ -313,6 +311,8 @@ def __hash__(self): ValueType.TIME_UUID: TimeUuid, ValueType.UUID_LIST: Array(Uuid), ValueType.TIME_UUID_LIST: Array(TimeUuid), + ValueType.UUID_SET: Set(Uuid), + ValueType.TIME_UUID_SET: Set(TimeUuid), } FEAST_TYPES_TO_PYARROW_TYPES = { diff --git a/sdk/python/feast/value_type.py b/sdk/python/feast/value_type.py index 94a8e61c1fd..0575d25a1f1 100644 --- a/sdk/python/feast/value_type.py +++ b/sdk/python/feast/value_type.py @@ -75,6 +75,8 @@ class ValueType(enum.Enum): TIME_UUID = 37 UUID_LIST = 38 TIME_UUID_LIST = 39 + UUID_SET = 40 + TIME_UUID_SET = 41 ListType = Union[ diff --git a/sdk/python/tests/unit/test_type_map.py b/sdk/python/tests/unit/test_type_map.py index c27958b0058..6715817d3cb 100644 --- a/sdk/python/tests/unit/test_type_map.py +++ b/sdk/python/tests/unit/test_type_map.py @@ -1500,6 +1500,55 @@ def test_time_uuid_object_list_roundtrip(self): assert all(isinstance(r, uuid.UUID) for r in result) assert result == test_uuids + def test_uuid_set_roundtrip(self): + """UUID set -> proto -> set of uuid.UUID objects roundtrip.""" + test_uuids = {uuid.uuid4(), uuid.uuid4(), uuid.uuid4()} + protos = python_values_to_proto_values([test_uuids], ValueType.UUID_SET) + + result = feast_value_type_to_python_type(protos[0]) + assert isinstance(result, set) + assert all(isinstance(r, uuid.UUID) for r in result) + assert result == test_uuids + + def test_time_uuid_set_roundtrip(self): + """TIME_UUID set -> proto -> set of uuid.UUID objects roundtrip.""" + test_uuids = {uuid.uuid1(), uuid.uuid1()} + protos = python_values_to_proto_values([test_uuids], ValueType.TIME_UUID_SET) + + result = feast_value_type_to_python_type(protos[0]) + assert isinstance(result, set) + assert all(isinstance(r, uuid.UUID) for r in result) + assert result == test_uuids + + def test_uuid_object_set_roundtrip(self): + """uuid.UUID objects in set -> proto -> set of uuid.UUID roundtrip.""" + test_uuids = {uuid.uuid4(), uuid.uuid4()} + protos = python_values_to_proto_values([test_uuids], ValueType.UUID_SET) + + result = feast_value_type_to_python_type(protos[0]) + assert isinstance(result, set) + assert result == test_uuids + + def test_uuid_set_backward_compat_string_set_val(self): + """UUIDs stored as string_set_val (old format) still work with feature_type hint.""" + from feast.protos.feast.types.Value_pb2 import StringSet + from feast.protos.feast.types.Value_pb2 import Value as ProtoValue + + test_uuids = {uuid.uuid4(), uuid.uuid4()} + # Simulate old-format proto with string_set_val + proto = ProtoValue(string_set_val=StringSet(val=[str(u) for u in test_uuids])) + + # Without feature_type, returns set of strings + result = feast_value_type_to_python_type(proto) + assert isinstance(result, set) + assert all(isinstance(r, str) for r in result) + + # With feature_type hint, returns set of uuid.UUID + result = feast_value_type_to_python_type(proto, ValueType.UUID_SET) + assert isinstance(result, set) + assert all(isinstance(r, uuid.UUID) for r in result) + assert result == test_uuids + def test_pg_uuid_type_mapping(self): """PostgreSQL uuid type maps to ValueType.UUID.""" assert pg_type_to_feast_value_type("uuid") == ValueType.UUID diff --git a/sdk/python/tests/unit/test_types.py b/sdk/python/tests/unit/test_types.py index 3b90ee70965..ed4b1383879 100644 --- a/sdk/python/tests/unit/test_types.py +++ b/sdk/python/tests/unit/test_types.py @@ -60,6 +60,16 @@ def test_uuid_array_feast_type(): assert from_value_type(array_time_uuid.to_value_type()) == array_time_uuid +def test_uuid_set_feast_type(): + set_uuid = Set(Uuid) + assert set_uuid.to_value_type() == ValueType.UUID_SET + assert from_value_type(set_uuid.to_value_type()) == set_uuid + + set_time_uuid = Set(TimeUuid) + assert set_time_uuid.to_value_type() == ValueType.TIME_UUID_SET + assert from_value_type(set_time_uuid.to_value_type()) == set_time_uuid + + def test_all_value_types(): for value in ValueType: # We do not support the NULL type. From 732088a3576260e9fe7b32f3c85e9650ecdff4bb Mon Sep 17 00:00:00 2001 From: soojin Date: Tue, 17 Feb 2026 22:01:09 +0900 Subject: [PATCH 10/15] fix: Preserve PDF_BYTES/IMAGE_BYTES enum values and add missing SET type mappings Keep PDF_BYTES=30 and IMAGE_BYTES=31 at their upstream values instead of renumbering them. Shift UUID types to 32-37 in both proto and Python enum. Also add missing SET type entries in _convert_value_type_str_to_value_type(), convert_array_column(), and _get_sample_values_by_type() for completeness. Signed-off-by: soojin Co-Authored-By: Claude Opus 4.6 --- sdk/python/feast/on_demand_feature_view.py | 8 +++++++ .../feast/protos/feast/types/Value_pb2.py | 2 +- sdk/python/feast/type_map.py | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/sdk/python/feast/on_demand_feature_view.py b/sdk/python/feast/on_demand_feature_view.py index 0e53e3e2370..530a96065a8 100644 --- a/sdk/python/feast/on_demand_feature_view.py +++ b/sdk/python/feast/on_demand_feature_view.py @@ -1178,6 +1178,14 @@ def _get_sample_values_by_type(self) -> dict[ValueType, list[Any]]: ValueType.UUID_LIST: [[uuid.uuid4(), uuid.uuid4()]], ValueType.TIME_UUID_LIST: [[uuid.uuid1(), uuid.uuid1()]], # Set types + ValueType.BYTES_SET: [{b"hello world", b"foo bar"}], + ValueType.STRING_SET: [{"hello world", "foo bar"}], + ValueType.INT32_SET: [{1, 2}], + ValueType.INT64_SET: [{1, 2}], + ValueType.DOUBLE_SET: [{1.0, 2.0}], + ValueType.FLOAT_SET: [{1.0, 2.0}], + ValueType.BOOL_SET: [{True, False}], + ValueType.UNIX_TIMESTAMP_SET: [{_utc_now()}], ValueType.UUID_SET: [{uuid.uuid4(), uuid.uuid4()}], ValueType.TIME_UUID_SET: [{uuid.uuid1(), uuid.uuid1()}], } diff --git a/sdk/python/feast/protos/feast/types/Value_pb2.py b/sdk/python/feast/protos/feast/types/Value_pb2.py index 79474d6b368..f4226cfafe4 100644 --- a/sdk/python/feast/protos/feast/types/Value_pb2.py +++ b/sdk/python/feast/protos/feast/types/Value_pb2.py @@ -15,7 +15,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types"\x8d\x04\n\tValueType"\xff\x03\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04UUID\x10\x1e\x12\r\n\tTIME_UUID\x10\x1f\x12\r\n\tUUID_LIST\x10 \x12\x12\n\x0eTIME_UUID_LIST\x10!\x12\x0c\n\x08UUID_SET\x10"\x12\x11\n\rTIME_UUID_SET\x10#"\xdb\n\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08uuid_val\x18\x1e \x01(\tH\x00\x12\x17\n\rtime_uuid_val\x18\x1f \x01(\tH\x00\x12\x30\n\ruuid_list_val\x18 \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x35\n\x12time_uuid_list_val\x18! \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12.\n\x0cuuid_set_val\x18" \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12\x33\n\x11time_uuid_set_val\x18# \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x42\x05\n\x03val"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3' + b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types"\x8d\x04\n\tValueType"\xff\x03\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04UUID\x10 \x12\r\n\tTIME_UUID\x10!\x12\r\n\tUUID_LIST\x10"\x12\x12\n\x0eTIME_UUID_LIST\x10#\x12\x0c\n\x08UUID_SET\x10$\x12\x11\n\rTIME_UUID_SET\x10%"\xdb\n\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08uuid_val\x18 \x01(\tH\x00\x12\x17\n\rtime_uuid_val\x18! \x01(\tH\x00\x12\x30\n\ruuid_list_val\x18" \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x35\n\x12time_uuid_list_val\x18# \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12.\n\x0cuuid_set_val\x18$ \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12\x33\n\x11time_uuid_set_val\x18% \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x42\x05\n\x03val"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3' ) _globals = globals() diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index c911f5b0b9a..1754a8df269 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -432,10 +432,20 @@ def _convert_value_type_str_to_value_type(type_str: str) -> ValueType: "JSON_LIST": ValueType.JSON_LIST, "STRUCT": ValueType.STRUCT, "STRUCT_LIST": ValueType.STRUCT_LIST, + "BYTES_SET": ValueType.BYTES_SET, + "STRING_SET": ValueType.STRING_SET, + "INT32_SET": ValueType.INT32_SET, + "INT64_SET": ValueType.INT64_SET, + "DOUBLE_SET": ValueType.DOUBLE_SET, + "FLOAT_SET": ValueType.FLOAT_SET, + "BOOL_SET": ValueType.BOOL_SET, + "UNIX_TIMESTAMP_SET": ValueType.UNIX_TIMESTAMP_SET, "UUID": ValueType.UUID, "TIME_UUID": ValueType.TIME_UUID, "UUID_LIST": ValueType.UUID_LIST, "TIME_UUID_LIST": ValueType.TIME_UUID_LIST, + "UUID_SET": ValueType.UUID_SET, + "TIME_UUID_SET": ValueType.TIME_UUID_SET, } return type_map.get(type_str, ValueType.STRING) @@ -1890,6 +1900,18 @@ def convert_array_column(series: pd.Series, value_type: ValueType) -> pd.Series: ValueType.STRING_LIST: object, ValueType.BYTES_LIST: object, ValueType.UNIX_TIMESTAMP_LIST: "datetime64[s]", + ValueType.UUID_LIST: object, + ValueType.TIME_UUID_LIST: object, + ValueType.BYTES_SET: object, + ValueType.STRING_SET: object, + ValueType.INT32_SET: np.int32, + ValueType.INT64_SET: np.int64, + ValueType.FLOAT_SET: np.float32, + ValueType.DOUBLE_SET: np.float64, + ValueType.BOOL_SET: np.bool_, + ValueType.UNIX_TIMESTAMP_SET: "datetime64[s]", + ValueType.UUID_SET: object, + ValueType.TIME_UUID_SET: object, } target_dtype = base_type_map.get(value_type, object) From 5d637c593208049280d646f2f9966cda6f7c8a95 Mon Sep 17 00:00:00 2001 From: soojin Date: Tue, 17 Feb 2026 22:37:01 +0900 Subject: [PATCH 11/15] fix: Correct misleading comment in Set.__init__ The comment claimed Sets do not support UUID/TimeUuid but the code intentionally allows them. Updated to reflect actual behavior. Signed-off-by: soojin Co-Authored-By: Claude Opus 4.6 --- sdk/python/feast/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/feast/types.py b/sdk/python/feast/types.py index a950abd0d87..6f027f08e0b 100644 --- a/sdk/python/feast/types.py +++ b/sdk/python/feast/types.py @@ -207,7 +207,7 @@ class Set(ComplexFeastType): base_type: Union[PrimitiveFeastType, ComplexFeastType] def __init__(self, base_type: Union[PrimitiveFeastType, ComplexFeastType]): - # Sets do not support MAP, UUID, or TimeUuid as base types + # Sets do not support MAP as a base type supported_set_types = [t for t in SUPPORTED_BASE_TYPES if t not in (Map,)] if base_type not in supported_set_types: raise ValueError( From e4b0a861dd321d2999f1ac95d7fdaee125ad8c47 Mon Sep 17 00:00:00 2001 From: soojin Date: Thu, 19 Feb 2026 00:15:31 +0900 Subject: [PATCH 12/15] refactor: Extract UUID Arrow conversion into helper and move import to top Signed-off-by: soojin Co-Authored-By: Claude Opus 4.6 --- sdk/python/feast/online_response.py | 56 +++++++++++++++-------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/sdk/python/feast/online_response.py b/sdk/python/feast/online_response.py index 2cd22969fec..7b6b4806e4d 100644 --- a/sdk/python/feast/online_response.py +++ b/sdk/python/feast/online_response.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import uuid as uuid_module from typing import TYPE_CHECKING, Any, Dict, List, Optional, TypeAlias, Union import pandas as pd @@ -103,33 +104,8 @@ def to_arrow(self, include_event_timestamps: bool = False) -> pa.Table: Args: include_event_timestamps: bool Optionally include feature timestamps in the table """ - import uuid as uuid_module - result = self.to_dict(include_event_timestamps) - # Convert uuid.UUID objects to strings for PyArrow compatibility - for key, values in result.items(): - first_valid = next((v for v in values if v is not None), None) - if isinstance(first_valid, uuid_module.UUID): - result[key] = [ - str(v) if isinstance(v, uuid_module.UUID) else v for v in values - ] - elif isinstance(first_valid, list): - inner = next((e for e in first_valid if e is not None), None) - if isinstance(inner, uuid_module.UUID): - result[key] = [ - [str(e) if isinstance(e, uuid_module.UUID) else e for e in v] - if isinstance(v, list) - else v - for v in values - ] - elif isinstance(first_valid, set): - inner = next((e for e in first_valid if e is not None), None) - if isinstance(inner, uuid_module.UUID): - result[key] = [ - [str(e) for e in v] if isinstance(v, set) else v for v in values - ] - else: - result[key] = [list(v) if isinstance(v, set) else v for v in values] + result = _convert_uuids_for_arrow(result) return pa.Table.from_pydict(result) def to_tensor( @@ -175,3 +151,31 @@ def to_tensor( values # Return as-is for strings or unsupported types ) return tensor_dict + + +def _convert_uuids_for_arrow(result: Dict[str, List[Any]]) -> Dict[str, List[Any]]: + """Convert uuid.UUID objects and sets to Arrow-compatible types.""" + for key, values in result.items(): + first_valid = next((v for v in values if v is not None), None) + if isinstance(first_valid, uuid_module.UUID): + result[key] = [ + str(v) if isinstance(v, uuid_module.UUID) else v for v in values + ] + elif isinstance(first_valid, list): + inner = next((e for e in first_valid if e is not None), None) + if isinstance(inner, uuid_module.UUID): + result[key] = [ + [str(e) if isinstance(e, uuid_module.UUID) else e for e in v] + if isinstance(v, list) + else v + for v in values + ] + elif isinstance(first_valid, set): + inner = next((e for e in first_valid if e is not None), None) + if isinstance(inner, uuid_module.UUID): + result[key] = [ + [str(e) for e in v] if isinstance(v, set) else v for v in values + ] + else: + result[key] = [list(v) if isinstance(v, set) else v for v in values] + return result From b9b15d206b72408289576223c3a6edbd67760452 Mon Sep 17 00:00:00 2001 From: soojin Date: Sat, 7 Mar 2026 17:00:13 +0900 Subject: [PATCH 13/15] fix: Handle UUID types in _proto_value_to_transport_value for JSON serialization Return UUID proto fields as plain strings instead of falling through to feast_value_type_to_python_type which converts them to uuid.UUID objects that are not JSON-serializable, causing TypeError during HTTP transport. Signed-off-by: soojin --- sdk/python/feast/infra/online_stores/remote.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sdk/python/feast/infra/online_stores/remote.py b/sdk/python/feast/infra/online_stores/remote.py index 78b88363ae0..f8e8dfce483 100644 --- a/sdk/python/feast/infra/online_stores/remote.py +++ b/sdk/python/feast/infra/online_stores/remote.py @@ -115,6 +115,16 @@ def _proto_value_to_transport_value(proto_value: ValueProto) -> Any: if val_attr in ("map_list_val", "struct_list_val"): return [json.dumps(v) for v in feast_value_type_to_python_type(proto_value)] + # UUID types are stored as strings in proto — return them directly + # to avoid feast_value_type_to_python_type converting to uuid.UUID + # objects which are not JSON-serializable. + if val_attr in ("uuid_val", "time_uuid_val"): + return getattr(proto_value, val_attr) + if val_attr in ("uuid_list_val", "time_uuid_list_val"): + return list(getattr(proto_value, val_attr).val) + if val_attr in ("uuid_set_val", "time_uuid_set_val"): + return list(getattr(proto_value, val_attr).val) + return feast_value_type_to_python_type(proto_value) def online_write_batch( From 3c07e12eaa8cd44646f60193610b6c196a3bd9be Mon Sep 17 00:00:00 2001 From: soojin Date: Fri, 27 Mar 2026 23:46:03 +0900 Subject: [PATCH 14/15] chore: Regenerate protobuf files with UUID type support Co-Authored-By: Claude Opus 4.6 Signed-off-by: soojin --- .../feast/core/FeatureViewProjection_pb2.pyi | 2 +- .../feast/protos/feast/core/Registry_pb2.pyi | 26 ++--- .../protos/feast/serving/GrpcServer_pb2.pyi | 18 +-- .../feast/protos/feast/types/Value_pb2.py | 110 +++++++++--------- .../feast/protos/feast/types/Value_pb2.pyi | 40 ++++++- 5 files changed, 113 insertions(+), 83 deletions(-) diff --git a/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi b/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi index a3c19938a43..6fd1010f2e4 100644 --- a/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/FeatureViewProjection_pb2.pyi @@ -19,7 +19,7 @@ else: DESCRIPTOR: google.protobuf.descriptor.FileDescriptor class FeatureViewProjection(google.protobuf.message.Message): - """A projection to be applied on top of a FeatureView. + """A projection to be applied on top of a FeatureView. Contains the modifications to a FeatureView such as the features subset to use. """ diff --git a/sdk/python/feast/protos/feast/core/Registry_pb2.pyi b/sdk/python/feast/protos/feast/core/Registry_pb2.pyi index e581fe07e5d..29bd76323e3 100644 --- a/sdk/python/feast/protos/feast/core/Registry_pb2.pyi +++ b/sdk/python/feast/protos/feast/core/Registry_pb2.pyi @@ -1,19 +1,19 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file - -* Copyright 2020 The Feast Authors -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and + +* Copyright 2020 The Feast Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* 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 builtins diff --git a/sdk/python/feast/protos/feast/serving/GrpcServer_pb2.pyi b/sdk/python/feast/protos/feast/serving/GrpcServer_pb2.pyi index 9c1f6a0d493..a83cd87a16e 100644 --- a/sdk/python/feast/protos/feast/serving/GrpcServer_pb2.pyi +++ b/sdk/python/feast/protos/feast/serving/GrpcServer_pb2.pyi @@ -4,7 +4,7 @@ isort:skip_file """ import builtins import collections.abc -import feast.protos.feast.types.Value_pb2 +import feast.types.Value_pb2 import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message @@ -42,12 +42,12 @@ class PushRequest(google.protobuf.message.Message): VALUE_FIELD_NUMBER: builtins.int key: builtins.str @property - def value(self) -> feast.protos.feast.types.Value_pb2.Value: ... + def value(self) -> feast.types.Value_pb2.Value: ... def __init__( self, *, key: builtins.str = ..., - value: feast.protos.feast.types.Value_pb2.Value | None = ..., + value: feast.types.Value_pb2.Value | None = ..., ) -> None: ... def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... @@ -63,7 +63,7 @@ class PushRequest(google.protobuf.message.Message): allow_registry_cache: builtins.bool to: builtins.str @property - def typed_features(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, feast.protos.feast.types.Value_pb2.Value]: ... + def typed_features(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, feast.types.Value_pb2.Value]: ... def __init__( self, *, @@ -71,7 +71,7 @@ class PushRequest(google.protobuf.message.Message): stream_feature_view: builtins.str = ..., allow_registry_cache: builtins.bool = ..., to: builtins.str = ..., - typed_features: collections.abc.Mapping[builtins.str, feast.protos.feast.types.Value_pb2.Value] | None = ..., + typed_features: collections.abc.Mapping[builtins.str, feast.types.Value_pb2.Value] | None = ..., ) -> None: ... def ClearField(self, field_name: typing_extensions.Literal["allow_registry_cache", b"allow_registry_cache", "features", b"features", "stream_feature_view", b"stream_feature_view", "to", b"to", "typed_features", b"typed_features"]) -> None: ... @@ -116,12 +116,12 @@ class WriteToOnlineStoreRequest(google.protobuf.message.Message): VALUE_FIELD_NUMBER: builtins.int key: builtins.str @property - def value(self) -> feast.protos.feast.types.Value_pb2.Value: ... + def value(self) -> feast.types.Value_pb2.Value: ... def __init__( self, *, key: builtins.str = ..., - value: feast.protos.feast.types.Value_pb2.Value | None = ..., + value: feast.types.Value_pb2.Value | None = ..., ) -> None: ... def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ... def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... @@ -135,14 +135,14 @@ class WriteToOnlineStoreRequest(google.protobuf.message.Message): feature_view_name: builtins.str allow_registry_cache: builtins.bool @property - def typed_features(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, feast.protos.feast.types.Value_pb2.Value]: ... + def typed_features(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, feast.types.Value_pb2.Value]: ... def __init__( self, *, features: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., feature_view_name: builtins.str = ..., allow_registry_cache: builtins.bool = ..., - typed_features: collections.abc.Mapping[builtins.str, feast.protos.feast.types.Value_pb2.Value] | None = ..., + typed_features: collections.abc.Mapping[builtins.str, feast.types.Value_pb2.Value] | None = ..., ) -> None: ... def ClearField(self, field_name: typing_extensions.Literal["allow_registry_cache", b"allow_registry_cache", "feature_view_name", b"feature_view_name", "features", b"features", "typed_features", b"typed_features"]) -> None: ... diff --git a/sdk/python/feast/protos/feast/types/Value_pb2.py b/sdk/python/feast/protos/feast/types/Value_pb2.py index f4226cfafe4..16b0a7a961c 100644 --- a/sdk/python/feast/protos/feast/types/Value_pb2.py +++ b/sdk/python/feast/protos/feast/types/Value_pb2.py @@ -3,73 +3,69 @@ # source: feast/types/Value.proto # Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" - from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder - # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types"\x8d\x04\n\tValueType"\xff\x03\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04UUID\x10 \x12\r\n\tTIME_UUID\x10!\x12\r\n\tUUID_LIST\x10"\x12\x12\n\x0eTIME_UUID_LIST\x10#\x12\x0c\n\x08UUID_SET\x10$\x12\x11\n\rTIME_UUID_SET\x10%"\xdb\n\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08uuid_val\x18 \x01(\tH\x00\x12\x17\n\rtime_uuid_val\x18! \x01(\tH\x00\x12\x30\n\ruuid_list_val\x18" \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x35\n\x12time_uuid_list_val\x18# \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12.\n\x0cuuid_set_val\x18$ \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12\x33\n\x11time_uuid_set_val\x18% \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x42\x05\n\x03val"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3' -) + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x66\x65\x61st/types/Value.proto\x12\x0b\x66\x65\x61st.types\"\xc3\x04\n\tValueType\"\xb5\x04\n\x04\x45num\x12\x0b\n\x07INVALID\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\n\n\x06STRING\x10\x02\x12\t\n\x05INT32\x10\x03\x12\t\n\x05INT64\x10\x04\x12\n\n\x06\x44OUBLE\x10\x05\x12\t\n\x05\x46LOAT\x10\x06\x12\x08\n\x04\x42OOL\x10\x07\x12\x12\n\x0eUNIX_TIMESTAMP\x10\x08\x12\x0e\n\nBYTES_LIST\x10\x0b\x12\x0f\n\x0bSTRING_LIST\x10\x0c\x12\x0e\n\nINT32_LIST\x10\r\x12\x0e\n\nINT64_LIST\x10\x0e\x12\x0f\n\x0b\x44OUBLE_LIST\x10\x0f\x12\x0e\n\nFLOAT_LIST\x10\x10\x12\r\n\tBOOL_LIST\x10\x11\x12\x17\n\x13UNIX_TIMESTAMP_LIST\x10\x12\x12\x08\n\x04NULL\x10\x13\x12\x07\n\x03MAP\x10\x14\x12\x0c\n\x08MAP_LIST\x10\x15\x12\r\n\tBYTES_SET\x10\x16\x12\x0e\n\nSTRING_SET\x10\x17\x12\r\n\tINT32_SET\x10\x18\x12\r\n\tINT64_SET\x10\x19\x12\x0e\n\nDOUBLE_SET\x10\x1a\x12\r\n\tFLOAT_SET\x10\x1b\x12\x0c\n\x08\x42OOL_SET\x10\x1c\x12\x16\n\x12UNIX_TIMESTAMP_SET\x10\x1d\x12\x08\n\x04JSON\x10 \x12\r\n\tJSON_LIST\x10!\x12\n\n\x06STRUCT\x10\"\x12\x0f\n\x0bSTRUCT_LIST\x10#\x12\x08\n\x04UUID\x10$\x12\r\n\tTIME_UUID\x10%\x12\r\n\tUUID_LIST\x10&\x12\x12\n\x0eTIME_UUID_LIST\x10\'\x12\x0c\n\x08UUID_SET\x10(\x12\x11\n\rTIME_UUID_SET\x10)\"\xfa\x0b\n\x05Value\x12\x13\n\tbytes_val\x18\x01 \x01(\x0cH\x00\x12\x14\n\nstring_val\x18\x02 \x01(\tH\x00\x12\x13\n\tint32_val\x18\x03 \x01(\x05H\x00\x12\x13\n\tint64_val\x18\x04 \x01(\x03H\x00\x12\x14\n\ndouble_val\x18\x05 \x01(\x01H\x00\x12\x13\n\tfloat_val\x18\x06 \x01(\x02H\x00\x12\x12\n\x08\x62ool_val\x18\x07 \x01(\x08H\x00\x12\x1c\n\x12unix_timestamp_val\x18\x08 \x01(\x03H\x00\x12\x30\n\x0e\x62ytes_list_val\x18\x0b \x01(\x0b\x32\x16.feast.types.BytesListH\x00\x12\x32\n\x0fstring_list_val\x18\x0c \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x30\n\x0eint32_list_val\x18\r \x01(\x0b\x32\x16.feast.types.Int32ListH\x00\x12\x30\n\x0eint64_list_val\x18\x0e \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12\x32\n\x0f\x64ouble_list_val\x18\x0f \x01(\x0b\x32\x17.feast.types.DoubleListH\x00\x12\x30\n\x0e\x66loat_list_val\x18\x10 \x01(\x0b\x32\x16.feast.types.FloatListH\x00\x12.\n\rbool_list_val\x18\x11 \x01(\x0b\x32\x15.feast.types.BoolListH\x00\x12\x39\n\x17unix_timestamp_list_val\x18\x12 \x01(\x0b\x32\x16.feast.types.Int64ListH\x00\x12%\n\x08null_val\x18\x13 \x01(\x0e\x32\x11.feast.types.NullH\x00\x12#\n\x07map_val\x18\x14 \x01(\x0b\x32\x10.feast.types.MapH\x00\x12,\n\x0cmap_list_val\x18\x15 \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12.\n\rbytes_set_val\x18\x16 \x01(\x0b\x32\x15.feast.types.BytesSetH\x00\x12\x30\n\x0estring_set_val\x18\x17 \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12.\n\rint32_set_val\x18\x18 \x01(\x0b\x32\x15.feast.types.Int32SetH\x00\x12.\n\rint64_set_val\x18\x19 \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x30\n\x0e\x64ouble_set_val\x18\x1a \x01(\x0b\x32\x16.feast.types.DoubleSetH\x00\x12.\n\rfloat_set_val\x18\x1b \x01(\x0b\x32\x15.feast.types.FloatSetH\x00\x12,\n\x0c\x62ool_set_val\x18\x1c \x01(\x0b\x32\x14.feast.types.BoolSetH\x00\x12\x37\n\x16unix_timestamp_set_val\x18\x1d \x01(\x0b\x32\x15.feast.types.Int64SetH\x00\x12\x12\n\x08json_val\x18 \x01(\tH\x00\x12\x30\n\rjson_list_val\x18! \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12&\n\nstruct_val\x18\" \x01(\x0b\x32\x10.feast.types.MapH\x00\x12/\n\x0fstruct_list_val\x18# \x01(\x0b\x32\x14.feast.types.MapListH\x00\x12\x12\n\x08uuid_val\x18$ \x01(\tH\x00\x12\x17\n\rtime_uuid_val\x18% \x01(\tH\x00\x12\x30\n\ruuid_list_val\x18& \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12\x35\n\x12time_uuid_list_val\x18\' \x01(\x0b\x32\x17.feast.types.StringListH\x00\x12.\n\x0cuuid_set_val\x18( \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x12\x33\n\x11time_uuid_set_val\x18) \x01(\x0b\x32\x16.feast.types.StringSetH\x00\x42\x05\n\x03val\"\x18\n\tBytesList\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x19\n\nStringList\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x18\n\tInt32List\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x18\n\tInt64List\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x19\n\nDoubleList\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x18\n\tFloatList\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x17\n\x08\x42oolList\x12\x0b\n\x03val\x18\x01 \x03(\x08\"\x17\n\x08\x42ytesSet\x12\x0b\n\x03val\x18\x01 \x03(\x0c\"\x18\n\tStringSet\x12\x0b\n\x03val\x18\x01 \x03(\t\"\x17\n\x08Int32Set\x12\x0b\n\x03val\x18\x01 \x03(\x05\"\x17\n\x08Int64Set\x12\x0b\n\x03val\x18\x01 \x03(\x03\"\x18\n\tDoubleSet\x12\x0b\n\x03val\x18\x01 \x03(\x01\"\x17\n\x08\x46loatSet\x12\x0b\n\x03val\x18\x01 \x03(\x02\"\x16\n\x07\x42oolSet\x12\x0b\n\x03val\x18\x01 \x03(\x08\"m\n\x03Map\x12&\n\x03val\x18\x01 \x03(\x0b\x32\x19.feast.types.Map.ValEntry\x1a>\n\x08ValEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.feast.types.Value:\x02\x38\x01\"(\n\x07MapList\x12\x1d\n\x03val\x18\x01 \x03(\x0b\x32\x10.feast.types.Map\"0\n\rRepeatedValue\x12\x1f\n\x03val\x18\x01 \x03(\x0b\x32\x12.feast.types.Value*\x10\n\x04Null\x12\x08\n\x04NULL\x10\x00\x42Q\n\x11\x66\x65\x61st.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/typesb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "feast.types.Value_pb2", _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals["DESCRIPTOR"]._options = None - _globals[ - "DESCRIPTOR" - ]._serialized_options = b"\n\021feast.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/types" - _globals["_MAP_VALENTRY"]._options = None - _globals["_MAP_VALENTRY"]._serialized_options = b"8\001" - _globals["_NULL"]._serialized_start = 2504 - _globals["_NULL"]._serialized_end = 2520 - _globals["_VALUETYPE"]._serialized_start = 41 - _globals["_VALUETYPE"]._serialized_end = 566 - _globals["_VALUETYPE_ENUM"]._serialized_start = 55 - _globals["_VALUETYPE_ENUM"]._serialized_end = 566 - _globals["_VALUE"]._serialized_start = 569 - _globals["_VALUE"]._serialized_end = 1940 - _globals["_BYTESLIST"]._serialized_start = 1942 - _globals["_BYTESLIST"]._serialized_end = 1966 - _globals["_STRINGLIST"]._serialized_start = 1968 - _globals["_STRINGLIST"]._serialized_end = 1993 - _globals["_INT32LIST"]._serialized_start = 1995 - _globals["_INT32LIST"]._serialized_end = 2019 - _globals["_INT64LIST"]._serialized_start = 2021 - _globals["_INT64LIST"]._serialized_end = 2045 - _globals["_DOUBLELIST"]._serialized_start = 2047 - _globals["_DOUBLELIST"]._serialized_end = 2072 - _globals["_FLOATLIST"]._serialized_start = 2074 - _globals["_FLOATLIST"]._serialized_end = 2098 - _globals["_BOOLLIST"]._serialized_start = 2100 - _globals["_BOOLLIST"]._serialized_end = 2123 - _globals["_BYTESSET"]._serialized_start = 2125 - _globals["_BYTESSET"]._serialized_end = 2148 - _globals["_STRINGSET"]._serialized_start = 2150 - _globals["_STRINGSET"]._serialized_end = 2174 - _globals["_INT32SET"]._serialized_start = 2176 - _globals["_INT32SET"]._serialized_end = 2199 - _globals["_INT64SET"]._serialized_start = 2201 - _globals["_INT64SET"]._serialized_end = 2224 - _globals["_DOUBLESET"]._serialized_start = 2226 - _globals["_DOUBLESET"]._serialized_end = 2250 - _globals["_FLOATSET"]._serialized_start = 2252 - _globals["_FLOATSET"]._serialized_end = 2275 - _globals["_BOOLSET"]._serialized_start = 2277 - _globals["_BOOLSET"]._serialized_end = 2299 - _globals["_MAP"]._serialized_start = 2301 - _globals["_MAP"]._serialized_end = 2410 - _globals["_MAP_VALENTRY"]._serialized_start = 2348 - _globals["_MAP_VALENTRY"]._serialized_end = 2410 - _globals["_MAPLIST"]._serialized_start = 2412 - _globals["_MAPLIST"]._serialized_end = 2452 - _globals["_REPEATEDVALUE"]._serialized_start = 2454 - _globals["_REPEATEDVALUE"]._serialized_end = 2502 +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'feast.types.Value_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\021feast.proto.typesB\nValueProtoZ0github.com/feast-dev/feast/go/protos/feast/types' + _globals['_MAP_VALENTRY']._options = None + _globals['_MAP_VALENTRY']._serialized_options = b'8\001' + _globals['_NULL']._serialized_start=2717 + _globals['_NULL']._serialized_end=2733 + _globals['_VALUETYPE']._serialized_start=41 + _globals['_VALUETYPE']._serialized_end=620 + _globals['_VALUETYPE_ENUM']._serialized_start=55 + _globals['_VALUETYPE_ENUM']._serialized_end=620 + _globals['_VALUE']._serialized_start=623 + _globals['_VALUE']._serialized_end=2153 + _globals['_BYTESLIST']._serialized_start=2155 + _globals['_BYTESLIST']._serialized_end=2179 + _globals['_STRINGLIST']._serialized_start=2181 + _globals['_STRINGLIST']._serialized_end=2206 + _globals['_INT32LIST']._serialized_start=2208 + _globals['_INT32LIST']._serialized_end=2232 + _globals['_INT64LIST']._serialized_start=2234 + _globals['_INT64LIST']._serialized_end=2258 + _globals['_DOUBLELIST']._serialized_start=2260 + _globals['_DOUBLELIST']._serialized_end=2285 + _globals['_FLOATLIST']._serialized_start=2287 + _globals['_FLOATLIST']._serialized_end=2311 + _globals['_BOOLLIST']._serialized_start=2313 + _globals['_BOOLLIST']._serialized_end=2336 + _globals['_BYTESSET']._serialized_start=2338 + _globals['_BYTESSET']._serialized_end=2361 + _globals['_STRINGSET']._serialized_start=2363 + _globals['_STRINGSET']._serialized_end=2387 + _globals['_INT32SET']._serialized_start=2389 + _globals['_INT32SET']._serialized_end=2412 + _globals['_INT64SET']._serialized_start=2414 + _globals['_INT64SET']._serialized_end=2437 + _globals['_DOUBLESET']._serialized_start=2439 + _globals['_DOUBLESET']._serialized_end=2463 + _globals['_FLOATSET']._serialized_start=2465 + _globals['_FLOATSET']._serialized_end=2488 + _globals['_BOOLSET']._serialized_start=2490 + _globals['_BOOLSET']._serialized_end=2512 + _globals['_MAP']._serialized_start=2514 + _globals['_MAP']._serialized_end=2623 + _globals['_MAP_VALENTRY']._serialized_start=2561 + _globals['_MAP_VALENTRY']._serialized_end=2623 + _globals['_MAPLIST']._serialized_start=2625 + _globals['_MAPLIST']._serialized_end=2665 + _globals['_REPEATEDVALUE']._serialized_start=2667 + _globals['_REPEATEDVALUE']._serialized_end=2715 # @@protoc_insertion_point(module_scope) diff --git a/sdk/python/feast/protos/feast/types/Value_pb2.pyi b/sdk/python/feast/protos/feast/types/Value_pb2.pyi index 64079291f4d..75487088939 100644 --- a/sdk/python/feast/protos/feast/types/Value_pb2.pyi +++ b/sdk/python/feast/protos/feast/types/Value_pb2.pyi @@ -86,6 +86,12 @@ class ValueType(google.protobuf.message.Message): JSON_LIST: ValueType._Enum.ValueType # 33 STRUCT: ValueType._Enum.ValueType # 34 STRUCT_LIST: ValueType._Enum.ValueType # 35 + UUID: ValueType._Enum.ValueType # 36 + TIME_UUID: ValueType._Enum.ValueType # 37 + UUID_LIST: ValueType._Enum.ValueType # 38 + TIME_UUID_LIST: ValueType._Enum.ValueType # 39 + UUID_SET: ValueType._Enum.ValueType # 40 + TIME_UUID_SET: ValueType._Enum.ValueType # 41 class Enum(_Enum, metaclass=_EnumEnumTypeWrapper): ... INVALID: ValueType.Enum.ValueType # 0 @@ -120,6 +126,12 @@ class ValueType(google.protobuf.message.Message): JSON_LIST: ValueType.Enum.ValueType # 33 STRUCT: ValueType.Enum.ValueType # 34 STRUCT_LIST: ValueType.Enum.ValueType # 35 + UUID: ValueType.Enum.ValueType # 36 + TIME_UUID: ValueType.Enum.ValueType # 37 + UUID_LIST: ValueType.Enum.ValueType # 38 + TIME_UUID_LIST: ValueType.Enum.ValueType # 39 + UUID_SET: ValueType.Enum.ValueType # 40 + TIME_UUID_SET: ValueType.Enum.ValueType # 41 def __init__( self, @@ -161,6 +173,12 @@ class Value(google.protobuf.message.Message): JSON_LIST_VAL_FIELD_NUMBER: builtins.int STRUCT_VAL_FIELD_NUMBER: builtins.int STRUCT_LIST_VAL_FIELD_NUMBER: builtins.int + UUID_VAL_FIELD_NUMBER: builtins.int + TIME_UUID_VAL_FIELD_NUMBER: builtins.int + UUID_LIST_VAL_FIELD_NUMBER: builtins.int + TIME_UUID_LIST_VAL_FIELD_NUMBER: builtins.int + UUID_SET_VAL_FIELD_NUMBER: builtins.int + TIME_UUID_SET_VAL_FIELD_NUMBER: builtins.int bytes_val: builtins.bytes string_val: builtins.str int32_val: builtins.int @@ -213,6 +231,16 @@ class Value(google.protobuf.message.Message): def struct_val(self) -> global___Map: ... @property def struct_list_val(self) -> global___MapList: ... + uuid_val: builtins.str + time_uuid_val: builtins.str + @property + def uuid_list_val(self) -> global___StringList: ... + @property + def time_uuid_list_val(self) -> global___StringList: ... + @property + def uuid_set_val(self) -> global___StringSet: ... + @property + def time_uuid_set_val(self) -> global___StringSet: ... def __init__( self, *, @@ -247,10 +275,16 @@ class Value(google.protobuf.message.Message): json_list_val: global___StringList | None = ..., struct_val: global___Map | None = ..., struct_list_val: global___MapList | None = ..., + uuid_val: builtins.str = ..., + time_uuid_val: builtins.str = ..., + uuid_list_val: global___StringList | None = ..., + time_uuid_list_val: global___StringList | None = ..., + uuid_set_val: global___StringSet | None = ..., + time_uuid_set_val: global___StringSet | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["bool_list_val", b"bool_list_val", "bool_set_val", b"bool_set_val", "bool_val", b"bool_val", "bytes_list_val", b"bytes_list_val", "bytes_set_val", b"bytes_set_val", "bytes_val", b"bytes_val", "double_list_val", b"double_list_val", "double_set_val", b"double_set_val", "double_val", b"double_val", "float_list_val", b"float_list_val", "float_set_val", b"float_set_val", "float_val", b"float_val", "int32_list_val", b"int32_list_val", "int32_set_val", b"int32_set_val", "int32_val", b"int32_val", "int64_list_val", b"int64_list_val", "int64_set_val", b"int64_set_val", "int64_val", b"int64_val", "json_list_val", b"json_list_val", "json_val", b"json_val", "map_list_val", b"map_list_val", "map_val", b"map_val", "null_val", b"null_val", "string_list_val", b"string_list_val", "string_set_val", b"string_set_val", "string_val", b"string_val", "struct_list_val", b"struct_list_val", "struct_val", b"struct_val", "unix_timestamp_list_val", b"unix_timestamp_list_val", "unix_timestamp_set_val", b"unix_timestamp_set_val", "unix_timestamp_val", b"unix_timestamp_val", "val", b"val"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["bool_list_val", b"bool_list_val", "bool_set_val", b"bool_set_val", "bool_val", b"bool_val", "bytes_list_val", b"bytes_list_val", "bytes_set_val", b"bytes_set_val", "bytes_val", b"bytes_val", "double_list_val", b"double_list_val", "double_set_val", b"double_set_val", "double_val", b"double_val", "float_list_val", b"float_list_val", "float_set_val", b"float_set_val", "float_val", b"float_val", "int32_list_val", b"int32_list_val", "int32_set_val", b"int32_set_val", "int32_val", b"int32_val", "int64_list_val", b"int64_list_val", "int64_set_val", b"int64_set_val", "int64_val", b"int64_val", "json_list_val", b"json_list_val", "json_val", b"json_val", "map_list_val", b"map_list_val", "map_val", b"map_val", "null_val", b"null_val", "string_list_val", b"string_list_val", "string_set_val", b"string_set_val", "string_val", b"string_val", "struct_list_val", b"struct_list_val", "struct_val", b"struct_val", "unix_timestamp_list_val", b"unix_timestamp_list_val", "unix_timestamp_set_val", b"unix_timestamp_set_val", "unix_timestamp_val", b"unix_timestamp_val", "val", b"val"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["val", b"val"]) -> typing_extensions.Literal["bytes_val", "string_val", "int32_val", "int64_val", "double_val", "float_val", "bool_val", "unix_timestamp_val", "bytes_list_val", "string_list_val", "int32_list_val", "int64_list_val", "double_list_val", "float_list_val", "bool_list_val", "unix_timestamp_list_val", "null_val", "map_val", "map_list_val", "bytes_set_val", "string_set_val", "int32_set_val", "int64_set_val", "double_set_val", "float_set_val", "bool_set_val", "unix_timestamp_set_val", "json_val", "json_list_val", "struct_val", "struct_list_val"] | None: ... + def HasField(self, field_name: typing_extensions.Literal["bool_list_val", b"bool_list_val", "bool_set_val", b"bool_set_val", "bool_val", b"bool_val", "bytes_list_val", b"bytes_list_val", "bytes_set_val", b"bytes_set_val", "bytes_val", b"bytes_val", "double_list_val", b"double_list_val", "double_set_val", b"double_set_val", "double_val", b"double_val", "float_list_val", b"float_list_val", "float_set_val", b"float_set_val", "float_val", b"float_val", "int32_list_val", b"int32_list_val", "int32_set_val", b"int32_set_val", "int32_val", b"int32_val", "int64_list_val", b"int64_list_val", "int64_set_val", b"int64_set_val", "int64_val", b"int64_val", "json_list_val", b"json_list_val", "json_val", b"json_val", "map_list_val", b"map_list_val", "map_val", b"map_val", "null_val", b"null_val", "string_list_val", b"string_list_val", "string_set_val", b"string_set_val", "string_val", b"string_val", "struct_list_val", b"struct_list_val", "struct_val", b"struct_val", "time_uuid_list_val", b"time_uuid_list_val", "time_uuid_set_val", b"time_uuid_set_val", "time_uuid_val", b"time_uuid_val", "unix_timestamp_list_val", b"unix_timestamp_list_val", "unix_timestamp_set_val", b"unix_timestamp_set_val", "unix_timestamp_val", b"unix_timestamp_val", "uuid_list_val", b"uuid_list_val", "uuid_set_val", b"uuid_set_val", "uuid_val", b"uuid_val", "val", b"val"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["bool_list_val", b"bool_list_val", "bool_set_val", b"bool_set_val", "bool_val", b"bool_val", "bytes_list_val", b"bytes_list_val", "bytes_set_val", b"bytes_set_val", "bytes_val", b"bytes_val", "double_list_val", b"double_list_val", "double_set_val", b"double_set_val", "double_val", b"double_val", "float_list_val", b"float_list_val", "float_set_val", b"float_set_val", "float_val", b"float_val", "int32_list_val", b"int32_list_val", "int32_set_val", b"int32_set_val", "int32_val", b"int32_val", "int64_list_val", b"int64_list_val", "int64_set_val", b"int64_set_val", "int64_val", b"int64_val", "json_list_val", b"json_list_val", "json_val", b"json_val", "map_list_val", b"map_list_val", "map_val", b"map_val", "null_val", b"null_val", "string_list_val", b"string_list_val", "string_set_val", b"string_set_val", "string_val", b"string_val", "struct_list_val", b"struct_list_val", "struct_val", b"struct_val", "time_uuid_list_val", b"time_uuid_list_val", "time_uuid_set_val", b"time_uuid_set_val", "time_uuid_val", b"time_uuid_val", "unix_timestamp_list_val", b"unix_timestamp_list_val", "unix_timestamp_set_val", b"unix_timestamp_set_val", "unix_timestamp_val", b"unix_timestamp_val", "uuid_list_val", b"uuid_list_val", "uuid_set_val", b"uuid_set_val", "uuid_val", b"uuid_val", "val", b"val"]) -> None: ... + def WhichOneof(self, oneof_group: typing_extensions.Literal["val", b"val"]) -> typing_extensions.Literal["bytes_val", "string_val", "int32_val", "int64_val", "double_val", "float_val", "bool_val", "unix_timestamp_val", "bytes_list_val", "string_list_val", "int32_list_val", "int64_list_val", "double_list_val", "float_list_val", "bool_list_val", "unix_timestamp_list_val", "null_val", "map_val", "map_list_val", "bytes_set_val", "string_set_val", "int32_set_val", "int64_set_val", "double_set_val", "float_set_val", "bool_set_val", "unix_timestamp_set_val", "json_val", "json_list_val", "struct_val", "struct_list_val", "uuid_val", "time_uuid_val", "uuid_list_val", "time_uuid_list_val", "uuid_set_val", "time_uuid_set_val"] | None: ... global___Value = Value From 7b600cb4be1d1e981bcc6f7e889b6ffb05e1b708 Mon Sep 17 00:00:00 2001 From: soojin Date: Sat, 28 Mar 2026 18:02:45 +0900 Subject: [PATCH 15/15] fix: Fix mypy type ignore comments for UUID collection conversions Add [misc] error code to type: ignore comments in UUID list/set proto conversion to satisfy mypy's stricter checking. Signed-off-by: Soojin Lee Co-Authored-By: Claude Opus 4.6 Signed-off-by: soojin --- sdk/python/feast/type_map.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/python/feast/type_map.py b/sdk/python/feast/type_map.py index 1754a8df269..4478445f4e4 100644 --- a/sdk/python/feast/type_map.py +++ b/sdk/python/feast/type_map.py @@ -749,7 +749,7 @@ def convert_set_to_list(value: Any) -> Any: return [ ( ProtoValue( - **{set_field_name: set_proto_type(val=[str(e) for e in value])} # type: ignore[arg-type] + **{set_field_name: set_proto_type(val=[str(e) for e in value])} # type: ignore[arg-type, misc] ) if value is not None else ProtoValue() @@ -822,7 +822,7 @@ def _convert_list_values_to_proto( return [ ( ProtoValue( - **{field_name: proto_type(val=[str(e) for e in value])} # type: ignore[arg-type] + **{field_name: proto_type(val=[str(e) for e in value])} # type: ignore[arg-type, misc] ) if value is not None else ProtoValue()