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.
Files changed (74) hide show
  1. snowflake/cortex/_complete.py +7 -33
  2. snowflake/ml/_internal/env_utils.py +11 -5
  3. snowflake/ml/_internal/exceptions/modeling_error_messages.py +4 -1
  4. snowflake/ml/_internal/telemetry.py +14 -0
  5. snowflake/ml/_internal/utils/pkg_version_utils.py +8 -22
  6. snowflake/ml/data/_internal/arrow_ingestor.py +66 -10
  7. snowflake/ml/data/data_connector.py +59 -6
  8. snowflake/ml/data/data_ingestor.py +18 -1
  9. snowflake/ml/data/{_internal/ingestor_utils.py → ingestor_utils.py} +5 -1
  10. snowflake/ml/data/torch_dataset.py +33 -0
  11. snowflake/ml/dataset/dataset_metadata.py +3 -1
  12. snowflake/ml/dataset/dataset_reader.py +9 -3
  13. snowflake/ml/feature_store/examples/airline_features/entities.py +16 -0
  14. snowflake/ml/feature_store/examples/airline_features/features/plane_features.py +31 -0
  15. snowflake/ml/feature_store/examples/airline_features/features/weather_features.py +42 -0
  16. snowflake/ml/feature_store/examples/airline_features/source.yaml +7 -0
  17. snowflake/ml/feature_store/examples/citibike_trip_features/features/station_feature.py +10 -4
  18. snowflake/ml/feature_store/examples/citibike_trip_features/features/trip_feature.py +6 -0
  19. snowflake/ml/feature_store/examples/citibike_trip_features/source.yaml +3 -0
  20. snowflake/ml/feature_store/examples/example_helper.py +69 -31
  21. snowflake/ml/feature_store/examples/new_york_taxi_features/entities.py +3 -3
  22. snowflake/ml/feature_store/examples/new_york_taxi_features/features/{dropoff_features.py → location_features.py} +14 -9
  23. snowflake/ml/feature_store/examples/new_york_taxi_features/features/trip_features.py +36 -0
  24. snowflake/ml/feature_store/examples/new_york_taxi_features/source.yaml +5 -1
  25. snowflake/ml/feature_store/examples/source_data/airline.yaml +4 -0
  26. snowflake/ml/feature_store/examples/source_data/citibike_trips.yaml +1 -1
  27. snowflake/ml/feature_store/examples/wine_quality_features/entities.py +3 -3
  28. snowflake/ml/feature_store/examples/wine_quality_features/features/managed_wine_features.py +13 -6
  29. snowflake/ml/feature_store/examples/wine_quality_features/features/static_wine_features.py +8 -5
  30. snowflake/ml/feature_store/examples/wine_quality_features/source.yaml +3 -0
  31. snowflake/ml/feature_store/feature_store.py +59 -24
  32. snowflake/ml/feature_store/feature_view.py +148 -4
  33. snowflake/ml/model/_client/model/model_impl.py +11 -2
  34. snowflake/ml/model/_client/model/model_version_impl.py +171 -20
  35. snowflake/ml/model/_client/ops/model_ops.py +105 -27
  36. snowflake/ml/model/_client/ops/service_ops.py +121 -0
  37. snowflake/ml/model/_client/service/model_deployment_spec.py +95 -0
  38. snowflake/ml/model/_client/service/model_deployment_spec_schema.py +31 -0
  39. snowflake/ml/model/_client/sql/model_version.py +13 -4
  40. snowflake/ml/model/_client/sql/service.py +129 -0
  41. snowflake/ml/model/_model_composer/model_composer.py +3 -0
  42. snowflake/ml/model/_model_composer/model_manifest/model_manifest.py +10 -2
  43. snowflake/ml/model/_model_composer/model_manifest/model_manifest_schema.py +3 -0
  44. snowflake/ml/model/_packager/model_env/model_env.py +7 -2
  45. snowflake/ml/model/_packager/model_handlers/_base.py +29 -12
  46. snowflake/ml/model/_packager/model_handlers/catboost.py +19 -12
  47. snowflake/ml/model/_packager/model_handlers/custom.py +6 -2
  48. snowflake/ml/model/_packager/model_handlers/huggingface_pipeline.py +9 -5
  49. snowflake/ml/model/_packager/model_handlers/lightgbm.py +27 -18
  50. snowflake/ml/model/_packager/model_handlers/llm.py +7 -3
  51. snowflake/ml/model/_packager/model_handlers/mlflow.py +8 -3
  52. snowflake/ml/model/_packager/model_handlers/pytorch.py +8 -3
  53. snowflake/ml/model/_packager/model_handlers/sentence_transformers.py +8 -3
  54. snowflake/ml/model/_packager/model_handlers/sklearn.py +87 -4
  55. snowflake/ml/model/_packager/model_handlers/snowmlmodel.py +7 -2
  56. snowflake/ml/model/_packager/model_handlers/tensorflow.py +9 -4
  57. snowflake/ml/model/_packager/model_handlers/torchscript.py +8 -3
  58. snowflake/ml/model/_packager/model_handlers/xgboost.py +25 -16
  59. snowflake/ml/model/_packager/model_meta/model_meta.py +32 -2
  60. snowflake/ml/model/_packager/model_meta/model_meta_schema.py +19 -0
  61. snowflake/ml/model/_packager/model_packager.py +2 -1
  62. snowflake/ml/model/_packager/model_runtime/model_runtime.py +4 -2
  63. snowflake/ml/model/type_hints.py +1 -3
  64. snowflake/ml/modeling/framework/base.py +28 -19
  65. snowflake/ml/modeling/pipeline/pipeline.py +3 -0
  66. snowflake/ml/registry/_manager/model_manager.py +16 -2
  67. snowflake/ml/utils/sql_client.py +22 -0
  68. snowflake/ml/version.py +1 -1
  69. {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/METADATA +35 -2
  70. {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/RECORD +73 -62
  71. snowflake/ml/feature_store/examples/new_york_taxi_features/features/pickup_features.py +0 -58
  72. {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/LICENSE.txt +0 -0
  73. {snowflake_ml_python-1.6.0.dist-info → snowflake_ml_python-1.6.1.dist-info}/WHEEL +0 -0
  74. {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
- class CreationMode(Enum):
144
- FAIL_IF_NOT_EXIST = 1
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 = self.list_entities().filter(F.col("NAME") == name.resolved()).collect()
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(results[0][0], results[0][1], self.list_entities().collect())
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 = self.list_entities().filter(F.col("NAME") == name.resolved()).collect()
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._load_serialized_feature_objects(cast(List[str], features))
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
- serialized_feature_views=[fv.to_json() for fv in features],
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 source_meta.properties.serialized_feature_views is None
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
- return self._load_serialized_feature_objects(source_meta.properties.serialized_feature_views)
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 _load_serialized_feature_objects(
2370
- self, serialized_feature_objs: List[str]
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 serialized_feature_objs:
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 as e:
2405
- if "'DATASETS' does not exist" in e.message:
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] = None
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 self._model_ops == __value._model_ops and self._model_name == __value._model_name
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
  )