snowflake-ml-python 1.53.0__py3-none-any.whl → 2.0.0__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 (99) hide show
  1. snowflake/ml/_internal/env_utils.py +13 -3
  2. snowflake/ml/_internal/platform_capabilities.py +1 -1
  3. snowflake/ml/_internal/telemetry.py +9 -5
  4. snowflake/ml/experiment/_source_info.py +30 -3
  5. snowflake/ml/experiment/experiment_tracking.py +4 -3
  6. snowflake/ml/feature_store/aggregation.py +26 -1
  7. snowflake/ml/feature_store/feature.py +1 -1
  8. snowflake/ml/feature_store/feature_store.py +374 -263
  9. snowflake/ml/feature_store/feature_view.py +24 -22
  10. snowflake/ml/feature_store/metadata_manager.py +21 -6
  11. snowflake/ml/feature_store/online_service.py +118 -46
  12. snowflake/ml/feature_store/stream_config.py +10 -3
  13. snowflake/ml/feature_store/streaming_registration.py +42 -4
  14. snowflake/ml/feature_store/tile_sql_generator.py +29 -9
  15. snowflake/ml/fileset/sfcfs.py +7 -8
  16. snowflake/ml/jobs/_interop/results.py +2 -5
  17. snowflake/ml/jobs/_utils/scripts/get_instance_ip.py +4 -8
  18. snowflake/ml/jobs/_utils/scripts/mljob_launcher.py +12 -15
  19. snowflake/ml/jobs/_utils/type_utils.py +22 -21
  20. snowflake/ml/jobs/decorators.py +6 -4
  21. snowflake/ml/jobs/job.py +67 -62
  22. snowflake/ml/jobs/job_definition.py +45 -26
  23. snowflake/ml/jobs/manager.py +48 -41
  24. snowflake/ml/model/__init__.py +2 -19
  25. snowflake/ml/model/_client/model/_loaded_model_telemetry.py +152 -0
  26. snowflake/ml/model/_client/model/batch_inference_task.py +93 -170
  27. snowflake/ml/model/_client/model/inference_engine_utils.py +3 -39
  28. snowflake/ml/model/_client/model/model_version_impl.py +162 -380
  29. snowflake/ml/model/_client/model_spec/legacy_model_spec.py +74 -0
  30. snowflake/ml/model/_client/model_spec/model_extension_spec.py +171 -0
  31. snowflake/ml/model/_client/model_spec/model_extension_spec_schema.py +113 -0
  32. snowflake/ml/model/_client/model_spec/model_spec.py +71 -0
  33. snowflake/ml/model/_client/model_spec/model_spec_parser.py +38 -0
  34. snowflake/ml/model/_client/ops/model_ops.py +187 -185
  35. snowflake/ml/model/_client/ops/service_ops.py +95 -290
  36. snowflake/ml/model/_client/service/model_deployment_spec.py +49 -183
  37. snowflake/ml/model/_client/service/model_deployment_spec_schema.py +29 -69
  38. snowflake/ml/model/_client/sql/model.py +63 -37
  39. snowflake/ml/model/_client/sql/model_version.py +49 -10
  40. snowflake/ml/model/_model_composer/model_method/function_generator.py +6 -1
  41. snowflake/ml/model/_model_composer/model_method/model_method.py +8 -2
  42. snowflake/ml/model/_model_composer/model_method/utils.py +14 -4
  43. snowflake/ml/model/_packager/model_env/model_env.py +4 -1
  44. snowflake/ml/model/_packager/model_handlers/peft_adapter.py +224 -0
  45. snowflake/ml/model/_packager/model_handlers/snowmlmodel.py +14 -8
  46. snowflake/ml/model/_packager/model_meta/model_meta.py +17 -5
  47. snowflake/ml/model/_packager/model_meta/model_meta_schema.py +11 -0
  48. snowflake/ml/model/_packager/model_packager.py +12 -8
  49. snowflake/ml/model/_packager/model_runtime/_snowml_inference_alternative_requirements.py +1 -5
  50. snowflake/ml/model/_packager/model_save_options.py +1 -0
  51. snowflake/ml/model/batch_inference/__init__.py +1 -1
  52. snowflake/ml/model/models/huggingface.py +88 -1
  53. snowflake/ml/model/models/huggingface_pipeline.py +24 -31
  54. snowflake/ml/model/openai_signatures.py +2 -0
  55. snowflake/ml/model/type_hints.py +22 -0
  56. snowflake/ml/model/volatility.py +1 -0
  57. snowflake/ml/modeling/_internal/optional_dependency.py +133 -0
  58. snowflake/ml/modeling/calibration/__init__.py +9 -1
  59. snowflake/ml/modeling/cluster/__init__.py +9 -1
  60. snowflake/ml/modeling/compose/__init__.py +9 -1
  61. snowflake/ml/modeling/covariance/__init__.py +9 -1
  62. snowflake/ml/modeling/decomposition/__init__.py +9 -1
  63. snowflake/ml/modeling/discriminant_analysis/__init__.py +9 -1
  64. snowflake/ml/modeling/ensemble/__init__.py +9 -1
  65. snowflake/ml/modeling/feature_selection/__init__.py +9 -1
  66. snowflake/ml/modeling/gaussian_process/__init__.py +9 -1
  67. snowflake/ml/modeling/impute/__init__.py +7 -1
  68. snowflake/ml/modeling/kernel_approximation/__init__.py +9 -1
  69. snowflake/ml/modeling/kernel_ridge/__init__.py +9 -1
  70. snowflake/ml/modeling/lightgbm/__init__.py +9 -1
  71. snowflake/ml/modeling/linear_model/__init__.py +9 -1
  72. snowflake/ml/modeling/manifold/__init__.py +9 -1
  73. snowflake/ml/modeling/metrics/__init__.py +8 -1
  74. snowflake/ml/modeling/mixture/__init__.py +9 -1
  75. snowflake/ml/modeling/model_selection/__init__.py +7 -1
  76. snowflake/ml/modeling/multiclass/__init__.py +9 -1
  77. snowflake/ml/modeling/naive_bayes/__init__.py +9 -1
  78. snowflake/ml/modeling/neighbors/__init__.py +9 -1
  79. snowflake/ml/modeling/neural_network/__init__.py +9 -1
  80. snowflake/ml/modeling/pipeline/__init__.py +7 -1
  81. snowflake/ml/modeling/preprocessing/__init__.py +7 -1
  82. snowflake/ml/modeling/semi_supervised/__init__.py +9 -1
  83. snowflake/ml/modeling/svm/__init__.py +9 -1
  84. snowflake/ml/modeling/tree/__init__.py +9 -1
  85. snowflake/ml/modeling/xgboost/__init__.py +9 -1
  86. snowflake/ml/registry/_manager/model_manager.py +299 -85
  87. snowflake/ml/registry/_manager/model_parameter_reconciler.py +34 -29
  88. snowflake/ml/registry/registry.py +14 -0
  89. snowflake/ml/version.py +1 -1
  90. {snowflake_ml_python-1.53.0.dist-info → snowflake_ml_python-2.0.0.dist-info}/METADATA +132 -10
  91. {snowflake_ml_python-1.53.0.dist-info → snowflake_ml_python-2.0.0.dist-info}/RECORD +94 -91
  92. snowflake/ml/model/_client/model/batch_inference_serialization.py +0 -58
  93. snowflake/ml/model/_client/model/batch_inference_specs.py +0 -153
  94. snowflake/ml/model/_client/model/batch_inference_task_v2.py +0 -156
  95. snowflake/ml/model/batch/__init__.py +0 -23
  96. snowflake/ml/utils/connection_params.py +0 -205
  97. {snowflake_ml_python-1.53.0.dist-info → snowflake_ml_python-2.0.0.dist-info}/WHEEL +0 -0
  98. {snowflake_ml_python-1.53.0.dist-info → snowflake_ml_python-2.0.0.dist-info}/licenses/LICENSE.txt +0 -0
  99. {snowflake_ml_python-1.53.0.dist-info → snowflake_ml_python-2.0.0.dist-info}/top_level.txt +0 -0
