snowflake-ml-python 1.19.0__py3-none-any.whl → 1.21.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 (52) hide show
  1. snowflake/ml/_internal/env_utils.py +16 -0
  2. snowflake/ml/_internal/platform_capabilities.py +36 -0
  3. snowflake/ml/_internal/telemetry.py +56 -7
  4. snowflake/ml/data/_internal/arrow_ingestor.py +67 -2
  5. snowflake/ml/data/data_connector.py +103 -1
  6. snowflake/ml/experiment/_client/experiment_tracking_sql_client.py +8 -2
  7. snowflake/ml/experiment/_entities/run.py +15 -0
  8. snowflake/ml/experiment/callback/keras.py +25 -2
  9. snowflake/ml/experiment/callback/lightgbm.py +27 -2
  10. snowflake/ml/experiment/callback/xgboost.py +25 -2
  11. snowflake/ml/experiment/experiment_tracking.py +123 -13
  12. snowflake/ml/experiment/utils.py +6 -0
  13. snowflake/ml/feature_store/access_manager.py +1 -0
  14. snowflake/ml/feature_store/feature_store.py +1 -1
  15. snowflake/ml/feature_store/feature_view.py +34 -24
  16. snowflake/ml/jobs/_interop/protocols.py +3 -0
  17. snowflake/ml/jobs/_utils/feature_flags.py +1 -0
  18. snowflake/ml/jobs/_utils/payload_utils.py +360 -357
  19. snowflake/ml/jobs/_utils/scripts/mljob_launcher.py +95 -8
  20. snowflake/ml/jobs/_utils/scripts/start_mlruntime.sh +92 -0
  21. snowflake/ml/jobs/_utils/scripts/startup.sh +112 -0
  22. snowflake/ml/jobs/_utils/spec_utils.py +2 -406
  23. snowflake/ml/jobs/_utils/stage_utils.py +22 -1
  24. snowflake/ml/jobs/_utils/types.py +14 -7
  25. snowflake/ml/jobs/job.py +8 -9
  26. snowflake/ml/jobs/manager.py +64 -129
  27. snowflake/ml/model/_client/model/inference_engine_utils.py +8 -4
  28. snowflake/ml/model/_client/model/model_version_impl.py +109 -28
  29. snowflake/ml/model/_client/ops/model_ops.py +32 -6
  30. snowflake/ml/model/_client/ops/service_ops.py +9 -4
  31. snowflake/ml/model/_client/sql/service.py +69 -2
  32. snowflake/ml/model/_packager/model_handler.py +8 -2
  33. snowflake/ml/model/_packager/model_handlers/{huggingface_pipeline.py → huggingface.py} +203 -76
  34. snowflake/ml/model/_packager/model_handlers/mlflow.py +6 -1
  35. snowflake/ml/model/_packager/model_runtime/_snowml_inference_alternative_requirements.py +1 -1
  36. snowflake/ml/model/_signatures/core.py +305 -8
  37. snowflake/ml/model/_signatures/utils.py +13 -4
  38. snowflake/ml/model/compute_pool.py +2 -0
  39. snowflake/ml/model/models/huggingface.py +285 -0
  40. snowflake/ml/model/models/huggingface_pipeline.py +25 -215
  41. snowflake/ml/model/type_hints.py +5 -1
  42. snowflake/ml/modeling/_internal/snowpark_implementations/distributed_hpo_trainer.py +2 -2
  43. snowflake/ml/monitoring/_client/model_monitor_sql_client.py +12 -0
  44. snowflake/ml/monitoring/_manager/model_monitor_manager.py +12 -0
  45. snowflake/ml/monitoring/entities/model_monitor_config.py +5 -0
  46. snowflake/ml/utils/html_utils.py +67 -1
  47. snowflake/ml/version.py +1 -1
  48. {snowflake_ml_python-1.19.0.dist-info → snowflake_ml_python-1.21.0.dist-info}/METADATA +94 -7
  49. {snowflake_ml_python-1.19.0.dist-info → snowflake_ml_python-1.21.0.dist-info}/RECORD +52 -48
  50. {snowflake_ml_python-1.19.0.dist-info → snowflake_ml_python-1.21.0.dist-info}/WHEEL +0 -0
  51. {snowflake_ml_python-1.19.0.dist-info → snowflake_ml_python-1.21.0.dist-info}/licenses/LICENSE.txt +0 -0
  52. {snowflake_ml_python-1.19.0.dist-info → snowflake_ml_python-1.21.0.dist-info}/top_level.txt +0 -0
