snowflake-ml-python 1.8.2__py3-none-any.whl → 1.8.4__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 (322) hide show
  1. snowflake/cortex/__init__.py +7 -1
  2. snowflake/cortex/_classify_text.py +3 -3
  3. snowflake/cortex/_complete.py +23 -24
  4. snowflake/cortex/_embed_text_1024.py +4 -4
  5. snowflake/cortex/_embed_text_768.py +4 -4
  6. snowflake/cortex/_finetune.py +8 -8
  7. snowflake/cortex/_util.py +8 -12
  8. snowflake/ml/_internal/env.py +4 -3
  9. snowflake/ml/_internal/env_utils.py +63 -34
  10. snowflake/ml/_internal/file_utils.py +10 -21
  11. snowflake/ml/_internal/human_readable_id/hrid_generator_base.py +5 -7
  12. snowflake/ml/_internal/init_utils.py +2 -3
  13. snowflake/ml/_internal/lineage/lineage_utils.py +6 -6
  14. snowflake/ml/_internal/platform_capabilities.py +18 -16
  15. snowflake/ml/_internal/telemetry.py +39 -52
  16. snowflake/ml/_internal/type_utils.py +3 -3
  17. snowflake/ml/_internal/utils/db_utils.py +2 -2
  18. snowflake/ml/_internal/utils/identifier.py +10 -10
  19. snowflake/ml/_internal/utils/import_utils.py +2 -2
  20. snowflake/ml/_internal/utils/parallelize.py +7 -7
  21. snowflake/ml/_internal/utils/pkg_version_utils.py +11 -11
  22. snowflake/ml/_internal/utils/query_result_checker.py +4 -4
  23. snowflake/ml/_internal/utils/snowflake_env.py +28 -6
  24. snowflake/ml/_internal/utils/snowpark_dataframe_utils.py +2 -2
  25. snowflake/ml/_internal/utils/sql_identifier.py +3 -3
  26. snowflake/ml/_internal/utils/table_manager.py +9 -9
  27. snowflake/ml/data/_internal/arrow_ingestor.py +7 -7
  28. snowflake/ml/data/data_connector.py +15 -36
  29. snowflake/ml/data/data_ingestor.py +4 -15
  30. snowflake/ml/data/data_source.py +2 -2
  31. snowflake/ml/data/ingestor_utils.py +3 -3
  32. snowflake/ml/data/torch_utils.py +5 -5
  33. snowflake/ml/dataset/dataset.py +11 -11
  34. snowflake/ml/dataset/dataset_metadata.py +8 -8
  35. snowflake/ml/dataset/dataset_reader.py +7 -7
  36. snowflake/ml/feature_store/__init__.py +1 -1
  37. snowflake/ml/feature_store/access_manager.py +7 -7
  38. snowflake/ml/feature_store/entity.py +6 -6
  39. snowflake/ml/feature_store/examples/airline_features/entities.py +1 -3
  40. snowflake/ml/feature_store/examples/airline_features/features/plane_features.py +1 -3
  41. snowflake/ml/feature_store/examples/airline_features/features/weather_features.py +1 -3
  42. snowflake/ml/feature_store/examples/citibike_trip_features/entities.py +1 -3
  43. snowflake/ml/feature_store/examples/citibike_trip_features/features/station_feature.py +1 -3
  44. snowflake/ml/feature_store/examples/citibike_trip_features/features/trip_feature.py +1 -3
  45. snowflake/ml/feature_store/examples/example_helper.py +16 -16
  46. snowflake/ml/feature_store/examples/new_york_taxi_features/entities.py +1 -3
  47. snowflake/ml/feature_store/examples/new_york_taxi_features/features/location_features.py +1 -3
  48. snowflake/ml/feature_store/examples/new_york_taxi_features/features/trip_features.py +1 -3
  49. snowflake/ml/feature_store/examples/wine_quality_features/entities.py +1 -3
  50. snowflake/ml/feature_store/examples/wine_quality_features/features/managed_wine_features.py +1 -3
  51. snowflake/ml/feature_store/examples/wine_quality_features/features/static_wine_features.py +1 -3
  52. snowflake/ml/feature_store/feature_store.py +52 -64
  53. snowflake/ml/feature_store/feature_view.py +24 -24
  54. snowflake/ml/fileset/embedded_stage_fs.py +5 -5
  55. snowflake/ml/fileset/fileset.py +5 -5
  56. snowflake/ml/fileset/sfcfs.py +13 -13
  57. snowflake/ml/fileset/stage_fs.py +15 -15
  58. snowflake/ml/jobs/_utils/constants.py +1 -1
  59. snowflake/ml/jobs/_utils/interop_utils.py +10 -10
  60. snowflake/ml/jobs/_utils/payload_utils.py +45 -46
  61. snowflake/ml/jobs/_utils/scripts/get_instance_ip.py +4 -4
  62. snowflake/ml/jobs/_utils/scripts/mljob_launcher.py +8 -5
  63. snowflake/ml/jobs/_utils/scripts/signal_workers.py +8 -8
  64. snowflake/ml/jobs/_utils/spec_utils.py +18 -29
  65. snowflake/ml/jobs/_utils/types.py +2 -2
  66. snowflake/ml/jobs/decorators.py +10 -5
  67. snowflake/ml/jobs/job.py +87 -30
  68. snowflake/ml/jobs/manager.py +86 -56
  69. snowflake/ml/lineage/lineage_node.py +5 -5
  70. snowflake/ml/model/_client/model/model_impl.py +3 -3
  71. snowflake/ml/model/_client/model/model_version_impl.py +103 -35
  72. snowflake/ml/model/_client/ops/metadata_ops.py +7 -7
  73. snowflake/ml/model/_client/ops/model_ops.py +41 -41
  74. snowflake/ml/model/_client/ops/service_ops.py +217 -32
  75. snowflake/ml/model/_client/service/model_deployment_spec.py +359 -65
  76. snowflake/ml/model/_client/service/model_deployment_spec_schema.py +69 -24
  77. snowflake/ml/model/_client/sql/model.py +8 -8
  78. snowflake/ml/model/_client/sql/model_version.py +26 -26
  79. snowflake/ml/model/_client/sql/service.py +17 -26
  80. snowflake/ml/model/_client/sql/stage.py +2 -2
  81. snowflake/ml/model/_client/sql/tag.py +6 -6
  82. snowflake/ml/model/_model_composer/model_composer.py +58 -32
  83. snowflake/ml/model/_model_composer/model_manifest/model_manifest.py +20 -16
  84. snowflake/ml/model/_model_composer/model_manifest/model_manifest_schema.py +14 -13
  85. snowflake/ml/model/_model_composer/model_method/model_method.py +3 -3
  86. snowflake/ml/model/_packager/model_env/model_env.py +28 -25
  87. snowflake/ml/model/_packager/model_handler.py +4 -4
  88. snowflake/ml/model/_packager/model_handlers/_base.py +2 -2
  89. snowflake/ml/model/_packager/model_handlers/_utils.py +47 -5
  90. snowflake/ml/model/_packager/model_handlers/catboost.py +5 -5
  91. snowflake/ml/model/_packager/model_handlers/custom.py +9 -5
  92. snowflake/ml/model/_packager/model_handlers/huggingface_pipeline.py +7 -21
  93. snowflake/ml/model/_packager/model_handlers/keras.py +4 -4
  94. snowflake/ml/model/_packager/model_handlers/lightgbm.py +4 -14
  95. snowflake/ml/model/_packager/model_handlers/mlflow.py +3 -3
  96. snowflake/ml/model/_packager/model_handlers/pytorch.py +5 -6
  97. snowflake/ml/model/_packager/model_handlers/sentence_transformers.py +5 -5
  98. snowflake/ml/model/_packager/model_handlers/sklearn.py +104 -46
  99. snowflake/ml/model/_packager/model_handlers/snowmlmodel.py +3 -3
  100. snowflake/ml/model/_packager/model_handlers/tensorflow.py +11 -8
  101. snowflake/ml/model/_packager/model_handlers/torchscript.py +6 -6
  102. snowflake/ml/model/_packager/model_handlers/xgboost.py +21 -22
  103. snowflake/ml/model/_packager/model_meta/model_blob_meta.py +2 -2
  104. snowflake/ml/model/_packager/model_meta/model_meta.py +39 -38
  105. snowflake/ml/model/_packager/model_meta/model_meta_schema.py +14 -11
  106. snowflake/ml/model/_packager/model_meta_migrator/base_migrator.py +3 -3
  107. snowflake/ml/model/_packager/model_meta_migrator/migrator_plans.py +3 -3
  108. snowflake/ml/model/_packager/model_meta_migrator/migrator_v1.py +4 -4
  109. snowflake/ml/model/_packager/model_packager.py +11 -9
  110. snowflake/ml/model/_packager/model_runtime/_snowml_inference_alternative_requirements.py +32 -1
  111. snowflake/ml/model/_packager/model_runtime/model_runtime.py +4 -2
  112. snowflake/ml/model/_signatures/core.py +16 -24
  113. snowflake/ml/model/_signatures/dmatrix_handler.py +17 -4
  114. snowflake/ml/model/_signatures/utils.py +6 -6
  115. snowflake/ml/model/custom_model.py +24 -11
  116. snowflake/ml/model/model_signature.py +12 -23
  117. snowflake/ml/model/models/huggingface_pipeline.py +7 -4
  118. snowflake/ml/model/type_hints.py +3 -3
  119. snowflake/ml/modeling/_internal/estimator_utils.py +7 -7
  120. snowflake/ml/modeling/_internal/local_implementations/pandas_handlers.py +6 -6
  121. snowflake/ml/modeling/_internal/local_implementations/pandas_trainer.py +7 -7
  122. snowflake/ml/modeling/_internal/model_specifications.py +8 -10
  123. snowflake/ml/modeling/_internal/model_trainer.py +5 -5
  124. snowflake/ml/modeling/_internal/model_trainer_builder.py +6 -6
  125. snowflake/ml/modeling/_internal/snowpark_implementations/distributed_hpo_trainer.py +30 -30
  126. snowflake/ml/modeling/_internal/snowpark_implementations/snowpark_handlers.py +13 -13
  127. snowflake/ml/modeling/_internal/snowpark_implementations/snowpark_trainer.py +31 -31
  128. snowflake/ml/modeling/_internal/snowpark_implementations/xgboost_external_memory_trainer.py +19 -19
  129. snowflake/ml/modeling/_internal/transformer_protocols.py +17 -17
  130. snowflake/ml/modeling/calibration/calibrated_classifier_cv.py +9 -1
  131. snowflake/ml/modeling/cluster/affinity_propagation.py +9 -1
  132. snowflake/ml/modeling/cluster/agglomerative_clustering.py +9 -1
  133. snowflake/ml/modeling/cluster/birch.py +9 -1
  134. snowflake/ml/modeling/cluster/bisecting_k_means.py +9 -1
  135. snowflake/ml/modeling/cluster/dbscan.py +9 -1
  136. snowflake/ml/modeling/cluster/feature_agglomeration.py +9 -1
  137. snowflake/ml/modeling/cluster/k_means.py +9 -1
  138. snowflake/ml/modeling/cluster/mean_shift.py +9 -1
  139. snowflake/ml/modeling/cluster/mini_batch_k_means.py +9 -1
  140. snowflake/ml/modeling/cluster/optics.py +9 -1
  141. snowflake/ml/modeling/cluster/spectral_biclustering.py +9 -1
  142. snowflake/ml/modeling/cluster/spectral_clustering.py +9 -1
  143. snowflake/ml/modeling/cluster/spectral_coclustering.py +9 -1
  144. snowflake/ml/modeling/compose/column_transformer.py +9 -1
  145. snowflake/ml/modeling/compose/transformed_target_regressor.py +9 -1
  146. snowflake/ml/modeling/covariance/elliptic_envelope.py +9 -1
  147. snowflake/ml/modeling/covariance/empirical_covariance.py +9 -1
  148. snowflake/ml/modeling/covariance/graphical_lasso.py +9 -1
  149. snowflake/ml/modeling/covariance/graphical_lasso_cv.py +9 -1
  150. snowflake/ml/modeling/covariance/ledoit_wolf.py +9 -1
  151. snowflake/ml/modeling/covariance/min_cov_det.py +9 -1
  152. snowflake/ml/modeling/covariance/oas.py +9 -1
  153. snowflake/ml/modeling/covariance/shrunk_covariance.py +9 -1
  154. snowflake/ml/modeling/decomposition/dictionary_learning.py +9 -1
  155. snowflake/ml/modeling/decomposition/factor_analysis.py +9 -1
  156. snowflake/ml/modeling/decomposition/fast_ica.py +9 -1
  157. snowflake/ml/modeling/decomposition/incremental_pca.py +9 -1
  158. snowflake/ml/modeling/decomposition/kernel_pca.py +9 -1
  159. snowflake/ml/modeling/decomposition/mini_batch_dictionary_learning.py +9 -1
  160. snowflake/ml/modeling/decomposition/mini_batch_sparse_pca.py +9 -1
  161. snowflake/ml/modeling/decomposition/pca.py +9 -1
  162. snowflake/ml/modeling/decomposition/sparse_pca.py +9 -1
  163. snowflake/ml/modeling/decomposition/truncated_svd.py +9 -1
  164. snowflake/ml/modeling/discriminant_analysis/linear_discriminant_analysis.py +9 -1
  165. snowflake/ml/modeling/discriminant_analysis/quadratic_discriminant_analysis.py +9 -1
  166. snowflake/ml/modeling/ensemble/ada_boost_classifier.py +9 -1
  167. snowflake/ml/modeling/ensemble/ada_boost_regressor.py +9 -1
  168. snowflake/ml/modeling/ensemble/bagging_classifier.py +9 -1
  169. snowflake/ml/modeling/ensemble/bagging_regressor.py +9 -1
  170. snowflake/ml/modeling/ensemble/extra_trees_classifier.py +9 -1
  171. snowflake/ml/modeling/ensemble/extra_trees_regressor.py +9 -1
  172. snowflake/ml/modeling/ensemble/gradient_boosting_classifier.py +9 -1
  173. snowflake/ml/modeling/ensemble/gradient_boosting_regressor.py +9 -1
  174. snowflake/ml/modeling/ensemble/hist_gradient_boosting_classifier.py +9 -1
  175. snowflake/ml/modeling/ensemble/hist_gradient_boosting_regressor.py +9 -1
  176. snowflake/ml/modeling/ensemble/isolation_forest.py +9 -1
  177. snowflake/ml/modeling/ensemble/random_forest_classifier.py +9 -1
  178. snowflake/ml/modeling/ensemble/random_forest_regressor.py +9 -1
  179. snowflake/ml/modeling/ensemble/stacking_regressor.py +9 -1
  180. snowflake/ml/modeling/ensemble/voting_classifier.py +9 -1
  181. snowflake/ml/modeling/ensemble/voting_regressor.py +9 -1
  182. snowflake/ml/modeling/feature_selection/generic_univariate_select.py +9 -1
  183. snowflake/ml/modeling/feature_selection/select_fdr.py +9 -1
  184. snowflake/ml/modeling/feature_selection/select_fpr.py +9 -1
  185. snowflake/ml/modeling/feature_selection/select_fwe.py +9 -1
  186. snowflake/ml/modeling/feature_selection/select_k_best.py +9 -1
  187. snowflake/ml/modeling/feature_selection/select_percentile.py +9 -1
  188. snowflake/ml/modeling/feature_selection/sequential_feature_selector.py +9 -1
  189. snowflake/ml/modeling/feature_selection/variance_threshold.py +9 -1
  190. snowflake/ml/modeling/framework/_utils.py +10 -10
  191. snowflake/ml/modeling/framework/base.py +32 -32
  192. snowflake/ml/modeling/gaussian_process/gaussian_process_classifier.py +9 -1
  193. snowflake/ml/modeling/gaussian_process/gaussian_process_regressor.py +9 -1
  194. snowflake/ml/modeling/impute/__init__.py +1 -1
  195. snowflake/ml/modeling/impute/iterative_imputer.py +9 -1
  196. snowflake/ml/modeling/impute/knn_imputer.py +9 -1
  197. snowflake/ml/modeling/impute/missing_indicator.py +9 -1
  198. snowflake/ml/modeling/impute/simple_imputer.py +5 -5
  199. snowflake/ml/modeling/kernel_approximation/additive_chi2_sampler.py +9 -1
  200. snowflake/ml/modeling/kernel_approximation/nystroem.py +9 -1
  201. snowflake/ml/modeling/kernel_approximation/polynomial_count_sketch.py +9 -1
  202. snowflake/ml/modeling/kernel_approximation/rbf_sampler.py +9 -1
  203. snowflake/ml/modeling/kernel_approximation/skewed_chi2_sampler.py +9 -1
  204. snowflake/ml/modeling/kernel_ridge/kernel_ridge.py +9 -1
  205. snowflake/ml/modeling/lightgbm/lgbm_classifier.py +9 -1
  206. snowflake/ml/modeling/lightgbm/lgbm_regressor.py +9 -1
  207. snowflake/ml/modeling/linear_model/ard_regression.py +9 -1
  208. snowflake/ml/modeling/linear_model/bayesian_ridge.py +9 -1
  209. snowflake/ml/modeling/linear_model/elastic_net.py +9 -1
  210. snowflake/ml/modeling/linear_model/elastic_net_cv.py +9 -1
  211. snowflake/ml/modeling/linear_model/gamma_regressor.py +9 -1
  212. snowflake/ml/modeling/linear_model/huber_regressor.py +9 -1
  213. snowflake/ml/modeling/linear_model/lars.py +9 -1
  214. snowflake/ml/modeling/linear_model/lars_cv.py +9 -1
  215. snowflake/ml/modeling/linear_model/lasso.py +9 -1
  216. snowflake/ml/modeling/linear_model/lasso_cv.py +9 -1
  217. snowflake/ml/modeling/linear_model/lasso_lars.py +9 -1
  218. snowflake/ml/modeling/linear_model/lasso_lars_cv.py +9 -1
  219. snowflake/ml/modeling/linear_model/lasso_lars_ic.py +9 -1
  220. snowflake/ml/modeling/linear_model/linear_regression.py +9 -1
  221. snowflake/ml/modeling/linear_model/logistic_regression.py +9 -1
  222. snowflake/ml/modeling/linear_model/logistic_regression_cv.py +9 -1
  223. snowflake/ml/modeling/linear_model/multi_task_elastic_net.py +9 -1
  224. snowflake/ml/modeling/linear_model/multi_task_elastic_net_cv.py +9 -1
  225. snowflake/ml/modeling/linear_model/multi_task_lasso.py +9 -1
  226. snowflake/ml/modeling/linear_model/multi_task_lasso_cv.py +9 -1
  227. snowflake/ml/modeling/linear_model/orthogonal_matching_pursuit.py +9 -1
  228. snowflake/ml/modeling/linear_model/passive_aggressive_classifier.py +9 -1
  229. snowflake/ml/modeling/linear_model/passive_aggressive_regressor.py +9 -1
  230. snowflake/ml/modeling/linear_model/perceptron.py +9 -1
  231. snowflake/ml/modeling/linear_model/poisson_regressor.py +9 -1
  232. snowflake/ml/modeling/linear_model/ransac_regressor.py +9 -1
  233. snowflake/ml/modeling/linear_model/ridge.py +9 -1
  234. snowflake/ml/modeling/linear_model/ridge_classifier.py +9 -1
  235. snowflake/ml/modeling/linear_model/ridge_classifier_cv.py +9 -1
  236. snowflake/ml/modeling/linear_model/ridge_cv.py +9 -1
  237. snowflake/ml/modeling/linear_model/sgd_classifier.py +9 -1
  238. snowflake/ml/modeling/linear_model/sgd_one_class_svm.py +9 -1
  239. snowflake/ml/modeling/linear_model/sgd_regressor.py +9 -1
  240. snowflake/ml/modeling/linear_model/theil_sen_regressor.py +9 -1
  241. snowflake/ml/modeling/linear_model/tweedie_regressor.py +9 -1
  242. snowflake/ml/modeling/manifold/isomap.py +9 -1
  243. snowflake/ml/modeling/manifold/mds.py +9 -1
  244. snowflake/ml/modeling/manifold/spectral_embedding.py +9 -1
  245. snowflake/ml/modeling/manifold/tsne.py +9 -1
  246. snowflake/ml/modeling/metrics/__init__.py +1 -1
  247. snowflake/ml/modeling/metrics/classification.py +39 -39
  248. snowflake/ml/modeling/metrics/metrics_utils.py +12 -12
  249. snowflake/ml/modeling/metrics/ranking.py +7 -7
  250. snowflake/ml/modeling/metrics/regression.py +13 -13
  251. snowflake/ml/modeling/mixture/bayesian_gaussian_mixture.py +9 -1
  252. snowflake/ml/modeling/mixture/gaussian_mixture.py +9 -1
  253. snowflake/ml/modeling/model_selection/__init__.py +1 -1
  254. snowflake/ml/modeling/model_selection/grid_search_cv.py +7 -7
  255. snowflake/ml/modeling/model_selection/randomized_search_cv.py +7 -7
  256. snowflake/ml/modeling/multiclass/one_vs_one_classifier.py +9 -1
  257. snowflake/ml/modeling/multiclass/one_vs_rest_classifier.py +9 -1
  258. snowflake/ml/modeling/multiclass/output_code_classifier.py +9 -1
  259. snowflake/ml/modeling/naive_bayes/bernoulli_nb.py +9 -1
  260. snowflake/ml/modeling/naive_bayes/categorical_nb.py +9 -1
  261. snowflake/ml/modeling/naive_bayes/complement_nb.py +9 -1
  262. snowflake/ml/modeling/naive_bayes/gaussian_nb.py +9 -1
  263. snowflake/ml/modeling/naive_bayes/multinomial_nb.py +9 -1
  264. snowflake/ml/modeling/neighbors/k_neighbors_classifier.py +9 -1
  265. snowflake/ml/modeling/neighbors/k_neighbors_regressor.py +9 -1
  266. snowflake/ml/modeling/neighbors/kernel_density.py +9 -1
  267. snowflake/ml/modeling/neighbors/local_outlier_factor.py +9 -1
  268. snowflake/ml/modeling/neighbors/nearest_centroid.py +9 -1
  269. snowflake/ml/modeling/neighbors/nearest_neighbors.py +9 -1
  270. snowflake/ml/modeling/neighbors/neighborhood_components_analysis.py +9 -1
  271. snowflake/ml/modeling/neighbors/radius_neighbors_classifier.py +9 -1
  272. snowflake/ml/modeling/neighbors/radius_neighbors_regressor.py +9 -1
  273. snowflake/ml/modeling/neural_network/bernoulli_rbm.py +9 -1
  274. snowflake/ml/modeling/neural_network/mlp_classifier.py +9 -1
  275. snowflake/ml/modeling/neural_network/mlp_regressor.py +9 -1
  276. snowflake/ml/modeling/pipeline/__init__.py +1 -1
  277. snowflake/ml/modeling/pipeline/pipeline.py +18 -18
  278. snowflake/ml/modeling/preprocessing/__init__.py +1 -1
  279. snowflake/ml/modeling/preprocessing/k_bins_discretizer.py +13 -13
  280. snowflake/ml/modeling/preprocessing/max_abs_scaler.py +4 -4
  281. snowflake/ml/modeling/preprocessing/min_max_scaler.py +8 -8
  282. snowflake/ml/modeling/preprocessing/normalizer.py +0 -1
  283. snowflake/ml/modeling/preprocessing/one_hot_encoder.py +28 -28
  284. snowflake/ml/modeling/preprocessing/ordinal_encoder.py +9 -9
  285. snowflake/ml/modeling/preprocessing/polynomial_features.py +9 -1
  286. snowflake/ml/modeling/preprocessing/robust_scaler.py +7 -7
  287. snowflake/ml/modeling/preprocessing/standard_scaler.py +5 -5
  288. snowflake/ml/modeling/semi_supervised/label_propagation.py +9 -1
  289. snowflake/ml/modeling/semi_supervised/label_spreading.py +9 -1
  290. snowflake/ml/modeling/svm/linear_svc.py +9 -1
  291. snowflake/ml/modeling/svm/linear_svr.py +9 -1
  292. snowflake/ml/modeling/svm/nu_svc.py +9 -1
  293. snowflake/ml/modeling/svm/nu_svr.py +9 -1
  294. snowflake/ml/modeling/svm/svc.py +9 -1
  295. snowflake/ml/modeling/svm/svr.py +9 -1
  296. snowflake/ml/modeling/tree/decision_tree_classifier.py +9 -1
  297. snowflake/ml/modeling/tree/decision_tree_regressor.py +9 -1
  298. snowflake/ml/modeling/tree/extra_tree_classifier.py +9 -1
  299. snowflake/ml/modeling/tree/extra_tree_regressor.py +9 -1
  300. snowflake/ml/modeling/xgboost/xgb_classifier.py +9 -1
  301. snowflake/ml/modeling/xgboost/xgb_regressor.py +9 -1
  302. snowflake/ml/modeling/xgboost/xgbrf_classifier.py +9 -1
  303. snowflake/ml/modeling/xgboost/xgbrf_regressor.py +9 -1
  304. snowflake/ml/monitoring/_client/model_monitor_sql_client.py +26 -26
  305. snowflake/ml/monitoring/_manager/model_monitor_manager.py +5 -5
  306. snowflake/ml/monitoring/entities/model_monitor_config.py +6 -6
  307. snowflake/ml/monitoring/explain_visualize.py +286 -0
  308. snowflake/ml/registry/_manager/model_manager.py +55 -32
  309. snowflake/ml/registry/registry.py +39 -31
  310. snowflake/ml/utils/authentication.py +2 -2
  311. snowflake/ml/utils/connection_params.py +5 -5
  312. snowflake/ml/utils/sparse.py +5 -4
  313. snowflake/ml/utils/sql_client.py +1 -2
  314. snowflake/ml/version.py +2 -1
  315. {snowflake_ml_python-1.8.2.dist-info → snowflake_ml_python-1.8.4.dist-info}/METADATA +55 -14
  316. snowflake_ml_python-1.8.4.dist-info/RECORD +419 -0
  317. {snowflake_ml_python-1.8.2.dist-info → snowflake_ml_python-1.8.4.dist-info}/WHEEL +1 -1
  318. snowflake/ml/model/_packager/model_meta/_packaging_requirements.py +0 -1
  319. snowflake/ml/modeling/_internal/constants.py +0 -2
  320. snowflake_ml_python-1.8.2.dist-info/RECORD +0 -420
  321. {snowflake_ml_python-1.8.2.dist-info → snowflake_ml_python-1.8.4.dist-info}/licenses/LICENSE.txt +0 -0
  322. {snowflake_ml_python-1.8.2.dist-info → snowflake_ml_python-1.8.4.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from math import ceil
3
3
  from pathlib import PurePath
4
- from typing import Any, Dict, List, Optional, Union
4
+ from typing import Any, Optional, Union
5
5
 
6
6
  from snowflake import snowpark
7
7
  from snowflake.ml._internal.utils import snowflake_env
@@ -11,14 +11,11 @@ from snowflake.ml.jobs._utils import constants, types
11
11
  def _get_node_resources(session: snowpark.Session, compute_pool: str) -> types.ComputeResources:
12
12
  """Extract resource information for the specified compute pool"""
13
13
  # Get the instance family
14
- rows = session.sql(f"show compute pools like '{compute_pool}'").collect()
14
+ rows = session.sql("show compute pools like ?", params=[compute_pool]).collect()
15
15
  if not rows:
16
16
  raise ValueError(f"Compute pool '{compute_pool}' not found")
17
17
  instance_family: str = rows[0]["instance_family"]
18
-
19
- # Get the cloud we're using (AWS, Azure, etc)
20
- region = snowflake_env.get_regions(session)[snowflake_env.get_current_region_id(session)]
21
- cloud = region["cloud"]
18
+ cloud = snowflake_env.get_current_cloud(session, default=snowflake_env.SnowflakeCloudType.AWS)
22
19
 
23
20
  return (
24
21
  constants.COMMON_INSTANCE_FAMILIES.get(instance_family)
@@ -26,22 +23,14 @@ def _get_node_resources(session: snowpark.Session, compute_pool: str) -> types.C
26
23
  )
27
24
 
28
25
 
29
- def _get_image_spec(session: snowpark.Session, compute_pool: str, image_tag: Optional[str] = None) -> types.ImageSpec:
26
+ def _get_image_spec(session: snowpark.Session, compute_pool: str) -> types.ImageSpec:
30
27
  # Retrieve compute pool node resources
31
28
  resources = _get_node_resources(session, compute_pool=compute_pool)
32
29
 
33
30
  # Use MLRuntime image
34
31
  image_repo = constants.DEFAULT_IMAGE_REPO
35
32
  image_name = constants.DEFAULT_IMAGE_GPU if resources.gpu > 0 else constants.DEFAULT_IMAGE_CPU
36
-
37
- # Try to pull latest image tag from server side if possible
38
- if not image_tag:
39
- query_result = session.sql("SHOW PARAMETERS LIKE 'constants.RUNTIME_BASE_IMAGE_TAG' IN ACCOUNT").collect()
40
- if query_result:
41
- image_tag = query_result[0]["value"]
42
-
43
- if image_tag is None:
44
- image_tag = constants.DEFAULT_IMAGE_TAG
33
+ image_tag = constants.DEFAULT_IMAGE_TAG
45
34
 
46
35
  # TODO: Should each instance consume the entire pod?
47
36
  return types.ImageSpec(
@@ -54,9 +43,9 @@ def _get_image_spec(session: snowpark.Session, compute_pool: str, image_tag: Opt
54
43
 
55
44
 
56
45
  def generate_spec_overrides(
57
- environment_vars: Optional[Dict[str, str]] = None,
58
- custom_overrides: Optional[Dict[str, Any]] = None,
59
- ) -> Dict[str, Any]:
46
+ environment_vars: Optional[dict[str, str]] = None,
47
+ custom_overrides: Optional[dict[str, Any]] = None,
48
+ ) -> dict[str, Any]:
60
49
  """
61
50
  Generate a dictionary of service specification overrides.
62
51
 
@@ -68,7 +57,7 @@ def generate_spec_overrides(
68
57
  Resulting service specifiation patch dict. Empty if no overrides were supplied.
69
58
  """
70
59
  # Generate container level overrides
71
- container_spec: Dict[str, Any] = {
60
+ container_spec: dict[str, Any] = {
72
61
  "name": constants.DEFAULT_CONTAINER_NAME,
73
62
  }
74
63
  if environment_vars:
@@ -95,10 +84,10 @@ def generate_service_spec(
95
84
  session: snowpark.Session,
96
85
  compute_pool: str,
97
86
  payload: types.UploadedPayload,
98
- args: Optional[List[str]] = None,
87
+ args: Optional[list[str]] = None,
99
88
  num_instances: Optional[int] = None,
100
89
  enable_metrics: bool = False,
101
- ) -> Dict[str, Any]:
90
+ ) -> dict[str, Any]:
102
91
  """
103
92
  Generate a service specification for a job.
104
93
 
@@ -117,11 +106,11 @@ def generate_service_spec(
117
106
  image_spec = _get_image_spec(session, compute_pool)
118
107
 
119
108
  # Set resource requests/limits, including nvidia.com/gpu quantity if applicable
120
- resource_requests: Dict[str, Union[str, int]] = {
109
+ resource_requests: dict[str, Union[str, int]] = {
121
110
  "cpu": f"{int(image_spec.resource_requests.cpu * 1000)}m",
122
111
  "memory": f"{image_spec.resource_limits.memory}Gi",
123
112
  }
124
- resource_limits: Dict[str, Union[str, int]] = {
113
+ resource_limits: dict[str, Union[str, int]] = {
125
114
  "cpu": f"{int(image_spec.resource_requests.cpu * 1000)}m",
126
115
  "memory": f"{image_spec.resource_limits.memory}Gi",
127
116
  }
@@ -130,8 +119,8 @@ def generate_service_spec(
130
119
  resource_limits["nvidia.com/gpu"] = image_spec.resource_limits.gpu
131
120
 
132
121
  # Add local volumes for ephemeral logs and artifacts
133
- volumes: List[Dict[str, str]] = []
134
- volume_mounts: List[Dict[str, str]] = []
122
+ volumes: list[dict[str, str]] = []
123
+ volume_mounts: list[dict[str, str]] = []
135
124
  for volume_name, mount_path in [
136
125
  ("system-logs", "/var/log/managedservices/system/mlrs"),
137
126
  ("user-logs", "/var/log/managedservices/user/mlrs"),
@@ -302,11 +291,11 @@ def merge_patch(base: Any, patch: Any, display_name: str = "") -> Any:
302
291
 
303
292
 
304
293
  def _merge_lists_of_dicts(
305
- base: List[Dict[str, Any]],
306
- patch: List[Dict[str, Any]],
294
+ base: list[dict[str, Any]],
295
+ patch: list[dict[str, Any]],
307
296
  merge_key: str = "name",
308
297
  display_name: str = "",
309
- ) -> List[Dict[str, Any]]:
298
+ ) -> list[dict[str, Any]]:
310
299
  """
311
300
  Attempts to merge lists of dicts by matching on a merge key (default "name").
312
301
  - If the merge key is missing, the behavior falls back to overwriting the list.
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from pathlib import PurePath
3
- from typing import List, Literal, Optional, Union
3
+ from typing import Literal, Optional, Union
4
4
 
5
5
  JOB_STATUS = Literal[
6
6
  "PENDING",
@@ -21,7 +21,7 @@ class PayloadEntrypoint:
21
21
  class UploadedPayload:
22
22
  # TODO: Include manifest of payload files for validation
23
23
  stage_path: PurePath
24
- entrypoint: List[Union[str, PurePath]]
24
+ entrypoint: list[Union[str, PurePath]]
25
25
 
26
26
 
27
27
  @dataclass(frozen=True)
@@ -1,6 +1,6 @@
1
1
  import copy
2
2
  import functools
3
- from typing import Callable, Dict, List, Optional, TypeVar
3
+ from typing import Callable, Optional, TypeVar
4
4
 
5
5
  from typing_extensions import ParamSpec
6
6
 
@@ -15,18 +15,19 @@ _Args = ParamSpec("_Args")
15
15
  _ReturnValue = TypeVar("_ReturnValue")
16
16
 
17
17
 
18
- @snowpark._internal.utils.private_preview(version="1.7.4")
19
18
  @telemetry.send_api_usage_telemetry(project=_PROJECT)
20
19
  def remote(
21
20
  compute_pool: str,
22
21
  *,
23
22
  stage_name: str,
24
- pip_requirements: Optional[List[str]] = None,
25
- external_access_integrations: Optional[List[str]] = None,
23
+ pip_requirements: Optional[list[str]] = None,
24
+ external_access_integrations: Optional[list[str]] = None,
26
25
  query_warehouse: Optional[str] = None,
27
- env_vars: Optional[Dict[str, str]] = None,
26
+ env_vars: Optional[dict[str, str]] = None,
28
27
  num_instances: Optional[int] = None,
29
28
  enable_metrics: bool = False,
29
+ database: Optional[str] = None,
30
+ schema: Optional[str] = None,
30
31
  session: Optional[snowpark.Session] = None,
31
32
  ) -> Callable[[Callable[_Args, _ReturnValue]], Callable[_Args, jb.MLJob[_ReturnValue]]]:
32
33
  """
@@ -41,6 +42,8 @@ def remote(
41
42
  env_vars: Environment variables to set in container
42
43
  num_instances: The number of nodes in the job. If none specified, create a single node job.
43
44
  enable_metrics: Whether to enable metrics publishing for the job.
45
+ database: The database to use for the job.
46
+ schema: The schema to use for the job.
44
47
  session: The Snowpark session to use. If none specified, uses active session.
45
48
 
46
49
  Returns:
@@ -68,6 +71,8 @@ def remote(
68
71
  env_vars=env_vars,
69
72
  num_instances=num_instances,
70
73
  enable_metrics=enable_metrics,
74
+ database=database,
75
+ schema=schema,
71
76
  session=session,
72
77
  )
73
78
  assert isinstance(job, jb.MLJob), f"Unexpected job type: {type(job)}"
snowflake/ml/jobs/job.py CHANGED
@@ -1,12 +1,15 @@
1
1
  import time
2
- from typing import Any, Dict, Generic, List, Optional, TypeVar, cast
2
+ from functools import cached_property
3
+ from typing import Any, Generic, Literal, Optional, TypeVar, Union, cast, overload
3
4
 
4
5
  import yaml
5
6
 
6
7
  from snowflake import snowpark
7
8
  from snowflake.ml._internal import telemetry
9
+ from snowflake.ml._internal.utils import identifier
8
10
  from snowflake.ml.jobs._utils import constants, interop_utils, types
9
- from snowflake.snowpark import context as sp_context
11
+ from snowflake.snowpark import Row, context as sp_context
12
+ from snowflake.snowpark.exceptions import SnowparkSQLException
10
13
 
11
14
  _PROJECT = "MLJob"
12
15
  TERMINAL_JOB_STATUSES = {"FAILED", "DONE", "INTERNAL_ERROR"}
@@ -18,16 +21,24 @@ class MLJob(Generic[T]):
18
21
  def __init__(
19
22
  self,
20
23
  id: str,
21
- service_spec: Optional[Dict[str, Any]] = None,
24
+ service_spec: Optional[dict[str, Any]] = None,
22
25
  session: Optional[snowpark.Session] = None,
23
26
  ) -> None:
24
27
  self._id = id
25
- self._service_spec_cached: Optional[Dict[str, Any]] = service_spec
28
+ self._service_spec_cached: Optional[dict[str, Any]] = service_spec
26
29
  self._session = session or sp_context.get_active_session()
27
30
 
28
31
  self._status: types.JOB_STATUS = "PENDING"
29
32
  self._result: Optional[interop_utils.ExecutionResult] = None
30
33
 
34
+ @cached_property
35
+ def name(self) -> str:
36
+ return identifier.parse_schema_level_object_identifier(self.id)[-1]
37
+
38
+ @cached_property
39
+ def num_instances(self) -> int:
40
+ return _get_num_instances(self._session, self.id)
41
+
31
42
  @property
32
43
  def id(self) -> str:
33
44
  """Get the unique job ID"""
@@ -42,18 +53,18 @@ class MLJob(Generic[T]):
42
53
  return self._status
43
54
 
44
55
  @property
45
- def _service_spec(self) -> Dict[str, Any]:
56
+ def _service_spec(self) -> dict[str, Any]:
46
57
  """Get the job's service spec."""
47
58
  if not self._service_spec_cached:
48
59
  self._service_spec_cached = _get_service_spec(self._session, self.id)
49
60
  return self._service_spec_cached
50
61
 
51
62
  @property
52
- def _container_spec(self) -> Dict[str, Any]:
63
+ def _container_spec(self) -> dict[str, Any]:
53
64
  """Get the job's main container spec."""
54
65
  containers = self._service_spec["spec"]["containers"]
55
66
  container_spec = next(c for c in containers if c["name"] == constants.DEFAULT_CONTAINER_NAME)
56
- return cast(Dict[str, Any], container_spec)
67
+ return cast(dict[str, Any], container_spec)
57
68
 
58
69
  @property
59
70
  def _stage_path(self) -> str:
@@ -67,11 +78,20 @@ class MLJob(Generic[T]):
67
78
  """Get the job's result file location."""
68
79
  result_path = self._container_spec["env"].get(constants.RESULT_PATH_ENV_VAR)
69
80
  if result_path is None:
70
- raise RuntimeError(f"Job {self.id} doesn't have a result path configured")
81
+ raise RuntimeError(f"Job {self.name} doesn't have a result path configured")
71
82
  return f"{self._stage_path}/{result_path}"
72
83
 
73
- @snowpark._internal.utils.private_preview(version="1.7.4")
74
- def get_logs(self, limit: int = -1, instance_id: Optional[int] = None) -> str:
84
+ @overload
85
+ def get_logs(self, limit: int = -1, instance_id: Optional[int] = None, *, as_list: Literal[True]) -> list[str]:
86
+ ...
87
+
88
+ @overload
89
+ def get_logs(self, limit: int = -1, instance_id: Optional[int] = None, *, as_list: Literal[False] = False) -> str:
90
+ ...
91
+
92
+ def get_logs(
93
+ self, limit: int = -1, instance_id: Optional[int] = None, *, as_list: bool = False
94
+ ) -> Union[str, list[str]]:
75
95
  """
76
96
  Return the job's execution logs.
77
97
 
@@ -79,15 +99,17 @@ class MLJob(Generic[T]):
79
99
  limit: The maximum number of lines to return. Negative values are treated as no limit.
80
100
  instance_id: Optional instance ID to get logs from a specific instance.
81
101
  If not provided, returns logs from the head node.
102
+ as_list: If True, returns logs as a list of lines. Otherwise, returns logs as a single string.
82
103
 
83
104
  Returns:
84
105
  The job's execution logs.
85
106
  """
86
107
  logs = _get_logs(self._session, self.id, limit, instance_id)
87
108
  assert isinstance(logs, str) # mypy
109
+ if as_list:
110
+ return logs.splitlines()
88
111
  return logs
89
112
 
90
- @snowpark._internal.utils.private_preview(version="1.7.4")
91
113
  def show_logs(self, limit: int = -1, instance_id: Optional[int] = None) -> None:
92
114
  """
93
115
  Display the job's execution logs.
@@ -97,9 +119,8 @@ class MLJob(Generic[T]):
97
119
  instance_id: Optional instance ID to get logs from a specific instance.
98
120
  If not provided, displays logs from the head node.
99
121
  """
100
- print(self.get_logs(limit, instance_id)) # noqa: T201: we need to print here.
122
+ print(self.get_logs(limit, instance_id, as_list=False)) # noqa: T201: we need to print here.
101
123
 
102
- @snowpark._internal.utils.private_preview(version="1.7.4")
103
124
  @telemetry.send_api_usage_telemetry(project=_PROJECT, func_params_to_log=["timeout"])
104
125
  def wait(self, timeout: float = -1) -> types.JOB_STATUS:
105
126
  """
@@ -118,7 +139,7 @@ class MLJob(Generic[T]):
118
139
  start_time = time.monotonic()
119
140
  while self.status not in TERMINAL_JOB_STATUSES:
120
141
  if timeout >= 0 and (elapsed := time.monotonic() - start_time) >= timeout:
121
- raise TimeoutError(f"Job {self.id} did not complete within {elapsed} seconds")
142
+ raise TimeoutError(f"Job {self.name} did not complete within {elapsed} seconds")
122
143
  time.sleep(delay)
123
144
  delay = min(delay * 2, constants.JOB_POLL_MAX_DELAY_SECONDS) # Exponential backoff
124
145
  return self.status
@@ -144,11 +165,11 @@ class MLJob(Generic[T]):
144
165
  try:
145
166
  self._result = interop_utils.fetch_result(self._session, self._result_path)
146
167
  except Exception as e:
147
- raise RuntimeError(f"Failed to retrieve result for job (id={self.id})") from e
168
+ raise RuntimeError(f"Failed to retrieve result for job (id={self.name})") from e
148
169
 
149
170
  if self._result.success:
150
171
  return cast(T, self._result.result)
151
- raise RuntimeError(f"Job execution failed (id={self.id})") from self._result.exception
172
+ raise RuntimeError(f"Job execution failed (id={self.name})") from self._result.exception
152
173
 
153
174
 
154
175
  @telemetry.send_api_usage_telemetry(project=_PROJECT, func_params_to_log=["job_id", "instance_id"])
@@ -162,15 +183,15 @@ def _get_status(session: snowpark.Session, job_id: str, instance_id: Optional[in
162
183
  return cast(types.JOB_STATUS, row["status"])
163
184
  raise ValueError(f"Instance {instance_id} not found in job {job_id}")
164
185
  else:
165
- (row,) = session.sql("DESCRIBE SERVICE IDENTIFIER(?)", params=(job_id,)).collect()
186
+ row = _get_service_info(session, job_id)
166
187
  return cast(types.JOB_STATUS, row["status"])
167
188
 
168
189
 
169
190
  @telemetry.send_api_usage_telemetry(project=_PROJECT, func_params_to_log=["job_id"])
170
- def _get_service_spec(session: snowpark.Session, job_id: str) -> Dict[str, Any]:
191
+ def _get_service_spec(session: snowpark.Session, job_id: str) -> dict[str, Any]:
171
192
  """Retrieve job execution service spec."""
172
- (row,) = session.sql("DESCRIBE SERVICE IDENTIFIER(?)", params=[job_id]).collect()
173
- return cast(Dict[str, Any], yaml.safe_load(row["spec"]))
193
+ row = _get_service_info(session, job_id)
194
+ return cast(dict[str, Any], yaml.safe_load(row["spec"]))
174
195
 
175
196
 
176
197
  @telemetry.send_api_usage_telemetry(project=_PROJECT, func_params_to_log=["job_id", "limit", "instance_id"])
@@ -186,13 +207,24 @@ def _get_logs(session: snowpark.Session, job_id: str, limit: int = -1, instance_
186
207
 
187
208
  Returns:
188
209
  The job's execution logs.
210
+
211
+ Raises:
212
+ SnowparkSQLException: if the container is pending
213
+ RuntimeError: if failed to get head instance_id
214
+
189
215
  """
190
216
  # If instance_id is not specified, try to get the head instance ID
191
217
  if instance_id is None:
192
- instance_id = _get_head_instance_id(session, job_id)
218
+ try:
219
+ instance_id = _get_head_instance_id(session, job_id)
220
+ except RuntimeError:
221
+ raise RuntimeError(
222
+ "Failed to retrieve job logs. "
223
+ "Logs may be inaccessible due to job expiration and can be retrieved from Event Table instead."
224
+ )
193
225
 
194
226
  # Assemble params: [job_id, instance_id, container_name, (optional) limit]
195
- params: List[Any] = [
227
+ params: list[Any] = [
196
228
  job_id,
197
229
  0 if instance_id is None else instance_id,
198
230
  constants.DEFAULT_CONTAINER_NAME,
@@ -200,10 +232,15 @@ def _get_logs(session: snowpark.Session, job_id: str, limit: int = -1, instance_
200
232
  if limit > 0:
201
233
  params.append(limit)
202
234
 
203
- (row,) = session.sql(
204
- f"SELECT SYSTEM$GET_SERVICE_LOGS(?, ?, ?{f', ?' if limit > 0 else ''})",
205
- params=params,
206
- ).collect()
235
+ try:
236
+ (row,) = session.sql(
237
+ f"SELECT SYSTEM$GET_SERVICE_LOGS(?, ?, ?{f', ?' if limit > 0 else ''})",
238
+ params=params,
239
+ ).collect()
240
+ except SnowparkSQLException as e:
241
+ if "Container Status: PENDING" in e.message:
242
+ return "Warning: Waiting for container to start. Logs will be shown when available."
243
+ raise
207
244
  return str(row[0])
208
245
 
209
246
 
@@ -213,18 +250,27 @@ def _get_head_instance_id(session: snowpark.Session, job_id: str) -> Optional[in
213
250
  Retrieve the head instance ID of a job.
214
251
 
215
252
  Args:
216
- session: The Snowpark session to use.
217
- job_id: The job ID.
253
+ session (Session): The Snowpark session to use.
254
+ job_id (str): The job ID.
218
255
 
219
256
  Returns:
220
- The head instance ID of the job. Returns None if the head instance has not started yet.
257
+ Optional[int]: The head instance ID of the job, or None if the head instance has not started yet.
258
+
259
+ Raises:
260
+ RuntimeError: If the instances died or if some instances disappeared.
221
261
  """
222
262
  rows = session.sql("SHOW SERVICE INSTANCES IN SERVICE IDENTIFIER(?)", params=(job_id,)).collect()
223
263
  if not rows:
224
264
  return None
265
+ if _get_num_instances(session, job_id) > len(rows):
266
+ raise RuntimeError("Couldn’t retrieve head instance due to missing instances.")
225
267
 
226
268
  # Sort by start_time first, then by instance_id
227
- sorted_instances = sorted(rows, key=lambda x: (x["start_time"], int(x["instance_id"])))
269
+ try:
270
+ sorted_instances = sorted(rows, key=lambda x: (x["start_time"], int(x["instance_id"])))
271
+ except TypeError:
272
+ raise RuntimeError("Job instance information unavailable.")
273
+
228
274
  head_instance = sorted_instances[0]
229
275
  if not head_instance["start_time"]:
230
276
  # If head instance hasn't started yet, return None
@@ -233,3 +279,14 @@ def _get_head_instance_id(session: snowpark.Session, job_id: str) -> Optional[in
233
279
  return int(head_instance["instance_id"])
234
280
  except (ValueError, TypeError):
235
281
  return 0
282
+
283
+
284
+ def _get_service_info(session: snowpark.Session, job_id: str) -> Row:
285
+ (row,) = session.sql("DESCRIBE SERVICE IDENTIFIER(?)", params=(job_id,)).collect()
286
+ return row
287
+
288
+
289
+ @telemetry.send_api_usage_telemetry(project=_PROJECT, func_params_to_log=["job_id"])
290
+ def _get_num_instances(session: snowpark.Session, job_id: str) -> int:
291
+ row = _get_service_info(session, job_id)
292
+ return int(row["target_instances"]) if row["target_instances"] else 0