snowflake-ml-python 2.3.0__py3-none-any.whl → 2.5.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 (227) hide show
  1. snowflake/cortex/_classify_text.py +51 -5
  2. snowflake/cortex/_complete.py +41 -9
  3. snowflake/cortex/_embed_text_1024.py +11 -5
  4. snowflake/cortex/_embed_text_768.py +11 -5
  5. snowflake/cortex/_extract_answer.py +54 -12
  6. snowflake/cortex/_sentiment.py +50 -10
  7. snowflake/cortex/_summarize.py +11 -5
  8. snowflake/cortex/_translate.py +11 -5
  9. snowflake/cortex/_util.py +35 -0
  10. snowflake/ml/_internal/env_utils.py +6 -2
  11. snowflake/ml/_internal/file_utils.py +18 -1
  12. snowflake/ml/experiment/_entities/run.py +74 -13
  13. snowflake/ml/experiment/callback/catboost.py +75 -0
  14. snowflake/ml/experiment/experiment_tracking.py +19 -17
  15. snowflake/ml/feature_store/feature_store.py +55 -0
  16. snowflake/ml/jobs/job.py +42 -12
  17. snowflake/ml/lineage/lineage_node.py +9 -1
  18. snowflake/ml/model/_client/model/batch_inference_job_specs.py +81 -1
  19. snowflake/ml/model/_client/model/batch_inference_task.py +24 -6
  20. snowflake/ml/model/_client/model/model_version_impl.py +115 -22
  21. snowflake/ml/model/_client/ops/model_ops.py +12 -19
  22. snowflake/ml/model/_client/ops/service_ops.py +8 -6
  23. snowflake/ml/model/_client/service/inference_job_service_spec.py +1 -1
  24. snowflake/ml/model/_client/service/model_deployment_spec.py +53 -33
  25. snowflake/ml/model/_client/sql/model.py +17 -51
  26. snowflake/ml/model/_packager/model_meta/model_meta.py +3 -1
  27. snowflake/ml/model/_packager/model_runtime/_snowml_inference_alternative_requirements.py +2 -2
  28. snowflake/ml/model/_packager/model_runtime/model_runtime.py +5 -1
  29. snowflake/ml/model/_signatures/core.py +9 -4
  30. snowflake/ml/model/model_signature.py +85 -0
  31. snowflake/ml/modeling/_internal/model_specifications.py +10 -4
  32. snowflake/ml/modeling/_internal/optional_dependency.py +6 -30
  33. snowflake/ml/modeling/_internal/snowpark_implementations/distributed_hpo_trainer.py +0 -2
  34. snowflake/ml/modeling/calibration/__init__.py +1 -2
  35. snowflake/ml/modeling/calibration/calibrated_classifier_cv.py +2 -1
  36. snowflake/ml/modeling/cluster/__init__.py +1 -2
  37. snowflake/ml/modeling/cluster/affinity_propagation.py +2 -1
  38. snowflake/ml/modeling/cluster/agglomerative_clustering.py +2 -1
  39. snowflake/ml/modeling/cluster/birch.py +2 -1
  40. snowflake/ml/modeling/cluster/bisecting_k_means.py +2 -1
  41. snowflake/ml/modeling/cluster/dbscan.py +2 -1
  42. snowflake/ml/modeling/cluster/feature_agglomeration.py +2 -1
  43. snowflake/ml/modeling/cluster/k_means.py +2 -1
  44. snowflake/ml/modeling/cluster/mean_shift.py +2 -1
  45. snowflake/ml/modeling/cluster/mini_batch_k_means.py +2 -1
  46. snowflake/ml/modeling/cluster/optics.py +2 -1
  47. snowflake/ml/modeling/cluster/spectral_biclustering.py +2 -1
  48. snowflake/ml/modeling/cluster/spectral_clustering.py +2 -1
  49. snowflake/ml/modeling/cluster/spectral_coclustering.py +2 -1
  50. snowflake/ml/modeling/compose/__init__.py +1 -2
  51. snowflake/ml/modeling/compose/column_transformer.py +2 -1
  52. snowflake/ml/modeling/compose/transformed_target_regressor.py +2 -1
  53. snowflake/ml/modeling/covariance/__init__.py +1 -2
  54. snowflake/ml/modeling/covariance/elliptic_envelope.py +2 -1
  55. snowflake/ml/modeling/covariance/empirical_covariance.py +2 -1
  56. snowflake/ml/modeling/covariance/graphical_lasso.py +2 -1
  57. snowflake/ml/modeling/covariance/graphical_lasso_cv.py +2 -1
  58. snowflake/ml/modeling/covariance/ledoit_wolf.py +2 -1
  59. snowflake/ml/modeling/covariance/min_cov_det.py +2 -1
  60. snowflake/ml/modeling/covariance/oas.py +2 -1
  61. snowflake/ml/modeling/covariance/shrunk_covariance.py +2 -1
  62. snowflake/ml/modeling/decomposition/__init__.py +1 -2
  63. snowflake/ml/modeling/decomposition/dictionary_learning.py +2 -1
  64. snowflake/ml/modeling/decomposition/factor_analysis.py +2 -1
  65. snowflake/ml/modeling/decomposition/fast_ica.py +2 -1
  66. snowflake/ml/modeling/decomposition/incremental_pca.py +2 -1
  67. snowflake/ml/modeling/decomposition/kernel_pca.py +2 -1
  68. snowflake/ml/modeling/decomposition/mini_batch_dictionary_learning.py +2 -1
  69. snowflake/ml/modeling/decomposition/mini_batch_sparse_pca.py +2 -1
  70. snowflake/ml/modeling/decomposition/pca.py +2 -1
  71. snowflake/ml/modeling/decomposition/sparse_pca.py +2 -1
  72. snowflake/ml/modeling/decomposition/truncated_svd.py +2 -1
  73. snowflake/ml/modeling/discriminant_analysis/__init__.py +1 -2
  74. snowflake/ml/modeling/discriminant_analysis/linear_discriminant_analysis.py +2 -1
  75. snowflake/ml/modeling/discriminant_analysis/quadratic_discriminant_analysis.py +2 -1
  76. snowflake/ml/modeling/ensemble/__init__.py +1 -2
  77. snowflake/ml/modeling/ensemble/ada_boost_classifier.py +2 -1
  78. snowflake/ml/modeling/ensemble/ada_boost_regressor.py +2 -1
  79. snowflake/ml/modeling/ensemble/bagging_classifier.py +2 -1
  80. snowflake/ml/modeling/ensemble/bagging_regressor.py +2 -1
  81. snowflake/ml/modeling/ensemble/extra_trees_classifier.py +2 -1
  82. snowflake/ml/modeling/ensemble/extra_trees_regressor.py +2 -1
  83. snowflake/ml/modeling/ensemble/gradient_boosting_classifier.py +2 -1
  84. snowflake/ml/modeling/ensemble/gradient_boosting_regressor.py +2 -1
  85. snowflake/ml/modeling/ensemble/hist_gradient_boosting_classifier.py +2 -1
  86. snowflake/ml/modeling/ensemble/hist_gradient_boosting_regressor.py +2 -1
  87. snowflake/ml/modeling/ensemble/isolation_forest.py +2 -1
  88. snowflake/ml/modeling/ensemble/random_forest_classifier.py +2 -1
  89. snowflake/ml/modeling/ensemble/random_forest_regressor.py +2 -1
  90. snowflake/ml/modeling/ensemble/stacking_regressor.py +2 -1
  91. snowflake/ml/modeling/ensemble/voting_classifier.py +2 -1
  92. snowflake/ml/modeling/ensemble/voting_regressor.py +2 -1
  93. snowflake/ml/modeling/feature_selection/__init__.py +1 -2
  94. snowflake/ml/modeling/feature_selection/generic_univariate_select.py +2 -1
  95. snowflake/ml/modeling/feature_selection/select_fdr.py +2 -1
  96. snowflake/ml/modeling/feature_selection/select_fpr.py +2 -1
  97. snowflake/ml/modeling/feature_selection/select_fwe.py +2 -1
  98. snowflake/ml/modeling/feature_selection/select_k_best.py +2 -1
  99. snowflake/ml/modeling/feature_selection/select_percentile.py +2 -1
  100. snowflake/ml/modeling/feature_selection/sequential_feature_selector.py +2 -1
  101. snowflake/ml/modeling/feature_selection/variance_threshold.py +2 -1
  102. snowflake/ml/modeling/gaussian_process/__init__.py +1 -2
  103. snowflake/ml/modeling/gaussian_process/gaussian_process_classifier.py +2 -1
  104. snowflake/ml/modeling/gaussian_process/gaussian_process_regressor.py +2 -1
  105. snowflake/ml/modeling/impute/__init__.py +1 -2
  106. snowflake/ml/modeling/impute/iterative_imputer.py +2 -1
  107. snowflake/ml/modeling/impute/knn_imputer.py +2 -1
  108. snowflake/ml/modeling/impute/missing_indicator.py +2 -1
  109. snowflake/ml/modeling/kernel_approximation/__init__.py +1 -2
  110. snowflake/ml/modeling/kernel_approximation/additive_chi2_sampler.py +2 -1
  111. snowflake/ml/modeling/kernel_approximation/nystroem.py +2 -1
  112. snowflake/ml/modeling/kernel_approximation/polynomial_count_sketch.py +2 -1
  113. snowflake/ml/modeling/kernel_approximation/rbf_sampler.py +2 -1
  114. snowflake/ml/modeling/kernel_approximation/skewed_chi2_sampler.py +2 -1
  115. snowflake/ml/modeling/kernel_ridge/__init__.py +1 -2
  116. snowflake/ml/modeling/kernel_ridge/kernel_ridge.py +2 -1
  117. snowflake/ml/modeling/lightgbm/__init__.py +1 -2
  118. snowflake/ml/modeling/lightgbm/lgbm_classifier.py +2 -1
  119. snowflake/ml/modeling/lightgbm/lgbm_regressor.py +2 -1
  120. snowflake/ml/modeling/linear_model/__init__.py +1 -2
  121. snowflake/ml/modeling/linear_model/ard_regression.py +2 -1
  122. snowflake/ml/modeling/linear_model/bayesian_ridge.py +2 -1
  123. snowflake/ml/modeling/linear_model/elastic_net.py +2 -1
  124. snowflake/ml/modeling/linear_model/elastic_net_cv.py +2 -1
  125. snowflake/ml/modeling/linear_model/gamma_regressor.py +2 -1
  126. snowflake/ml/modeling/linear_model/huber_regressor.py +2 -1
  127. snowflake/ml/modeling/linear_model/lars.py +2 -1
  128. snowflake/ml/modeling/linear_model/lars_cv.py +2 -1
  129. snowflake/ml/modeling/linear_model/lasso.py +2 -1
  130. snowflake/ml/modeling/linear_model/lasso_cv.py +2 -1
  131. snowflake/ml/modeling/linear_model/lasso_lars.py +2 -1
  132. snowflake/ml/modeling/linear_model/lasso_lars_cv.py +2 -1
  133. snowflake/ml/modeling/linear_model/lasso_lars_ic.py +2 -1
  134. snowflake/ml/modeling/linear_model/linear_regression.py +2 -1
  135. snowflake/ml/modeling/linear_model/logistic_regression.py +2 -1
  136. snowflake/ml/modeling/linear_model/logistic_regression_cv.py +2 -1
  137. snowflake/ml/modeling/linear_model/multi_task_elastic_net.py +2 -1
  138. snowflake/ml/modeling/linear_model/multi_task_elastic_net_cv.py +2 -1
  139. snowflake/ml/modeling/linear_model/multi_task_lasso.py +2 -1
  140. snowflake/ml/modeling/linear_model/multi_task_lasso_cv.py +2 -1
  141. snowflake/ml/modeling/linear_model/orthogonal_matching_pursuit.py +2 -1
  142. snowflake/ml/modeling/linear_model/passive_aggressive_classifier.py +2 -1
  143. snowflake/ml/modeling/linear_model/passive_aggressive_regressor.py +2 -1
  144. snowflake/ml/modeling/linear_model/perceptron.py +2 -1
  145. snowflake/ml/modeling/linear_model/poisson_regressor.py +2 -1
  146. snowflake/ml/modeling/linear_model/ransac_regressor.py +2 -1
  147. snowflake/ml/modeling/linear_model/ridge.py +2 -1
  148. snowflake/ml/modeling/linear_model/ridge_classifier.py +2 -1
  149. snowflake/ml/modeling/linear_model/ridge_classifier_cv.py +2 -1
  150. snowflake/ml/modeling/linear_model/ridge_cv.py +2 -1
  151. snowflake/ml/modeling/linear_model/sgd_classifier.py +2 -1
  152. snowflake/ml/modeling/linear_model/sgd_one_class_svm.py +2 -1
  153. snowflake/ml/modeling/linear_model/sgd_regressor.py +2 -1
  154. snowflake/ml/modeling/linear_model/theil_sen_regressor.py +2 -1
  155. snowflake/ml/modeling/linear_model/tweedie_regressor.py +2 -1
  156. snowflake/ml/modeling/manifold/__init__.py +1 -2
  157. snowflake/ml/modeling/manifold/isomap.py +2 -1
  158. snowflake/ml/modeling/manifold/mds.py +2 -1
  159. snowflake/ml/modeling/manifold/spectral_embedding.py +2 -1
  160. snowflake/ml/modeling/manifold/tsne.py +2 -1
  161. snowflake/ml/modeling/metrics/__init__.py +2 -3
  162. snowflake/ml/modeling/metrics/classification.py +1 -0
  163. snowflake/ml/modeling/metrics/metrics_utils.py +6 -0
  164. snowflake/ml/modeling/metrics/ranking.py +3 -0
  165. snowflake/ml/modeling/metrics/regression.py +3 -0
  166. snowflake/ml/modeling/mixture/__init__.py +1 -2
  167. snowflake/ml/modeling/mixture/bayesian_gaussian_mixture.py +2 -1
  168. snowflake/ml/modeling/mixture/gaussian_mixture.py +2 -1
  169. snowflake/ml/modeling/model_selection/__init__.py +1 -2
  170. snowflake/ml/modeling/model_selection/grid_search_cv.py +2 -0
  171. snowflake/ml/modeling/model_selection/randomized_search_cv.py +2 -0
  172. snowflake/ml/modeling/multiclass/__init__.py +1 -2
  173. snowflake/ml/modeling/multiclass/one_vs_one_classifier.py +2 -1
  174. snowflake/ml/modeling/multiclass/one_vs_rest_classifier.py +2 -1
  175. snowflake/ml/modeling/multiclass/output_code_classifier.py +2 -1
  176. snowflake/ml/modeling/naive_bayes/__init__.py +1 -2
  177. snowflake/ml/modeling/naive_bayes/bernoulli_nb.py +2 -1
  178. snowflake/ml/modeling/naive_bayes/categorical_nb.py +2 -1
  179. snowflake/ml/modeling/naive_bayes/complement_nb.py +2 -1
  180. snowflake/ml/modeling/naive_bayes/gaussian_nb.py +2 -1
  181. snowflake/ml/modeling/naive_bayes/multinomial_nb.py +2 -1
  182. snowflake/ml/modeling/neighbors/__init__.py +1 -2
  183. snowflake/ml/modeling/neighbors/k_neighbors_classifier.py +2 -1
  184. snowflake/ml/modeling/neighbors/k_neighbors_regressor.py +2 -1
  185. snowflake/ml/modeling/neighbors/kernel_density.py +2 -1
  186. snowflake/ml/modeling/neighbors/local_outlier_factor.py +2 -1
  187. snowflake/ml/modeling/neighbors/nearest_centroid.py +2 -1
  188. snowflake/ml/modeling/neighbors/nearest_neighbors.py +2 -1
  189. snowflake/ml/modeling/neighbors/neighborhood_components_analysis.py +2 -1
  190. snowflake/ml/modeling/neighbors/radius_neighbors_classifier.py +2 -1
  191. snowflake/ml/modeling/neighbors/radius_neighbors_regressor.py +2 -1
  192. snowflake/ml/modeling/neural_network/__init__.py +1 -2
  193. snowflake/ml/modeling/neural_network/bernoulli_rbm.py +2 -1
  194. snowflake/ml/modeling/neural_network/mlp_classifier.py +2 -1
  195. snowflake/ml/modeling/neural_network/mlp_regressor.py +2 -1
  196. snowflake/ml/modeling/pipeline/__init__.py +1 -2
  197. snowflake/ml/modeling/pipeline/pipeline.py +6 -1
  198. snowflake/ml/modeling/preprocessing/__init__.py +1 -2
  199. snowflake/ml/modeling/preprocessing/polynomial_features.py +2 -1
  200. snowflake/ml/modeling/semi_supervised/__init__.py +1 -2
  201. snowflake/ml/modeling/semi_supervised/label_propagation.py +2 -1
  202. snowflake/ml/modeling/semi_supervised/label_spreading.py +2 -1
  203. snowflake/ml/modeling/svm/__init__.py +1 -2
  204. snowflake/ml/modeling/svm/linear_svc.py +2 -1
  205. snowflake/ml/modeling/svm/linear_svr.py +2 -1
  206. snowflake/ml/modeling/svm/nu_svc.py +2 -1
  207. snowflake/ml/modeling/svm/nu_svr.py +2 -1
  208. snowflake/ml/modeling/svm/svc.py +2 -1
  209. snowflake/ml/modeling/svm/svr.py +2 -1
  210. snowflake/ml/modeling/tree/__init__.py +1 -2
  211. snowflake/ml/modeling/tree/decision_tree_classifier.py +2 -1
  212. snowflake/ml/modeling/tree/decision_tree_regressor.py +2 -1
  213. snowflake/ml/modeling/tree/extra_tree_classifier.py +2 -1
  214. snowflake/ml/modeling/tree/extra_tree_regressor.py +2 -1
  215. snowflake/ml/modeling/xgboost/__init__.py +1 -2
  216. snowflake/ml/modeling/xgboost/xgb_classifier.py +2 -1
  217. snowflake/ml/modeling/xgboost/xgb_regressor.py +2 -1
  218. snowflake/ml/modeling/xgboost/xgbrf_classifier.py +2 -1
  219. snowflake/ml/modeling/xgboost/xgbrf_regressor.py +2 -1
  220. snowflake/ml/registry/_manager/model_manager.py +0 -1
  221. snowflake/ml/registry/_manager/model_parameter_reconciler.py +7 -7
  222. snowflake/ml/version.py +1 -1
  223. {snowflake_ml_python-2.3.0.dist-info → snowflake_ml_python-2.5.0.dist-info}/METADATA +73 -6
  224. {snowflake_ml_python-2.3.0.dist-info → snowflake_ml_python-2.5.0.dist-info}/RECORD +227 -226
  225. {snowflake_ml_python-2.3.0.dist-info → snowflake_ml_python-2.5.0.dist-info}/WHEEL +0 -0
  226. {snowflake_ml_python-2.3.0.dist-info → snowflake_ml_python-2.5.0.dist-info}/licenses/LICENSE.txt +0 -0
  227. {snowflake_ml_python-2.3.0.dist-info → snowflake_ml_python-2.5.0.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,43 @@
1
- from typing import Union, cast
1
+ import json
2
+ from typing import Any, cast
2
3
 
3
4
  from typing_extensions import deprecated
4
5
 
5
6
  from snowflake import snowpark
6
- from snowflake.cortex._util import CORTEX_FUNCTIONS_TELEMETRY_PROJECT, call_sql_function
7
+ from snowflake.cortex._util import (
8
+ CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
9
+ call_sql_function,
10
+ warn_cortex_deprecated,
11
+ )
7
12
  from snowflake.ml._internal import telemetry
13
+ from snowflake.snowpark import functions
14
+
15
+ _AI_CLASSIFY_FUNCTION_NAME = "AI_CLASSIFY"
16
+ # Legacy CLASSIFY_TEXT always returned exactly one label. AI_CLASSIFY defaults to single-label mode, but
17
+ # we set output_mode explicitly so the single-label contract can't drift and multi-label results can
18
+ # never leak through.
19
+ _AI_CLASSIFY_SINGLE_LABEL_CONFIG = {"output_mode": "single"}
20
+
21
+
22
+ def _adapt_ai_classify_result(result: Any) -> str:
23
+ """Map an AI_CLASSIFY result to the legacy CLASSIFY_TEXT {"label": ...} JSON string."""
24
+ # Legacy classify_text returned the raw CLASSIFY_TEXT OBJECT, which the immediate (non-Column) path
25
+ # collects as a JSON string such as '{"label": "positive"}'. We reproduce that exact shape so
26
+ # existing callers that json.loads the result and read ["label"] keep working.
27
+ parsed: Any = result
28
+ if isinstance(result, str):
29
+ try:
30
+ parsed = json.loads(result)
31
+ except json.JSONDecodeError:
32
+ return result
33
+ label: Any = None
34
+ if isinstance(parsed, dict):
35
+ labels = parsed.get("labels")
36
+ if isinstance(labels, list) and labels:
37
+ label = labels[0]
38
+ elif parsed.get("label") is not None:
39
+ label = parsed.get("label")
40
+ return json.dumps({"label": label})
8
41
 
9
42
 
10
43
  @telemetry.send_api_usage_telemetry(
@@ -25,17 +58,30 @@ def classify_text(
25
58
  Returns:
26
59
  A column of classification responses.
27
60
  """
28
-
29
- return _classify_text_impl("snowflake.cortex.classify_text", str_input, categories, session=session)
61
+ warn_cortex_deprecated("classify_text", "ai_classify")
62
+ result = _classify_text_impl(
63
+ _AI_CLASSIFY_FUNCTION_NAME, str_input, categories, _AI_CLASSIFY_SINGLE_LABEL_CONFIG, session=session
64
+ )
65
+ if isinstance(result, snowpark.Column):
66
+ # AI_CLASSIFY returns {"labels": [...]}; re-wrap as the legacy CLASSIFY_TEXT {"label": ...} OBJECT
67
+ # so downstream SQL that reads col["label"] is unchanged. Subscript access (not functions.get with
68
+ # a bare string, which Snowpark treats as a column identifier) extracts the field by literal key.
69
+ label = functions.coalesce(result["labels"][0], result["label"])
70
+ return functions.object_construct(functions.lit("label"), label)
71
+ return _adapt_ai_classify_result(result)
30
72
 
31
73
 
32
74
  def _classify_text_impl(
33
75
  function: str,
34
76
  str_input: str | snowpark.Column,
35
77
  categories: list[str] | snowpark.Column,
78
+ config: dict[str, Any] | None = None,
36
79
  session: snowpark.Session | None = None,
37
80
  ) -> str | snowpark.Column:
38
- return cast(Union[str, snowpark.Column], call_sql_function(function, session, str_input, categories))
81
+ args: list[Any] = [str_input, categories]
82
+ if config is not None:
83
+ args.append(config)
84
+ return cast(str | snowpark.Column, call_sql_function(function, session, *args))
39
85
 
40
86
 
41
87
  ClassifyText = deprecated(
@@ -14,6 +14,7 @@ from snowflake.cortex._util import (
14
14
  CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
15
15
  SnowflakeAuthenticationException,
16
16
  SnowflakeConfigurationException,
17
+ warn_cortex_deprecated,
17
18
  )
18
19
  from snowflake.ml._internal import telemetry
19
20
  from snowflake.snowpark import context, functions
@@ -331,12 +332,46 @@ def _return_stream_response(
331
332
  pass
332
333
 
333
334
 
334
- def _complete_call_sql_function_snowpark(
335
- function: str, *args: str | snowpark.Column | CompleteOptions
336
- ) -> snowpark.Column:
335
+ def _complete_call_sql_function_snowpark(function: str, *args: Any) -> snowpark.Column:
337
336
  return cast(snowpark.Column, functions.builtin(function)(*args))
338
337
 
339
338
 
339
+ _SQL_MODEL_PARAMETER_KEYS = ("max_tokens", "temperature", "top_p", "guardrails")
340
+
341
+
342
+ def _sql_ai_complete_args(
343
+ model: str | snowpark.Column,
344
+ prompt: str | list[ConversationMessage] | snowpark.Column,
345
+ options: CompleteOptions | snowpark.Column | None,
346
+ ) -> tuple[Any, ...]:
347
+ """Build positional SQL arguments for AI_COMPLETE.
348
+
349
+ Args:
350
+ model: Model name or Column of model names.
351
+ prompt: Prompt string, conversation history, or Column of prompts.
352
+ options: Complete options, a Column of options, or None.
353
+
354
+ Returns:
355
+ Positional AI_COMPLETE arguments with response format separate from
356
+ model parameters.
357
+ """
358
+ if options is None:
359
+ return (model, prompt)
360
+ if isinstance(options, snowpark.Column):
361
+ # Can't introspect a Column's keys at build time, so split response_format out of the options
362
+ # object in SQL: model_parameters is the object minus response_format, response_format is read
363
+ # out separately (NULL when absent, which AI_COMPLETE treats as "no response format").
364
+ model_parameters_col = functions.builtin("object_delete")(options, functions.lit("response_format"))
365
+ response_format_col = functions.get(options, functions.lit("response_format"))
366
+ return (model, prompt, model_parameters_col, response_format_col)
367
+ option_values = cast(dict[str, Any], options)
368
+ model_parameters = {key: option_values[key] for key in _SQL_MODEL_PARAMETER_KEYS if key in option_values}
369
+ response_format = options.get("response_format")
370
+ if response_format is None:
371
+ return (model, prompt, model_parameters) if model_parameters else (model, prompt)
372
+ return (model, prompt, model_parameters, response_format)
373
+
374
+
340
375
  def _complete_non_streaming_immediate(
341
376
  snow_api_xp_request_handler: Callable[..., dict[str, Any]] | None,
342
377
  model: str,
@@ -366,10 +401,7 @@ def _complete_non_streaming_impl(
366
401
  deadline: float | None = None,
367
402
  ) -> str | snowpark.Column:
368
403
  if isinstance(prompt, snowpark.Column):
369
- if options is not None:
370
- return _complete_call_sql_function_snowpark(function, model, prompt, options)
371
- else:
372
- return _complete_call_sql_function_snowpark(function, model, prompt)
404
+ return _complete_call_sql_function_snowpark(function, *_sql_ai_complete_args(model, prompt, options))
373
405
  if isinstance(model, snowpark.Column):
374
406
  raise ValueError("'model' cannot be a snowpark.Column when 'prompt' is a string.")
375
407
  if isinstance(options, snowpark.Column):
@@ -414,7 +446,7 @@ def _complete_impl(
414
446
  model: str | snowpark.Column,
415
447
  prompt: str | list[ConversationMessage] | snowpark.Column,
416
448
  snow_api_xp_request_handler: Callable[..., dict[str, Any]] | None = None,
417
- function: str = "snowflake.cortex.complete",
449
+ function: str = "AI_COMPLETE",
418
450
  options: CompleteOptions | None = None,
419
451
  session: snowpark.Session | None = None,
420
452
  stream: bool = False,
@@ -480,7 +512,7 @@ def complete(
480
512
  Returns:
481
513
  A column of string responses.
482
514
  """
483
-
515
+ warn_cortex_deprecated("complete", "ai_complete")
484
516
  # Set the XP snow api function, if available.
485
517
  snow_api_xp_request_handler = None
486
518
  if is_in_stored_procedure(): # type: ignore[no-untyped-call]
@@ -1,11 +1,17 @@
1
- from typing import Union, cast
1
+ from typing import cast
2
2
 
3
3
  from typing_extensions import deprecated
4
4
 
5
5
  from snowflake import snowpark
6
- from snowflake.cortex._util import CORTEX_FUNCTIONS_TELEMETRY_PROJECT, call_sql_function
6
+ from snowflake.cortex._util import (
7
+ CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
8
+ call_sql_function,
9
+ warn_cortex_deprecated,
10
+ )
7
11
  from snowflake.ml._internal import telemetry
8
12
 
13
+ _AI_EMBED_FUNCTION_NAME = "AI_EMBED"
14
+
9
15
 
10
16
  @telemetry.send_api_usage_telemetry(
11
17
  project=CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
@@ -26,8 +32,8 @@ def embed_text_1024(
26
32
  Returns:
27
33
  A column of vectors containing embeddings.
28
34
  """
29
-
30
- return _embed_text_1024_impl("snowflake.cortex.embed_text_1024", model, text, session=session)
35
+ warn_cortex_deprecated("embed_text_1024", "ai_embed")
36
+ return _embed_text_1024_impl(_AI_EMBED_FUNCTION_NAME, model, text, session=session)
31
37
 
32
38
 
33
39
  def _embed_text_1024_impl(
@@ -36,7 +42,7 @@ def _embed_text_1024_impl(
36
42
  text: str | snowpark.Column,
37
43
  session: snowpark.Session | None = None,
38
44
  ) -> list[float] | snowpark.Column:
39
- return cast(Union[list[float], snowpark.Column], call_sql_function(function, session, model, text))
45
+ return cast(list[float] | snowpark.Column, call_sql_function(function, session, model, text))
40
46
 
41
47
 
42
48
  EmbedText1024 = deprecated(
@@ -1,11 +1,17 @@
1
- from typing import Union, cast
1
+ from typing import cast
2
2
 
3
3
  from typing_extensions import deprecated
4
4
 
5
5
  from snowflake import snowpark
6
- from snowflake.cortex._util import CORTEX_FUNCTIONS_TELEMETRY_PROJECT, call_sql_function
6
+ from snowflake.cortex._util import (
7
+ CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
8
+ call_sql_function,
9
+ warn_cortex_deprecated,
10
+ )
7
11
  from snowflake.ml._internal import telemetry
8
12
 
13
+ _AI_EMBED_FUNCTION_NAME = "AI_EMBED"
14
+
9
15
 
10
16
  @telemetry.send_api_usage_telemetry(
11
17
  project=CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
@@ -26,8 +32,8 @@ def embed_text_768(
26
32
  Returns:
27
33
  A column of vectors containing embeddings.
28
34
  """
29
-
30
- return _embed_text_768_impl("snowflake.cortex.embed_text_768", model, text, session=session)
35
+ warn_cortex_deprecated("embed_text_768", "ai_embed")
36
+ return _embed_text_768_impl(_AI_EMBED_FUNCTION_NAME, model, text, session=session)
31
37
 
32
38
 
33
39
  def _embed_text_768_impl(
@@ -36,7 +42,7 @@ def _embed_text_768_impl(
36
42
  text: str | snowpark.Column,
37
43
  session: snowpark.Session | None = None,
38
44
  ) -> list[float] | snowpark.Column:
39
- return cast(Union[list[float], snowpark.Column], call_sql_function(function, session, model, text))
45
+ return cast(list[float] | snowpark.Column, call_sql_function(function, session, model, text))
40
46
 
41
47
 
42
48
  EmbedText768 = deprecated(
@@ -1,10 +1,39 @@
1
- from typing import Union, cast
1
+ from typing import Any, cast
2
2
 
3
3
  from typing_extensions import deprecated
4
4
 
5
5
  from snowflake import snowpark
6
- from snowflake.cortex._util import CORTEX_FUNCTIONS_TELEMETRY_PROJECT, call_sql_function
6
+ from snowflake.cortex._util import (
7
+ CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
8
+ SnowflakeAuthenticationException,
9
+ warn_cortex_deprecated,
10
+ )
7
11
  from snowflake.ml._internal import telemetry
12
+ from snowflake.snowpark import context, functions
13
+
14
+ _AI_EXTRACT_FUNCTION_NAME = "AI_EXTRACT"
15
+ _ANSWER_KEY = "answer"
16
+
17
+
18
+ def _legacy_extract_answer_column(
19
+ from_text: str | snowpark.Column,
20
+ response_format: dict[str, Any] | snowpark.Column,
21
+ ) -> snowpark.Column:
22
+ """Call AI_EXTRACT and reshape it to the legacy EXTRACT_ANSWER [{"answer": ..., "score": ...}] ARRAY."""
23
+ # AI_EXTRACT returns the score only when the `scores` argument is TRUE, and `scores` is a named-only
24
+ # argument that the Snowpark builtin wrapper can't pass positionally -- inject it as a raw SQL fragment.
25
+ # AI_EXTRACT(<text>, <response_format>, scores => TRUE) returns:
26
+ # {"response": {"answer": ...}, "scoring": {"scores": {"answer": {"score": ...}}}}
27
+ extracted = functions.builtin(_AI_EXTRACT_FUNCTION_NAME)(
28
+ from_text, response_format, functions.sql_expr("scores => TRUE")
29
+ )
30
+ # Subscript access (not functions.get with a bare string, which Snowpark treats as a column
31
+ # identifier) extracts fields by literal key.
32
+ answer = extracted["response"][_ANSWER_KEY]
33
+ score = extracted["scoring"]["scores"][_ANSWER_KEY]["score"]
34
+ return functions.array_construct(
35
+ functions.object_construct(functions.lit(_ANSWER_KEY), answer, functions.lit("score"), score)
36
+ )
8
37
 
9
38
 
10
39
  @telemetry.send_api_usage_telemetry(
@@ -23,19 +52,32 @@ def extract_answer(
23
52
  session: The snowpark session to use. Will be inferred by context if not specified.
24
53
 
25
54
  Returns:
26
- A column of strings containing answers.
27
- """
55
+ A column of arrays, each [{"answer": ..., "score": ...}], matching legacy EXTRACT_ANSWER.
28
56
 
29
- return _extract_answer_impl("snowflake.cortex.extract_answer", from_text, question, session=session)
57
+ Raises:
58
+ SnowflakeAuthenticationException: if no session is provided and none is active.
59
+ """
60
+ warn_cortex_deprecated("extract_answer", "ai_extract")
61
+ if isinstance(question, snowpark.Column):
62
+ response_format: dict[str, Any] | snowpark.Column = functions.object_construct(
63
+ functions.lit(_ANSWER_KEY), question
64
+ )
65
+ else:
66
+ response_format = {_ANSWER_KEY: question}
30
67
 
68
+ column = _legacy_extract_answer_column(from_text, response_format)
69
+ if isinstance(from_text, snowpark.Column) or isinstance(question, snowpark.Column):
70
+ return column
31
71
 
32
- def _extract_answer_impl(
33
- function: str,
34
- from_text: str | snowpark.Column,
35
- question: str | snowpark.Column,
36
- session: snowpark.Session | None = None,
37
- ) -> str | snowpark.Column:
38
- return cast(Union[str, snowpark.Column], call_sql_function(function, session, from_text, question))
72
+ # Immediate (literal) path: evaluate the expression and return the collected ARRAY as a JSON string,
73
+ # exactly as the legacy EXTRACT_ANSWER immediate path did.
74
+ session = session or context.get_active_session()
75
+ if session is None:
76
+ raise SnowflakeAuthenticationException(
77
+ """Session required. Provide the session through a session=... argument or ensure an active session is
78
+ available in your environment."""
79
+ )
80
+ return cast(str, session.create_dataframe([snowpark.Row()]).select(column).collect()[0][0])
39
81
 
40
82
 
41
83
  ExtractAnswer = deprecated(
@@ -3,8 +3,43 @@ from typing import cast
3
3
  from typing_extensions import deprecated
4
4
 
5
5
  from snowflake import snowpark
6
- from snowflake.cortex._util import CORTEX_FUNCTIONS_TELEMETRY_PROJECT, call_sql_function
6
+ from snowflake.cortex._util import (
7
+ CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
8
+ SnowflakeAuthenticationException,
9
+ warn_cortex_deprecated,
10
+ )
7
11
  from snowflake.ml._internal import telemetry
12
+ from snowflake.snowpark import context, functions, types
13
+
14
+ _AI_COMPLETE_FUNCTION_NAME = "AI_COMPLETE"
15
+
16
+ # AI_SENTIMENT returns a categorical label (positive/negative/neutral/mixed), not the legacy FLOAT in
17
+ # [-1, 1] that SNOWFLAKE.CORTEX.SENTIMENT produced. To keep the numeric return contract we score the
18
+ # text with AI_COMPLETE and a constrained prompt instead. The score is model-derived, so exact values
19
+ # differ from the legacy sentiment model.
20
+ _SENTIMENT_MODEL = "llama3.1-8b"
21
+ _SENTIMENT_PROMPT_PREFIX = (
22
+ "Rate the sentiment of the following text from -1.0 (very negative) to 1.0 (very positive). "
23
+ "Respond with only the score. Text: "
24
+ )
25
+ _SENTIMENT_MODEL_PARAMETERS = {"temperature": 0}
26
+ _SENTIMENT_RESPONSE_FORMAT = {
27
+ "type": "json",
28
+ "schema": {"type": "object", "properties": {"score": {"type": "number"}}, "required": ["score"]},
29
+ }
30
+
31
+
32
+ def _sentiment_score_column(text: snowpark.Column) -> snowpark.Column:
33
+ """Build the AI_COMPLETE sentiment-score expression, clamped to the legacy [-1, 1] range."""
34
+ prompt = functions.concat(functions.lit(_SENTIMENT_PROMPT_PREFIX), text)
35
+ completion = functions.builtin(_AI_COMPLETE_FUNCTION_NAME)(
36
+ functions.lit(_SENTIMENT_MODEL),
37
+ prompt,
38
+ _SENTIMENT_MODEL_PARAMETERS,
39
+ _SENTIMENT_RESPONSE_FORMAT,
40
+ )
41
+ score = functions.get(functions.parse_json(completion), functions.lit("score")).cast(types.FloatType())
42
+ return functions.least(functions.lit(1.0), functions.greatest(functions.lit(-1.0), score))
8
43
 
9
44
 
10
45
  @telemetry.send_api_usage_telemetry(
@@ -19,17 +54,22 @@ def sentiment(text: str | snowpark.Column, session: snowpark.Session | None = No
19
54
 
20
55
  Returns:
21
56
  A column of floats. 1 represents positive sentiment, -1 represents negative sentiment.
22
- """
23
- return _sentiment_impl("snowflake.cortex.sentiment", text, session=session)
24
57
 
58
+ Raises:
59
+ SnowflakeAuthenticationException: if no session is provided and none is active.
60
+ """
61
+ warn_cortex_deprecated("sentiment", "ai_complete")
62
+ if isinstance(text, snowpark.Column):
63
+ return _sentiment_score_column(text)
25
64
 
26
- def _sentiment_impl(
27
- function: str, text: str | snowpark.Column, session: snowpark.Session | None = None
28
- ) -> float | snowpark.Column:
29
-
30
- output = call_sql_function(function, session, text)
31
- if isinstance(output, snowpark.Column):
32
- return output
65
+ session = session or context.get_active_session()
66
+ if session is None:
67
+ raise SnowflakeAuthenticationException(
68
+ """Session required. Provide the session through a session=... argument or ensure an active session is
69
+ available in your environment."""
70
+ )
71
+ score_column = _sentiment_score_column(functions.lit(text))
72
+ output = session.create_dataframe([snowpark.Row()]).select(score_column).collect()[0][0]
33
73
  return float(cast(str, output))
34
74
 
35
75
 
@@ -1,11 +1,17 @@
1
- from typing import Union, cast
1
+ from typing import cast
2
2
 
3
3
  from typing_extensions import deprecated
4
4
 
5
5
  from snowflake import snowpark
6
- from snowflake.cortex._util import CORTEX_FUNCTIONS_TELEMETRY_PROJECT, call_sql_function
6
+ from snowflake.cortex._util import (
7
+ CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
8
+ call_sql_function,
9
+ warn_cortex_deprecated,
10
+ )
7
11
  from snowflake.ml._internal import telemetry
8
12
 
13
+ _AI_SUMMARIZE_FUNCTION_NAME = "AI_SUMMARIZE"
14
+
9
15
 
10
16
  @telemetry.send_api_usage_telemetry(
11
17
  project=CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
@@ -23,8 +29,8 @@ def summarize(
23
29
  Returns:
24
30
  A column of string summaries.
25
31
  """
26
-
27
- return _summarize_impl("snowflake.cortex.summarize", text, session=session)
32
+ warn_cortex_deprecated("summarize", "ai_complete")
33
+ return _summarize_impl(_AI_SUMMARIZE_FUNCTION_NAME, text, session=session)
28
34
 
29
35
 
30
36
  def _summarize_impl(
@@ -32,7 +38,7 @@ def _summarize_impl(
32
38
  text: str | snowpark.Column,
33
39
  session: snowpark.Session | None = None,
34
40
  ) -> str | snowpark.Column:
35
- return cast(Union[str, snowpark.Column], call_sql_function(function, session, text))
41
+ return cast(str | snowpark.Column, call_sql_function(function, session, text))
36
42
 
37
43
 
38
44
  Summarize = deprecated("Summarize() is deprecated and will be removed in a future release. Use summarize() instead")(
@@ -1,11 +1,17 @@
1
- from typing import Union, cast
1
+ from typing import cast
2
2
 
3
3
  from typing_extensions import deprecated
4
4
 
5
5
  from snowflake import snowpark
6
- from snowflake.cortex._util import CORTEX_FUNCTIONS_TELEMETRY_PROJECT, call_sql_function
6
+ from snowflake.cortex._util import (
7
+ CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
8
+ call_sql_function,
9
+ warn_cortex_deprecated,
10
+ )
7
11
  from snowflake.ml._internal import telemetry
8
12
 
13
+ _AI_TRANSLATE_FUNCTION_NAME = "AI_TRANSLATE"
14
+
9
15
 
10
16
  @telemetry.send_api_usage_telemetry(
11
17
  project=CORTEX_FUNCTIONS_TELEMETRY_PROJECT,
@@ -27,8 +33,8 @@ def translate(
27
33
  Returns:
28
34
  A column of string translations.
29
35
  """
30
-
31
- return _translate_impl("snowflake.cortex.translate", text, from_language, to_language, session=session)
36
+ warn_cortex_deprecated("translate", "ai_translate")
37
+ return _translate_impl(_AI_TRANSLATE_FUNCTION_NAME, text, from_language, to_language, session=session)
32
38
 
33
39
 
34
40
  def _translate_impl(
@@ -38,7 +44,7 @@ def _translate_impl(
38
44
  to_language: str | snowpark.Column,
39
45
  session: snowpark.Session | None = None,
40
46
  ) -> str | snowpark.Column:
41
- return cast(Union[str, snowpark.Column], call_sql_function(function, session, text, from_language, to_language))
47
+ return cast(str | snowpark.Column, call_sql_function(function, session, text, from_language, to_language))
42
48
 
43
49
 
44
50
  Translate = deprecated("Translate() is deprecated and will be removed in a future release. Use translate() instead")(
snowflake/cortex/_util.py CHANGED
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from typing import Any, Union, cast
2
3
 
3
4
  from snowflake import snowpark
@@ -8,6 +9,40 @@ from snowflake.snowpark import context, functions
8
9
  CORTEX_FUNCTIONS_TELEMETRY_PROJECT = "CortexFunctions"
9
10
 
10
11
 
12
+ _SNOWPARK_FUNCTIONS_DOC_URL = (
13
+ "https://docs.snowflake.com/en/developer-guide/snowpark/reference/python/latest/"
14
+ "snowpark/api/snowflake.snowpark.functions.{replacement}"
15
+ )
16
+ _MIGRATION_GUIDE_URL = "https://docs.snowflake.com/en/user-guide/snowflake-cortex/aisql-migrate-legacy-functions"
17
+
18
+ # Functions that have already emitted their deprecation warning this process. Guarding on this keeps a
19
+ # repeated call (e.g. inside a loop) from spamming the console -- each deprecated function warns once.
20
+ _emitted_deprecations: set[str] = set()
21
+
22
+
23
+ def warn_cortex_deprecated(name: str, replacement: str) -> None:
24
+ """Emit the deprecation warning for the named deprecated snowflake.cortex function, once per process.
25
+
26
+ Args:
27
+ name: The deprecated snowflake.cortex function name (e.g. "classify_text").
28
+ replacement: The snowflake.snowpark.functions replacement to point callers at (e.g. "ai_classify").
29
+ """
30
+ # Warn on use (not on package import) so callers of functions that are NOT deprecated -- e.g.
31
+ # Finetune -- are never warned.
32
+ if name in _emitted_deprecations:
33
+ return
34
+ _emitted_deprecations.add(name)
35
+ warnings.warn(
36
+ f"snowflake.cortex.{name} is deprecated and will be removed in a future release. "
37
+ f"Its backend is now redirected to Snowflake AI Functions. "
38
+ f"Use snowflake.snowpark.functions.{replacement} instead. "
39
+ f"API: {_SNOWPARK_FUNCTIONS_DOC_URL.format(replacement=replacement)} "
40
+ f"Migration guide: {_MIGRATION_GUIDE_URL}",
41
+ DeprecationWarning,
42
+ stacklevel=3,
43
+ )
44
+
45
+
11
46
  class SnowflakeAuthenticationException(Exception):
12
47
  """This exception is raised when there is an issue with Snowflake's configuration."""
13
48
 
@@ -90,6 +90,9 @@ def _validate_pip_requirement_string(req_str: str) -> requirements.Requirement:
90
90
  Returns:
91
91
  A requirements.Requirement object containing the requirement information.
92
92
  """
93
+ # A requirement string must not carry surrounding whitespace: packaging>=26 accepts only a
94
+ # requirement that ends at the end of the string, so a trailing newline is a parse error.
95
+ req_str = req_str.strip()
93
96
  try:
94
97
  r = requirements.Requirement(req_str)
95
98
 
@@ -780,8 +783,9 @@ def load_requirements_file(path: pathlib.Path) -> list[requirements.Requirement]
780
783
  with open(path, encoding="utf-8") as f:
781
784
  reqs = f.readlines()
782
785
 
783
- # Filter out pip options
784
- reqs = [line for line in reqs if line.strip() and not line.strip().startswith(("-", "#"))]
786
+ # "-" lines are pip options (e.g. --extra-index-url) and "#" lines are comments; neither is a
787
+ # requirement. Strip as well, since a requirement string must not keep readlines()' newline.
788
+ reqs = [stripped for line in reqs if (stripped := line.strip()) and not stripped.startswith(("-", "#"))]
785
789
 
786
790
  return validate_pip_requirement_string_list(reqs)
787
791
 
@@ -120,7 +120,21 @@ def make_archive(
120
120
  )
121
121
 
122
122
 
123
- def zip_python_package(zipfile_path: str, package_name: str, ignore_generated_py_file: bool = True) -> None:
123
+ def zip_python_package(
124
+ zipfile_path: str,
125
+ package_name: str,
126
+ ignore_generated_py_file: bool = True,
127
+ *,
128
+ exclude_dir_names: tuple[str, ...] = (),
129
+ ) -> None:
130
+ """Zip an imported Python package.
131
+
132
+ Args:
133
+ zipfile_path: Destination zip path.
134
+ package_name: Dotted package name to zip.
135
+ ignore_generated_py_file: When True, omit generated bytecode and stub files.
136
+ exclude_dir_names: Directory names to omit anywhere in the package. The directory entry itself is not written.
137
+ """
124
138
  import importlib_resources
125
139
  from importlib_resources import abc as importlib_resources_abc
126
140
 
@@ -131,6 +145,7 @@ def zip_python_package(zipfile_path: str, package_name: str, ignore_generated_py
131
145
 
132
146
  base_dirs = package_name.split(".")
133
147
  assert len(base_dirs) >= 1, "Invalid package name."
148
+ excluded_dirs = set(exclude_dir_names)
134
149
 
135
150
  with zipfile.ZipFile(zipfile_path, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
136
151
  for i in range(len(base_dirs)):
@@ -141,6 +156,8 @@ def zip_python_package(zipfile_path: str, package_name: str, ignore_generated_py
141
156
  def _add_to_zip(
142
157
  zf: zipfile.ZipFile, path_info: importlib_resources_abc.Traversable, base_arcname: pathlib.PurePosixPath
143
158
  ) -> None:
159
+ if path_info.name in excluded_dirs:
160
+ return
144
161
  if path_info.is_file():
145
162
  if ignore_generated_py_file and path_info.name.endswith(GENERATED_PY_FILE_EXT):
146
163
  return