snowflake-ml-python 1.22.0__py3-none-any.whl → 1.24.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/platform_capabilities.py +0 -4
- snowflake/ml/feature_store/__init__.py +2 -0
- snowflake/ml/feature_store/aggregation.py +367 -0
- snowflake/ml/feature_store/feature.py +366 -0
- snowflake/ml/feature_store/feature_store.py +234 -20
- snowflake/ml/feature_store/feature_view.py +189 -4
- snowflake/ml/feature_store/metadata_manager.py +425 -0
- snowflake/ml/feature_store/tile_sql_generator.py +1079 -0
- snowflake/ml/jobs/__init__.py +2 -0
- snowflake/ml/jobs/_utils/constants.py +1 -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 +117 -0
- 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/model/__init__.py +4 -0
- snowflake/ml/model/_client/model/batch_inference_specs.py +38 -2
- snowflake/ml/model/_client/model/model_version_impl.py +120 -89
- snowflake/ml/model/_client/ops/model_ops.py +4 -26
- snowflake/ml/model/_client/ops/param_utils.py +124 -0
- snowflake/ml/model/_client/ops/service_ops.py +63 -23
- snowflake/ml/model/_client/service/model_deployment_spec.py +12 -5
- snowflake/ml/model/_client/service/model_deployment_spec_schema.py +1 -0
- snowflake/ml/model/_client/sql/service.py +25 -54
- 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 +3 -1
- snowflake/ml/model/_packager/model_handlers/huggingface.py +74 -10
- snowflake/ml/model/_packager/model_handlers/sentence_transformers.py +121 -29
- snowflake/ml/model/_signatures/utils.py +130 -0
- snowflake/ml/model/openai_signatures.py +97 -0
- snowflake/ml/registry/_manager/model_parameter_reconciler.py +1 -1
- snowflake/ml/version.py +1 -1
- {snowflake_ml_python-1.22.0.dist-info → snowflake_ml_python-1.24.0.dist-info}/METADATA +105 -1
- {snowflake_ml_python-1.22.0.dist-info → snowflake_ml_python-1.24.0.dist-info}/RECORD +41 -35
- {snowflake_ml_python-1.22.0.dist-info → snowflake_ml_python-1.24.0.dist-info}/WHEEL +1 -1
- snowflake/ml/experiment/callback/__init__.py +0 -0
- {snowflake_ml_python-1.22.0.dist-info → snowflake_ml_python-1.24.0.dist-info}/licenses/LICENSE.txt +0 -0
- {snowflake_ml_python-1.22.0.dist-info → snowflake_ml_python-1.24.0.dist-info}/top_level.txt +0 -0
|
@@ -16,6 +16,7 @@ from snowflake.ml.model._packager.model_meta import (
|
|
|
16
16
|
model_meta as model_meta_api,
|
|
17
17
|
model_meta_schema,
|
|
18
18
|
)
|
|
19
|
+
from snowflake.ml.model._signatures import utils as model_signature_utils
|
|
19
20
|
from snowflake.snowpark._internal import utils as snowpark_utils
|
|
20
21
|
|
|
21
22
|
if TYPE_CHECKING:
|
|
@@ -23,24 +24,59 @@ if TYPE_CHECKING:
|
|
|
23
24
|
|
|
24
25
|
logger = logging.getLogger(__name__)
|
|
25
26
|
|
|
27
|
+
# Allowlist of supported target methods for SentenceTransformer models.
|
|
28
|
+
# Note: sentence-transformers >= 3.0 uses singular names (encode_query, encode_document)
|
|
29
|
+
# while older versions may use plural names (encode_queries, encode_documents).
|
|
30
|
+
_ALLOWED_TARGET_METHODS = ["encode", "encode_query", "encode_document", "encode_queries", "encode_documents"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _validate_sentence_transformers_signatures(
|
|
34
|
+
sigs: dict[str, model_signature.ModelSignature],
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Validate signatures for SentenceTransformer models.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
sigs: Dictionary mapping method names to their signatures.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
ValueError: If signatures are empty, contain unsupported methods, or violate per-method constraints.
|
|
43
|
+
"""
|
|
44
|
+
# Check that signatures are non-empty
|
|
45
|
+
if not sigs:
|
|
46
|
+
raise ValueError("At least one signature must be provided.")
|
|
47
|
+
|
|
48
|
+
# Check that all methods are in the allowlist
|
|
49
|
+
unsupported_methods = set(sigs.keys()) - set(_ALLOWED_TARGET_METHODS)
|
|
50
|
+
if unsupported_methods:
|
|
51
|
+
raise ValueError(
|
|
52
|
+
f"Unsupported target methods: {sorted(unsupported_methods)}. "
|
|
53
|
+
f"Supported methods are: {_ALLOWED_TARGET_METHODS}."
|
|
54
|
+
)
|
|
26
55
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if len(sigs["encode"].inputs) != 1:
|
|
32
|
-
raise ValueError("SentenceTransformer can only accept 1 input column")
|
|
56
|
+
# Validate per-method constraints
|
|
57
|
+
for method_name, sig in sigs.items():
|
|
58
|
+
if len(sig.inputs) != 1:
|
|
59
|
+
raise ValueError(f"SentenceTransformer method '{method_name}' must have exactly 1 input column.")
|
|
33
60
|
|
|
34
|
-
|
|
35
|
-
|
|
61
|
+
if len(sig.outputs) != 1:
|
|
62
|
+
raise ValueError(f"SentenceTransformer method '{method_name}' must have exactly 1 output column.")
|
|
36
63
|
|
|
37
|
-
|
|
64
|
+
# FeatureSpec is expected here; FeatureGroupSpec would indicate a nested/grouped input
|
|
65
|
+
# which SentenceTransformer does not support.
|
|
66
|
+
if not isinstance(sig.inputs[0], model_signature.FeatureSpec):
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"SentenceTransformer method '{method_name}' requires a FeatureSpec input, "
|
|
69
|
+
f"got {type(sig.inputs[0]).__name__}."
|
|
70
|
+
)
|
|
38
71
|
|
|
39
|
-
|
|
40
|
-
|
|
72
|
+
if sig.inputs[0]._shape is not None:
|
|
73
|
+
raise ValueError(f"SentenceTransformer method '{method_name}' does not support input shape.")
|
|
41
74
|
|
|
42
|
-
|
|
43
|
-
|
|
75
|
+
if sig.inputs[0]._dtype != model_signature.DataType.STRING:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
f"SentenceTransformer method '{method_name}' only accepts STRING input, "
|
|
78
|
+
f"got {sig.inputs[0]._dtype.name}."
|
|
79
|
+
)
|
|
44
80
|
|
|
45
81
|
|
|
46
82
|
@final
|
|
@@ -51,7 +87,9 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
51
87
|
_HANDLER_MIGRATOR_PLANS: dict[str, type[base_migrator.BaseModelHandlerMigrator]] = {}
|
|
52
88
|
|
|
53
89
|
MODEL_BLOB_FILE_OR_DIR = "model"
|
|
54
|
-
|
|
90
|
+
# Default to singular names which are used in sentence-transformers >= 3.0
|
|
91
|
+
DEFAULT_TARGET_METHODS = ["encode", "encode_query", "encode_document"]
|
|
92
|
+
IS_AUTO_SIGNATURE = True
|
|
55
93
|
|
|
56
94
|
@classmethod
|
|
57
95
|
def can_handle(
|
|
@@ -98,11 +136,17 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
98
136
|
target_methods=kwargs.pop("target_methods", None),
|
|
99
137
|
default_target_methods=cls.DEFAULT_TARGET_METHODS,
|
|
100
138
|
)
|
|
101
|
-
|
|
102
|
-
|
|
139
|
+
|
|
140
|
+
# Validate target_methods
|
|
141
|
+
if not target_methods:
|
|
142
|
+
raise ValueError("At least one target method must be specified.")
|
|
143
|
+
|
|
144
|
+
if not set(target_methods).issubset(_ALLOWED_TARGET_METHODS):
|
|
145
|
+
raise ValueError(f"target_methods {target_methods} must be a subset of {_ALLOWED_TARGET_METHODS}.")
|
|
103
146
|
|
|
104
147
|
def get_prediction(
|
|
105
|
-
target_method_name: str,
|
|
148
|
+
target_method_name: str,
|
|
149
|
+
sample_input_data: model_types.SupportedLocalDataType,
|
|
106
150
|
) -> model_types.SupportedLocalDataType:
|
|
107
151
|
if not isinstance(sample_input_data, pd.DataFrame):
|
|
108
152
|
sample_input_data = model_signature._convert_local_data_to_df(data=sample_input_data)
|
|
@@ -113,8 +157,13 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
113
157
|
)
|
|
114
158
|
X_list = sample_input_data.iloc[:, 0].tolist()
|
|
115
159
|
|
|
116
|
-
|
|
117
|
-
|
|
160
|
+
# Call the appropriate method based on target_method_name
|
|
161
|
+
method_to_call = getattr(model, target_method_name, None)
|
|
162
|
+
if not callable(method_to_call):
|
|
163
|
+
raise ValueError(
|
|
164
|
+
f"SentenceTransformer model does not have a callable method '{target_method_name}'."
|
|
165
|
+
)
|
|
166
|
+
return pd.DataFrame({0: method_to_call(X_list, batch_size=batch_size).tolist()})
|
|
118
167
|
|
|
119
168
|
if model_meta.signatures:
|
|
120
169
|
handlers_utils.validate_target_methods(model, list(model_meta.signatures.keys()))
|
|
@@ -135,6 +184,36 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
135
184
|
sample_input_data=sample_input_data,
|
|
136
185
|
get_prediction_fn=get_prediction,
|
|
137
186
|
)
|
|
187
|
+
else:
|
|
188
|
+
# Auto-infer signature from model when no sample_input_data is provided
|
|
189
|
+
# Get the embedding dimension from the model
|
|
190
|
+
embedding_dim = model.get_sentence_embedding_dimension()
|
|
191
|
+
if embedding_dim is None:
|
|
192
|
+
raise ValueError(
|
|
193
|
+
"Unable to auto-infer signature: model.get_sentence_embedding_dimension() returned None. "
|
|
194
|
+
"Please provide sample_input_data or signatures explicitly."
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
for target_method in target_methods:
|
|
198
|
+
# target_methods are already validated as callable by get_target_methods()
|
|
199
|
+
inferred_sig = model_signature_utils.sentence_transformers_signature_auto_infer(
|
|
200
|
+
target_method=target_method,
|
|
201
|
+
embedding_dim=embedding_dim,
|
|
202
|
+
)
|
|
203
|
+
if inferred_sig is None:
|
|
204
|
+
raise ValueError(
|
|
205
|
+
f"Unable to auto-infer signature for method '{target_method}'. "
|
|
206
|
+
"Please provide sample_input_data or signatures explicitly."
|
|
207
|
+
)
|
|
208
|
+
model_meta.signatures[target_method] = inferred_sig
|
|
209
|
+
|
|
210
|
+
# Ensure at least one method was successfully inferred
|
|
211
|
+
if not model_meta.signatures:
|
|
212
|
+
raise ValueError(
|
|
213
|
+
"No valid target methods found on the model. "
|
|
214
|
+
"Please provide sample_input_data or signatures explicitly, "
|
|
215
|
+
"or specify target_methods that exist on your model."
|
|
216
|
+
)
|
|
138
217
|
|
|
139
218
|
_validate_sentence_transformers_signatures(model_meta.signatures)
|
|
140
219
|
|
|
@@ -160,7 +239,10 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
160
239
|
|
|
161
240
|
model_meta.env.include_if_absent(
|
|
162
241
|
[
|
|
163
|
-
model_env.ModelDependency(
|
|
242
|
+
model_env.ModelDependency(
|
|
243
|
+
requirement="sentence-transformers",
|
|
244
|
+
pip_name="sentence-transformers",
|
|
245
|
+
),
|
|
164
246
|
model_env.ModelDependency(requirement="transformers", pip_name="transformers"),
|
|
165
247
|
model_env.ModelDependency(requirement="pytorch", pip_name="torch"),
|
|
166
248
|
],
|
|
@@ -169,7 +251,9 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
169
251
|
model_meta.env.cuda_version = kwargs.get("cuda_version", handlers_utils.get_default_cuda_version())
|
|
170
252
|
|
|
171
253
|
@staticmethod
|
|
172
|
-
def _get_device_config(
|
|
254
|
+
def _get_device_config(
|
|
255
|
+
**kwargs: Unpack[model_types.SentenceTransformersLoadOptions],
|
|
256
|
+
) -> Optional[str]:
|
|
173
257
|
if kwargs.get("device", None) is not None:
|
|
174
258
|
return kwargs["device"]
|
|
175
259
|
elif kwargs.get("use_gpu", False):
|
|
@@ -226,7 +310,8 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
226
310
|
model_meta: model_meta_api.ModelMetadata,
|
|
227
311
|
) -> type[custom_model.CustomModel]:
|
|
228
312
|
batch_size = cast(
|
|
229
|
-
model_meta_schema.SentenceTransformersModelBlobOptions,
|
|
313
|
+
model_meta_schema.SentenceTransformersModelBlobOptions,
|
|
314
|
+
model_meta.models[model_meta.name].options,
|
|
230
315
|
).get("batch_size", None)
|
|
231
316
|
|
|
232
317
|
def get_prediction(
|
|
@@ -234,22 +319,30 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
234
319
|
signature: model_signature.ModelSignature,
|
|
235
320
|
target_method: str,
|
|
236
321
|
) -> Callable[[custom_model.CustomModel, pd.DataFrame], pd.DataFrame]:
|
|
322
|
+
# Capture target_method in closure to call the correct model method
|
|
323
|
+
method_to_call = getattr(raw_model, target_method, None)
|
|
324
|
+
if not callable(method_to_call):
|
|
325
|
+
raise ValueError(
|
|
326
|
+
f"SentenceTransformer model does not have a callable method '{target_method}'. "
|
|
327
|
+
f"This method may not be available in your version of sentence-transformers."
|
|
328
|
+
)
|
|
329
|
+
|
|
237
330
|
@custom_model.inference_api
|
|
238
331
|
def fn(self: custom_model.CustomModel, X: pd.DataFrame) -> pd.DataFrame:
|
|
239
332
|
X_list = X.iloc[:, 0].tolist()
|
|
240
333
|
|
|
241
334
|
return pd.DataFrame(
|
|
242
|
-
{signature.outputs[0].name:
|
|
335
|
+
{signature.outputs[0].name: method_to_call(X_list, batch_size=batch_size).tolist()}
|
|
243
336
|
)
|
|
244
337
|
|
|
245
338
|
return fn
|
|
246
339
|
|
|
247
340
|
type_method_dict = {}
|
|
248
341
|
for target_method_name, sig in model_meta.signatures.items():
|
|
249
|
-
if target_method_name
|
|
342
|
+
if target_method_name in _ALLOWED_TARGET_METHODS:
|
|
250
343
|
type_method_dict[target_method_name] = get_prediction(raw_model, sig, target_method_name)
|
|
251
344
|
else:
|
|
252
|
-
ValueError(f"{target_method_name} is currently not supported.")
|
|
345
|
+
raise ValueError(f"{target_method_name} is currently not supported.")
|
|
253
346
|
|
|
254
347
|
_SentenceTransformer = type(
|
|
255
348
|
"_SentenceTransformer",
|
|
@@ -262,7 +355,6 @@ class SentenceTransformerHandler(_base.BaseModelHandler["sentence_transformers.S
|
|
|
262
355
|
model = raw_model
|
|
263
356
|
|
|
264
357
|
_SentenceTransformer = _create_custom_model(model, model_meta)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return sentence_transformers_SentenceTransformer_model
|
|
358
|
+
sentence_transformers_model = _SentenceTransformer(custom_model.ModelContext())
|
|
359
|
+
|
|
360
|
+
return sentence_transformers_model
|
|
@@ -9,6 +9,7 @@ from snowflake.ml._internal.exceptions import (
|
|
|
9
9
|
error_codes,
|
|
10
10
|
exceptions as snowml_exceptions,
|
|
11
11
|
)
|
|
12
|
+
from snowflake.ml.model import openai_signatures
|
|
12
13
|
from snowflake.ml.model._signatures import core
|
|
13
14
|
|
|
14
15
|
|
|
@@ -259,6 +260,66 @@ def huggingface_pipeline_signature_auto_infer(
|
|
|
259
260
|
],
|
|
260
261
|
)
|
|
261
262
|
|
|
263
|
+
# https://huggingface.co/docs/transformers/en/main_classes/pipelines#transformers.ImageClassificationPipeline
|
|
264
|
+
if task == "image-classification":
|
|
265
|
+
return core.ModelSignature(
|
|
266
|
+
inputs=[
|
|
267
|
+
core.FeatureSpec(name="images", dtype=core.DataType.BYTES),
|
|
268
|
+
],
|
|
269
|
+
outputs=[
|
|
270
|
+
core.FeatureGroupSpec(
|
|
271
|
+
name="labels",
|
|
272
|
+
specs=[
|
|
273
|
+
core.FeatureSpec(name="label", dtype=core.DataType.STRING),
|
|
274
|
+
core.FeatureSpec(name="score", dtype=core.DataType.DOUBLE),
|
|
275
|
+
],
|
|
276
|
+
shape=(-1,),
|
|
277
|
+
),
|
|
278
|
+
],
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# https://huggingface.co/docs/transformers/en/main_classes/pipelines#transformers.AutomaticSpeechRecognitionPipeline
|
|
282
|
+
if task == "automatic-speech-recognition":
|
|
283
|
+
return core.ModelSignature(
|
|
284
|
+
inputs=[
|
|
285
|
+
core.FeatureSpec(name="audio", dtype=core.DataType.BYTES),
|
|
286
|
+
],
|
|
287
|
+
outputs=[
|
|
288
|
+
core.FeatureGroupSpec(
|
|
289
|
+
name="outputs",
|
|
290
|
+
specs=[
|
|
291
|
+
core.FeatureSpec(name="text", dtype=core.DataType.STRING),
|
|
292
|
+
core.FeatureGroupSpec(
|
|
293
|
+
name="chunks",
|
|
294
|
+
specs=[
|
|
295
|
+
core.FeatureSpec(name="timestamp", dtype=core.DataType.DOUBLE, shape=(2,)),
|
|
296
|
+
core.FeatureSpec(name="text", dtype=core.DataType.STRING),
|
|
297
|
+
],
|
|
298
|
+
shape=(-1,), # Variable length list of chunks
|
|
299
|
+
),
|
|
300
|
+
],
|
|
301
|
+
)
|
|
302
|
+
],
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# https://huggingface.co/docs/transformers/en/main_classes/pipelines#transformers.VideoClassificationPipeline
|
|
306
|
+
if task == "video-classification":
|
|
307
|
+
return core.ModelSignature(
|
|
308
|
+
inputs=[
|
|
309
|
+
core.FeatureSpec(name="video", dtype=core.DataType.BYTES),
|
|
310
|
+
],
|
|
311
|
+
outputs=[
|
|
312
|
+
core.FeatureGroupSpec(
|
|
313
|
+
name="labels",
|
|
314
|
+
specs=[
|
|
315
|
+
core.FeatureSpec(name="label", dtype=core.DataType.STRING),
|
|
316
|
+
core.FeatureSpec(name="score", dtype=core.DataType.DOUBLE),
|
|
317
|
+
],
|
|
318
|
+
shape=(-1,),
|
|
319
|
+
),
|
|
320
|
+
],
|
|
321
|
+
)
|
|
322
|
+
|
|
262
323
|
# https://huggingface.co/docs/transformers/en/main_classes/pipelines#transformers.TextGenerationPipeline
|
|
263
324
|
if task == "text-generation":
|
|
264
325
|
if params.get("return_tensors", False):
|
|
@@ -288,6 +349,22 @@ def huggingface_pipeline_signature_auto_infer(
|
|
|
288
349
|
)
|
|
289
350
|
],
|
|
290
351
|
)
|
|
352
|
+
|
|
353
|
+
# https://huggingface.co/docs/transformers/en/main_classes/pipelines#transformers.ImageTextToTextPipeline
|
|
354
|
+
if task in [
|
|
355
|
+
"image-text-to-text",
|
|
356
|
+
"video-text-to-text",
|
|
357
|
+
"audio-text-to-text",
|
|
358
|
+
]:
|
|
359
|
+
if params.get("return_tensors", False):
|
|
360
|
+
raise NotImplementedError(
|
|
361
|
+
f"Auto deployment for HuggingFace pipeline {task} "
|
|
362
|
+
"when `return_tensors` set to `True` has not been supported yet."
|
|
363
|
+
)
|
|
364
|
+
# Always generate a dict per input
|
|
365
|
+
# defaulting to OPENAI_CHAT_SIGNATURE_SPEC for image-text-to-text pipeline
|
|
366
|
+
return openai_signatures._OPENAI_CHAT_SIGNATURE_SPEC
|
|
367
|
+
|
|
291
368
|
# https://huggingface.co/docs/transformers/en/main_classes/pipelines#transformers.Text2TextGenerationPipeline
|
|
292
369
|
if task == "text2text-generation":
|
|
293
370
|
if params.get("return_tensors", False):
|
|
@@ -406,3 +483,56 @@ def infer_dict(name: str, data: dict[str, Any]) -> core.FeatureGroupSpec:
|
|
|
406
483
|
|
|
407
484
|
def check_if_series_is_empty(series: Optional[pd.Series]) -> bool:
|
|
408
485
|
return series is None or series.empty
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def sentence_transformers_signature_auto_infer(
|
|
489
|
+
target_method: str,
|
|
490
|
+
embedding_dim: int,
|
|
491
|
+
) -> Optional[core.ModelSignature]:
|
|
492
|
+
"""Auto-infer signature for SentenceTransformer models.
|
|
493
|
+
|
|
494
|
+
SentenceTransformer models have a simple signature: they take a string input
|
|
495
|
+
and return an embedding vector (array of floats).
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
target_method: The target method name. Supported methods:
|
|
499
|
+
- "encode": General encoding method
|
|
500
|
+
- "encode_query" / "encode_queries": Query encoding for asymmetric search
|
|
501
|
+
- "encode_document" / "encode_documents": Document encoding for asymmetric search
|
|
502
|
+
embedding_dim: The dimension of the embedding vector output by the model.
|
|
503
|
+
|
|
504
|
+
Returns:
|
|
505
|
+
A ModelSignature for the target method, or None if the method is not supported.
|
|
506
|
+
|
|
507
|
+
Note:
|
|
508
|
+
sentence-transformers >= 3.0 uses singular names (encode_query, encode_document)
|
|
509
|
+
while older versions may use plural names (encode_queries, encode_documents).
|
|
510
|
+
Both naming conventions are supported for backward compatibility.
|
|
511
|
+
"""
|
|
512
|
+
# Support both singular (new) and plural (old) naming conventions
|
|
513
|
+
supported_methods = [
|
|
514
|
+
"encode",
|
|
515
|
+
"encode_query",
|
|
516
|
+
"encode_document",
|
|
517
|
+
"encode_queries",
|
|
518
|
+
"encode_documents",
|
|
519
|
+
]
|
|
520
|
+
|
|
521
|
+
if target_method not in supported_methods:
|
|
522
|
+
return None
|
|
523
|
+
|
|
524
|
+
# All SentenceTransformer encode methods have the same signature pattern:
|
|
525
|
+
# - Input: a single string column
|
|
526
|
+
# - Output: a single column containing embedding vectors (array of floats)
|
|
527
|
+
return core.ModelSignature(
|
|
528
|
+
inputs=[
|
|
529
|
+
core.FeatureSpec(name="text", dtype=core.DataType.STRING),
|
|
530
|
+
],
|
|
531
|
+
outputs=[
|
|
532
|
+
core.FeatureSpec(
|
|
533
|
+
name="output",
|
|
534
|
+
dtype=core.DataType.DOUBLE,
|
|
535
|
+
shape=(embedding_dim,),
|
|
536
|
+
),
|
|
537
|
+
],
|
|
538
|
+
)
|
|
@@ -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}
|
|
@@ -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]
|
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.24.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snowflake-ml-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.24.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,60 @@ 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.24.0
|
|
421
|
+
|
|
422
|
+
### New Features
|
|
423
|
+
|
|
424
|
+
* Feature Store: Added tile-based aggregation support with a new `Feature` API for efficient and
|
|
425
|
+
point-in-time correct time-series feature computation using pre-computed tiles stored in Dynamic Tables.
|
|
426
|
+
|
|
427
|
+
* Registry: Added auto-signature inference for SentenceTransformer models. When logging a SentenceTransformer
|
|
428
|
+
model, `sample_input_data` is now optional. If not provided, the signature is automatically inferred from
|
|
429
|
+
the model's embedding dimension. Supported methods: `encode`, `encode_query`, `encode_document`,
|
|
430
|
+
`encode_queries`, `encode_documents`.
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
import sentence_transformers
|
|
434
|
+
from snowflake.ml.registry import Registry
|
|
435
|
+
|
|
436
|
+
# Create model
|
|
437
|
+
model = sentence_transformers.SentenceTransformer("all-MiniLM-L6-v2")
|
|
438
|
+
|
|
439
|
+
# Log model without sample_input_data - signature is auto-inferred
|
|
440
|
+
registry = Registry(session)
|
|
441
|
+
mv = registry.log_model(
|
|
442
|
+
model=model,
|
|
443
|
+
model_name="my_sentence_transformer",
|
|
444
|
+
version_name="v1",
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# Run inference with auto-inferred signature (input: "text", output: "output")
|
|
448
|
+
import pandas as pd
|
|
449
|
+
result = mv.run(pd.DataFrame({"text": ["Hello world"]}))
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Bug Fixes
|
|
453
|
+
|
|
454
|
+
### Behavior Changes
|
|
455
|
+
|
|
456
|
+
### Deprecations
|
|
457
|
+
|
|
458
|
+
## 1.23.0
|
|
459
|
+
|
|
460
|
+
### New Features
|
|
461
|
+
|
|
462
|
+
* ML Jobs: Enabled support for Python 3.11 and Python 3.12 by default. Jobs will automatically select a
|
|
463
|
+
runtime environment matching the client Python version.
|
|
464
|
+
|
|
465
|
+
### Bug Fixes
|
|
466
|
+
|
|
467
|
+
* Registry: Fix failures on empty output in HuggingFace's Token Classification (aka Named Entity Recognition) models.
|
|
468
|
+
* Model serving: don't parse instance or container statuses to fix bug with missing container status.
|
|
469
|
+
|
|
470
|
+
### Behavior Changes
|
|
471
|
+
|
|
472
|
+
### Deprecations
|
|
473
|
+
|
|
420
474
|
## 1.22.0
|
|
421
475
|
|
|
422
476
|
### New Features
|
|
@@ -439,10 +493,60 @@ mv = registry.log_model(
|
|
|
439
493
|
)
|
|
440
494
|
```
|
|
441
495
|
|
|
496
|
+
* Registry: Added support for `image-text-to-text` task type in `huggingface.TransformersPipeline`.
|
|
497
|
+
Note: Requires vLLM inference engine while creating the service.
|
|
498
|
+
|
|
442
499
|
### Bug Fixes
|
|
443
500
|
|
|
444
501
|
### Behavior Changes
|
|
445
502
|
|
|
503
|
+
* Registry: The `openai_signatures.OPENAI_CHAT_SIGNATURE` signature now handles content parts and
|
|
504
|
+
requires input data to be passed in list of dictionary. To use string content (previous behavior),
|
|
505
|
+
use `openai_signatures.OPENAI_CHAT_SIGNATURE_WITH_CONTENT_FORMAT_STRING`.
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
from snowflake.ml.model import openai_signatures
|
|
509
|
+
import pandas as pd
|
|
510
|
+
|
|
511
|
+
mv = snowflake_registry.log_model(
|
|
512
|
+
model=generator,
|
|
513
|
+
model_name=...,
|
|
514
|
+
...,
|
|
515
|
+
signatures=openai_signatures.OPENAI_CHAT_SIGNATURE,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
# create a pd.DataFrame with openai.client.chat.completions arguments like below:
|
|
519
|
+
x_df = pd.DataFrame.from_records(
|
|
520
|
+
[
|
|
521
|
+
{
|
|
522
|
+
"messages": [
|
|
523
|
+
{"role": "system", "content": "Complete the sentence."},
|
|
524
|
+
{
|
|
525
|
+
"role": "user",
|
|
526
|
+
"content": [
|
|
527
|
+
{
|
|
528
|
+
"type": "text",
|
|
529
|
+
"text": "A descendant of the Lost City of Atlantis, who swam to Earth while saying, ",
|
|
530
|
+
}
|
|
531
|
+
],
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
"max_completion_tokens": 250,
|
|
535
|
+
"temperature": 0.9,
|
|
536
|
+
"stop": None,
|
|
537
|
+
"n": 3,
|
|
538
|
+
"stream": False,
|
|
539
|
+
"top_p": 1.0,
|
|
540
|
+
"frequency_penalty": 0.1,
|
|
541
|
+
"presence_penalty": 0.2,
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
# OpenAI Chat Completion compatible output
|
|
547
|
+
output_df = mv.run(X=x_df)
|
|
548
|
+
```
|
|
549
|
+
|
|
446
550
|
### Deprecations
|
|
447
551
|
|
|
448
552
|
## 1.21.0
|