@@ -481,10 +481,280 @@ class FeatureGroupSpec(BaseFeatureSpec):
481
481
  return FeatureGroupSpec(name=input_dict["name"], specs=specs, shape=shape)
482
482
 
483
483
 
484
+ class BaseParamSpec(ABC):
485
+ """Abstract Class for specification of a parameter."""
486
+
487
+ def __init__(self, name: str, shape: Optional[tuple[int, ...]] = None) -> None:
488
+ self._name = name
489
+
490
+ if shape is not None and not isinstance(shape, tuple):
491
+ raise snowml_exceptions.SnowflakeMLException(
492
+ error_code=error_codes.INVALID_TYPE,
493
+ original_exception=TypeError("Shape should be a tuple if presented."),
494
+ )
495
+ self._shape = shape
496
+
497
+ @final
498
+ @property
499
+ def name(self) -> str:
500
+ """Name of the parameter."""
501
+ return self._name
502
+
503
+ @final
504
+ @property
505
+ def shape(self) -> Optional[tuple[int, ...]]:
506
+ """Shape of the parameter. None means scalar."""
507
+ return self._shape
508
+
509
+ @abstractmethod
510
+ def to_dict(self) -> dict[str, Any]:
511
+ """Serialization"""
512
+
513
+ @classmethod
514
+ @abstractmethod
515
+ def from_dict(cls, input_dict: dict[str, Any]) -> "BaseParamSpec":
516
+ """Deserialization"""
517
+
518
+
519
+ class ParamSpec(BaseParamSpec):
520
+ """Specification of a parameter in Snowflake native model packaging."""
521
+
522
+ def __init__(
523
+ self,
524
+ name: str,
525
+ dtype: DataType,
526
+ default_value: Any,
527
+ shape: Optional[tuple[int, ...]] = None,
528
+ ) -> None:
529
+ """Initialize a parameter.
530
+
531
+ Args:
532
+ name: Name of the parameter.
533
+ dtype: Type of the parameter.
534
+ default_value: Default value of the parameter.
535
+ shape: Shape of the parameter. None means scalar, otherwise a tuple
536
+ representing dimensions. Use -1 for variable length dimensions.
537
+ """
538
+ super().__init__(name=name, shape=shape)
539
+
540
+ self._validate_default_value(dtype, default_value, shape)
541
+ self._dtype = dtype
542
+ self._default_value = default_value
543
+
544
+ @staticmethod
545
+ def _validate_default_value(dtype: DataType, default_value: Any, shape: Optional[tuple[int, ...]]) -> None:
546
+ """Validate that default_value is compatible with dtype and shape.
547
+
548
+ Args:
549
+ dtype: The expected data type.
550
+ default_value: The default value to validate. None is allowed and means no default.
551
+ shape: The expected shape. None means scalar.
552
+
553
+ Raises:
554
+ SnowflakeMLException: ValueError: When the default_value is not compatible with dtype/shape.
555
+ """
556
+ if default_value is None:
557
+ return
558
+
559
+ try:
560
+ arr = np.array(default_value, dtype=dtype._numpy_type)
561
+
562
+ # Validate shape compatibility
563
+ if shape is None:
564
+ # Scalar expected
565
+ if arr.ndim != 0:
566
+ raise snowml_exceptions.SnowflakeMLException(
567
+ error_code=error_codes.INVALID_ARGUMENT,
568
+ original_exception=ValueError(f"Expected scalar value, got array with shape {arr.shape}"),
569
+ )
570
+ else:
571
+ # Non-scalar expected
572
+ if arr.ndim != len(shape):
573
+ raise snowml_exceptions.SnowflakeMLException(
574
+ error_code=error_codes.INVALID_ARGUMENT,
575
+ original_exception=ValueError(
576
+ f"Expected {len(shape)}-dimensional value, got {arr.ndim}-dimensional"
577
+ ),
578
+ )
579
+ # Check each dimension (-1 means variable length)
580
+ for i, (expected, actual) in enumerate(zip(shape, arr.shape)):
581
+ if expected != -1 and expected != actual:
582
+ raise snowml_exceptions.SnowflakeMLException(
583
+ error_code=error_codes.INVALID_ARGUMENT,
584
+ original_exception=ValueError(f"Dimension {i}: expected {expected}, got {actual}"),
585
+ )
586
+
587
+ except (ValueError, TypeError, OverflowError) as e:
588
+ raise snowml_exceptions.SnowflakeMLException(
589
+ error_code=error_codes.INVALID_ARGUMENT,
590
+ original_exception=ValueError(
591
+ f"Default value {repr(default_value)} (type: {type(default_value).__name__}) "
592
+ f"is not compatible with dtype {dtype} and shape {shape}. {str(e)}"
593
+ ),
594
+ )
595
+
596
+ @property
597
+ def dtype(self) -> DataType:
598
+ """Type of the parameter."""
599
+ return self._dtype
600
+
601
+ @property
602
+ def default_value(self) -> Any:
603
+ """Default value of the parameter."""
604
+ return self._default_value
605
+
606
+ def to_dict(self) -> dict[str, Any]:
607
+ """Serialize the parameter specification into a dict.
608
+
609
+ Returns:
610
+ A dict that serializes the parameter specification.
611
+ """
612
+ result: dict[str, Any] = {
613
+ "name": self._name,
614
+ "dtype": self._dtype.name,
615
+ "default_value": self._default_value,
616
+ }
617
+ if self._shape is not None:
618
+ result["shape"] = self._shape
619
+ return result
620
+
621
+ @classmethod
622
+ def from_dict(cls, input_dict: dict[str, Any]) -> "ParamSpec":
623
+ """Deserialize the parameter specification from a dict.
624
+
625
+ Args:
626
+ input_dict: The dict containing information of the parameter specification.
627
+
628
+ Returns:
629
+ ParamSpec: The deserialized parameter specification.
630
+ """
631
+ shape = input_dict.get("shape", None)
632
+ if shape is not None:
633
+ shape = tuple(shape)
634
+ return ParamSpec(
635
+ name=input_dict["name"],
636
+ dtype=DataType[input_dict["dtype"]],
637
+ default_value=input_dict["default_value"],
638
+ shape=shape,
639
+ )
640
+
641
+ def __eq__(self, other: object) -> bool:
642
+ if isinstance(other, ParamSpec):
643
+ return (
644
+ self._name == other._name
645
+ and self._dtype == other._dtype
646
+ and np.array_equal(self._default_value, other._default_value)
647
+ and self._shape == other._shape
648
+ )
649
+ else:
650
+ return False
651
+
652
+ def __repr__(self) -> str:
653
+ shape_str = f", shape={repr(self._shape)}" if self._shape else ""
654
+ return (
655
+ f"ParamSpec(name={repr(self._name)}, dtype={repr(self._dtype)}, "
656
+ f"default_value={repr(self._default_value)}{shape_str})"
657
+ )
658
+
659
+ @classmethod
660
+ def from_mlflow_spec(cls, param_spec: "mlflow.types.ParamSpec") -> "ParamSpec":
661
+ return ParamSpec(
662
+ name=param_spec.name,
663
+ dtype=DataType.from_numpy_type(param_spec.dtype.to_numpy()),
664
+ default_value=param_spec.default,
665
+ )
666
+
667
+
668
+ class ParamGroupSpec(BaseParamSpec):
669
+ """Specification of a group of parameters in Snowflake native model packaging."""
670
+
671
+ def __init__(
672
+ self,
673
+ name: str,
674
+ specs: list[BaseParamSpec],
675
+ shape: Optional[tuple[int, ...]] = None,
676
+ ) -> None:
677
+ """Initialize a parameter group.
678
+
679
+ Args:
680
+ name: Name of the parameter group.
681
+ specs: A list of parameter specifications that composes the group.
682
+ shape: Shape of the parameter group. None means scalar, otherwise a tuple
683
+ representing dimensions. Use -1 for variable length dimensions.
684
+ """
685
+ super().__init__(name=name, shape=shape)
686
+ self._specs = specs
687
+ self._validate()
688
+
689
+ def _validate(self) -> None:
690
+ if len(self._specs) == 0:
691
+ raise snowml_exceptions.SnowflakeMLException(
692
+ error_code=error_codes.INVALID_ARGUMENT, original_exception=ValueError("No children param specs.")
693
+ )
694
+
695
+ @property
696
+ def specs(self) -> list[BaseParamSpec]:
697
+ """List of parameter specifications in the group."""
698
+ return self._specs
699
+
700
+ def __eq__(self, other: object) -> bool:
701
+ if isinstance(other, ParamGroupSpec):
702
+ return self._name == other._name and self._specs == other._specs and self._shape == other._shape
703
+ return False
704
+
705
+ def __repr__(self) -> str:
706
+ spec_strs = ",\n\t\t".join(repr(spec) for spec in self._specs)
707
+ shape_str = f",\nshape={repr(self._shape)}" if self._shape else ""
708
+ return textwrap.dedent(
709
+ f"""ParamGroupSpec(
710
+ name={repr(self._name)},
711
+ specs=[
712
+ {spec_strs}
713
+ ]{shape_str}
714
+ )
715
+ """
716
+ )
717
+
718
+ def to_dict(self) -> dict[str, Any]:
719
+ """Serialize the parameter group into a dict.
720
+
721
+ Returns:
722
+ A dict that serializes the parameter group.
723
+ """
724
+ result: dict[str, Any] = {"name": self._name, "specs": [s.to_dict() for s in self._specs]}
725
+ if self._shape is not None:
726
+ result["shape"] = self._shape
727
+ return result
728
+
729
+ @classmethod
730
+ def from_dict(cls, input_dict: dict[str, Any]) -> "ParamGroupSpec":
731
+ """Deserialize the parameter group from a dict.
732
+
733
+ Args:
734
+ input_dict: The dict containing information of the parameter group.
735
+
736
+ Returns:
737
+ A parameter group instance deserialized and created from the dict.
738
+ """
739
+ specs: list[BaseParamSpec] = []
740
+ for e in input_dict["specs"]:
741
+ spec: BaseParamSpec = ParamGroupSpec.from_dict(e) if "specs" in e else ParamSpec.from_dict(e)
742
+ specs.append(spec)
743
+ shape = input_dict.get("shape", None)
744
+ if shape is not None:
745
+ shape = tuple(shape)
746
+ return ParamGroupSpec(name=input_dict["name"], specs=specs, shape=shape)
747
+
748
+
484
749
  class ModelSignature:
485
750
  """Signature of a model that specifies the input and output of a model."""
486
751
 
487
- def __init__(self, inputs: Sequence[BaseFeatureSpec], outputs: Sequence[BaseFeatureSpec]) -> None:
752
+ def __init__(
753
+ self,
754
+ inputs: Sequence[BaseFeatureSpec],
755
+ outputs: Sequence[BaseFeatureSpec],
756
+ params: Optional[Sequence[BaseParamSpec]] = None,
757
+ ) -> None:
488
758
  """Initialize a model signature.
489
759
 
490
760
  Args:
@@ -492,9 +762,12 @@ class ModelSignature:
492
762
  the input of the model.
493
763
  outputs: A sequence of feature specifications and feature group specifications that will compose
494
764
  the output of the model.
765
+ params: A sequence of parameter specifications and parameter group specifications that will compose
766
+ the parameters of the model. Defaults to None.
495
767
  """
496
768
  self._inputs = inputs
497
769
  self._outputs = outputs
770
+ self._params = params or []
498
771
 
499
772
  @property
500
773
  def inputs(self) -> Sequence[BaseFeatureSpec]:
@@ -506,9 +779,18 @@ class ModelSignature:
506
779
  """Outputs of the model, containing a sequence of feature specifications and feature group specifications."""