@@ -689,22 +689,30 @@ def save_requirements_file(
689
689
  path: pathlib.Path,
690
690
  pip_deps: list[requirements.Requirement],
691
691
  cuda_version: Optional[str] = None,
692
- ) -> None:
692
+ ) -> list[str]:
693
693
  """Generate Python requirements.txt file in the given directory path.
694
694
 
695
695
  Args:
696
696
  path: Path to the requirements.txt file.
697
697
  pip_deps: List of dependencies after validation.
698
698
  cuda_version: Optional CUDA version for GPU models. If provided and torch is in
699
- dependencies, adds PyTorch CUDA index URL and pins torch to CUDA variant.
699
+ dependencies, pins torch to CUDA variant.
700
+
701
+ Returns:
702
+ List of extra pip index URLs (e.g. ``["https://download.pytorch.org/whl/cu124"]``)
703
+ that were NOT written to requirements.txt because SPCS rejects non-PEP-508 lines.
704
+ Callers should persist these via model metadata so the build step can apply them.
700
705
  """
701
706
  assert path.suffix in [".txt"], "PIP requirement file should have extension of txt."
702
707
  path.parent.mkdir(parents=True, exist_ok=True)
703
708
 
704
709
  lines: list[str] = []
710
+ pip_extra_index_urls: list[str] = []
705
711
 
