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.
- snowflake/ml/_internal/utils/url.py +42 -0
- snowflake/ml/jobs/__init__.py +2 -0
- snowflake/ml/jobs/_utils/constants.py +2 -0
- snowflake/ml/jobs/_utils/payload_utils.py +38 -18
- snowflake/ml/jobs/_utils/query_helper.py +8 -1
- snowflake/ml/jobs/_utils/runtime_env_utils.py +58 -4
- snowflake/ml/jobs/_utils/spec_utils.py +0 -31
- snowflake/ml/jobs/_utils/stage_utils.py +2 -2
- snowflake/ml/jobs/_utils/types.py +22 -2
- snowflake/ml/jobs/job_definition.py +232 -0
- snowflake/ml/jobs/manager.py +16 -177
- snowflake/ml/lineage/lineage_node.py +1 -1
- snowflake/ml/model/__init__.py +6 -0
- snowflake/ml/model/_client/model/batch_inference_specs.py +16 -1
- snowflake/ml/model/_client/model/model_version_impl.py +109 -32
- snowflake/ml/model/_client/ops/deployment_step.py +36 -0
- snowflake/ml/model/_client/ops/model_ops.py +45 -2
- snowflake/ml/model/_client/ops/param_utils.py +124 -0
- snowflake/ml/model/_client/ops/service_ops.py +81 -61
- snowflake/ml/model/_client/service/import_model_spec_schema.py +23 -0
- snowflake/ml/model/_client/service/model_deployment_spec.py +24 -9
- snowflake/ml/model/_client/service/model_deployment_spec_schema.py +4 -0
- snowflake/ml/model/_client/sql/model_version.py +30 -6
- snowflake/ml/model/_client/sql/service.py +30 -29
- snowflake/ml/model/_model_composer/model_composer.py +1 -1
- snowflake/ml/model/_model_composer/model_manifest/model_manifest_schema.py +5 -0
- snowflake/ml/model/_model_composer/model_method/infer_function.py_template +21 -3
- snowflake/ml/model/_model_composer/model_method/infer_partitioned.py_template +21 -3
- snowflake/ml/model/_model_composer/model_method/infer_table_function.py_template +21 -3
- snowflake/ml/model/_model_composer/model_method/model_method.py +62 -2
- snowflake/ml/model/_packager/model_handlers/custom.py +52 -0
- snowflake/ml/model/_packager/model_handlers/huggingface.py +54 -10
- snowflake/ml/model/_packager/model_handlers/sentence_transformers.py +52 -16
- snowflake/ml/model/_packager/model_handlers/xgboost.py +26 -1
- snowflake/ml/model/_packager/model_meta/model_meta.py +40 -7
- snowflake/ml/model/_packager/model_packager.py +1 -1
- snowflake/ml/model/_signatures/core.py +85 -0
- snowflake/ml/model/_signatures/utils.py +55 -0
- snowflake/ml/model/code_path.py +104 -0
- snowflake/ml/model/custom_model.py +55 -13
- snowflake/ml/model/model_signature.py +13 -1
- snowflake/ml/model/openai_signatures.py +97 -0
- snowflake/ml/model/type_hints.py +2 -0
- snowflake/ml/registry/_manager/model_manager.py +230 -15
- snowflake/ml/registry/_manager/model_parameter_reconciler.py +1 -1
- snowflake/ml/registry/registry.py +4 -4
- snowflake/ml/version.py +1 -1
- {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/METADATA +95 -1
- {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/RECORD +52 -46
- {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/WHEEL +0 -0
- {snowflake_ml_python-1.21.0.dist-info → snowflake_ml_python-1.23.0.dist-info}/licenses/LICENSE.txt +0 -0
- {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}
|
snowflake/ml/model/type_hints.py
CHANGED
|
@@ -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[
|
|
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[
|
|
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 =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
|
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[
|
|
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[
|
|
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.
|
|
2
|
+
VERSION = "1.23.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snowflake-ml-python
|
|
3
|
-
Version: 1.
|
|
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
|