snowflake-ml-python 1.6.0__py3-none-any.whl → 1.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snowflake/cortex/_complete.py +7 -33
- snowflake/ml/_internal/env_utils.py +11 -5
- snowflake/ml/_internal/exceptions/modeling_error_messages.py +4 -1
- snowflake/ml/_internal/telemetry.py +14 -0
- snowflake/ml/_internal/utils/pkg_version_utils.py +8 -22
- snowflake/ml/data/_internal/arrow_ingestor.py +66 -10
- snowflake/ml/data/data_connector.py +59 -6
- snowflake/ml/data/data_ingestor.py +18 -1
- snowflake/ml/data/{_internal/ingestor_utils.py → ingestor_utils.py} +5 -1
- snowflake/ml/data/torch_dataset.py +33 -0
- snowflake/ml/dataset/dataset_metadata.py +3 -1
- snowflake/ml/dataset/dataset_reader.py +9 -3
- snowflake/ml/feature_store/examples/airline_features/entities.py +16 -0
- snowflake/ml/feature_store/examples/airline_features/features/plane_features.py +31 -0
- snowflake/ml/feature_store/examples/airline_features/features/weather_features.py +42 -0
- snowflake/ml/feature_store/examples/airline_features/source.yaml +7 -0
- snowflake/ml/feature_store/examples/citibike_trip_features/features/station_feature.py +10 -4
- snowflake/ml/feature_store/examples/citibike_trip_features/features/trip_feature.py +6 -0
- snowflake/ml/feature_store/examples/citibike_trip_features/source.yaml +3 -0
- snowflake/ml/feature_store/examples/example_helper.py +69 -31
- snowflake/ml/feature_store/examples/new_york_taxi_features/entities.py +3 -3
- snowflake/ml/feature_store/examples/new_york_taxi_features/features/{dropoff_features.py → location_features.py} +14 -9
- snowflake/ml/feature_store/examples/new_york_taxi_features/features/trip_features.py +36 -0
- snowflake/ml/feature_store/examples/new_york_taxi_features/source.yaml +5 -1
- snowflake/ml/feature_store/examples/source_data/airline.yaml +4 -0
- snowflake/ml/feature_store/examples/source_data/citibike_trips.yaml +1 -1
- snowflake/ml/feature_store/examples/wine_quality_features/entities.py +3 -3
- snowflake/ml/feature_store/examples/wine_quality_features/features/managed_wine_features.py +13 -6
- snowflake/ml/feature_store/examples/wine_quality_features/features/static_wine_features.py +8 -5
- snowflake/ml/feature_store/examples/wine_quality_features/source.yaml +3 -0
- snowflake/ml/feature_store/feature_store.py +59 -24
- snowflake/ml/feature_store/feature_view.py +148 -4
- snowflake/ml/model/_client/model/model_impl.py +11 -2
- snowflake/ml/model/_client/model/model_version_impl.py +171 -20
- snowflake/ml/model/_client/ops/model_ops.py +105 -27
- snowflake/ml/model/_client/ops/service_ops.py +121 -0
- snowflake/ml/model/_client/service/model_deployment_spec.py +95 -0
- snowflake/ml/model/_client/service/model_deployment_spec_schema.py +31 -0
- snowflake/ml/model/_client/sql/model_version.py +13 -4
- snowflake/ml/model/_client/sql/service.py +129 -0
- snowflake/ml/model/_model_composer/model_composer.py +3 -0
- snowflake/ml/model/_model_composer/model_manifest/model_manifest.py +10 -2
- snowflake/ml/model/_model_composer/model_manifest/model_manifest_schema.py +3 -0
- snowflake/ml/model/_packager/model_env/model_env.py +7 -2
- snowflake/ml/model/_packager/model_handlers/_base.py +29 -12
- snowflake/ml/model/_packager/model_handlers/catboost.py +19 -12
- snowflake/ml/model/_packager/model_handlers/custom.py +6 -2
- snowflake/ml/model/_packager/model_handlers/huggingface_pipeline.py +9 -5
- snowflake/ml/model/_packager/model_handlers/lightgbm.py +27 -18
- snowflake/ml/model/_packager/model_handlers/llm.py +7 -3
- snowflake/ml/model/_packager/model_handlers/mlflow.py +8 -3
- snowflake/ml/model/_packager/model_handlers/pytorch.py +8 -3
- snowflake/ml/model/_packager/model_handlers/sentence_transformers.py +8 -3
- snowflake/ml/model/_packager/model_handlers/sklearn.py +87 -4
- snowflake/ml/model/_packager/model_handlers/snowmlmodel.py +7 -2
- snowflake/ml/model/_packager/model_handlers/tensorflow.py +9 -4
- snowflake/ml/model/_packager/model_handlers/torchscript.py +8 -3
- snowflake/ml/model/_packager/model_handlers/xgboost.py +25 -16
- snowflake/ml/model/_packager/model_meta/model_meta.py +32 -2
- snowflake/ml/model/_packager/model_meta/model_meta_schema.py +19 -0
- snowflake/ml/model/_packager/model_packager.py +2 -1
- snowflake/ml/model/_packager/model_runtime/model_runtime.py +4 -2
- snowflake/ml/model/type_hints.py +1 -3
- snowflake/ml/modeling/framework/base.py +28 -19
- snowflake/ml/modeling/pipeline/pipeline.py +3 -0
- snowflake/ml/registry/_manager/model_manager.py +16 -2
- snowflake/ml/utils/sql_client.py +22 -0
- snowflake/ml/version.py +1 -1
- {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/METADATA +35 -2
- {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/RECORD +73 -62
- snowflake/ml/feature_store/examples/new_york_taxi_features/features/pickup_features.py +0 -58
- {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/LICENSE.txt +0 -0
- {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/WHEEL +0 -0
- {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/top_level.txt +0 -0
@@ -52,6 +52,7 @@ from snowflake.ml.feature_store.feature_view import (
|
|
52
52
|
FeatureViewVersion,
|
53
53
|
_FeatureViewMetadata,
|
54
54
|
)
|
55
|
+
from snowflake.ml.utils import sql_client
|
55
56
|
from snowflake.snowpark import DataFrame, Row, Session, functions as F
|
56
57
|
from snowflake.snowpark.exceptions import SnowparkSQLException
|
57
58
|
from snowflake.snowpark.types import (
|
@@ -94,13 +95,12 @@ class _FeatureStoreObjInfo:
|
|
94
95
|
return cls(**state_dict) # type: ignore[arg-type]
|
95
96
|
|
96
97
|
|
97
|
-
# TODO: remove "" after dataset is updated
|
98
98
|
class _FeatureStoreObjTypes(Enum):
|
99
99
|
UNKNOWN = "UNKNOWN" # for forward compatibility
|
100
100
|
MANAGED_FEATURE_VIEW = "MANAGED_FEATURE_VIEW"
|
101
101
|
EXTERNAL_FEATURE_VIEW = "EXTERNAL_FEATURE_VIEW"
|
102
102
|
FEATURE_VIEW_REFRESH_TASK = "FEATURE_VIEW_REFRESH_TASK"
|
103
|
-
TRAINING_DATA = ""
|
103
|
+
TRAINING_DATA = "TRAINING_DATA"
|
104
104
|
|
105
105
|
@classmethod
|
106
106
|
def parse(cls, val: str) -> _FeatureStoreObjTypes:
|
@@ -140,9 +140,8 @@ _LIST_FEATURE_VIEW_SCHEMA = StructType(
|
|
140
140
|
)
|
141
141
|
|
142
142
|
|
143
|
-
|
144
|
-
|
145
|
-
CREATE_IF_NOT_EXIST = 2
|
143
|
+
CreationMode = sql_client.CreationOption
|
144
|
+
CreationMode.__module__ = __name__
|
146
145
|
|
147
146
|
|
148
147
|
@dataclass(frozen=True)
|
@@ -426,7 +425,9 @@ class FeatureStore:
|
|
426
425
|
|
427
426
|
"""
|
428
427
|
name = SqlIdentifier(name)
|
429
|
-
found_rows =
|
428
|
+
found_rows = (
|
429
|
+
self.list_entities().filter(F.col("NAME") == name.resolved()).collect(statement_params=self._telemetry_stmp)
|
430
|
+
)
|
430
431
|
|
431
432
|
if len(found_rows) == 0:
|
432
433
|
warnings.warn(
|
@@ -853,7 +854,9 @@ class FeatureStore:
|
|
853
854
|
original_exception=ValueError(f"Failed to find FeatureView {name}/{version}: {results}"),
|
854
855
|
)
|
855
856
|
|
856
|
-
return self._compose_feature_view(
|
857
|
+
return self._compose_feature_view(
|
858
|
+
results[0][0], results[0][1], self.list_entities().collect(statement_params=self._telemetry_stmp)
|
859
|
+
)
|
857
860
|
|
858
861
|
@overload
|
859
862
|
def refresh_feature_view(self, feature_view: FeatureView) -> None:
|
@@ -1223,7 +1226,11 @@ class FeatureStore:
|
|
1223
1226
|
"""
|
1224
1227
|
name = SqlIdentifier(name)
|
1225
1228
|
try:
|
1226
|
-
result =
|
1229
|
+
result = (
|
1230
|
+
self.list_entities()
|
1231
|
+
.filter(F.col("NAME") == name.resolved())
|
1232
|
+
.collect(statement_params=self._telemetry_stmp)
|
1233
|
+
)
|
1227
1234
|
except Exception as e:
|
1228
1235
|
raise snowml_exceptions.SnowflakeMLException(
|
1229
1236
|
error_code=error_codes.INTERNAL_SNOWPARK_ERROR,
|
@@ -1357,7 +1364,7 @@ class FeatureStore:
|
|
1357
1364
|
if len(features) == 0:
|
1358
1365
|
raise ValueError("features cannot be empty")
|
1359
1366
|
if isinstance(features[0], str):
|
1360
|
-
features = self.
|
1367
|
+
features = self._load_serialized_feature_views(cast(List[str], features))
|
1361
1368
|
|
1362
1369
|
df, _ = self._join_features(
|
1363
1370
|
spine_df,
|
@@ -1441,8 +1448,19 @@ class FeatureStore:
|
|
1441
1448
|
if save_as is not None:
|
1442
1449
|
try:
|
1443
1450
|
save_as = self._get_fully_qualified_name(save_as)
|
1444
|
-
result_df.write.mode("errorifexists").save_as_table(save_as)
|
1451
|
+
result_df.write.mode("errorifexists").save_as_table(save_as, statement_params=self._telemetry_stmp)
|
1452
|
+
|
1453
|
+
# Add tag
|
1454
|
+
task_obj_info = _FeatureStoreObjInfo(_FeatureStoreObjTypes.TRAINING_DATA, snowml_version.VERSION)
|
1455
|
+
self._session.sql(
|
1456
|
+
f"""
|
1457
|
+
ALTER TABLE {save_as}
|
1458
|
+
SET TAG {self._get_fully_qualified_name(_FEATURE_STORE_OBJECT_TAG)}='{task_obj_info.to_json()}'
|
1459
|
+
"""
|
1460
|
+
).collect(statement_params=self._telemetry_stmp)
|
1461
|
+
|
1445
1462
|
return self._session.table(save_as)
|
1463
|
+
|
1446
1464
|
except SnowparkSQLException as e:
|
1447
1465
|
if e.sql_error_code == sql_error_codes.OBJECT_ALREADY_EXISTS:
|
1448
1466
|
raise snowml_exceptions.SnowflakeMLException(
|
@@ -1572,7 +1590,7 @@ class FeatureStore:
|
|
1572
1590
|
|
1573
1591
|
fs_meta = FeatureStoreMetadata(
|
1574
1592
|
spine_query=spine_df.queries["queries"][-1],
|
1575
|
-
|
1593
|
+
compact_feature_views=[fv._get_compact_repr().to_json() for fv in features],
|
1576
1594
|
spine_timestamp_col=spine_timestamp_col,
|
1577
1595
|
)
|
1578
1596
|
|
@@ -1607,6 +1625,7 @@ class FeatureStore:
|
|
1607
1625
|
" to generate the data as a Snowflake Table."
|
1608
1626
|
),
|
1609
1627
|
)
|
1628
|
+
# TODO: Add feature store tag once Dataset (version) supports tags
|
1610
1629
|
ds: dataset.Dataset = dataset.create_from_dataframe(
|
1611
1630
|
self._session,
|
1612
1631
|
name,
|
@@ -1675,11 +1694,18 @@ class FeatureStore:
|
|
1675
1694
|
if (
|
1676
1695
|
source_meta is None
|
1677
1696
|
or not isinstance(source_meta.properties, FeatureStoreMetadata)
|
1678
|
-
or
|
1697
|
+
or (
|
1698
|
+
source_meta.properties.serialized_feature_views is None
|
1699
|
+
and source_meta.properties.compact_feature_views is None
|
1700
|
+
)
|
1679
1701
|
):
|
1680
1702
|
raise ValueError(f"Dataset {ds} does not contain valid feature view information.")
|
1681
1703
|
|
1682
|
-
|
1704
|
+
properties = source_meta.properties
|
1705
|
+
if properties.serialized_feature_views:
|
1706
|
+
return self._load_serialized_feature_views(properties.serialized_feature_views)
|
1707
|
+
else:
|
1708
|
+
return self._load_compact_feature_views(properties.compact_feature_views) # type: ignore[arg-type]
|
1683
1709
|
|
1684
1710
|
@dispatch_decorator()
|
1685
1711
|
def _clear(self, dryrun: bool = True) -> None:
|
@@ -1700,8 +1726,8 @@ class FeatureStore:
|
|
1700
1726
|
|
1701
1727
|
all_fvs_df = self.list_feature_views()
|
1702
1728
|
all_entities_df = self.list_entities()
|
1703
|
-
all_fvs_rows = all_fvs_df.collect()
|
1704
|
-
all_entities_rows = all_entities_df.collect()
|
1729
|
+
all_fvs_rows = all_fvs_df.collect(statement_params=self._telemetry_stmp)
|
1730
|
+
all_entities_rows = all_entities_df.collect(statement_params=self._telemetry_stmp)
|
1705
1731
|
|
1706
1732
|
if dryrun:
|
1707
1733
|
logger.info(
|
@@ -1768,6 +1794,7 @@ class FeatureStore:
|
|
1768
1794
|
{tagging_clause}
|
1769
1795
|
)
|
1770
1796
|
WAREHOUSE = {warehouse}
|
1797
|
+
REFRESH_MODE = {feature_view.refresh_mode}
|
1771
1798
|
AS {feature_view.query}
|
1772
1799
|
"""
|
1773
1800
|
self._session.sql(query).collect(block=block, statement_params=self._telemetry_stmp)
|
@@ -1985,7 +2012,7 @@ class FeatureStore:
|
|
1985
2012
|
MATCH_CONDITION ( spine.ts >= feature.ts )
|
1986
2013
|
ON spine.id = feature.id;
|
1987
2014
|
"""
|
1988
|
-
).collect()
|
2015
|
+
).collect(statement_params=self._telemetry_stmp)
|
1989
2016
|
except SnowparkSQLException:
|
1990
2017
|
return False
|
1991
2018
|
return result is not None and len(result) == 1
|
@@ -2366,11 +2393,11 @@ class FeatureStore:
|
|
2366
2393
|
result.append(row)
|
2367
2394
|
return result
|
2368
2395
|
|
2369
|
-
def
|
2370
|
-
self,
|
2396
|
+
def _load_serialized_feature_views(
|
2397
|
+
self, serialized_feature_views: List[str]
|
2371
2398
|
) -> List[Union[FeatureView, FeatureViewSlice]]:
|
2372
2399
|
results: List[Union[FeatureView, FeatureViewSlice]] = []
|
2373
|
-
for obj in
|
2400
|
+
for obj in serialized_feature_views:
|
2374
2401
|
try:
|
2375
2402
|
obj_type = json.loads(obj)[_FEATURE_OBJ_TYPE]
|
2376
2403
|
except Exception as e:
|
@@ -2384,6 +2411,14 @@ class FeatureStore:
|
|
2384
2411
|
raise ValueError(f"Unsupported feature object type: {obj_type}")
|
2385
2412
|
return results
|
2386
2413
|
|
2414
|
+
def _load_compact_feature_views(
|
2415
|
+
self, compact_feature_views: List[str]
|
2416
|
+
) -> List[Union[FeatureView, FeatureViewSlice]]:
|
2417
|
+
results: List[Union[FeatureView, FeatureViewSlice]] = []
|
2418
|
+
for obj in compact_feature_views:
|
2419
|
+
results.append(FeatureView._load_from_compact_repr(self._session, obj))
|
2420
|
+
return results
|
2421
|
+
|
2387
2422
|
def _exclude_columns(self, df: DataFrame, exclude_columns: List[str]) -> DataFrame:
|
2388
2423
|
exclude_columns = to_sql_identifiers(exclude_columns) # type: ignore[assignment]
|
2389
2424
|
df_cols = to_sql_identifiers(df.columns)
|
@@ -2399,12 +2434,12 @@ class FeatureStore:
|
|
2399
2434
|
|
2400
2435
|
def _is_dataset_enabled(self) -> bool:
|
2401
2436
|
try:
|
2402
|
-
self._session.sql(f"SHOW DATASETS IN SCHEMA {self._config.full_schema_path}").collect(
|
2437
|
+
self._session.sql(f"SHOW DATASETS IN SCHEMA {self._config.full_schema_path}").collect(
|
2438
|
+
statement_params=self._telemetry_stmp
|
2439
|
+
)
|
2403
2440
|
return True
|
2404
|
-
except SnowparkSQLException
|
2405
|
-
|
2406
|
-
return False
|
2407
|
-
raise
|
2441
|
+
except SnowparkSQLException:
|
2442
|
+
return False
|
2408
2443
|
|
2409
2444
|
def _check_feature_store_object_versions(self) -> None:
|
2410
2445
|
versions = self._collapse_object_versions()
|
@@ -6,7 +6,7 @@ import warnings
|
|
6
6
|
from collections import OrderedDict
|
7
7
|
from dataclasses import asdict, dataclass
|
8
8
|
from enum import Enum
|
9
|
-
from typing import Any, Dict, List, Optional
|
9
|
+
from typing import Any, Dict, List, Optional, Union
|
10
10
|
|
11
11
|
from snowflake.ml._internal.exceptions import (
|
12
12
|
error_codes,
|
@@ -60,6 +60,29 @@ class _FeatureViewMetadata:
|
|
60
60
|
return cls(**state_dict)
|
61
61
|
|
62
62
|
|
63
|
+
@dataclass(frozen=True)
|
64
|
+
class _CompactRepresentation:
|
65
|
+
"""
|
66
|
+
A compact representation for FeatureView and FeatureViewSlice, which contains fully qualified name
|
67
|
+
and optionally a list of feature indices (None means all features will be included).
|
68
|
+
This is to make the metadata much smaller when generating dataset.
|
69
|
+
"""
|
70
|
+
|
71
|
+
db: str
|
72
|
+
sch: str
|
73
|
+
name: str
|
74
|
+
version: str
|
75
|
+
feature_indices: Optional[List[int]] = None
|
76
|
+
|
77
|
+
def to_json(self) -> str:
|
78
|
+
return json.dumps(asdict(self))
|
79
|
+
|
80
|
+
@classmethod
|
81
|
+
def from_json(cls, json_str: str) -> _CompactRepresentation:
|
82
|
+
state_dict = json.loads(json_str)
|
83
|
+
return cls(**state_dict)
|
84
|
+
|
85
|
+
|
63
86
|
class FeatureViewVersion(str):
|
64
87
|
def __new__(cls, version: str) -> FeatureViewVersion:
|
65
88
|
if not _FEATURE_VIEW_VERSION_RE.match(version) or len(version) > _FEATURE_VIEW_VERSION_MAX_LENGTH:
|
@@ -115,6 +138,19 @@ class FeatureViewSlice:
|
|
115
138
|
json_dict["feature_view_ref"] = FeatureView.from_json(json_dict["feature_view_ref"], session)
|
116
139
|
return cls(**json_dict)
|
117
140
|
|
141
|
+
def _get_compact_repr(self) -> _CompactRepresentation:
|
142
|
+
return _CompactRepresentation(
|
143
|
+
db=self.feature_view_ref.database.identifier(), # type: ignore[union-attr]
|
144
|
+
sch=self.feature_view_ref.schema.identifier(), # type: ignore[union-attr]
|
145
|
+
name=self.feature_view_ref.name.identifier(),
|
146
|
+
version=self.feature_view_ref.version, # type: ignore[arg-type]
|
147
|
+
feature_indices=self._feature_names_to_indices(),
|
148
|
+
)
|
149
|
+
|
150
|
+
def _feature_names_to_indices(self) -> List[int]:
|
151
|
+
name_to_indices_map = {name: idx for idx, name in enumerate(self.feature_view_ref.feature_names)}
|
152
|
+
return [name_to_indices_map[n] for n in self.names]
|
153
|
+
|
118
154
|
|
119
155
|
class FeatureView(lineage_node.LineageNode):
|
120
156
|
"""
|
@@ -196,7 +232,7 @@ class FeatureView(lineage_node.LineageNode):
|
|
196
232
|
self._database: Optional[SqlIdentifier] = None
|
197
233
|
self._schema: Optional[SqlIdentifier] = None
|
198
234
|
self._warehouse: Optional[SqlIdentifier] = SqlIdentifier(warehouse) if warehouse is not None else None
|
199
|
-
self._refresh_mode: Optional[str] =
|
235
|
+
self._refresh_mode: Optional[str] = _kwargs.get("refresh_mode", "AUTO")
|
200
236
|
self._refresh_mode_reason: Optional[str] = None
|
201
237
|
self._owner: Optional[str] = None
|
202
238
|
self._validate()
|
@@ -394,6 +430,54 @@ class FeatureView(lineage_node.LineageNode):
|
|
394
430
|
def feature_descs(self) -> Dict[SqlIdentifier, str]:
|
395
431
|
return self._feature_desc
|
396
432
|
|
433
|
+
def list_columns(self) -> DataFrame:
|
434
|
+
"""List all columns and their information.
|
435
|
+
|
436
|
+
Returns:
|
437
|
+
A Snowpark DataFrame contains feature information.
|
438
|
+
|
439
|
+
Example::
|
440
|
+
|
441
|
+
>>> fs = FeatureStore(...)
|
442
|
+
>>> e = Entity("foo", ["id"], desc='my entity')
|
443
|
+
>>> fs.register_entity(e)
|
444
|
+
<BLANKLINE>
|
445
|
+
>>> draft_fv = FeatureView(
|
446
|
+
... name="fv",
|
447
|
+
... entities=[e],
|
448
|
+
... feature_df=self._session.table(<source_table>).select(["NAME", "ID", "TITLE", "AGE", "TS"]),
|
449
|
+
... timestamp_col="ts",
|
450
|
+
>>> ).attach_feature_desc({"AGE": "my age", "TITLE": '"my title"'})
|
451
|
+
>>> fv = fs.register_feature_view(draft_fv, '1.0')
|
452
|
+
<BLANKLINE>
|
453
|
+
>>> fv.list_columns().show()
|
454
|
+
--------------------------------------------------
|
455
|
+
|"NAME" |"CATEGORY" |"DTYPE" |"DESC" |
|
456
|
+
--------------------------------------------------
|
457
|
+
|NAME |FEATURE |string(64) | |
|
458
|
+
|ID |ENTITY |bigint |my entity |
|
459
|
+
|TITLE |FEATURE |string(128) |"my title" |
|
460
|
+
|AGE |FEATURE |bigint |my age |
|
461
|
+
|TS |TIMESTAMP |bigint |NULL |
|
462
|
+
--------------------------------------------------
|
463
|
+
|
464
|
+
"""
|
465
|
+
session = self._feature_df.session
|
466
|
+
rows = []
|
467
|
+
for name, type in self._feature_df.dtypes:
|
468
|
+
if SqlIdentifier(name) in self.feature_descs:
|
469
|
+
desc = self.feature_descs[SqlIdentifier(name)]
|
470
|
+
rows.append((name, "FEATURE", type, desc))
|
471
|
+
elif SqlIdentifier(name) == self._timestamp_col:
|
472
|
+
rows.append((name, "TIMESTAMP", type, None)) # type: ignore[arg-type]
|
473
|
+
else:
|
474
|
+
for e in self._entities:
|
475
|
+
if SqlIdentifier(name) in e.join_keys:
|
476
|
+
rows.append((name, "ENTITY", type, e.desc))
|
477
|
+
break
|
478
|
+
|
479
|
+
return session.create_dataframe(rows, schema=["name", "category", "dtype", "desc"])
|
480
|
+
|
397
481
|
@property
|
398
482
|
def refresh_freq(self) -> Optional[str]:
|
399
483
|
return self._refresh_freq
|
@@ -599,12 +683,50 @@ Got {len(self._feature_df.queries['queries'])}: {self._feature_df.queries['queri
|
|
599
683
|
|
600
684
|
return fv_dict
|
601
685
|
|
602
|
-
def to_df(self, session: Session) -> DataFrame:
|
686
|
+
def to_df(self, session: Optional[Session] = None) -> DataFrame:
|
687
|
+
"""Convert feature view to a Snowpark DataFrame object.
|
688
|
+
|
689
|
+
Args:
|
690
|
+
session: [deprecated] This argument has no effect. No need to pass a session object.
|
691
|
+
|
692
|
+
Returns:
|
693
|
+
A Snowpark Dataframe object contains the information about feature view.
|
694
|
+
|
695
|
+
Example::
|
696
|
+
|
697
|
+
>>> fs = FeatureStore(...)
|
698
|
+
>>> e = Entity("foo", ["id"], desc='my entity')
|
699
|
+
>>> fs.register_entity(e)
|
700
|
+
<BLANKLINE>
|
701
|
+
>>> draft_fv = FeatureView(
|
702
|
+
... name="fv",
|
703
|
+
... entities=[e],
|
704
|
+
... feature_df=self._session.table(<source_table>).select(["NAME", "ID", "TITLE", "AGE", "TS"]),
|
705
|
+
... timestamp_col="ts",
|
706
|
+
>>> ).attach_feature_desc({"AGE": "my age", "TITLE": '"my title"'})
|
707
|
+
>>> fv = fs.register_feature_view(draft_fv, '1.0')
|
708
|
+
<BLANKLINE>
|
709
|
+
fv.to_df().show()
|
710
|
+
----------------------------------------------------------------...
|
711
|
+
|"NAME" |"ENTITIES" |"TIMESTAMP_COL" |"DESC" |
|
712
|
+
----------------------------------------------------------------...
|
713
|
+
|FV |[ |TS |foobar |
|
714
|
+
| | { | | |
|
715
|
+
| | "desc": "my entity", | | |
|
716
|
+
| | "join_keys": [ | | |
|
717
|
+
| | "ID" | | |
|
718
|
+
| | ], | | |
|
719
|
+
| | "name": "FOO", | | |
|
720
|
+
| | "owner": null | | |
|
721
|
+
| | } | | |
|
722
|
+
| |] | | |
|
723
|
+
----------------------------------------------------------------...
|
724
|
+
"""
|
603
725
|
values = list(self._to_dict().values())
|
604
726
|
schema = [x.lstrip("_") for x in list(self._to_dict().keys())]
|
605
727
|
values.append(str(FeatureView._get_physical_name(self._name, self._version))) # type: ignore[arg-type]
|
606
728
|
schema.append("physical_name")
|
607
|
-
return session.create_dataframe([values], schema=schema)
|
729
|
+
return self._feature_df.session.create_dataframe([values], schema=schema)
|
608
730
|
|
609
731
|
def to_json(self) -> str:
|
610
732
|
state_dict = self._to_dict()
|
@@ -643,6 +765,14 @@ Got {len(self._feature_df.queries['queries'])}: {self._feature_df.queries['queri
|
|
643
765
|
session=session,
|
644
766
|
)
|
645
767
|
|
768
|
+
def _get_compact_repr(self) -> _CompactRepresentation:
|
769
|
+
return _CompactRepresentation(
|
770
|
+
db=self.database.identifier(), # type: ignore[union-attr]
|
771
|
+
sch=self.schema.identifier(), # type: ignore[union-attr]
|
772
|
+
name=self.name.identifier(),
|
773
|
+
version=self.version, # type: ignore[arg-type]
|
774
|
+
)
|
775
|
+
|
646
776
|
@staticmethod
|
647
777
|
def _get_physical_name(fv_name: SqlIdentifier, fv_version: FeatureViewVersion) -> SqlIdentifier:
|
648
778
|
return SqlIdentifier(
|
@@ -655,6 +785,20 @@ Got {len(self._feature_df.queries['queries'])}: {self._feature_df.queries['queri
|
|
655
785
|
)
|
656
786
|
)
|
657
787
|
|
788
|
+
@staticmethod
|
789
|
+
def _load_from_compact_repr(session: Session, serialized_repr: str) -> Union[FeatureView, FeatureViewSlice]:
|
790
|
+
compact_repr = _CompactRepresentation.from_json(serialized_repr)
|
791
|
+
|
792
|
+
fs = feature_store.FeatureStore(
|
793
|
+
session, compact_repr.db, compact_repr.sch, default_warehouse=session.get_current_warehouse()
|
794
|
+
)
|
795
|
+
fv = fs.get_feature_view(compact_repr.name, compact_repr.version)
|
796
|
+
|
797
|
+
if compact_repr.feature_indices is not None:
|
798
|
+
feature_names = [fv.feature_names[i] for i in compact_repr.feature_indices]
|
799
|
+
return fv.slice(feature_names) # type: ignore[no-any-return]
|
800
|
+
return fv # type: ignore[no-any-return]
|
801
|
+
|
658
802
|
@staticmethod
|
659
803
|
def _load_from_lineage_node(session: Session, name: str, version: str) -> FeatureView:
|
660
804
|
db_name, feature_store_name, feature_view_name, _ = identifier.parse_schema_level_object_identifier(name)
|
@@ -5,7 +5,7 @@ import pandas as pd
|
|
5
5
|
from snowflake.ml._internal import telemetry
|
6
6
|
from snowflake.ml._internal.utils import sql_identifier
|
7
7
|
from snowflake.ml.model._client.model import model_version_impl
|
8
|
-
from snowflake.ml.model._client.ops import model_ops
|
8
|
+
from snowflake.ml.model._client.ops import model_ops, service_ops
|
9
9
|
|
10
10
|
_TELEMETRY_PROJECT = "MLOps"
|
11
11
|
_TELEMETRY_SUBPROJECT = "ModelManagement"
|
@@ -19,6 +19,7 @@ class Model:
|
|
19
19
|
"""Model Object containing multiple versions. Mapping to SQL's MODEL object."""
|
20
20
|
|
21
21
|
_model_ops: model_ops.ModelOperator
|
22
|
+
_service_ops: service_ops.ServiceOperator
|
22
23
|
_model_name: sql_identifier.SqlIdentifier
|
23
24
|
|
24
25
|
def __init__(self) -> None:
|
@@ -29,17 +30,23 @@ class Model:
|
|
29
30
|
cls,
|
30
31
|
model_ops: model_ops.ModelOperator,
|
31
32
|
*,
|
33
|
+
service_ops: service_ops.ServiceOperator,
|
32
34
|
model_name: sql_identifier.SqlIdentifier,
|
33
35
|
) -> "Model":
|
34
36
|
self: "Model" = object.__new__(cls)
|
35
37
|
self._model_ops = model_ops
|
38
|
+
self._service_ops = service_ops
|
36
39
|
self._model_name = model_name
|
37
40
|
return self
|
38
41
|
|
39
42
|
def __eq__(self, __value: object) -> bool:
|
40
43
|
if not isinstance(__value, Model):
|
41
44
|
return False
|
42
|
-
return
|
45
|
+
return (
|
46
|
+
self._model_ops == __value._model_ops
|
47
|
+
and self._service_ops == __value._service_ops
|
48
|
+
and self._model_name == __value._model_name
|
49
|
+
)
|
43
50
|
|
44
51
|
@property
|
45
52
|
def name(self) -> str:
|
@@ -208,6 +215,7 @@ class Model:
|
|
208
215
|
|
209
216
|
return model_version_impl.ModelVersion._ref(
|
210
217
|
self._model_ops,
|
218
|
+
service_ops=self._service_ops,
|
211
219
|
model_name=self._model_name,
|
212
220
|
version_name=version_id,
|
213
221
|
)
|
@@ -235,6 +243,7 @@ class Model:
|
|
235
243
|
return [
|
236
244
|
model_version_impl.ModelVersion._ref(
|
237
245
|
self._model_ops,
|
246
|
+
service_ops=self._service_ops,
|
238
247
|
model_name=self._model_name,
|
239
248
|
version_name=version_name,
|
240
249
|
)
|