507
780
  return self._outputs
508
781
 
782
+ @property
783
+ def params(self) -> Sequence[BaseParamSpec]:
784
+ """Parameters of the model, containing a sequence of parameter specifications."""
785
+ return self._params
786
+
509
787
  def __eq__(self, other: object) -> bool:
510
788
  if isinstance(other, ModelSignature):
511
- return self._inputs == other._inputs and self._outputs == other._outputs
789
+ return (
790
+ self._inputs == other._inputs
791
+ and self._outputs == other._outputs
792
+ and getattr(other, "_params", []) == self._params # handles backward compatibility
793
+ )
512
794
  else:
513
795
  return False
514
796
 
@@ -522,6 +804,7 @@ class ModelSignature:
522
804
  return {
523
805
  "inputs": [spec.to_dict() for spec in self._inputs],
524
806
  "outputs": [spec.to_dict() for spec in self._outputs],
807
+ "params": [spec.to_dict() for spec in self._params],
525
808
  }
526
809
 
527
810
  @classmethod
@@ -536,18 +819,26 @@ class ModelSignature:
536
819
  """
537
820
  sig_outs = loaded["outputs"]
538
821
  sig_inputs = loaded["inputs"]
822
+ # If parameters is not provided, default to empty list for backward compatibility.
823
+ sig_params = loaded.get("params", [])
539
824
 
540
825
  deserialize_spec: Callable[[dict[str, Any]], BaseFeatureSpec] = lambda sig_spec: (
541
826
  FeatureGroupSpec.from_dict(sig_spec) if "specs" in sig_spec else FeatureSpec.from_dict(sig_spec)
542
827
  )
828
+ deserialize_param: Callable[[dict[str, Any]], BaseParamSpec] = lambda sig_param: (
829
+ ParamGroupSpec.from_dict(sig_param) if "specs" in sig_param else ParamSpec.from_dict(sig_param)
830
+ )
543
831
 
544
832
  return ModelSignature(
545
- inputs=[deserialize_spec(s) for s in sig_inputs], outputs=[deserialize_spec(s) for s in sig_outs]
833
+ inputs=[deserialize_spec(s) for s in sig_inputs],
834
+ outputs=[deserialize_spec(s) for s in sig_outs],
835
+ params=[deserialize_param(s) for s in sig_params],
546
836
  )
547
837
 
548
838
  def __repr__(self) -> str:
549
839
  inputs_spec_strs = ",\n\t\t".join(repr(spec) for spec in self._inputs)
550
840
  outputs_spec_strs = ",\n\t\t".join(repr(spec) for spec in self._outputs)
841
+ params_spec_strs = ",\n\t\t".join(repr(spec) for spec in self._params)
551
842
  return textwrap.dedent(
552
843
  f"""ModelSignature(
553
844
  inputs=[
@@ -555,6 +846,9 @@ class ModelSignature:
555
846
  ],
556
847
  outputs=[
557
848
  {outputs_spec_strs}
849
+ ],
850
+ params=[
851
+ {params_spec_strs}
558
852
  ]
559
853
  )"""
560
854
  )
@@ -570,15 +864,17 @@ class ModelSignature:
570
864
  # Create collapsible sections for inputs and outputs
571
865
  inputs_content = html_utils.create_features_html(self.inputs, "Input")
572
866
  outputs_content = html_utils.create_features_html(self.outputs, "Output")
573
-
867
+ params_content = html_utils.create_parameters_html(self.params, "Parameter")
574
868
  inputs_section = html_utils.create_collapsible_section("Inputs", inputs_content, open_by_default=True)
575
869
  outputs_section = html_utils.create_collapsible_section("Outputs", outputs_content, open_by_default=True)
870
+ params_section = html_utils.create_collapsible_section("Parameters", params_content, open_by_default=True)
576
871
 
577
872
  content = f"""