706
712
  if cuda_version and _has_torch_dependency(pip_deps):
707
- lines.append(_get_pytorch_cuda_index_url(cuda_version))
713
+ # Strip the "--extra-index-url " prefix to store just the URL.
714
+ raw = _get_pytorch_cuda_index_url(cuda_version)
715
+ pip_extra_index_urls.append(raw.removeprefix("--extra-index-url "))
708
716
  pip_deps = _pin_torch_to_cuda_variant(pip_deps, cuda_version)
709
717
 
710
718
  lines.extend(map(str, pip_deps))
@@ -712,6 +720,8 @@ def save_requirements_file(
712
720
  with open(path, "w", encoding="utf-8") as out:
713
721
  out.write("\n".join(lines))
714
722
 
723
+ return pip_extra_index_urls
724
+
715
725
 
716
726
  def load_conda_env_file(
717
727
  path: pathlib.Path,
@@ -84,7 +84,7 @@ class PlatformCapabilities:
84
84
  return self._get_bool_feature(LIVE_COMMIT_PARAMETER, False)
85
85
 
86
86
  def is_hidden_live_commit_enabled(self) -> bool:
87
- return False
87
+ return self._get_bool_feature(HIDDEN_LIVE_COMMIT_PARAMETER, False)
88
88
 
89
89
  def is_model_method_signature_parameters_enabled(self) -> bool:
90
90
  return self._get_bool_feature(ENABLE_MODEL_METHOD_SIGNATURE_PARAMETERS, False)
@@ -9,7 +9,8 @@ import sys
9
9
  import time
10
10
  import traceback
11
11
  import types
12
- from typing import Any, Callable, Iterable, Mapping, Optional, TypeVar, Union, cast
12
+ from collections.abc import Iterable, Mapping
13
+ from typing import Any, Callable, Optional, TypeVar, Union, cast
13
14
 
14
15
  from typing_extensions import ParamSpec
15
16
 
@@ -119,6 +120,7 @@ class TelemetryField(enum.Enum):
119
120
  TYPE_FUNCTION_USAGE = "function_usage"
120
121
  TYPE_SNOWML_SPCS_USAGE = "snowml_spcs_usage"
121
122
  TYPE_SNOWML_PIPELINE_USAGE = "snowml_pipeline_usage"
123
+ TYPE_SNOWML_LOADED_MODEL_USAGE = "snowml_loaded_model_usage"
122
124
  # message keys for telemetry
123
125
  KEY_PROJECT = "project"
124
126
  KEY_SUBPROJECT = "subproject"
@@ -717,6 +719,12 @@ def _extract_arg_value(field: str, sig: inspect.Signature, args: Any, kwargs: An
717
719
  Tuple: First value indicates if `field` exists.
718
720
  Second value is the extracted value if existed.
719
721
  """
722
+ # Check keyword arguments first. The field may have no named parameter of its own and instead be
723
+ # absorbed by a variadic keyword parameter (**kwargs), in which case the signature lookup below
724
+ # would not find it even though the caller passed a value.
725
+ if field in kwargs:
726
+ return True, kwargs[field]
727
+
720
728
  if field not in sig.parameters:
721
729
  return False, None
722
730
 
@@ -729,10 +737,6 @@ def _extract_arg_value(field: str, sig: inspect.Signature, args: Any, kwargs: An
729
737
  if idx < len(args):
730
738
  return True, args[idx]
731
739
 
732
- # Check if value was passed as keyword argument
733
- if field in kwargs:
734
- return True, kwargs[field]
735
-
736
740
  # Fall back to default value if available
737
741
  if param.default is not inspect.Parameter.empty:
738
742
  return True, param.default
@@ -44,6 +44,11 @@ _SNOWFLAKE_FILE_DOMAIN_TYPE_ENV = "SNOWFLAKE_FILE_DOMAIN_TYPE"
44
44
  _SNOWFLAKE_FILE_DOMAIN_NAME_ENV = "SNOWFLAKE_FILE_DOMAIN_NAME"
45
45
  _SNOWFLAKE_MAIN_FILE_PATH_ENV = "SNOWFLAKE_MAIN_FILE_PATH"
46
46
 
47
+ # Environment variable Snowflake injects into an ML job's container, carrying the
48
+ # job's fully-qualified id. Its presence is how we recognize that the run was
49
+ # created from inside an ML job.
50
+ _ML_JOB_ID_ENV = "MLRS_JOB_ID"
51
+
47
52
 
48
53
  @dataclasses.dataclass(frozen=True)
49
54
  class GitInfo:
@@ -72,6 +77,9 @@ class SourceInfo:
72
77
  # ``entry_point``, not duplicated here.
73
78
  snowflake_file_domain_type: Optional[str] = None
74
79
  snowflake_file_domain_name: Optional[str] = None
80
+ # The fully-qualified id of the ML job this run was created from, taken from
81
+ # the environment. ``None`` when not running inside an ML job.
82
+ ml_job_id: Optional[str] = None
75
83
 
76
84
  def is_empty(self) -> bool:
77
85
  """Return True when there is nothing useful to send."""
@@ -80,6 +88,7 @@ class SourceInfo:
80
88
  and (self.git is None or self.git.is_empty())
81
89
  and self.snowflake_file_domain_type is None
82
90
  and self.snowflake_file_domain_name is None
91
+ and self.ml_job_id is None
83
92
  )
84
93
 
85
94
  def to_json_dict(self) -> dict[str, Any]:
@@ -92,8 +101,9 @@ class SourceInfo:
92
101
 
93
102
  Returns:
94
103
  A JSON-serializable dict whose keys (``entry_point``, ``git``,
95
- ``snowflake_file_domain_type``, ``snowflake_file_domain_name``) are
96
- included only when resolved. May be empty when nothing was collected.
104
+ ``snowflake_file_domain_type``, ``snowflake_file_domain_name``,
105
+ ``ml_job_id``) are included only when resolved. May be empty when
106
+ nothing was collected.
97
107
  """
98
108
  out: dict[str, Any] = {}
99
109
  if self.entry_point is not None and "$$" not in self.entry_point:
@@ -110,6 +120,8 @@ class SourceInfo:
110
120
  out["snowflake_file_domain_type"] = self.snowflake_file_domain_type
111
121
  if self.snowflake_file_domain_name is not None and "$$" not in self.snowflake_file_domain_name:
112
122
  out["snowflake_file_domain_name"] = self.snowflake_file_domain_name
123
+ if self.ml_job_id is not None and "$$" not in self.ml_job_id:
124
+ out["ml_job_id"] = self.ml_job_id
113
125
  return out
114
126
 
115
127
  @classmethod
@@ -121,24 +133,39 @@ class SourceInfo:
121
133
  be resolved or collection failed.
122
134
  """
123
135
  try:
136
+ # An ML job id is orthogonal to how the entry point is described, so
137
+ # it is resolved once and attached to whichever shape is returned.
138
+ ml_job_id = _collect_ml_job_id()
139
+
124
140
  # Snowflake-managed files (notebooks or .py workspace files) describe
125
141
  # the running file via environment variables rather than a resolvable
126
142
  # local path, so they are handled first and independently of the
127
143
  # git/notebook-path machinery.
128
144
  snowflake_file = _collect_snowflake_file()
129
145
  if snowflake_file is not None:
130
- return snowflake_file
146
+ return dataclasses.replace(snowflake_file, ml_job_id=ml_job_id)
131
147
 
132
148
  # Resolve the notebook once so entry-point and git agree on the same file.
133
149
  notebook_path = _resolve_notebook_path() if _in_ipython_kernel() else None
134
150
  return cls(
135
151
  entry_point=_detect_entry_point(notebook_path),
136
152
  git=_collect_git(notebook_path),
153
+ ml_job_id=ml_job_id,
137
154
  )
138
155
  except Exception:
139
156
  return cls()
140
157
 
141
158
 
159
+ def _collect_ml_job_id() -> Optional[str]:
160
+ """Collect the fully-qualified id of the enclosing ML job.
161
+
162
+ Returns:
163
+ The ML job id from the environment, or ``None`` when not running inside
164
+ an ML job (or when the server does not expose the id).
165
+ """
166
+ return os.environ.get(_ML_JOB_ID_ENV) or None
167
+
168
+
142
169
  def _collect_snowflake_file() -> Optional[SourceInfo]:
143
170
  """Collect source info when running inside a Snowflake-managed file.
144
171
 
@@ -63,9 +63,10 @@ class ExperimentTracking:
63
63
  schema_name: The name of the schema. If None, the current schema of the session
64
64
  will be used. If there is no active schema, the PUBLIC schema will be used. Defaults to None.
65
65
  capture_source_info: If True, each new run records best-effort source provenance — the
66
- entry-point filename and any surrounding git commit, branch, and remote URL. Set to
67
- False to skip collection entirely. Collection is always non-fatal and never blocks
68
- run creation. Defaults to True.
66
+ entry-point filename, any surrounding git commit, branch, and remote URL, and the id
67
+ of the enclosing ML job when running inside one. Set to False to skip collection
68
+ entirely. Collection is always non-fatal and never blocks run creation. Defaults to
69
+ True.
69
70
 
70
71
  Raises:
71
72
  ValueError: If no database is provided and no active database exists in the session.
@@ -33,6 +33,12 @@ class AggregationType(Enum):
33
33
  - Sketch aggregations (APPROX_COUNT_DISTINCT, APPROX_PERCENTILE): Stored as mergeable state in tiles
34
34
  - List aggregations (LAST_N, LAST_DISTINCT_N, FIRST_N, FIRST_DISTINCT_N): Stored as arrays in tiles
35
35
  - Secondary-key array (_SECONDARY_KEY_ARRAY): Internal-only ARRAY_AGG of the secondary-key column
36
+
37
+ Each value is the short function token used in the imperative metadata
38
+ tag / FeatureView JSON and in auto-generated output column names. Snowflake
39
+ ``DESCRIBE ... TYPE = SPECIFICATION`` reports its own SQL spelling for some
40
+ functions (e.g. ``stddev`` for ``STD``); the declarative layer reconciles
41
+ that spelling on read/hash rather than changing these values.
36
42
  """
37
43
 
38
44
  SUM = "sum"
@@ -50,6 +56,25 @@ class AggregationType(Enum):
50
56
  FIRST_DISTINCT_N = "first_distinct_n"
51
57
  _SECONDARY_KEY_ARRAY = "secondary_key_array"
52
58
 
59
+ @classmethod
60
+ def _missing_(cls, value: object) -> AggregationType | None:
61
+ """Accept Snowflake's SPECIFICATION spelling ``stddev`` as ``STD``.
62
+
63
+ The imperative wire token stays ``"std"``; ``DESCRIBE ... TYPE =
64
+ SPECIFICATION`` reports ``"stddev"`` for the same function, so reads of
65
+ the applied spec must resolve that spelling back to ``STD``.
66
+
67
+ Args:
68
+ value: The raw value that failed direct enum lookup.
69
+
70
+ Returns:
71
+ ``STD`` when ``value`` is the string ``"stddev"`` (case-insensitive),
72
+ otherwise ``None`` to preserve the standard ``ValueError``.
73
+ """
74
+ if isinstance(value, str) and value.lower() == "stddev":
75
+ return cls.STD
76
+ return None
77
+
53
78
  def is_simple(self) -> bool:
54
79
  """Check if this is a simple aggregation (scalar result per tile).
55
80
 
@@ -223,7 +248,7 @@ class AggregationSpec:
223
248
  AggregationType.VAR,
224
249
  )
225
250
  if self.function not in supported_lifetime_types:
226
- supported_names = ", ".join(t.value.upper() for t in supported_lifetime_types)
251
+ supported_names = ", ".join(t.name for t in supported_lifetime_types)
227
252
  raise ValueError(
228
253
  f"Lifetime window is not supported for {self.function.value} aggregation "
229
254
  f"'{self.output_column}'. Lifetime is only supported for: {supported_names}."
@@ -17,8 +17,8 @@ from snowflake.ml.feature_store.aggregation import (
17
17
  )
18
18
 
19
19
  _NO_OFFSET = "0"
20
- _KEYS = "keys"
21
20
  _LIFETIME = "lifetime"
21
+ _KEYS = "keys"
22
22
 
23
23
 
24
24
  class Feature: