snowflake-ml-python 1.21.0__py3-none-any.whl → 1.23.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/utils/url.py +42 -0
  2. snowflake/ml/jobs/__init__.py +2 -0
  3. snowflake/ml/jobs/_utils/constants.py +2 -0
  4. snowflake/ml/jobs/_utils/payload_utils.py +38 -18
  5. snowflake/ml/jobs/_utils/query_helper.py +8 -1
  6. snowflake/ml/jobs/_utils/runtime_env_utils.py +58 -4
  7. snowflake/ml/jobs/_utils/spec_utils.py +0 -31
  8. snowflake/ml/jobs/_utils/stage_utils.py +2 -2
  9. snowflake/ml/jobs/_utils/types.py +22 -2
  10. snowflake/ml/jobs/job_definition.py +232 -0
  11. snowflake/ml/jobs/manager.py +16 -177
  12. snowflake/ml/lineage/lineage_node.py +1 -1
  13. snowflake/ml/model/__init__.py +6 -0
  14. snowflake/ml/model/_client/model/batch_inference_specs.py +16 -1
  15. snowflake/ml/model/_client/model/model_version_impl.py +109 -32
  16. snowflake/ml/model/_client/ops/deployment_step.py +36 -0
  17. snowflake/ml/model/_client/ops/model_ops.py +45 -2
  18. snowflake/ml/model/_client/ops/param_utils.py +124 -0
  19. snowflake/ml/model/_client/ops/service_ops.py +81 -61
  20. snowflake/ml/model/_client/service/import_model_spec_schema.py +23 -0
  21. snowflake/ml/model/_client/service/model_deployment_spec.py +24 -9
  22. snowflake/ml/model/_client/service/model_deployment_spec_schema.py +4 -0
  23. snowflake/ml/model/_client/sql/model_version.py +30 -6
  24. snowflake/ml/model/_client/sql/service.py +30 -29
  25. snowflake/ml/model/_model_composer/model_composer.py +1 -1
  26. snowflake/ml/model/_model_composer/model_manifest/model_manifest_schema.py +5 -0
  27. snowflake/ml/model/_model_composer/model_method/infer_function.py_template +21 -3
  28. snowflake/ml/model/_model_composer/model_method/infer_partitioned.py_template +21 -3
  29. snowflake/ml/model/_model_composer/model_method/infer_table_function.py_template +21 -3
  30. snowflake/ml/model/_model_composer/model_method/model_method.py +62 -2
  31. snowflake/ml/model/_packager/model_handlers/custom.py +52 -0
  32. snowflake/ml/model/_packager/model_handlers/huggingface.py +54 -10
  33. snowflake/ml/model/_packager/model_handlers/sentence_transformers.py +52 -16
  34. snowflake/ml/model/_packager/model_handlers/xgboost.py +26 -1
  35. snowflake/ml/model/_packager/model_meta/model_meta.py +40 -7
  36. snowflake/ml/model/_packager/model_packager.py +1 -1
  37. snowflake/ml/model/_signatures/core.py +85 -0
  38. snowflake/ml/model/_signatures/utils.py +55 -0
  39. snowflake/ml/model/code_path.py +104 -0
  40. snowflake/ml/model/custom_model.py +55 -13
  41. snowflake/ml/model/model_signature.py +13 -1
  42. snowflake/ml/model/openai_signatures.py +97 -0
  43. snowflake/ml/model/type_hints.py +2 -0
  44. snowflake/ml/registry/_manager/model_manager.py +230 -15
  45. snowflake/ml/registry/_manager/model_parameter_reconciler.py +1 -1
  46. snowflake/ml/registry/registry.py +4 -4
  47. snowflake/ml/version.py +1 -1
  48. {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/METADATA +95 -1
  49. {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/RECORD +52 -46
  50. {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/WHEEL +0 -0
  51. {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/licenses/LICENSE.txt +0 -0
  52. {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/top_level.txt +0 -0
@@ -34,6 +34,9 @@ DataType = core.DataType
34
34
  BaseFeatureSpec = core.BaseFeatureSpec
35
35
  FeatureSpec = core.FeatureSpec
36
36
  FeatureGroupSpec = core.FeatureGroupSpec
37
+ BaseParamSpec = core.BaseParamSpec
38
+ ParamSpec = core.ParamSpec
39
+ ParamGroupSpec = core.ParamGroupSpec
37
40
  ModelSignature = core.ModelSignature
38
41
 
39
42
 
@@ -711,6 +714,7 @@ def infer_signature(
711
714
  output_feature_names: Optional[list[str]] = None,
712
715
  input_data_limit: Optional[int] = 100,
713
716
  output_data_limit: Optional[int] = 100,
717
+ params: Optional[Sequence[core.BaseParamSpec]] = None,
714
718
  ) -> core.ModelSignature:
715
719
  """
716
720
  Infer model signature from given input and output sample data.
@@ -740,12 +744,20 @@ def infer_signature(
740
744
  output_data_limit: Limit the number of rows to be used in signature inference in the output data. Defaults to
741
745
  100. If None, all rows are used. If the number of rows in the output data is less than the limit, all rows
742
746
  are used.
747
+ params: Optional sequence of parameter specifications to include in the signature. Parameters define
748
+ optional configuration values that can be passed to model inference. Defaults to None.
749
+
750
+ Raises:
751
+ SnowflakeMLException: ValueError: Raised when input data contains columns matching parameter names.
743
752
 
744
753
  Returns:
745
754
  A model signature inferred from the given input and output sample data.
755
+
756
+ # noqa: DAR402
746
757
  """
747
758
  inputs = _infer_signature(_truncate_data(input_data, input_data_limit), role="input")
748
759
  inputs = utils.rename_features(inputs, input_feature_names)
760
+
749
761
  outputs = _infer_signature(_truncate_data(output_data, output_data_limit), role="output")
750
762
  outputs = utils.rename_features(outputs, output_feature_names)
751
- return core.ModelSignature(inputs, outputs)
763
+ return core.ModelSignature(inputs, outputs, params=params)
@@ -1,6 +1,94 @@
1
1
  from snowflake.ml.model._signatures import core
2
2
 
3
3
  _OPENAI_CHAT_SIGNATURE_SPEC = core.ModelSignature(
4
+ inputs=[
5
+ core.FeatureGroupSpec(
6
+ name="messages",
7
+ specs=[
8
+ core.FeatureGroupSpec(
9
+ name="content",
10
+ specs=[
11
+ core.FeatureSpec(name="type", dtype=core.DataType.STRING),
12
+ # Text prompts
13
+ core.FeatureSpec(name="text", dtype=core.DataType.STRING),
14
+ # Image URL prompts
15
+ core.FeatureGroupSpec(
16
+ name="image_url",
17
+ specs=[
18
+ # Base64 encoded image URL or image URL
19
+ core.FeatureSpec(name="url", dtype=core.DataType.STRING),
20
+ # Image detail level (e.g., "low", "high", "auto")
21
+ core.FeatureSpec(name="detail", dtype=core.DataType.STRING),
22
+ ],
23
+ ),
24
+ # Video URL prompts
25
+ core.FeatureGroupSpec(
26
+ name="video_url",
27
+ specs=[
28
+ # Base64 encoded video URL
29
+ core.FeatureSpec(name="url", dtype=core.DataType.STRING),
30
+ ],
31
+ ),
32
+ # Audio prompts
33
+ core.FeatureGroupSpec(
34
+ name="input_audio",
35
+ specs=[
36
+ core.FeatureSpec(name="data", dtype=core.DataType.STRING),
37
+ core.FeatureSpec(name="format", dtype=core.DataType.STRING),
38
+ ],
39
+ ),
40
+ ],
41
+ shape=(-1,),
42
+ ),
43
+ core.FeatureSpec(name="name", dtype=core.DataType.STRING),
44
+ core.FeatureSpec(name="role", dtype=core.DataType.STRING),
45
+ core.FeatureSpec(name="title", dtype=core.DataType.STRING),
46
+ ],
47
+ shape=(-1,),
48
+ ),
49
+ core.FeatureSpec(name="temperature", dtype=core.DataType.DOUBLE),
50
+ core.FeatureSpec(name="max_completion_tokens", dtype=core.DataType.INT64),
51
+ core.FeatureSpec(name="stop", dtype=core.DataType.STRING, shape=(-1,)),
52
+ core.FeatureSpec(name="n", dtype=core.DataType.INT32),
53
+ core.FeatureSpec(name="stream", dtype=core.DataType.BOOL),
54
+ core.FeatureSpec(name="top_p", dtype=core.DataType.DOUBLE),
55
+ core.FeatureSpec(name="frequency_penalty", dtype=core.DataType.DOUBLE),
56
+ core.FeatureSpec(name="presence_penalty", dtype=core.DataType.DOUBLE),
57
+ ],
58
+ outputs=[
59
+ core.FeatureSpec(name="id", dtype=core.DataType.STRING),
60
+ core.FeatureSpec(name="object", dtype=core.DataType.STRING),
61
+ core.FeatureSpec(name="created", dtype=core.DataType.FLOAT),
62
+ core.FeatureSpec(name="model", dtype=core.DataType.STRING),
63
+ core.FeatureGroupSpec(
64
+ name="choices",
65
+ specs=[
66
+ core.FeatureSpec(name="index", dtype=core.DataType.INT32),
67
+ core.FeatureGroupSpec(
68
+ name="message",
69
+ specs=[
70
+ core.FeatureSpec(name="content", dtype=core.DataType.STRING),
71
+ core.FeatureSpec(name="name", dtype=core.DataType.STRING),
72
+ core.FeatureSpec(name="role", dtype=core.DataType.STRING),
73
+ ],
74
+ ),
75
+ core.FeatureSpec(name="logprobs", dtype=core.DataType.STRING),
76
+ core.FeatureSpec(name="finish_reason", dtype=core.DataType.STRING),
77
+ ],
78
+ shape=(-1,),
79
+ ),
80
+ core.FeatureGroupSpec(
81
+ name="usage",
82
+ specs=[
83
+ core.FeatureSpec(name="completion_tokens", dtype=core.DataType.INT32),
84
+ core.FeatureSpec(name="prompt_tokens", dtype=core.DataType.INT32),
85
+ core.FeatureSpec(name="total_tokens", dtype=core.DataType.INT32),
86
+ ],
87
+ ),
88
+ ],
89
+ )
90
+
91
+ _OPENAI_CHAT_SIGNATURE_SPEC_WITH_CONTENT_FORMAT_STRING = core.ModelSignature(
4
92
  inputs=[
5
93
  core.FeatureGroupSpec(
6
94
  name="messages",
@@ -54,4 +142,13 @@ _OPENAI_CHAT_SIGNATURE_SPEC = core.ModelSignature(
54
142
  ],
55
143
  )
56
144
 
145
+
146
+ # Refer vLLM documentation: https://docs.vllm.ai/en/stable/serving/openai_compatible_server/#chat-template
147
+
148
+ # Use this if you prefer to use the content format string instead of the default ChatML template.
149
+ # Most models support this.
150
+ OPENAI_CHAT_SIGNATURE_WITH_CONTENT_FORMAT_STRING = {"__call__": _OPENAI_CHAT_SIGNATURE_SPEC_WITH_CONTENT_FORMAT_STRING}
151
+
152
+ # This is the default signature.
153
+ # The content format allows vLLM to handler content parts like text, image, video, audio, file, etc.
57
154
  OPENAI_CHAT_SIGNATURE = {"__call__": _OPENAI_CHAT_SIGNATURE_SPEC}
@@ -13,6 +13,7 @@ from typing import (
13
13
  import numpy.typing as npt
14
14
  from typing_extensions import NotRequired
15
15
 
16
+ from snowflake.ml.model.code_path import CodePath
16
17
  from snowflake.ml.model.compute_pool import (
17
18
  DEFAULT_CPU_COMPUTE_POOL,
18
19
  DEFAULT_GPU_COMPUTE_POOL,
@@ -366,6 +367,7 @@ ModelLoadOption = Union[
366
367
 
367
368
 
368
369
  SupportedTargetPlatformType = Union[TargetPlatform, str]
370
+ CodePathLike = Union[str, CodePath]
369
371
 
370
372
 
371
373
  class ProgressStatus(Protocol):
@@ -1,8 +1,10 @@
1
+ import json
1
2
  import logging
2
3
  from types import ModuleType
3
4
  from typing import TYPE_CHECKING, Any, Optional, Union
4
5
 
5
6
  import pandas as pd
7
+ import yaml
6
8
 
7
9
  from snowflake.ml._internal import platform_capabilities, telemetry
8
10
  from snowflake.ml._internal.exceptions import error_codes, exceptions
@@ -11,8 +13,13 @@ from snowflake.ml._internal.utils import sql_identifier
11
13
  from snowflake.ml.model import model_signature, task, type_hints
12
14
  from snowflake.ml.model._client.model import model_impl, model_version_impl
13
15
  from snowflake.ml.model._client.ops import metadata_ops, model_ops, service_ops
16
+ from snowflake.ml.model._client.service import (
17
+ import_model_spec_schema,
18
+ model_deployment_spec_schema,
19
+ )
14
20
  from snowflake.ml.model._model_composer import model_composer
15
21
  from snowflake.ml.model._packager.model_meta import model_meta
22
+ from snowflake.ml.model.models import huggingface
16
23
  from snowflake.ml.registry._manager import model_parameter_reconciler
17
24
  from snowflake.snowpark import exceptions as snowpark_exceptions, session
18
25
  from snowflake.snowpark._internal import utils as snowpark_utils
@@ -59,7 +66,7 @@ class ModelManager:
59
66
  signatures: Optional[dict[str, model_signature.ModelSignature]] = None,
60
67
  sample_input_data: Optional[type_hints.SupportedDataType] = None,
61
68
  user_files: Optional[dict[str, list[str]]] = None,
62
- code_paths: Optional[list[str]] = None,
69
+ code_paths: Optional[list[type_hints.CodePathLike]] = None,
63
70
  ext_modules: Optional[list[ModuleType]] = None,
64
71
  task: type_hints.Task = task.Task.UNKNOWN,
65
72
  experiment_info: Optional["ExperimentInfo"] = None,
@@ -170,7 +177,7 @@ class ModelManager:
170
177
  signatures: Optional[dict[str, model_signature.ModelSignature]] = None,
171
178
  sample_input_data: Optional[type_hints.SupportedDataType] = None,
172
179
  user_files: Optional[dict[str, list[str]]] = None,
173
- code_paths: Optional[list[str]] = None,
180
+ code_paths: Optional[list[type_hints.CodePathLike]] = None,
174
181
  ext_modules: Optional[list[ModuleType]] = None,
175
182
  task: type_hints.Task = task.Task.UNKNOWN,
176
183
  experiment_info: Optional["ExperimentInfo"] = None,
@@ -180,6 +187,31 @@ class ModelManager:
180
187
  database_name_id, schema_name_id, model_name_id = sql_identifier.parse_fully_qualified_name(model_name)
181
188
  version_name_id = sql_identifier.SqlIdentifier(version_name)
182
189
 
190
+ # Check if model is HuggingFace TransformersPipeline with no repo_snapshot_dir
191
+ # If so, use remote logging via SYSTEM$IMPORT_MODEL
192
+ if (
193
+ isinstance(model, huggingface.TransformersPipeline)
194
+ and model.compute_pool_for_log is not None
195
+ and (not hasattr(model, "repo_snapshot_dir") or model.repo_snapshot_dir is None)
196
+ ):
197
+ logger.info("HuggingFace model has compute_pool_for_log, using remote logging")
198
+ return self._remote_log_huggingface_model(
199
+ model=model,
200
+ model_name=model_name,
201
+ version_name=version_name,
202
+ database_name_id=database_name_id,
203
+ schema_name_id=schema_name_id,
204
+ model_name_id=model_name_id,
205
+ version_name_id=version_name_id,
206
+ comment=comment,
207
+ conda_dependencies=conda_dependencies,
208
+ pip_requirements=pip_requirements,
209
+ target_platforms=target_platforms,
210
+ options=options,
211
+ statement_params=statement_params,
212
+ progress_status=progress_status,
213
+ )
214
+
183
215
  # TODO(SNOW-2091317): Remove this when the snowpark enables file PUT operation for snowurls
184
216
  use_live_commit = (
185
217
  not snowpark_utils.is_in_stored_procedure() # type: ignore[no-untyped-call]
@@ -298,19 +330,11 @@ class ModelManager:
298
330
  use_live_commit=use_live_commit,
299
331
  )
300
332
 
301
- mv = model_version_impl.ModelVersion._ref(
302
- model_ops=model_ops.ModelOperator(
303
- self._model_ops._session,
304
- database_name=database_name_id or self._database_name,
305
- schema_name=schema_name_id or self._schema_name,
306
- ),
307
- service_ops=service_ops.ServiceOperator(
308
- self._service_ops._session,
309
- database_name=database_name_id or self._database_name,
310
- schema_name=schema_name_id or self._schema_name,
311
- ),
312
- model_name=model_name_id,
313
- version_name=version_name_id,
333
+ mv = self._create_model_version_ref(
334
+ database_name_id=database_name_id,
335
+ schema_name_id=schema_name_id,
336
+ model_name_id=model_name_id,
337
+ version_name_id=version_name_id,
314
338
  )
315
339
 
316
340
  progress_status.update("setting model metadata...")
@@ -333,6 +357,73 @@ class ModelManager:
333
357
 
334
358
  return mv
335
359
 
360
+ def _remote_log_huggingface_model(
361
+ self,
362
+ model: huggingface.TransformersPipeline,
363
+ model_name: str,
364
+ version_name: str,
365
+ database_name_id: Optional[sql_identifier.SqlIdentifier],
366
+ schema_name_id: Optional[sql_identifier.SqlIdentifier],
367
+ model_name_id: sql_identifier.SqlIdentifier,
368
+ version_name_id: sql_identifier.SqlIdentifier,
369
+ comment: Optional[str],
370
+ conda_dependencies: Optional[list[str]],
371
+ pip_requirements: Optional[list[str]],
372
+ target_platforms: Optional[list[type_hints.SupportedTargetPlatformType]],
373
+ options: Optional[type_hints.ModelSaveOption],
374
+ statement_params: Optional[dict[str, Any]],
375
+ progress_status: type_hints.ProgressStatus,
376
+ ) -> model_version_impl.ModelVersion:
377
+ """Log HuggingFace model remotely using SYSTEM$IMPORT_MODEL."""
378
+ if not isinstance(model, huggingface.TransformersPipeline):
379
+ raise ValueError(
380
+ f"Model must be a TransformersPipeline object. The provided model is a {type(model)} object"
381
+ )
382
+ progress_status.update("preparing remote model logging...")
383
+ progress_status.increment()
384
+
385
+ # Get compute pool from options or use default
386
+ compute_pool = model.compute_pool_for_log
387
+ if compute_pool is None:
388
+ raise ValueError("compute_pool_for_log is required for remote logging")
389
+
390
+ # Construct fully qualified model name
391
+ db_name = database_name_id.identifier() if database_name_id else self._database_name.identifier()
392
+ schema_name = schema_name_id.identifier() if schema_name_id else self._schema_name.identifier()
393
+ fq_model_name = f"{db_name}.{schema_name}.{model_name_id.identifier()}"
394
+
395
+ # Build YAML spec for import model
396
+ yaml_content = self._build_import_model_yaml_spec(
397
+ model=model,
398
+ fq_model_name=fq_model_name,
399
+ version_name=version_name,
400
+ compute_pool=compute_pool,
401
+ comment=comment,
402
+ conda_dependencies=conda_dependencies,
403
+ pip_requirements=pip_requirements,
404
+ target_platforms=target_platforms,
405
+ )
406
+
407
+ progress_status.update("Remotely logging the model...")
408
+ progress_status.increment()
409
+
410
+ self._model_ops.run_import_model_query(
411
+ database_name=db_name,
412
+ schema_name=schema_name,
413
+ yaml_content=yaml_content,
414
+ statement_params=statement_params,
415
+ )
416
+ progress_status.update("Remotely logged the model")
417
+ progress_status.increment()
418
+
419
+ # Return ModelVersion object
420
+ return self._create_model_version_ref(
421
+ database_name_id=database_name_id,
422
+ schema_name_id=schema_name_id,
423
+ model_name_id=model_name_id,
424
+ version_name_id=version_name_id,
425
+ )
426
+
336
427
  def get_model(
337
428
  self,
338
429
  model_name: str,
@@ -408,6 +499,130 @@ class ModelManager:
408
499
  statement_params=statement_params,
409
500
  )
410
501
 
502
+ def _create_model_version_ref(
503
+ self,
504
+ database_name_id: Optional[sql_identifier.SqlIdentifier],
505
+ schema_name_id: Optional[sql_identifier.SqlIdentifier],
506
+ model_name_id: sql_identifier.SqlIdentifier,
507
+ version_name_id: sql_identifier.SqlIdentifier,
508
+ ) -> model_version_impl.ModelVersion:
509
+ """Create a ModelVersion reference object.
510
+
511
+ Args:
512
+ database_name_id: Database name identifier, falls back to instance database if None.
513
+ schema_name_id: Schema name identifier, falls back to instance schema if None.
514
+ model_name_id: Model name identifier.
515
+ version_name_id: Version name identifier.
516
+
517
+ Returns:
518
+ ModelVersion reference object.
519
+ """
520
+ return model_version_impl.ModelVersion._ref(
521
+ model_ops=model_ops.ModelOperator(
522
+ self._model_ops._session,
523
+ database_name=database_name_id or self._database_name,
524
+ schema_name=schema_name_id or self._schema_name,
525
+ ),
526
+ service_ops=service_ops.ServiceOperator(
527
+ self._service_ops._session,
528
+ database_name=database_name_id or self._database_name,
529
+ schema_name=schema_name_id or self._schema_name,
530
+ ),
531
+ model_name=model_name_id,
532
+ version_name=version_name_id,
533
+ )
534
+
535
+ def _build_import_model_yaml_spec(
536
+ self,
537
+ model: huggingface.TransformersPipeline,
538
+ fq_model_name: str,
539
+ version_name: str,
540
+ compute_pool: str,
541
+ comment: Optional[str],
542
+ conda_dependencies: Optional[list[str]],
543
+ pip_requirements: Optional[list[str]],
544
+ target_platforms: Optional[list[type_hints.SupportedTargetPlatformType]],
545
+ ) -> str:
546
+ """Build YAML spec for SYSTEM$IMPORT_MODEL.
547
+
548
+ Args:
549
+ model: HuggingFace TransformersPipeline model.
550
+ fq_model_name: Fully qualified model name.
551
+ version_name: Model version name.
552
+ compute_pool: Compute pool name.
553
+ comment: Optional comment for the model.
554
+ conda_dependencies: Optional conda dependencies.
555
+ pip_requirements: Optional pip requirements.
556
+ target_platforms: Optional target platforms.
557
+
558
+ Returns:
559
+ YAML string representing the import model spec.
560
+ """
561
+ # Convert target_platforms to list of strings
562
+ target_platforms_list = self._convert_target_platforms_to_list(target_platforms)
563
+
564
+ # Build HuggingFaceModel spec
565
+ hf_model = model_deployment_spec_schema.HuggingFaceModel(
566
+ hf_model_name=model.model,
567
+ task=model.task,
568
+ tokenizer=getattr(model, "tokenizer", None),
569
+ token_secret_object=model.secret_identifier,
570
+ trust_remote_code=model.trust_remote_code if model.trust_remote_code is not None else False,
571
+ revision=model.revision,
572
+ hf_model_kwargs=json.dumps(model.model_kwargs) if model.model_kwargs else "{}",
573
+ )
574
+
575
+ # Build LogModelArgs
576
+ log_model_args = model_deployment_spec_schema.LogModelArgs(
577
+ pip_requirements=pip_requirements,
578
+ conda_dependencies=conda_dependencies,
579
+ target_platforms=target_platforms_list,
580
+ comment=comment,
581
+ )
582
+
583
+ # Build ModelSpec
584
+ model_spec = import_model_spec_schema.ModelSpec(
585
+ name=import_model_spec_schema.ModelName(
586
+ model_name=fq_model_name,
587
+ version_name=version_name,
588
+ ),
589
+ hf_model=hf_model,
590
+ log_model_args=log_model_args,
591
+ )
592
+
593
+ # Build ImportModelSpec
594
+ import_spec = import_model_spec_schema.ImportModelSpec(
595
+ compute_pool=compute_pool,
596
+ models=[model_spec],
597
+ )
598
+
599
+ # Convert to YAML
600
+ return yaml.safe_dump(import_spec.model_dump(exclude_none=True))
601
+
602
+ def _convert_target_platforms_to_list(
603
+ self, target_platforms: Optional[list[type_hints.SupportedTargetPlatformType]]
604
+ ) -> Optional[list[str]]:
605
+ """Convert target_platforms to list of strings.
606
+
607
+ Args:
608
+ target_platforms: List of target platforms (enums or strings).
609
+
610
+ Returns:
611
+ List of platform strings, or None if input is None.
612
+ """
613
+ if not target_platforms:
614
+ return None
615
+
616
+ target_platforms_list = []
617
+ for tp in target_platforms:
618
+ if hasattr(tp, "value"):
619
+ # It's an enum, get the value
620
+ target_platforms_list.append(tp.value)
621
+ else:
622
+ # It's already a string
623
+ target_platforms_list.append(str(tp))
624
+ return target_platforms_list
625
+
411
626
  def _parse_fully_qualified_name(
412
627
  self, model_name: str
413
628
  ) -> tuple[
@@ -126,7 +126,7 @@ class ModelParameterReconciler:
126
126
  # Default the target platform to SPCS if not specified when running in ML runtime
127
127
  if env.IN_ML_RUNTIME:
128
128
  logger.info(
129
- "Logging the model on Container Runtime for ML without specifying `target_platforms`. "
129
+ "Logging the model on Container Runtime without specifying `target_platforms`. "
130
130
  'Default to `target_platforms=["SNOWPARK_CONTAINER_SERVICES"]`.'
131
131
  )
132
132
  return [target_platform.TargetPlatform.SNOWPARK_CONTAINER_SERVICES]
@@ -146,7 +146,7 @@ class Registry:
146
146
  signatures: Optional[dict[str, model_signature.ModelSignature]] = None,
147
147
  sample_input_data: Optional[type_hints.SupportedDataType] = None,
148
148
  user_files: Optional[dict[str, list[str]]] = None,
149
- code_paths: Optional[list[str]] = None,
149
+ code_paths: Optional[list[type_hints.CodePathLike]] = None,
150
150
  ext_modules: Optional[list[ModuleType]] = None,
151
151
  task: task.Task = task.Task.UNKNOWN,
152
152
  options: Optional[type_hints.ModelSaveOption] = None,
@@ -200,7 +200,7 @@ class Registry:
200
200
  It would also be used as background data in explanation and to capture data lineage. Defaults to None.
201
201
  user_files: Dictionary where the keys are subdirectories, and values are lists of local file name
202
202
  strings. The local file name strings can include wildcards (? or *) for matching multiple files.
203
- code_paths: List of directories containing code to import. Defaults to None.
203
+ code_paths: List of directories or CodePath objects containing code to import. Defaults to None.
204
204
  ext_modules: List of external modules to pickle with the model object.
205
205
  Only supported when logging the following types of model:
206
206
  Scikit-learn, Snowpark ML, PyTorch, TorchScript and Custom Model. Defaults to None.
@@ -298,7 +298,7 @@ class Registry:
298
298
  signatures: Optional[dict[str, model_signature.ModelSignature]] = None,
299
299
  sample_input_data: Optional[type_hints.SupportedDataType] = None,
300
300
  user_files: Optional[dict[str, list[str]]] = None,
301
- code_paths: Optional[list[str]] = None,
301
+ code_paths: Optional[list[type_hints.CodePathLike]] = None,
302
302
  ext_modules: Optional[list[ModuleType]] = None,
303
303
  task: task.Task = task.Task.UNKNOWN,
304
304
  options: Optional[type_hints.ModelSaveOption] = None,
@@ -352,7 +352,7 @@ class Registry:
352
352
  It would also be used as background data in explanation and to capture data lineage. Defaults to None.
353
353
  user_files: Dictionary where the keys are subdirectories, and values are lists of local file name
354
354
  strings. The local file name strings can include wildcards (? or *) for matching multiple files.
355
- code_paths: List of directories containing code to import. Defaults to None.
355
+ code_paths: List of directories or CodePath objects containing code to import. Defaults to None.
356
356
  ext_modules: List of external modules to pickle with the model object.
357
357
  Only supported when logging the following types of model:
358
358
  Scikit-learn, Snowpark ML, PyTorch, TorchScript and Custom Model. Defaults to None.
snowflake/ml/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # This is parsed by regex in conda recipe meta file. Make sure not to break it.
2
- VERSION = "1.21.0"
2
+ VERSION = "1.23.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snowflake-ml-python
3
- Version: 1.21.0
3
+ Version: 1.23.0
4
4
  Summary: The machine learning client library that is used for interacting with Snowflake to build machine learning solutions.
5
5
  Author-email: "Snowflake, Inc" <support@snowflake.com>
6
6
  License:
@@ -417,6 +417,100 @@ NOTE: Version 1.7.0 is used as example here. Please choose the the latest versio
417
417
 
418
418
  # Release History
419
419
 
420
+ ## 1.23.0
421
+
422
+ ### New Features
423
+
424
+ * ML Jobs: Enabled support for Python 3.11 and Python 3.12 by default. Jobs will automatically select a
425
+ runtime environment matching the client Python version.
426
+
427
+ ### Bug Fixes
428
+
429
+ * Registry: Fix failures on empty output in HuggingFace's Token Classification (aka Named Entity Recognition) models.
430
+ * Model serving: don't parse instance or container statuses to fix bug with missing container status.
431
+
432
+ ### Behavior Changes
433
+
434
+ ### Deprecations
435
+
436
+ ## 1.22.0
437
+
438
+ ### New Features
439
+
440
+ * Registry: Introducing remotely logging a transformer pipeline model using SPCS job.
441
+
442
+ ```python
443
+ # create reference of the model
444
+ model = huggingface.TransformersPipeline(
445
+ model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
446
+ task="text-generation",
447
+ )
448
+
449
+ # Remotely log the model, a SPCS job will run async and log the model
450
+ mv = registry.log_model(
451
+ model=model,
452
+ model_name="tinyllama_remote_log",
453
+ target_platforms=["SNOWPARK_CONTAINER_SERVICES"],
454
+ signatures=openai_signatures.OPENAI_CHAT_SIGNATURE,
455
+ )
456
+ ```
457
+
458
+ * Registry: Added support for `image-text-to-text` task type in `huggingface.TransformersPipeline`.
459
+ Note: Requires vLLM inference engine while creating the service.
460
+
461
+ ### Bug Fixes
462
+
463
+ ### Behavior Changes
464
+
465
+ * Registry: The `openai_signatures.OPENAI_CHAT_SIGNATURE` signature now handles content parts and
466
+ requires input data to be passed in list of dictionary. To use string content (previous behavior),
467
+ use `openai_signatures.OPENAI_CHAT_SIGNATURE_WITH_CONTENT_FORMAT_STRING`.
468
+
469
+ ```python
470
+ from snowflake.ml.model import openai_signatures
471
+ import pandas as pd
472
+
473
+ mv = snowflake_registry.log_model(
474
+ model=generator,
475
+ model_name=...,
476
+ ...,
477
+ signatures=openai_signatures.OPENAI_CHAT_SIGNATURE,
478
+ )
479
+
480
+ # create a pd.DataFrame with openai.client.chat.completions arguments like below:
481
+ x_df = pd.DataFrame.from_records(
482
+ [
483
+ {
484
+ "messages": [
485
+ {"role": "system", "content": "Complete the sentence."},
486
+ {
487
+ "role": "user",
488
+ "content": [
489
+ {
490
+ "type": "text",
491
+ "text": "A descendant of the Lost City of Atlantis, who swam to Earth while saying, ",
492
+ }
493
+ ],
494
+ },
495
+ ],
496
+ "max_completion_tokens": 250,
497
+ "temperature": 0.9,
498
+ "stop": None,
499
+ "n": 3,
500
+ "stream": False,
501
+ "top_p": 1.0,
502
+ "frequency_penalty": 0.1,
503
+ "presence_penalty": 0.2,
504
+ }
505
+ ],
506
+ )
507
+
508
+ # OpenAI Chat Completion compatible output
509
+ output_df = mv.run(X=x_df)
510
+ ```
511
+
512
+ ### Deprecations
513
+
420
514
  ## 1.21.0
421
515
 
422
516
  ### New Features