578
- <div style="margin-top: 10px;">
579
- {inputs_section}
580
- {outputs_section}
581
- </div>
873
+ <div style="margin-top: 10px;">
874
+ {inputs_section}
875
+ {outputs_section}
876
+ {params_section}
877
+ </div>
582
878
  """
583
879
 
584
880
  return html_utils.create_base_container("Model Signature", content)
@@ -593,4 +889,5 @@ class ModelSignature:
593
889
  FeatureSpec.from_mlflow_spec(spec, f"output_feature_{idx}")
594
890
  for idx, spec in enumerate(mlflow_sig.outputs)
595
891
  ],
892
+ params=[ParamSpec.from_mlflow_spec(spec) for spec in mlflow_sig.params or []],
596
893
  )
@@ -20,15 +20,21 @@ def convert_list_to_ndarray(data: list[Any]) -> npt.NDArray[Any]:
20
20
 
21
21
  Raises:
22
22
  SnowflakeMLException: ValueError: Raised when ragged nested list or list containing non-basic type confronted.
23
- SnowflakeMLException: ValueError: Raised when ragged nested list or list containing non-basic type confronted.
24
23
 
25
24
  Returns:
26
25
  The converted numpy array.
27
26
  """
28
- warnings.filterwarnings("error", category=np.VisibleDeprecationWarning)
27
+ # VisibleDeprecationWarning was removed in numpy>2
28
+ visible_deprecation_warning = getattr(np, "VisibleDeprecationWarning", None)
29
+ exception_types = (ValueError,)
30
+
31
+ if visible_deprecation_warning is not None:
32
+ warnings.filterwarnings("error", category=visible_deprecation_warning)
33
+ exception_types = (visible_deprecation_warning, ValueError) # type: ignore[assignment]
34
+
29
35
  try:
30
36
  arr = np.array(data)
31
- except (np.VisibleDeprecationWarning, ValueError):
37
+ except exception_types:
32
38
  # In recent version of numpy, this warning should be raised when bad list provided.
33
39
  raise snowml_exceptions.SnowflakeMLException(
34
40
  error_code=error_codes.INVALID_DATA,
@@ -36,7 +42,10 @@ def convert_list_to_ndarray(data: list[Any]) -> npt.NDArray[Any]:
36
42
  f"Unable to construct signature: Ragged nested or Unsupported list-like data {data} confronted."
37
43
  ),
38
44
  )
39
- warnings.filterwarnings("default", category=np.VisibleDeprecationWarning)
45
+ finally:
46
+ if visible_deprecation_warning is not None:
47
+ warnings.filterwarnings("default", category=visible_deprecation_warning)
48
+
40
49
  if arr.dtype == object:
41
50
  # If not raised, then a array of object would be created.
42
51
  raise snowml_exceptions.SnowflakeMLException(
@@ -0,0 +1,2 @@
1
+ DEFAULT_CPU_COMPUTE_POOL = "SYSTEM_COMPUTE_POOL_CPU"
2
+ DEFAULT_GPU_COMPUTE_POOL = "SYSTEM_COMPUTE_POOL_GPU"