genesis-flow 1.0.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 (645) hide show
  1. genesis_flow-1.0.0.dist-info/METADATA +822 -0
  2. genesis_flow-1.0.0.dist-info/RECORD +645 -0
  3. genesis_flow-1.0.0.dist-info/WHEEL +5 -0
  4. genesis_flow-1.0.0.dist-info/entry_points.txt +19 -0
  5. genesis_flow-1.0.0.dist-info/licenses/LICENSE.txt +202 -0
  6. genesis_flow-1.0.0.dist-info/top_level.txt +1 -0
  7. mlflow/__init__.py +367 -0
  8. mlflow/__main__.py +3 -0
  9. mlflow/ag2/__init__.py +56 -0
  10. mlflow/ag2/ag2_logger.py +294 -0
  11. mlflow/anthropic/__init__.py +40 -0
  12. mlflow/anthropic/autolog.py +129 -0
  13. mlflow/anthropic/chat.py +144 -0
  14. mlflow/artifacts/__init__.py +268 -0
  15. mlflow/autogen/__init__.py +144 -0
  16. mlflow/autogen/chat.py +142 -0
  17. mlflow/azure/__init__.py +26 -0
  18. mlflow/azure/auth_handler.py +257 -0
  19. mlflow/azure/client.py +319 -0
  20. mlflow/azure/config.py +120 -0
  21. mlflow/azure/connection_factory.py +340 -0
  22. mlflow/azure/exceptions.py +27 -0
  23. mlflow/azure/stores.py +327 -0
  24. mlflow/azure/utils.py +183 -0
  25. mlflow/bedrock/__init__.py +45 -0
  26. mlflow/bedrock/_autolog.py +202 -0
  27. mlflow/bedrock/chat.py +122 -0
  28. mlflow/bedrock/stream.py +160 -0
  29. mlflow/bedrock/utils.py +43 -0
  30. mlflow/cli.py +707 -0
  31. mlflow/client.py +12 -0
  32. mlflow/config/__init__.py +56 -0
  33. mlflow/crewai/__init__.py +79 -0
  34. mlflow/crewai/autolog.py +253 -0
  35. mlflow/crewai/chat.py +29 -0
  36. mlflow/data/__init__.py +75 -0
  37. mlflow/data/artifact_dataset_sources.py +170 -0
  38. mlflow/data/code_dataset_source.py +40 -0
  39. mlflow/data/dataset.py +123 -0
  40. mlflow/data/dataset_registry.py +168 -0
  41. mlflow/data/dataset_source.py +110 -0
  42. mlflow/data/dataset_source_registry.py +219 -0
  43. mlflow/data/delta_dataset_source.py +167 -0
  44. mlflow/data/digest_utils.py +108 -0
  45. mlflow/data/evaluation_dataset.py +562 -0
  46. mlflow/data/filesystem_dataset_source.py +81 -0
  47. mlflow/data/http_dataset_source.py +145 -0
  48. mlflow/data/huggingface_dataset.py +258 -0
  49. mlflow/data/huggingface_dataset_source.py +118 -0
  50. mlflow/data/meta_dataset.py +104 -0
  51. mlflow/data/numpy_dataset.py +223 -0
  52. mlflow/data/pandas_dataset.py +231 -0
  53. mlflow/data/polars_dataset.py +352 -0
  54. mlflow/data/pyfunc_dataset_mixin.py +31 -0
  55. mlflow/data/schema.py +76 -0
  56. mlflow/data/sources.py +1 -0
  57. mlflow/data/spark_dataset.py +406 -0
  58. mlflow/data/spark_dataset_source.py +74 -0
  59. mlflow/data/spark_delta_utils.py +118 -0
  60. mlflow/data/tensorflow_dataset.py +350 -0
  61. mlflow/data/uc_volume_dataset_source.py +81 -0
  62. mlflow/db.py +27 -0
  63. mlflow/dspy/__init__.py +17 -0
  64. mlflow/dspy/autolog.py +197 -0
  65. mlflow/dspy/callback.py +398 -0
  66. mlflow/dspy/constant.py +1 -0
  67. mlflow/dspy/load.py +93 -0
  68. mlflow/dspy/save.py +393 -0
  69. mlflow/dspy/util.py +109 -0
  70. mlflow/dspy/wrapper.py +226 -0
  71. mlflow/entities/__init__.py +104 -0
  72. mlflow/entities/_mlflow_object.py +52 -0
  73. mlflow/entities/assessment.py +545 -0
  74. mlflow/entities/assessment_error.py +80 -0
  75. mlflow/entities/assessment_source.py +141 -0
  76. mlflow/entities/dataset.py +92 -0
  77. mlflow/entities/dataset_input.py +51 -0
  78. mlflow/entities/dataset_summary.py +62 -0
  79. mlflow/entities/document.py +48 -0
  80. mlflow/entities/experiment.py +109 -0
  81. mlflow/entities/experiment_tag.py +35 -0
  82. mlflow/entities/file_info.py +45 -0
  83. mlflow/entities/input_tag.py +35 -0
  84. mlflow/entities/lifecycle_stage.py +35 -0
  85. mlflow/entities/logged_model.py +228 -0
  86. mlflow/entities/logged_model_input.py +26 -0
  87. mlflow/entities/logged_model_output.py +32 -0
  88. mlflow/entities/logged_model_parameter.py +46 -0
  89. mlflow/entities/logged_model_status.py +74 -0
  90. mlflow/entities/logged_model_tag.py +33 -0
  91. mlflow/entities/metric.py +200 -0
  92. mlflow/entities/model_registry/__init__.py +29 -0
  93. mlflow/entities/model_registry/_model_registry_entity.py +13 -0
  94. mlflow/entities/model_registry/model_version.py +243 -0
  95. mlflow/entities/model_registry/model_version_deployment_job_run_state.py +44 -0
  96. mlflow/entities/model_registry/model_version_deployment_job_state.py +70 -0
  97. mlflow/entities/model_registry/model_version_search.py +25 -0
  98. mlflow/entities/model_registry/model_version_stages.py +25 -0
  99. mlflow/entities/model_registry/model_version_status.py +35 -0
  100. mlflow/entities/model_registry/model_version_tag.py +35 -0
  101. mlflow/entities/model_registry/prompt.py +73 -0
  102. mlflow/entities/model_registry/prompt_version.py +244 -0
  103. mlflow/entities/model_registry/registered_model.py +175 -0
  104. mlflow/entities/model_registry/registered_model_alias.py +35 -0
  105. mlflow/entities/model_registry/registered_model_deployment_job_state.py +39 -0
  106. mlflow/entities/model_registry/registered_model_search.py +25 -0
  107. mlflow/entities/model_registry/registered_model_tag.py +35 -0
  108. mlflow/entities/multipart_upload.py +74 -0
  109. mlflow/entities/param.py +49 -0
  110. mlflow/entities/run.py +97 -0
  111. mlflow/entities/run_data.py +84 -0
  112. mlflow/entities/run_info.py +188 -0
  113. mlflow/entities/run_inputs.py +59 -0
  114. mlflow/entities/run_outputs.py +43 -0
  115. mlflow/entities/run_status.py +41 -0
  116. mlflow/entities/run_tag.py +36 -0
  117. mlflow/entities/source_type.py +31 -0
  118. mlflow/entities/span.py +774 -0
  119. mlflow/entities/span_event.py +96 -0
  120. mlflow/entities/span_status.py +102 -0
  121. mlflow/entities/trace.py +317 -0
  122. mlflow/entities/trace_data.py +71 -0
  123. mlflow/entities/trace_info.py +220 -0
  124. mlflow/entities/trace_info_v2.py +162 -0
  125. mlflow/entities/trace_location.py +173 -0
  126. mlflow/entities/trace_state.py +39 -0
  127. mlflow/entities/trace_status.py +68 -0
  128. mlflow/entities/view_type.py +51 -0
  129. mlflow/environment_variables.py +866 -0
  130. mlflow/evaluation/__init__.py +16 -0
  131. mlflow/evaluation/assessment.py +369 -0
  132. mlflow/evaluation/evaluation.py +411 -0
  133. mlflow/evaluation/evaluation_tag.py +61 -0
  134. mlflow/evaluation/fluent.py +48 -0
  135. mlflow/evaluation/utils.py +201 -0
  136. mlflow/exceptions.py +213 -0
  137. mlflow/experiments.py +140 -0
  138. mlflow/gemini/__init__.py +81 -0
  139. mlflow/gemini/autolog.py +186 -0
  140. mlflow/gemini/chat.py +261 -0
  141. mlflow/genai/__init__.py +71 -0
  142. mlflow/genai/datasets/__init__.py +67 -0
  143. mlflow/genai/datasets/evaluation_dataset.py +131 -0
  144. mlflow/genai/evaluation/__init__.py +3 -0
  145. mlflow/genai/evaluation/base.py +411 -0
  146. mlflow/genai/evaluation/constant.py +23 -0
  147. mlflow/genai/evaluation/utils.py +244 -0
  148. mlflow/genai/judges/__init__.py +21 -0
  149. mlflow/genai/judges/databricks.py +404 -0
  150. mlflow/genai/label_schemas/__init__.py +153 -0
  151. mlflow/genai/label_schemas/label_schemas.py +209 -0
  152. mlflow/genai/labeling/__init__.py +159 -0
  153. mlflow/genai/labeling/labeling.py +250 -0
  154. mlflow/genai/optimize/__init__.py +13 -0
  155. mlflow/genai/optimize/base.py +198 -0
  156. mlflow/genai/optimize/optimizers/__init__.py +4 -0
  157. mlflow/genai/optimize/optimizers/base_optimizer.py +38 -0
  158. mlflow/genai/optimize/optimizers/dspy_mipro_optimizer.py +221 -0
  159. mlflow/genai/optimize/optimizers/dspy_optimizer.py +91 -0
  160. mlflow/genai/optimize/optimizers/utils/dspy_mipro_callback.py +76 -0
  161. mlflow/genai/optimize/optimizers/utils/dspy_mipro_utils.py +18 -0
  162. mlflow/genai/optimize/types.py +75 -0
  163. mlflow/genai/optimize/util.py +30 -0
  164. mlflow/genai/prompts/__init__.py +206 -0
  165. mlflow/genai/scheduled_scorers.py +431 -0
  166. mlflow/genai/scorers/__init__.py +26 -0
  167. mlflow/genai/scorers/base.py +492 -0
  168. mlflow/genai/scorers/builtin_scorers.py +765 -0
  169. mlflow/genai/scorers/scorer_utils.py +138 -0
  170. mlflow/genai/scorers/validation.py +165 -0
  171. mlflow/genai/utils/data_validation.py +146 -0
  172. mlflow/genai/utils/enum_utils.py +23 -0
  173. mlflow/genai/utils/trace_utils.py +211 -0
  174. mlflow/groq/__init__.py +42 -0
  175. mlflow/groq/_groq_autolog.py +74 -0
  176. mlflow/johnsnowlabs/__init__.py +888 -0
  177. mlflow/langchain/__init__.py +24 -0
  178. mlflow/langchain/api_request_parallel_processor.py +330 -0
  179. mlflow/langchain/autolog.py +147 -0
  180. mlflow/langchain/chat_agent_langgraph.py +340 -0
  181. mlflow/langchain/constant.py +1 -0
  182. mlflow/langchain/constants.py +1 -0
  183. mlflow/langchain/databricks_dependencies.py +444 -0
  184. mlflow/langchain/langchain_tracer.py +597 -0
  185. mlflow/langchain/model.py +919 -0
  186. mlflow/langchain/output_parsers.py +142 -0
  187. mlflow/langchain/retriever_chain.py +153 -0
  188. mlflow/langchain/runnables.py +527 -0
  189. mlflow/langchain/utils/chat.py +402 -0
  190. mlflow/langchain/utils/logging.py +671 -0
  191. mlflow/langchain/utils/serialization.py +36 -0
  192. mlflow/legacy_databricks_cli/__init__.py +0 -0
  193. mlflow/legacy_databricks_cli/configure/__init__.py +0 -0
  194. mlflow/legacy_databricks_cli/configure/provider.py +482 -0
  195. mlflow/litellm/__init__.py +175 -0
  196. mlflow/llama_index/__init__.py +22 -0
  197. mlflow/llama_index/autolog.py +55 -0
  198. mlflow/llama_index/chat.py +43 -0
  199. mlflow/llama_index/constant.py +1 -0
  200. mlflow/llama_index/model.py +577 -0
  201. mlflow/llama_index/pyfunc_wrapper.py +332 -0
  202. mlflow/llama_index/serialize_objects.py +188 -0
  203. mlflow/llama_index/tracer.py +561 -0
  204. mlflow/metrics/__init__.py +479 -0
  205. mlflow/metrics/base.py +39 -0
  206. mlflow/metrics/genai/__init__.py +25 -0
  207. mlflow/metrics/genai/base.py +101 -0
  208. mlflow/metrics/genai/genai_metric.py +771 -0
  209. mlflow/metrics/genai/metric_definitions.py +450 -0
  210. mlflow/metrics/genai/model_utils.py +371 -0
  211. mlflow/metrics/genai/prompt_template.py +68 -0
  212. mlflow/metrics/genai/prompts/__init__.py +0 -0
  213. mlflow/metrics/genai/prompts/v1.py +422 -0
  214. mlflow/metrics/genai/utils.py +6 -0
  215. mlflow/metrics/metric_definitions.py +619 -0
  216. mlflow/mismatch.py +34 -0
  217. mlflow/mistral/__init__.py +34 -0
  218. mlflow/mistral/autolog.py +71 -0
  219. mlflow/mistral/chat.py +135 -0
  220. mlflow/ml_package_versions.py +452 -0
  221. mlflow/models/__init__.py +97 -0
  222. mlflow/models/auth_policy.py +83 -0
  223. mlflow/models/cli.py +354 -0
  224. mlflow/models/container/__init__.py +294 -0
  225. mlflow/models/container/scoring_server/__init__.py +0 -0
  226. mlflow/models/container/scoring_server/nginx.conf +39 -0
  227. mlflow/models/dependencies_schemas.py +287 -0
  228. mlflow/models/display_utils.py +158 -0
  229. mlflow/models/docker_utils.py +211 -0
  230. mlflow/models/evaluation/__init__.py +23 -0
  231. mlflow/models/evaluation/_shap_patch.py +64 -0
  232. mlflow/models/evaluation/artifacts.py +194 -0
  233. mlflow/models/evaluation/base.py +1811 -0
  234. mlflow/models/evaluation/calibration_curve.py +109 -0
  235. mlflow/models/evaluation/default_evaluator.py +996 -0
  236. mlflow/models/evaluation/deprecated.py +23 -0
  237. mlflow/models/evaluation/evaluator_registry.py +80 -0
  238. mlflow/models/evaluation/evaluators/classifier.py +704 -0
  239. mlflow/models/evaluation/evaluators/default.py +233 -0
  240. mlflow/models/evaluation/evaluators/regressor.py +96 -0
  241. mlflow/models/evaluation/evaluators/shap.py +296 -0
  242. mlflow/models/evaluation/lift_curve.py +178 -0
  243. mlflow/models/evaluation/utils/metric.py +123 -0
  244. mlflow/models/evaluation/utils/trace.py +179 -0
  245. mlflow/models/evaluation/validation.py +434 -0
  246. mlflow/models/flavor_backend.py +93 -0
  247. mlflow/models/flavor_backend_registry.py +53 -0
  248. mlflow/models/model.py +1639 -0
  249. mlflow/models/model_config.py +150 -0
  250. mlflow/models/notebook_resources/agent_evaluation_template.html +235 -0
  251. mlflow/models/notebook_resources/eval_with_dataset_example.py +22 -0
  252. mlflow/models/notebook_resources/eval_with_synthetic_example.py +22 -0
  253. mlflow/models/python_api.py +369 -0
  254. mlflow/models/rag_signatures.py +128 -0
  255. mlflow/models/resources.py +321 -0
  256. mlflow/models/signature.py +662 -0
  257. mlflow/models/utils.py +2054 -0
  258. mlflow/models/wheeled_model.py +280 -0
  259. mlflow/openai/__init__.py +57 -0
  260. mlflow/openai/_agent_tracer.py +364 -0
  261. mlflow/openai/api_request_parallel_processor.py +131 -0
  262. mlflow/openai/autolog.py +509 -0
  263. mlflow/openai/constant.py +1 -0
  264. mlflow/openai/model.py +824 -0
  265. mlflow/openai/utils/chat_schema.py +367 -0
  266. mlflow/optuna/__init__.py +3 -0
  267. mlflow/optuna/storage.py +646 -0
  268. mlflow/plugins/__init__.py +72 -0
  269. mlflow/plugins/base.py +358 -0
  270. mlflow/plugins/builtin/__init__.py +24 -0
  271. mlflow/plugins/builtin/pytorch_plugin.py +150 -0
  272. mlflow/plugins/builtin/sklearn_plugin.py +158 -0
  273. mlflow/plugins/builtin/transformers_plugin.py +187 -0
  274. mlflow/plugins/cli.py +321 -0
  275. mlflow/plugins/discovery.py +340 -0
  276. mlflow/plugins/manager.py +465 -0
  277. mlflow/plugins/registry.py +316 -0
  278. mlflow/plugins/templates/framework_plugin_template.py +329 -0
  279. mlflow/prompt/constants.py +20 -0
  280. mlflow/prompt/promptlab_model.py +197 -0
  281. mlflow/prompt/registry_utils.py +248 -0
  282. mlflow/promptflow/__init__.py +495 -0
  283. mlflow/protos/__init__.py +0 -0
  284. mlflow/protos/assessments_pb2.py +174 -0
  285. mlflow/protos/databricks_artifacts_pb2.py +489 -0
  286. mlflow/protos/databricks_filesystem_service_pb2.py +196 -0
  287. mlflow/protos/databricks_managed_catalog_messages_pb2.py +95 -0
  288. mlflow/protos/databricks_managed_catalog_service_pb2.py +86 -0
  289. mlflow/protos/databricks_pb2.py +267 -0
  290. mlflow/protos/databricks_trace_server_pb2.py +374 -0
  291. mlflow/protos/databricks_uc_registry_messages_pb2.py +1249 -0
  292. mlflow/protos/databricks_uc_registry_service_pb2.py +170 -0
  293. mlflow/protos/facet_feature_statistics_pb2.py +296 -0
  294. mlflow/protos/internal_pb2.py +77 -0
  295. mlflow/protos/mlflow_artifacts_pb2.py +336 -0
  296. mlflow/protos/model_registry_pb2.py +1073 -0
  297. mlflow/protos/scalapb/__init__.py +0 -0
  298. mlflow/protos/scalapb/scalapb_pb2.py +104 -0
  299. mlflow/protos/service_pb2.py +2600 -0
  300. mlflow/protos/unity_catalog_oss_messages_pb2.py +457 -0
  301. mlflow/protos/unity_catalog_oss_service_pb2.py +130 -0
  302. mlflow/protos/unity_catalog_prompt_messages_pb2.py +447 -0
  303. mlflow/protos/unity_catalog_prompt_messages_pb2_grpc.py +24 -0
  304. mlflow/protos/unity_catalog_prompt_service_pb2.py +164 -0
  305. mlflow/protos/unity_catalog_prompt_service_pb2_grpc.py +785 -0
  306. mlflow/py.typed +0 -0
  307. mlflow/pydantic_ai/__init__.py +57 -0
  308. mlflow/pydantic_ai/autolog.py +173 -0
  309. mlflow/pyfunc/__init__.py +3844 -0
  310. mlflow/pyfunc/_mlflow_pyfunc_backend_predict.py +61 -0
  311. mlflow/pyfunc/backend.py +523 -0
  312. mlflow/pyfunc/context.py +78 -0
  313. mlflow/pyfunc/dbconnect_artifact_cache.py +144 -0
  314. mlflow/pyfunc/loaders/__init__.py +7 -0
  315. mlflow/pyfunc/loaders/chat_agent.py +117 -0
  316. mlflow/pyfunc/loaders/chat_model.py +125 -0
  317. mlflow/pyfunc/loaders/code_model.py +31 -0
  318. mlflow/pyfunc/loaders/responses_agent.py +112 -0
  319. mlflow/pyfunc/mlserver.py +46 -0
  320. mlflow/pyfunc/model.py +1473 -0
  321. mlflow/pyfunc/scoring_server/__init__.py +604 -0
  322. mlflow/pyfunc/scoring_server/app.py +7 -0
  323. mlflow/pyfunc/scoring_server/client.py +146 -0
  324. mlflow/pyfunc/spark_model_cache.py +48 -0
  325. mlflow/pyfunc/stdin_server.py +44 -0
  326. mlflow/pyfunc/utils/__init__.py +3 -0
  327. mlflow/pyfunc/utils/data_validation.py +224 -0
  328. mlflow/pyfunc/utils/environment.py +22 -0
  329. mlflow/pyfunc/utils/input_converter.py +47 -0
  330. mlflow/pyfunc/utils/serving_data_parser.py +11 -0
  331. mlflow/pytorch/__init__.py +1171 -0
  332. mlflow/pytorch/_lightning_autolog.py +580 -0
  333. mlflow/pytorch/_pytorch_autolog.py +50 -0
  334. mlflow/pytorch/pickle_module.py +35 -0
  335. mlflow/rfunc/__init__.py +42 -0
  336. mlflow/rfunc/backend.py +134 -0
  337. mlflow/runs.py +89 -0
  338. mlflow/server/__init__.py +302 -0
  339. mlflow/server/auth/__init__.py +1224 -0
  340. mlflow/server/auth/__main__.py +4 -0
  341. mlflow/server/auth/basic_auth.ini +6 -0
  342. mlflow/server/auth/cli.py +11 -0
  343. mlflow/server/auth/client.py +537 -0
  344. mlflow/server/auth/config.py +34 -0
  345. mlflow/server/auth/db/__init__.py +0 -0
  346. mlflow/server/auth/db/cli.py +18 -0
  347. mlflow/server/auth/db/migrations/__init__.py +0 -0
  348. mlflow/server/auth/db/migrations/alembic.ini +110 -0
  349. mlflow/server/auth/db/migrations/env.py +76 -0
  350. mlflow/server/auth/db/migrations/versions/8606fa83a998_initial_migration.py +51 -0
  351. mlflow/server/auth/db/migrations/versions/__init__.py +0 -0
  352. mlflow/server/auth/db/models.py +67 -0
  353. mlflow/server/auth/db/utils.py +37 -0
  354. mlflow/server/auth/entities.py +165 -0
  355. mlflow/server/auth/logo.py +14 -0
  356. mlflow/server/auth/permissions.py +65 -0
  357. mlflow/server/auth/routes.py +18 -0
  358. mlflow/server/auth/sqlalchemy_store.py +263 -0
  359. mlflow/server/graphql/__init__.py +0 -0
  360. mlflow/server/graphql/autogenerated_graphql_schema.py +353 -0
  361. mlflow/server/graphql/graphql_custom_scalars.py +24 -0
  362. mlflow/server/graphql/graphql_errors.py +15 -0
  363. mlflow/server/graphql/graphql_no_batching.py +89 -0
  364. mlflow/server/graphql/graphql_schema_extensions.py +74 -0
  365. mlflow/server/handlers.py +3217 -0
  366. mlflow/server/prometheus_exporter.py +17 -0
  367. mlflow/server/validation.py +30 -0
  368. mlflow/shap/__init__.py +691 -0
  369. mlflow/sklearn/__init__.py +1994 -0
  370. mlflow/sklearn/utils.py +1041 -0
  371. mlflow/smolagents/__init__.py +66 -0
  372. mlflow/smolagents/autolog.py +139 -0
  373. mlflow/smolagents/chat.py +29 -0
  374. mlflow/store/__init__.py +10 -0
  375. mlflow/store/_unity_catalog/__init__.py +1 -0
  376. mlflow/store/_unity_catalog/lineage/__init__.py +1 -0
  377. mlflow/store/_unity_catalog/lineage/constants.py +2 -0
  378. mlflow/store/_unity_catalog/registry/__init__.py +6 -0
  379. mlflow/store/_unity_catalog/registry/prompt_info.py +75 -0
  380. mlflow/store/_unity_catalog/registry/rest_store.py +1740 -0
  381. mlflow/store/_unity_catalog/registry/uc_oss_rest_store.py +507 -0
  382. mlflow/store/_unity_catalog/registry/utils.py +121 -0
  383. mlflow/store/artifact/__init__.py +0 -0
  384. mlflow/store/artifact/artifact_repo.py +472 -0
  385. mlflow/store/artifact/artifact_repository_registry.py +154 -0
  386. mlflow/store/artifact/azure_blob_artifact_repo.py +275 -0
  387. mlflow/store/artifact/azure_data_lake_artifact_repo.py +295 -0
  388. mlflow/store/artifact/cli.py +141 -0
  389. mlflow/store/artifact/cloud_artifact_repo.py +332 -0
  390. mlflow/store/artifact/databricks_artifact_repo.py +729 -0
  391. mlflow/store/artifact/databricks_artifact_repo_resources.py +301 -0
  392. mlflow/store/artifact/databricks_logged_model_artifact_repo.py +93 -0
  393. mlflow/store/artifact/databricks_models_artifact_repo.py +216 -0
  394. mlflow/store/artifact/databricks_sdk_artifact_repo.py +134 -0
  395. mlflow/store/artifact/databricks_sdk_models_artifact_repo.py +97 -0
  396. mlflow/store/artifact/dbfs_artifact_repo.py +240 -0
  397. mlflow/store/artifact/ftp_artifact_repo.py +132 -0
  398. mlflow/store/artifact/gcs_artifact_repo.py +296 -0
  399. mlflow/store/artifact/hdfs_artifact_repo.py +209 -0
  400. mlflow/store/artifact/http_artifact_repo.py +218 -0
  401. mlflow/store/artifact/local_artifact_repo.py +142 -0
  402. mlflow/store/artifact/mlflow_artifacts_repo.py +94 -0
  403. mlflow/store/artifact/models_artifact_repo.py +259 -0
  404. mlflow/store/artifact/optimized_s3_artifact_repo.py +356 -0
  405. mlflow/store/artifact/presigned_url_artifact_repo.py +173 -0
  406. mlflow/store/artifact/r2_artifact_repo.py +70 -0
  407. mlflow/store/artifact/runs_artifact_repo.py +265 -0
  408. mlflow/store/artifact/s3_artifact_repo.py +330 -0
  409. mlflow/store/artifact/sftp_artifact_repo.py +141 -0
  410. mlflow/store/artifact/uc_volume_artifact_repo.py +76 -0
  411. mlflow/store/artifact/unity_catalog_models_artifact_repo.py +168 -0
  412. mlflow/store/artifact/unity_catalog_oss_models_artifact_repo.py +168 -0
  413. mlflow/store/artifact/utils/__init__.py +0 -0
  414. mlflow/store/artifact/utils/models.py +148 -0
  415. mlflow/store/db/__init__.py +0 -0
  416. mlflow/store/db/base_sql_model.py +3 -0
  417. mlflow/store/db/db_types.py +10 -0
  418. mlflow/store/db/utils.py +314 -0
  419. mlflow/store/db_migrations/__init__.py +0 -0
  420. mlflow/store/db_migrations/alembic.ini +74 -0
  421. mlflow/store/db_migrations/env.py +84 -0
  422. mlflow/store/db_migrations/versions/0584bdc529eb_add_cascading_deletion_to_datasets_from_experiments.py +88 -0
  423. mlflow/store/db_migrations/versions/0a8213491aaa_drop_duplicate_killed_constraint.py +49 -0
  424. mlflow/store/db_migrations/versions/0c779009ac13_add_deleted_time_field_to_runs_table.py +24 -0
  425. mlflow/store/db_migrations/versions/181f10493468_allow_nulls_for_metric_values.py +35 -0
  426. mlflow/store/db_migrations/versions/27a6a02d2cf1_add_model_version_tags_table.py +38 -0
  427. mlflow/store/db_migrations/versions/2b4d017a5e9b_add_model_registry_tables_to_db.py +77 -0
  428. mlflow/store/db_migrations/versions/2d6e25af4d3e_increase_max_param_val_length.py +33 -0
  429. mlflow/store/db_migrations/versions/3500859a5d39_add_model_aliases_table.py +50 -0
  430. mlflow/store/db_migrations/versions/39d1c3be5f05_add_is_nan_constraint_for_metrics_tables_if_necessary.py +41 -0
  431. mlflow/store/db_migrations/versions/400f98739977_add_logged_model_tables.py +123 -0
  432. mlflow/store/db_migrations/versions/4465047574b1_increase_max_dataset_schema_size.py +38 -0
  433. mlflow/store/db_migrations/versions/451aebb31d03_add_metric_step.py +35 -0
  434. mlflow/store/db_migrations/versions/5b0e9adcef9c_add_cascade_deletion_to_trace_tables_fk.py +40 -0
  435. mlflow/store/db_migrations/versions/6953534de441_add_step_to_inputs_table.py +25 -0
  436. mlflow/store/db_migrations/versions/728d730b5ebd_add_registered_model_tags_table.py +38 -0
  437. mlflow/store/db_migrations/versions/7ac759974ad8_update_run_tags_with_larger_limit.py +36 -0
  438. mlflow/store/db_migrations/versions/7f2a7d5fae7d_add_datasets_inputs_input_tags_tables.py +82 -0
  439. mlflow/store/db_migrations/versions/84291f40a231_add_run_link_to_model_version.py +26 -0
  440. mlflow/store/db_migrations/versions/867495a8f9d4_add_trace_tables.py +90 -0
  441. mlflow/store/db_migrations/versions/89d4b8295536_create_latest_metrics_table.py +169 -0
  442. mlflow/store/db_migrations/versions/90e64c465722_migrate_user_column_to_tags.py +64 -0
  443. mlflow/store/db_migrations/versions/97727af70f4d_creation_time_last_update_time_experiments.py +25 -0
  444. mlflow/store/db_migrations/versions/__init__.py +0 -0
  445. mlflow/store/db_migrations/versions/a8c4a736bde6_allow_nulls_for_run_id.py +27 -0
  446. mlflow/store/db_migrations/versions/acf3f17fdcc7_add_storage_location_field_to_model_.py +29 -0
  447. mlflow/store/db_migrations/versions/bd07f7e963c5_create_index_on_run_uuid.py +26 -0
  448. mlflow/store/db_migrations/versions/bda7b8c39065_increase_model_version_tag_value_limit.py +38 -0
  449. mlflow/store/db_migrations/versions/c48cb773bb87_reset_default_value_for_is_nan_in_metrics_table_for_mysql.py +41 -0
  450. mlflow/store/db_migrations/versions/cbc13b556ace_add_v3_trace_schema_columns.py +31 -0
  451. mlflow/store/db_migrations/versions/cc1f77228345_change_param_value_length_to_500.py +34 -0
  452. mlflow/store/db_migrations/versions/cfd24bdc0731_update_run_status_constraint_with_killed.py +78 -0
  453. mlflow/store/db_migrations/versions/df50e92ffc5e_add_experiment_tags_table.py +38 -0
  454. mlflow/store/db_migrations/versions/f5a4f2784254_increase_run_tag_value_limit.py +36 -0
  455. mlflow/store/entities/__init__.py +3 -0
  456. mlflow/store/entities/paged_list.py +18 -0
  457. mlflow/store/model_registry/__init__.py +10 -0
  458. mlflow/store/model_registry/abstract_store.py +1081 -0
  459. mlflow/store/model_registry/base_rest_store.py +44 -0
  460. mlflow/store/model_registry/databricks_workspace_model_registry_rest_store.py +37 -0
  461. mlflow/store/model_registry/dbmodels/__init__.py +0 -0
  462. mlflow/store/model_registry/dbmodels/models.py +206 -0
  463. mlflow/store/model_registry/file_store.py +1091 -0
  464. mlflow/store/model_registry/rest_store.py +481 -0
  465. mlflow/store/model_registry/sqlalchemy_store.py +1286 -0
  466. mlflow/store/tracking/__init__.py +23 -0
  467. mlflow/store/tracking/abstract_store.py +816 -0
  468. mlflow/store/tracking/dbmodels/__init__.py +0 -0
  469. mlflow/store/tracking/dbmodels/initial_models.py +243 -0
  470. mlflow/store/tracking/dbmodels/models.py +1073 -0
  471. mlflow/store/tracking/file_store.py +2438 -0
  472. mlflow/store/tracking/postgres_managed_identity.py +146 -0
  473. mlflow/store/tracking/rest_store.py +1131 -0
  474. mlflow/store/tracking/sqlalchemy_store.py +2785 -0
  475. mlflow/system_metrics/__init__.py +61 -0
  476. mlflow/system_metrics/metrics/__init__.py +0 -0
  477. mlflow/system_metrics/metrics/base_metrics_monitor.py +32 -0
  478. mlflow/system_metrics/metrics/cpu_monitor.py +23 -0
  479. mlflow/system_metrics/metrics/disk_monitor.py +21 -0
  480. mlflow/system_metrics/metrics/gpu_monitor.py +71 -0
  481. mlflow/system_metrics/metrics/network_monitor.py +34 -0
  482. mlflow/system_metrics/metrics/rocm_monitor.py +123 -0
  483. mlflow/system_metrics/system_metrics_monitor.py +198 -0
  484. mlflow/tracing/__init__.py +16 -0
  485. mlflow/tracing/assessment.py +356 -0
  486. mlflow/tracing/client.py +531 -0
  487. mlflow/tracing/config.py +125 -0
  488. mlflow/tracing/constant.py +105 -0
  489. mlflow/tracing/destination.py +81 -0
  490. mlflow/tracing/display/__init__.py +40 -0
  491. mlflow/tracing/display/display_handler.py +196 -0
  492. mlflow/tracing/export/async_export_queue.py +186 -0
  493. mlflow/tracing/export/inference_table.py +138 -0
  494. mlflow/tracing/export/mlflow_v3.py +137 -0
  495. mlflow/tracing/export/utils.py +70 -0
  496. mlflow/tracing/fluent.py +1417 -0
  497. mlflow/tracing/processor/base_mlflow.py +199 -0
  498. mlflow/tracing/processor/inference_table.py +175 -0
  499. mlflow/tracing/processor/mlflow_v3.py +47 -0
  500. mlflow/tracing/processor/otel.py +73 -0
  501. mlflow/tracing/provider.py +487 -0
  502. mlflow/tracing/trace_manager.py +200 -0
  503. mlflow/tracing/utils/__init__.py +616 -0
  504. mlflow/tracing/utils/artifact_utils.py +28 -0
  505. mlflow/tracing/utils/copy.py +55 -0
  506. mlflow/tracing/utils/environment.py +55 -0
  507. mlflow/tracing/utils/exception.py +21 -0
  508. mlflow/tracing/utils/once.py +35 -0
  509. mlflow/tracing/utils/otlp.py +63 -0
  510. mlflow/tracing/utils/processor.py +54 -0
  511. mlflow/tracing/utils/search.py +292 -0
  512. mlflow/tracing/utils/timeout.py +250 -0
  513. mlflow/tracing/utils/token.py +19 -0
  514. mlflow/tracing/utils/truncation.py +124 -0
  515. mlflow/tracing/utils/warning.py +76 -0
  516. mlflow/tracking/__init__.py +39 -0
  517. mlflow/tracking/_model_registry/__init__.py +1 -0
  518. mlflow/tracking/_model_registry/client.py +764 -0
  519. mlflow/tracking/_model_registry/fluent.py +853 -0
  520. mlflow/tracking/_model_registry/registry.py +67 -0
  521. mlflow/tracking/_model_registry/utils.py +251 -0
  522. mlflow/tracking/_tracking_service/__init__.py +0 -0
  523. mlflow/tracking/_tracking_service/client.py +883 -0
  524. mlflow/tracking/_tracking_service/registry.py +56 -0
  525. mlflow/tracking/_tracking_service/utils.py +275 -0
  526. mlflow/tracking/artifact_utils.py +179 -0
  527. mlflow/tracking/client.py +5900 -0
  528. mlflow/tracking/context/__init__.py +0 -0
  529. mlflow/tracking/context/abstract_context.py +35 -0
  530. mlflow/tracking/context/databricks_cluster_context.py +15 -0
  531. mlflow/tracking/context/databricks_command_context.py +15 -0
  532. mlflow/tracking/context/databricks_job_context.py +49 -0
  533. mlflow/tracking/context/databricks_notebook_context.py +41 -0
  534. mlflow/tracking/context/databricks_repo_context.py +43 -0
  535. mlflow/tracking/context/default_context.py +51 -0
  536. mlflow/tracking/context/git_context.py +32 -0
  537. mlflow/tracking/context/registry.py +98 -0
  538. mlflow/tracking/context/system_environment_context.py +15 -0
  539. mlflow/tracking/default_experiment/__init__.py +1 -0
  540. mlflow/tracking/default_experiment/abstract_context.py +43 -0
  541. mlflow/tracking/default_experiment/databricks_notebook_experiment_provider.py +44 -0
  542. mlflow/tracking/default_experiment/registry.py +75 -0
  543. mlflow/tracking/fluent.py +3595 -0
  544. mlflow/tracking/metric_value_conversion_utils.py +93 -0
  545. mlflow/tracking/multimedia.py +206 -0
  546. mlflow/tracking/registry.py +86 -0
  547. mlflow/tracking/request_auth/__init__.py +0 -0
  548. mlflow/tracking/request_auth/abstract_request_auth_provider.py +34 -0
  549. mlflow/tracking/request_auth/registry.py +60 -0
  550. mlflow/tracking/request_header/__init__.py +0 -0
  551. mlflow/tracking/request_header/abstract_request_header_provider.py +36 -0
  552. mlflow/tracking/request_header/databricks_request_header_provider.py +38 -0
  553. mlflow/tracking/request_header/default_request_header_provider.py +17 -0
  554. mlflow/tracking/request_header/registry.py +79 -0
  555. mlflow/transformers/__init__.py +2982 -0
  556. mlflow/transformers/flavor_config.py +258 -0
  557. mlflow/transformers/hub_utils.py +83 -0
  558. mlflow/transformers/llm_inference_utils.py +468 -0
  559. mlflow/transformers/model_io.py +301 -0
  560. mlflow/transformers/peft.py +51 -0
  561. mlflow/transformers/signature.py +183 -0
  562. mlflow/transformers/torch_utils.py +55 -0
  563. mlflow/types/__init__.py +21 -0
  564. mlflow/types/agent.py +270 -0
  565. mlflow/types/chat.py +240 -0
  566. mlflow/types/llm.py +935 -0
  567. mlflow/types/responses.py +139 -0
  568. mlflow/types/responses_helpers.py +416 -0
  569. mlflow/types/schema.py +1505 -0
  570. mlflow/types/type_hints.py +647 -0
  571. mlflow/types/utils.py +753 -0
  572. mlflow/utils/__init__.py +283 -0
  573. mlflow/utils/_capture_modules.py +256 -0
  574. mlflow/utils/_capture_transformers_modules.py +75 -0
  575. mlflow/utils/_spark_utils.py +201 -0
  576. mlflow/utils/_unity_catalog_oss_utils.py +97 -0
  577. mlflow/utils/_unity_catalog_utils.py +479 -0
  578. mlflow/utils/annotations.py +218 -0
  579. mlflow/utils/arguments_utils.py +16 -0
  580. mlflow/utils/async_logging/__init__.py +1 -0
  581. mlflow/utils/async_logging/async_artifacts_logging_queue.py +258 -0
  582. mlflow/utils/async_logging/async_logging_queue.py +366 -0
  583. mlflow/utils/async_logging/run_artifact.py +38 -0
  584. mlflow/utils/async_logging/run_batch.py +58 -0
  585. mlflow/utils/async_logging/run_operations.py +49 -0
  586. mlflow/utils/autologging_utils/__init__.py +737 -0
  587. mlflow/utils/autologging_utils/client.py +432 -0
  588. mlflow/utils/autologging_utils/config.py +33 -0
  589. mlflow/utils/autologging_utils/events.py +294 -0
  590. mlflow/utils/autologging_utils/logging_and_warnings.py +328 -0
  591. mlflow/utils/autologging_utils/metrics_queue.py +71 -0
  592. mlflow/utils/autologging_utils/safety.py +1104 -0
  593. mlflow/utils/autologging_utils/versioning.py +95 -0
  594. mlflow/utils/checkpoint_utils.py +206 -0
  595. mlflow/utils/class_utils.py +6 -0
  596. mlflow/utils/cli_args.py +257 -0
  597. mlflow/utils/conda.py +354 -0
  598. mlflow/utils/credentials.py +231 -0
  599. mlflow/utils/data_utils.py +17 -0
  600. mlflow/utils/databricks_utils.py +1436 -0
  601. mlflow/utils/docstring_utils.py +477 -0
  602. mlflow/utils/doctor.py +133 -0
  603. mlflow/utils/download_cloud_file_chunk.py +43 -0
  604. mlflow/utils/env_manager.py +16 -0
  605. mlflow/utils/env_pack.py +131 -0
  606. mlflow/utils/environment.py +1009 -0
  607. mlflow/utils/exception_utils.py +14 -0
  608. mlflow/utils/file_utils.py +978 -0
  609. mlflow/utils/git_utils.py +77 -0
  610. mlflow/utils/gorilla.py +797 -0
  611. mlflow/utils/import_hooks/__init__.py +363 -0
  612. mlflow/utils/lazy_load.py +51 -0
  613. mlflow/utils/logging_utils.py +168 -0
  614. mlflow/utils/mime_type_utils.py +58 -0
  615. mlflow/utils/mlflow_tags.py +103 -0
  616. mlflow/utils/model_utils.py +486 -0
  617. mlflow/utils/name_utils.py +346 -0
  618. mlflow/utils/nfs_on_spark.py +62 -0
  619. mlflow/utils/openai_utils.py +164 -0
  620. mlflow/utils/os.py +12 -0
  621. mlflow/utils/oss_registry_utils.py +29 -0
  622. mlflow/utils/plugins.py +17 -0
  623. mlflow/utils/process.py +182 -0
  624. mlflow/utils/promptlab_utils.py +146 -0
  625. mlflow/utils/proto_json_utils.py +743 -0
  626. mlflow/utils/pydantic_utils.py +54 -0
  627. mlflow/utils/request_utils.py +279 -0
  628. mlflow/utils/requirements_utils.py +704 -0
  629. mlflow/utils/rest_utils.py +673 -0
  630. mlflow/utils/search_logged_model_utils.py +127 -0
  631. mlflow/utils/search_utils.py +2111 -0
  632. mlflow/utils/secure_loading.py +221 -0
  633. mlflow/utils/security_validation.py +384 -0
  634. mlflow/utils/server_cli_utils.py +61 -0
  635. mlflow/utils/spark_utils.py +15 -0
  636. mlflow/utils/string_utils.py +138 -0
  637. mlflow/utils/thread_utils.py +63 -0
  638. mlflow/utils/time.py +54 -0
  639. mlflow/utils/timeout.py +42 -0
  640. mlflow/utils/uri.py +572 -0
  641. mlflow/utils/validation.py +662 -0
  642. mlflow/utils/virtualenv.py +458 -0
  643. mlflow/utils/warnings_utils.py +25 -0
  644. mlflow/utils/yaml_utils.py +179 -0
  645. mlflow/version.py +24 -0
@@ -0,0 +1,3595 @@
1
+ """
2
+ Internal module implementing the fluent API, allowing management of an active
3
+ MLflow run. This module is exposed to users at the top-level :py:mod:`mlflow` module.
4
+ """
5
+
6
+ import atexit
7
+ import contextlib
8
+ import importlib
9
+ import inspect
10
+ import logging
11
+ import os
12
+ import threading
13
+ from copy import deepcopy
14
+ from typing import TYPE_CHECKING, Any, Generator, Literal, Optional, Union, overload
15
+
16
+ import mlflow
17
+ from mlflow.entities import (
18
+ DatasetInput,
19
+ Experiment,
20
+ InputTag,
21
+ LoggedModel,
22
+ LoggedModelInput,
23
+ LoggedModelOutput,
24
+ LoggedModelStatus,
25
+ Metric,
26
+ Param,
27
+ Run,
28
+ RunStatus,
29
+ RunTag,
30
+ ViewType,
31
+ )
32
+ from mlflow.entities.lifecycle_stage import LifecycleStage
33
+ from mlflow.environment_variables import (
34
+ _MLFLOW_ACTIVE_MODEL_ID,
35
+ MLFLOW_ACTIVE_MODEL_ID,
36
+ MLFLOW_ENABLE_ASYNC_LOGGING,
37
+ MLFLOW_ENABLE_SYSTEM_METRICS_LOGGING,
38
+ MLFLOW_EXPERIMENT_ID,
39
+ MLFLOW_EXPERIMENT_NAME,
40
+ MLFLOW_RUN_ID,
41
+ )
42
+ from mlflow.exceptions import MlflowException
43
+ from mlflow.protos.databricks_pb2 import (
44
+ INVALID_PARAMETER_VALUE,
45
+ RESOURCE_DOES_NOT_EXIST,
46
+ )
47
+ from mlflow.store.tracking import SEARCH_MAX_RESULTS_DEFAULT
48
+ from mlflow.tracing.provider import _get_trace_exporter
49
+ from mlflow.tracking._tracking_service.client import TrackingServiceClient
50
+ from mlflow.tracking._tracking_service.utils import _resolve_tracking_uri
51
+ from mlflow.utils import get_results_from_paginated_fn
52
+ from mlflow.utils.annotations import experimental
53
+ from mlflow.utils.async_logging.run_operations import RunOperations
54
+ from mlflow.utils.autologging_utils import (
55
+ AUTOLOGGING_CONF_KEY_IS_GLOBALLY_CONFIGURED,
56
+ AUTOLOGGING_INTEGRATIONS,
57
+ autologging_conf_lock,
58
+ autologging_integration,
59
+ autologging_is_disabled,
60
+ is_testing,
61
+ )
62
+ from mlflow.utils.databricks_utils import (
63
+ is_in_databricks_model_serving_environment,
64
+ is_in_databricks_runtime,
65
+ )
66
+ from mlflow.utils.file_utils import TempDir
67
+ from mlflow.utils.import_hooks import register_post_import_hook
68
+ from mlflow.utils.mlflow_tags import (
69
+ MLFLOW_DATASET_CONTEXT,
70
+ MLFLOW_EXPERIMENT_PRIMARY_METRIC_GREATER_IS_BETTER,
71
+ MLFLOW_EXPERIMENT_PRIMARY_METRIC_NAME,
72
+ MLFLOW_MODEL_IS_EXTERNAL,
73
+ MLFLOW_PARENT_RUN_ID,
74
+ MLFLOW_RUN_NAME,
75
+ MLFLOW_RUN_NOTE,
76
+ )
77
+ from mlflow.utils.thread_utils import ThreadLocalVariable
78
+ from mlflow.utils.time import get_current_time_millis
79
+ from mlflow.utils.validation import _validate_experiment_id_type, _validate_run_id
80
+ from mlflow.version import IS_TRACING_SDK_ONLY
81
+
82
+ if not IS_TRACING_SDK_ONLY:
83
+ from mlflow.data.dataset import Dataset
84
+ from mlflow.tracking import _get_artifact_repo, _get_store, artifact_utils
85
+ from mlflow.tracking.client import MlflowClient
86
+ from mlflow.tracking.context import registry as context_registry
87
+ from mlflow.tracking.default_experiment import registry as default_experiment_registry
88
+
89
+
90
+ if TYPE_CHECKING:
91
+ import matplotlib
92
+ import matplotlib.figure
93
+ import numpy
94
+ import pandas
95
+ import PIL
96
+ import plotly
97
+
98
+
99
+ _active_experiment_id = None
100
+
101
+ SEARCH_MAX_RESULTS_PANDAS = 100000
102
+ NUM_RUNS_PER_PAGE_PANDAS = 10000
103
+
104
+ _logger = logging.getLogger(__name__)
105
+
106
+
107
+ run_id_to_system_metrics_monitor = {}
108
+
109
+
110
+ _active_run_stack = ThreadLocalVariable(default_factory=lambda: [])
111
+
112
+ _last_active_run_id = ThreadLocalVariable(default_factory=lambda: None)
113
+ _last_logged_model_id = ThreadLocalVariable(default_factory=lambda: None)
114
+
115
+
116
+ def _reset_last_logged_model_id() -> None:
117
+ """
118
+ Should be called only for testing purposes.
119
+ """
120
+ _last_logged_model_id.set(None)
121
+
122
+
123
+ _experiment_lock = threading.Lock()
124
+
125
+
126
+ def set_experiment(
127
+ experiment_name: Optional[str] = None, experiment_id: Optional[str] = None
128
+ ) -> Experiment:
129
+ """
130
+ Set the given experiment as the active experiment. The experiment must either be specified by
131
+ name via `experiment_name` or by ID via `experiment_id`. The experiment name and ID cannot
132
+ both be specified.
133
+
134
+ .. note::
135
+ If the experiment being set by name does not exist, a new experiment will be
136
+ created with the given name. After the experiment has been created, it will be set
137
+ as the active experiment. On certain platforms, such as Databricks, the experiment name
138
+ must be an absolute path, e.g. ``"/Users/<username>/my-experiment"``.
139
+
140
+ Args:
141
+ experiment_name: Case sensitive name of the experiment to be activated.
142
+ experiment_id: ID of the experiment to be activated. If an experiment with this ID
143
+ does not exist, an exception is thrown.
144
+
145
+ Returns:
146
+ An instance of :py:class:`mlflow.entities.Experiment` representing the new active
147
+ experiment.
148
+
149
+ .. code-block:: python
150
+ :test:
151
+ :caption: Example
152
+
153
+ import mlflow
154
+
155
+ # Set an experiment name, which must be unique and case-sensitive.
156
+ experiment = mlflow.set_experiment("Social NLP Experiments")
157
+ # Get Experiment Details
158
+ print(f"Experiment_id: {experiment.experiment_id}")
159
+ print(f"Artifact Location: {experiment.artifact_location}")
160
+ print(f"Tags: {experiment.tags}")
161
+ print(f"Lifecycle_stage: {experiment.lifecycle_stage}")
162
+
163
+ .. code-block:: text
164
+ :caption: Output
165
+
166
+ Experiment_id: 1
167
+ Artifact Location: file:///.../mlruns/1
168
+ Tags: {}
169
+ Lifecycle_stage: active
170
+ """
171
+ if (experiment_name is not None and experiment_id is not None) or (
172
+ experiment_name is None and experiment_id is None
173
+ ):
174
+ raise MlflowException(
175
+ message="Must specify exactly one of: `experiment_id` or `experiment_name`.",
176
+ error_code=INVALID_PARAMETER_VALUE,
177
+ )
178
+
179
+ client = TrackingServiceClient(_resolve_tracking_uri())
180
+
181
+ with _experiment_lock:
182
+ if experiment_id is None:
183
+ experiment = client.get_experiment_by_name(experiment_name)
184
+ if not experiment:
185
+ _logger.info(
186
+ "Experiment with name '%s' does not exist. Creating a new experiment.",
187
+ experiment_name,
188
+ )
189
+ try:
190
+ experiment_id = client.create_experiment(experiment_name)
191
+ except MlflowException as e:
192
+ if e.error_code == "RESOURCE_ALREADY_EXISTS":
193
+ # NB: If two simultaneous processes attempt to set the same experiment
194
+ # simultaneously, a race condition may be encountered here wherein
195
+ # experiment creation fails
196
+ return client.get_experiment_by_name(experiment_name)
197
+ raise
198
+
199
+ experiment = client.get_experiment(experiment_id)
200
+ else:
201
+ experiment = client.get_experiment(experiment_id)
202
+ if experiment is None:
203
+ raise MlflowException(
204
+ message=f"Experiment with ID '{experiment_id}' does not exist.",
205
+ error_code=RESOURCE_DOES_NOT_EXIST,
206
+ )
207
+
208
+ if experiment.lifecycle_stage != LifecycleStage.ACTIVE:
209
+ raise MlflowException(
210
+ message=(
211
+ f"Cannot set a deleted experiment {experiment.name!r} as the active"
212
+ " experiment. "
213
+ "You can restore the experiment, or permanently delete the "
214
+ "experiment to create a new one."
215
+ ),
216
+ error_code=INVALID_PARAMETER_VALUE,
217
+ )
218
+
219
+ global _active_experiment_id
220
+ _active_experiment_id = experiment.experiment_id
221
+
222
+ # Set 'MLFLOW_EXPERIMENT_ID' environment variable
223
+ # so that subprocess can inherit it.
224
+ MLFLOW_EXPERIMENT_ID.set(_active_experiment_id)
225
+
226
+ return experiment
227
+
228
+
229
+ def _set_experiment_primary_metric(
230
+ experiment_id: str, primary_metric: str, greater_is_better: bool
231
+ ):
232
+ client = MlflowClient()
233
+ client.set_experiment_tag(experiment_id, MLFLOW_EXPERIMENT_PRIMARY_METRIC_NAME, primary_metric)
234
+ client.set_experiment_tag(
235
+ experiment_id, MLFLOW_EXPERIMENT_PRIMARY_METRIC_GREATER_IS_BETTER, str(greater_is_better)
236
+ )
237
+
238
+
239
+ class ActiveRun(Run):
240
+ """Wrapper around :py:class:`mlflow.entities.Run` to enable using Python ``with`` syntax."""
241
+
242
+ def __init__(self, run):
243
+ Run.__init__(self, run.info, run.data)
244
+
245
+ def __enter__(self):
246
+ return self
247
+
248
+ def __exit__(self, exc_type, exc_val, exc_tb):
249
+ active_run_stack = _active_run_stack.get()
250
+
251
+ # Check if the run is still active. We check based on ID instead of
252
+ # using referential equality, because some tools (e.g. AutoML) may
253
+ # stop a run and start it again with the same ID to restore session state
254
+ if any(r.info.run_id == self.info.run_id for r in active_run_stack):
255
+ status = RunStatus.FINISHED if exc_type is None else RunStatus.FAILED
256
+ end_run(RunStatus.to_string(status))
257
+
258
+ return exc_type is None
259
+
260
+
261
+ def start_run(
262
+ run_id: Optional[str] = None,
263
+ experiment_id: Optional[str] = None,
264
+ run_name: Optional[str] = None,
265
+ nested: bool = False,
266
+ parent_run_id: Optional[str] = None,
267
+ tags: Optional[dict[str, Any]] = None,
268
+ description: Optional[str] = None,
269
+ log_system_metrics: Optional[bool] = None,
270
+ ) -> ActiveRun:
271
+ """
272
+ Start a new MLflow run, setting it as the active run under which metrics and parameters
273
+ will be logged. The return value can be used as a context manager within a ``with`` block;
274
+ otherwise, you must call ``end_run()`` to terminate the current run.
275
+
276
+ If you pass a ``run_id`` or the ``MLFLOW_RUN_ID`` environment variable is set,
277
+ ``start_run`` attempts to resume a run with the specified run ID and
278
+ other parameters are ignored. ``run_id`` takes precedence over ``MLFLOW_RUN_ID``.
279
+
280
+ If resuming an existing run, the run status is set to ``RunStatus.RUNNING``.
281
+
282
+ MLflow sets a variety of default tags on the run, as defined in
283
+ `MLflow system tags <../../tracking/tracking-api.html#system_tags>`_.
284
+
285
+ Args:
286
+ run_id: If specified, get the run with the specified UUID and log parameters
287
+ and metrics under that run. The run's end time is unset and its status
288
+ is set to running, but the run's other attributes (``source_version``,
289
+ ``source_type``, etc.) are not changed.
290
+ experiment_id: ID of the experiment under which to create the current run (applicable
291
+ only when ``run_id`` is not specified). If ``experiment_id`` argument
292
+ is unspecified, will look for valid experiment in the following order:
293
+ activated using ``set_experiment``, ``MLFLOW_EXPERIMENT_NAME``
294
+ environment variable, ``MLFLOW_EXPERIMENT_ID`` environment variable,
295
+ or the default experiment as defined by the tracking server.
296
+ run_name: Name of new run, should be a non-empty string. Used only when ``run_id`` is
297
+ unspecified. If a new run is created and ``run_name`` is not specified,
298
+ a random name will be generated for the run.
299
+ nested: Controls whether run is nested in parent run. ``True`` creates a nested run.
300
+ parent_run_id: If specified, the current run will be nested under the the run with
301
+ the specified UUID. The parent run must be in the ACTIVE state.
302
+ tags: An optional dictionary of string keys and values to set as tags on the run.
303
+ If a run is being resumed, these tags are set on the resumed run. If a new run is
304
+ being created, these tags are set on the new run.
305
+ description: An optional string that populates the description box of the run.
306
+ If a run is being resumed, the description is set on the resumed run.
307
+ If a new run is being created, the description is set on the new run.
308
+ log_system_metrics: bool, defaults to None. If True, system metrics will be logged
309
+ to MLflow, e.g., cpu/gpu utilization. If None, we will check environment variable
310
+ `MLFLOW_ENABLE_SYSTEM_METRICS_LOGGING` to determine whether to log system metrics.
311
+ System metrics logging is an experimental feature in MLflow 2.8 and subject to change.
312
+
313
+ Returns:
314
+ :py:class:`mlflow.ActiveRun` object that acts as a context manager wrapping the
315
+ run's state.
316
+
317
+ .. code-block:: python
318
+ :test:
319
+ :caption: Example
320
+
321
+ import mlflow
322
+
323
+ # Create nested runs
324
+ experiment_id = mlflow.create_experiment("experiment1")
325
+ with mlflow.start_run(
326
+ run_name="PARENT_RUN",
327
+ experiment_id=experiment_id,
328
+ tags={"version": "v1", "priority": "P1"},
329
+ description="parent",
330
+ ) as parent_run:
331
+ mlflow.log_param("parent", "yes")
332
+ with mlflow.start_run(
333
+ run_name="CHILD_RUN",
334
+ experiment_id=experiment_id,
335
+ description="child",
336
+ nested=True,
337
+ ) as child_run:
338
+ mlflow.log_param("child", "yes")
339
+ print("parent run:")
340
+ print(f"run_id: {parent_run.info.run_id}")
341
+ print("description: {}".format(parent_run.data.tags.get("mlflow.note.content")))
342
+ print("version tag value: {}".format(parent_run.data.tags.get("version")))
343
+ print("priority tag value: {}".format(parent_run.data.tags.get("priority")))
344
+ print("--")
345
+
346
+ # Search all child runs with a parent id
347
+ query = f"tags.mlflow.parentRunId = '{parent_run.info.run_id}'"
348
+ results = mlflow.search_runs(experiment_ids=[experiment_id], filter_string=query)
349
+ print("child runs:")
350
+ print(results[["run_id", "params.child", "tags.mlflow.runName"]])
351
+
352
+ # Create a nested run under the existing parent run
353
+ with mlflow.start_run(
354
+ run_name="NEW_CHILD_RUN",
355
+ experiment_id=experiment_id,
356
+ description="new child",
357
+ parent_run_id=parent_run.info.run_id,
358
+ ) as child_run:
359
+ mlflow.log_param("new-child", "yes")
360
+
361
+ .. code-block:: text
362
+ :caption: Output
363
+
364
+ parent run:
365
+ run_id: 8979459433a24a52ab3be87a229a9cdf
366
+ description: starting a parent for experiment 7
367
+ version tag value: v1
368
+ priority tag value: P1
369
+ --
370
+ child runs:
371
+ run_id params.child tags.mlflow.runName
372
+ 0 7d175204675e40328e46d9a6a5a7ee6a yes CHILD_RUN
373
+ """
374
+ active_run_stack = _active_run_stack.get()
375
+ _validate_experiment_id_type(experiment_id)
376
+ # back compat for int experiment_id
377
+ experiment_id = str(experiment_id) if isinstance(experiment_id, int) else experiment_id
378
+ if len(active_run_stack) > 0 and not nested:
379
+ raise Exception(
380
+ (
381
+ "Run with UUID {} is already active. To start a new run, first end the "
382
+ + "current run with mlflow.end_run(). To start a nested "
383
+ + "run, call start_run with nested=True"
384
+ ).format(active_run_stack[0].info.run_id)
385
+ )
386
+ client = MlflowClient()
387
+ if run_id:
388
+ existing_run_id = run_id
389
+ elif run_id := MLFLOW_RUN_ID.get():
390
+ existing_run_id = run_id
391
+ del os.environ[MLFLOW_RUN_ID.name]
392
+ else:
393
+ existing_run_id = None
394
+ if existing_run_id:
395
+ _validate_run_id(existing_run_id)
396
+ active_run_obj = client.get_run(existing_run_id)
397
+ # Check to see if experiment_id from environment matches experiment_id from set_experiment()
398
+ if (
399
+ _active_experiment_id is not None
400
+ and _active_experiment_id != active_run_obj.info.experiment_id
401
+ ):
402
+ raise MlflowException(
403
+ f"Cannot start run with ID {existing_run_id} because active run ID "
404
+ "does not match environment run ID. Make sure --experiment-name "
405
+ "or --experiment-id matches experiment set with "
406
+ "set_experiment(), or just use command-line arguments"
407
+ )
408
+ # Check if the current run has been deleted.
409
+ if active_run_obj.info.lifecycle_stage == LifecycleStage.DELETED:
410
+ raise MlflowException(
411
+ f"Cannot start run with ID {existing_run_id} because it is in the deleted state."
412
+ )
413
+ # Use previous `end_time` because a value is required for `update_run_info`.
414
+ end_time = active_run_obj.info.end_time
415
+ _get_store().update_run_info(
416
+ existing_run_id, run_status=RunStatus.RUNNING, end_time=end_time, run_name=None
417
+ )
418
+ tags = tags or {}
419
+ if description:
420
+ if MLFLOW_RUN_NOTE in tags:
421
+ raise MlflowException(
422
+ f"Description is already set via the tag {MLFLOW_RUN_NOTE} in tags."
423
+ f"Remove the key {MLFLOW_RUN_NOTE} from the tags or omit the description.",
424
+ error_code=INVALID_PARAMETER_VALUE,
425
+ )
426
+ tags[MLFLOW_RUN_NOTE] = description
427
+
428
+ if tags:
429
+ client.log_batch(
430
+ run_id=existing_run_id,
431
+ tags=[RunTag(key, str(value)) for key, value in tags.items()],
432
+ )
433
+ active_run_obj = client.get_run(existing_run_id)
434
+ else:
435
+ if parent_run_id:
436
+ _validate_run_id(parent_run_id)
437
+ # Make sure parent_run_id matches the current run id, if there is an active run
438
+ if len(active_run_stack) > 0 and parent_run_id != active_run_stack[-1].info.run_id:
439
+ current_run_id = active_run_stack[-1].info.run_id
440
+ raise MlflowException(
441
+ f"Current run with UUID {current_run_id} does not match the specified "
442
+ f"parent_run_id {parent_run_id}. To start a new nested run under "
443
+ f"the parent run with UUID {current_run_id}, first end the current run "
444
+ "with mlflow.end_run()."
445
+ )
446
+ parent_run_obj = client.get_run(parent_run_id)
447
+ # Check if the specified parent_run has been deleted.
448
+ if parent_run_obj.info.lifecycle_stage == LifecycleStage.DELETED:
449
+ raise MlflowException(
450
+ f"Cannot start run under parent run with ID {parent_run_id} "
451
+ f"because it is in the deleted state."
452
+ )
453
+ else:
454
+ parent_run_id = active_run_stack[-1].info.run_id if len(active_run_stack) > 0 else None
455
+
456
+ exp_id_for_run = experiment_id if experiment_id is not None else _get_experiment_id()
457
+
458
+ user_specified_tags = deepcopy(tags) or {}
459
+ if description:
460
+ if MLFLOW_RUN_NOTE in user_specified_tags:
461
+ raise MlflowException(
462
+ f"Description is already set via the tag {MLFLOW_RUN_NOTE} in tags."
463
+ f"Remove the key {MLFLOW_RUN_NOTE} from the tags or omit the description.",
464
+ error_code=INVALID_PARAMETER_VALUE,
465
+ )
466
+ user_specified_tags[MLFLOW_RUN_NOTE] = description
467
+ if parent_run_id is not None:
468
+ user_specified_tags[MLFLOW_PARENT_RUN_ID] = parent_run_id
469
+ if run_name:
470
+ user_specified_tags[MLFLOW_RUN_NAME] = run_name
471
+
472
+ resolved_tags = context_registry.resolve_tags(user_specified_tags)
473
+
474
+ active_run_obj = client.create_run(
475
+ experiment_id=exp_id_for_run,
476
+ tags=resolved_tags,
477
+ run_name=run_name,
478
+ )
479
+
480
+ if log_system_metrics is None:
481
+ # If `log_system_metrics` is not specified, we will check environment variable.
482
+ log_system_metrics = MLFLOW_ENABLE_SYSTEM_METRICS_LOGGING.get()
483
+
484
+ if log_system_metrics:
485
+ if importlib.util.find_spec("psutil") is None:
486
+ raise MlflowException(
487
+ "Failed to start system metrics monitoring as package `psutil` is not installed. "
488
+ "Please run `pip install psutil` to resolve the issue, otherwise you can disable "
489
+ "system metrics logging by passing `log_system_metrics=False` to "
490
+ "`mlflow.start_run()` or calling `mlflow.disable_system_metrics_logging`."
491
+ )
492
+ try:
493
+ from mlflow.system_metrics.system_metrics_monitor import SystemMetricsMonitor
494
+
495
+ system_monitor = SystemMetricsMonitor(
496
+ active_run_obj.info.run_id,
497
+ resume_logging=existing_run_id is not None,
498
+ )
499
+ run_id_to_system_metrics_monitor[active_run_obj.info.run_id] = system_monitor
500
+ system_monitor.start()
501
+ except Exception as e:
502
+ _logger.error(f"Failed to start system metrics monitoring: {e}.")
503
+
504
+ active_run_stack.append(ActiveRun(active_run_obj))
505
+ return active_run_stack[-1]
506
+
507
+
508
+ def end_run(status: str = RunStatus.to_string(RunStatus.FINISHED)) -> None:
509
+ """
510
+ End an active MLflow run (if there is one).
511
+
512
+ .. code-block:: python
513
+ :test:
514
+ :caption: Example
515
+
516
+ import mlflow
517
+
518
+ # Start run and get status
519
+ mlflow.start_run()
520
+ run = mlflow.active_run()
521
+ print(f"run_id: {run.info.run_id}; status: {run.info.status}")
522
+
523
+ # End run and get status
524
+ mlflow.end_run()
525
+ run = mlflow.get_run(run.info.run_id)
526
+ print(f"run_id: {run.info.run_id}; status: {run.info.status}")
527
+ print("--")
528
+
529
+ # Check for any active runs
530
+ print(f"Active run: {mlflow.active_run()}")
531
+
532
+ .. code-block:: text
533
+ :caption: Output
534
+
535
+ run_id: b47ee4563368419880b44ad8535f6371; status: RUNNING
536
+ run_id: b47ee4563368419880b44ad8535f6371; status: FINISHED
537
+ --
538
+ Active run: None
539
+ """
540
+ active_run_stack = _active_run_stack.get()
541
+ if len(active_run_stack) > 0:
542
+ # Clear out the global existing run environment variable as well.
543
+ MLFLOW_RUN_ID.unset()
544
+ run = active_run_stack.pop()
545
+ last_active_run_id = run.info.run_id
546
+ _last_active_run_id.set(last_active_run_id)
547
+ MlflowClient().set_terminated(last_active_run_id, status)
548
+ if last_active_run_id in run_id_to_system_metrics_monitor:
549
+ system_metrics_monitor = run_id_to_system_metrics_monitor.pop(last_active_run_id)
550
+ system_metrics_monitor.finish()
551
+
552
+
553
+ def _safe_end_run():
554
+ with contextlib.suppress(Exception):
555
+ end_run()
556
+
557
+
558
+ atexit.register(_safe_end_run)
559
+
560
+
561
+ def active_run() -> Optional[ActiveRun]:
562
+ """
563
+ Get the currently active ``Run``, or None if no such run exists.
564
+
565
+ .. attention::
566
+ This API is **thread-local** and returns only the active run in the current thread.
567
+ If your application is multi-threaded and a run is started in a different thread,
568
+ this API will not retrieve that run.
569
+
570
+ **Note**: You cannot access currently-active run attributes
571
+ (parameters, metrics, etc.) through the run returned by ``mlflow.active_run``. In order
572
+ to access such attributes, use the :py:class:`mlflow.client.MlflowClient` as follows:
573
+
574
+ .. code-block:: python
575
+ :test:
576
+ :caption: Example
577
+
578
+ import mlflow
579
+
580
+ mlflow.start_run()
581
+ run = mlflow.active_run()
582
+ print(f"Active run_id: {run.info.run_id}")
583
+ mlflow.end_run()
584
+
585
+ .. code-block:: text
586
+ :caption: Output
587
+
588
+ Active run_id: 6f252757005748708cd3aad75d1ff462
589
+ """
590
+ active_run_stack = _active_run_stack.get()
591
+ return active_run_stack[-1] if len(active_run_stack) > 0 else None
592
+
593
+
594
+ def last_active_run() -> Optional[Run]:
595
+ """Gets the most recent active run.
596
+
597
+ Examples:
598
+
599
+ .. code-block:: python
600
+ :test:
601
+ :caption: To retrieve the most recent autologged run:
602
+
603
+ import mlflow
604
+
605
+ from sklearn.model_selection import train_test_split
606
+ from sklearn.datasets import load_diabetes
607
+ from sklearn.ensemble import RandomForestRegressor
608
+
609
+ mlflow.autolog()
610
+
611
+ db = load_diabetes()
612
+ X_train, X_test, y_train, y_test = train_test_split(db.data, db.target)
613
+
614
+ # Create and train models.
615
+ rf = RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
616
+ rf.fit(X_train, y_train)
617
+
618
+ # Use the model to make predictions on the test dataset.
619
+ predictions = rf.predict(X_test)
620
+ autolog_run = mlflow.last_active_run()
621
+
622
+ .. code-block:: python
623
+ :test:
624
+ :caption: To get the most recently active run that ended:
625
+
626
+ import mlflow
627
+
628
+ mlflow.start_run()
629
+ mlflow.end_run()
630
+ run = mlflow.last_active_run()
631
+
632
+ .. code-block:: python
633
+ :test:
634
+ :caption: To retrieve the currently active run:
635
+
636
+ import mlflow
637
+
638
+ mlflow.start_run()
639
+ run = mlflow.last_active_run()
640
+ mlflow.end_run()
641
+
642
+ Returns:
643
+ The active run (this is equivalent to ``mlflow.active_run()``) if one exists.
644
+ Otherwise, the last run started from the current Python process that reached
645
+ a terminal status (i.e. FINISHED, FAILED, or KILLED).
646
+ """
647
+ _active_run = active_run()
648
+ if _active_run is not None:
649
+ return _active_run
650
+
651
+ last_active_run_id = _last_active_run_id.get()
652
+ if last_active_run_id is None:
653
+ return None
654
+ return get_run(last_active_run_id)
655
+
656
+
657
+ def _get_latest_active_run():
658
+ """
659
+ Get active run from global context by checking all threads. The `mlflow.active_run` API
660
+ only returns active run from current thread. This API is useful for the case where one
661
+ needs to get a run started from a separate thread.
662
+ """
663
+ all_active_runs = [
664
+ run for run_stack in _active_run_stack.get_all_thread_values().values() for run in run_stack
665
+ ]
666
+ if all_active_runs:
667
+ return max(all_active_runs, key=lambda run: run.info.start_time)
668
+ return None
669
+
670
+
671
+ def get_run(run_id: str) -> Run:
672
+ """
673
+ Fetch the run from backend store. The resulting Run contains a collection of run metadata --
674
+ RunInfo as well as a collection of run parameters, tags, and metrics -- RunData. It also
675
+ contains a collection of run inputs (experimental), including information about datasets used by
676
+ the run -- RunInputs. In the case where multiple metrics with the same key are logged for the
677
+ run, the RunData contains the most recently logged value at the largest step for each metric.
678
+
679
+ Args:
680
+ run_id: Unique identifier for the run.
681
+
682
+ Returns:
683
+ A single Run object, if the run exists. Otherwise, raises an exception.
684
+
685
+ .. code-block:: python
686
+ :test:
687
+ :caption: Example
688
+
689
+ import mlflow
690
+
691
+ with mlflow.start_run() as run:
692
+ mlflow.log_param("p", 0)
693
+ run_id = run.info.run_id
694
+ print(
695
+ f"run_id: {run_id}; lifecycle_stage: {mlflow.get_run(run_id).info.lifecycle_stage}"
696
+ )
697
+
698
+ .. code-block:: text
699
+ :caption: Output
700
+
701
+ run_id: 7472befefc754e388e8e922824a0cca5; lifecycle_stage: active
702
+ """
703
+ return MlflowClient().get_run(run_id)
704
+
705
+
706
+ def get_metric_history(run_id: str, key: str):
707
+ """
708
+ Return a list of metric objects corresponding to all values logged for a given metric.
709
+
710
+ Args:
711
+ run_id: Unique identifier for the run
712
+ key: Metric name
713
+
714
+ Returns:
715
+ A list of Metric entities if logged, else empty list
716
+ """
717
+ return MlflowClient().get_metric_history(run_id, key)
718
+
719
+
720
+ def get_parent_run(run_id: str) -> Optional[Run]:
721
+ """Gets the parent run for the given run id if one exists.
722
+
723
+ Args:
724
+ run_id: Unique identifier for the child run.
725
+
726
+ Returns:
727
+ A single :py:class:`mlflow.entities.Run` object, if the parent run exists. Otherwise,
728
+ returns None.
729
+
730
+ .. code-block:: python
731
+ :test:
732
+ :caption: Example
733
+
734
+ import mlflow
735
+
736
+ # Create nested runs
737
+ with mlflow.start_run():
738
+ with mlflow.start_run(nested=True) as child_run:
739
+ child_run_id = child_run.info.run_id
740
+
741
+ parent_run = mlflow.get_parent_run(child_run_id)
742
+
743
+ print(f"child_run_id: {child_run_id}")
744
+ print(f"parent_run_id: {parent_run.info.run_id}")
745
+
746
+ .. code-block:: text
747
+ :caption: Output
748
+
749
+ child_run_id: 7d175204675e40328e46d9a6a5a7ee6a
750
+ parent_run_id: 8979459433a24a52ab3be87a229a9cdf
751
+ """
752
+ return MlflowClient().get_parent_run(run_id)
753
+
754
+
755
+ def log_param(key: str, value: Any, synchronous: Optional[bool] = None) -> Any:
756
+ """
757
+ Log a parameter (e.g. model hyperparameter) under the current run. If no run is active,
758
+ this method will create a new active run.
759
+
760
+ Args:
761
+ key: Parameter name. This string may only contain alphanumerics, underscores (_), dashes
762
+ (-), periods (.), spaces ( ), and slashes (/). All backend stores support keys up to
763
+ length 250, but some may support larger keys.
764
+ value: Parameter value, but will be string-ified if not. All built-in backend stores support
765
+ values up to length 6000, but some may support larger values.
766
+ synchronous: *Experimental* If True, blocks until the parameter is logged successfully. If
767
+ False, logs the parameter asynchronously and returns a future representing the logging
768
+ operation. If None, read from environment variable `MLFLOW_ENABLE_ASYNC_LOGGING`,
769
+ which defaults to False if not set.
770
+
771
+ Returns:
772
+ When `synchronous=True`, returns parameter value. When `synchronous=False`, returns an
773
+ :py:class:`mlflow.utils.async_logging.run_operations.RunOperations` instance that represents
774
+ future for logging operation.
775
+
776
+ .. code-block:: python
777
+ :test:
778
+ :caption: Example
779
+
780
+ import mlflow
781
+
782
+ with mlflow.start_run():
783
+ value = mlflow.log_param("learning_rate", 0.01)
784
+ assert value == 0.01
785
+ value = mlflow.log_param("learning_rate", 0.02, synchronous=False)
786
+ """
787
+ run_id = _get_or_start_run().info.run_id
788
+ synchronous = synchronous if synchronous is not None else not MLFLOW_ENABLE_ASYNC_LOGGING.get()
789
+ return MlflowClient().log_param(run_id, key, value, synchronous=synchronous)
790
+
791
+
792
+ def flush_async_logging() -> None:
793
+ """Flush all pending async logging."""
794
+ _get_store().flush_async_logging()
795
+
796
+
797
+ def _shut_down_async_logging() -> None:
798
+ """Shutdown the async logging and flush all pending data."""
799
+ _get_store().shut_down_async_logging()
800
+
801
+
802
+ def flush_artifact_async_logging() -> None:
803
+ """Flush all pending artifact async logging."""
804
+ run_id = _get_or_start_run().info.run_id
805
+ _artifact_repo = _get_artifact_repo(run_id)
806
+ if _artifact_repo:
807
+ _artifact_repo.flush_async_logging()
808
+
809
+
810
+ def flush_trace_async_logging(terminate=False) -> None:
811
+ """
812
+ Flush all pending trace async logging.
813
+
814
+ Args:
815
+ terminate: If True, shut down the logging threads after flushing.
816
+ """
817
+ try:
818
+ _get_trace_exporter()._async_queue.flush(terminate=terminate)
819
+ except Exception as e:
820
+ _logger.error(f"Failed to flush trace async logging: {e}")
821
+
822
+
823
+ def set_experiment_tag(key: str, value: Any) -> None:
824
+ """
825
+ Set a tag on the current experiment. Value is converted to a string.
826
+
827
+ Args:
828
+ key: Tag name. This string may only contain alphanumerics, underscores (_), dashes (-),
829
+ periods (.), spaces ( ), and slashes (/). All backend stores will support keys up to
830
+ length 250, but some may support larger keys.
831
+ value: Tag value, but will be string-ified if not. All backend stores will support values
832
+ up to length 5000, but some may support larger values.
833
+
834
+ .. code-block:: python
835
+ :test:
836
+ :caption: Example
837
+
838
+ import mlflow
839
+
840
+ with mlflow.start_run():
841
+ mlflow.set_experiment_tag("release.version", "2.2.0")
842
+ """
843
+ experiment_id = _get_experiment_id()
844
+ MlflowClient().set_experiment_tag(experiment_id, key, value)
845
+
846
+
847
+ def set_tag(key: str, value: Any, synchronous: Optional[bool] = None) -> Optional[RunOperations]:
848
+ """
849
+ Set a tag under the current run. If no run is active, this method will create a new active
850
+ run.
851
+
852
+ Args:
853
+ key: Tag name. This string may only contain alphanumerics, underscores (_), dashes (-),
854
+ periods (.), spaces ( ), and slashes (/). All backend stores will support keys up to
855
+ length 250, but some may support larger keys.
856
+ value: Tag value, but will be string-ified if not. All backend stores will support values
857
+ up to length 5000, but some may support larger values.
858
+ synchronous: *Experimental* If True, blocks until the tag is logged successfully. If False,
859
+ logs the tag asynchronously and returns a future representing the logging operation.
860
+ If None, read from environment variable `MLFLOW_ENABLE_ASYNC_LOGGING`, which
861
+ defaults to False if not set.
862
+
863
+ Returns:
864
+ When `synchronous=True`, returns None. When `synchronous=False`, returns an
865
+ :py:class:`mlflow.utils.async_logging.run_operations.RunOperations` instance that
866
+ represents future for logging operation.
867
+
868
+ .. code-block:: python
869
+ :test:
870
+ :caption: Example
871
+
872
+ import mlflow
873
+
874
+ # Set a tag.
875
+ with mlflow.start_run():
876
+ mlflow.set_tag("release.version", "2.2.0")
877
+
878
+ # Set a tag in async fashion.
879
+ with mlflow.start_run():
880
+ mlflow.set_tag("release.version", "2.2.1", synchronous=False)
881
+ """
882
+ run_id = _get_or_start_run().info.run_id
883
+ synchronous = synchronous if synchronous is not None else not MLFLOW_ENABLE_ASYNC_LOGGING.get()
884
+ return MlflowClient().set_tag(run_id, key, value, synchronous=synchronous)
885
+
886
+
887
+ def delete_tag(key: str) -> None:
888
+ """
889
+ Delete a tag from a run. This is irreversible. If no run is active, this method
890
+ will create a new active run.
891
+
892
+ Args:
893
+ key: Name of the tag
894
+
895
+ .. code-block:: python
896
+ :test:
897
+ :caption: Example
898
+
899
+ import mlflow
900
+
901
+ tags = {"engineering": "ML Platform", "engineering_remote": "ML Platform"}
902
+
903
+ with mlflow.start_run() as run:
904
+ mlflow.set_tags(tags)
905
+
906
+ with mlflow.start_run(run_id=run.info.run_id):
907
+ mlflow.delete_tag("engineering_remote")
908
+ """
909
+ run_id = _get_or_start_run().info.run_id
910
+ MlflowClient().delete_tag(run_id, key)
911
+
912
+
913
+ def log_metric(
914
+ key: str,
915
+ value: float,
916
+ step: Optional[int] = None,
917
+ synchronous: Optional[bool] = None,
918
+ timestamp: Optional[int] = None,
919
+ run_id: Optional[str] = None,
920
+ model_id: Optional[str] = None,
921
+ dataset: Optional["Dataset"] = None,
922
+ ) -> Optional[RunOperations]:
923
+ """
924
+ Log a metric under the current run. If no run is active, this method will create
925
+ a new active run.
926
+
927
+ Args:
928
+ key: Metric name. This string may only contain alphanumerics, underscores (_),
929
+ dashes (-), periods (.), spaces ( ), and slashes (/).
930
+ All backend stores will support keys up to length 250, but some may
931
+ support larger keys.
932
+ value: Metric value. Note that some special values such as +/- Infinity may be
933
+ replaced by other values depending on the store. For example, the
934
+ SQLAlchemy store replaces +/- Infinity with max / min float values.
935
+ All backend stores will support values up to length 5000, but some
936
+ may support larger values.
937
+ step: Metric step. Defaults to zero if unspecified.
938
+ synchronous: *Experimental* If True, blocks until the metric is logged
939
+ successfully. If False, logs the metric asynchronously and
940
+ returns a future representing the logging operation. If None, read from environment
941
+ variable `MLFLOW_ENABLE_ASYNC_LOGGING`, which defaults to False if not set.
942
+ timestamp: Time when this metric was calculated. Defaults to the current system time.
943
+ run_id: If specified, log the metric to the specified run. If not specified, log the metric
944
+ to the currently active run.
945
+ model_id: The ID of the model associated with the metric. If not specified, use the current
946
+ active model ID set by :py:func:`mlflow.set_active_model`. If no active model exists,
947
+ the models IDs associated with the specified or active run will be used.
948
+ dataset: The dataset associated with the metric.
949
+
950
+ Returns:
951
+ When `synchronous=True`, returns None.
952
+ When `synchronous=False`, returns `RunOperations` that represents future for
953
+ logging operation.
954
+
955
+ .. code-block:: python
956
+ :test:
957
+ :caption: Example
958
+
959
+ import mlflow
960
+
961
+ # Log a metric
962
+ with mlflow.start_run():
963
+ mlflow.log_metric("mse", 2500.00)
964
+
965
+ # Log a metric in async fashion.
966
+ with mlflow.start_run():
967
+ mlflow.log_metric("mse", 2500.00, synchronous=False)
968
+ """
969
+ run_id = run_id or _get_or_start_run().info.run_id
970
+ synchronous = synchronous if synchronous is not None else not MLFLOW_ENABLE_ASYNC_LOGGING.get()
971
+ model_id = model_id or get_active_model_id()
972
+ _log_inputs_for_metrics_if_necessary(
973
+ run_id,
974
+ [
975
+ Metric(
976
+ key=key,
977
+ value=value,
978
+ timestamp=timestamp or get_current_time_millis(),
979
+ step=step or 0,
980
+ model_id=model_id,
981
+ dataset_name=dataset.name if dataset is not None else None,
982
+ dataset_digest=dataset.digest if dataset is not None else None,
983
+ ),
984
+ ],
985
+ datasets=[dataset] if dataset is not None else None,
986
+ )
987
+ timestamp = timestamp or get_current_time_millis()
988
+ step = step or 0
989
+ model_ids = (
990
+ [model_id]
991
+ if model_id is not None
992
+ else (_get_model_ids_for_new_metric_if_exist(run_id, step) or [None])
993
+ )
994
+ for model_id in model_ids:
995
+ return MlflowClient().log_metric(
996
+ run_id,
997
+ key,
998
+ value,
999
+ timestamp,
1000
+ step,
1001
+ synchronous=synchronous,
1002
+ model_id=model_id,
1003
+ dataset_name=dataset.name if dataset is not None else None,
1004
+ dataset_digest=dataset.digest if dataset is not None else None,
1005
+ )
1006
+
1007
+
1008
+ def _log_inputs_for_metrics_if_necessary(
1009
+ run_id, metrics: list[Metric], datasets: Optional[list["Dataset"]] = None
1010
+ ) -> None:
1011
+ client = MlflowClient()
1012
+ run = client.get_run(run_id)
1013
+ datasets = datasets or []
1014
+ for metric in metrics:
1015
+ input_model_ids = [i.model_id for i in (run.inputs and run.inputs.model_inputs) or []]
1016
+ output_model_ids = [o.model_id for o in (run.outputs and run.outputs.model_outputs) or []]
1017
+ if (
1018
+ metric.model_id is not None
1019
+ and metric.model_id not in input_model_ids + output_model_ids
1020
+ ):
1021
+ client.log_inputs(run_id, models=[LoggedModelInput(model_id=metric.model_id)])
1022
+ if (metric.dataset_name, metric.dataset_digest) not in [
1023
+ (inp.dataset.name, inp.dataset.digest) for inp in run.inputs.dataset_inputs
1024
+ ]:
1025
+ matching_dataset = next(
1026
+ (
1027
+ dataset
1028
+ for dataset in datasets
1029
+ if dataset.name == metric.dataset_name
1030
+ and dataset.digest == metric.dataset_digest
1031
+ ),
1032
+ None,
1033
+ )
1034
+ if matching_dataset is not None:
1035
+ client.log_inputs(
1036
+ run_id,
1037
+ datasets=[DatasetInput(matching_dataset._to_mlflow_entity(), tags=[])],
1038
+ )
1039
+
1040
+
1041
+ def _get_model_ids_for_new_metric_if_exist(run_id: str, metric_step: str) -> list[str]:
1042
+ client = MlflowClient()
1043
+ run = client.get_run(run_id)
1044
+ outputs = run.outputs.model_outputs if run.outputs else []
1045
+ model_outputs_at_step = [mo for mo in outputs if mo.step == metric_step]
1046
+ return [mo.model_id for mo in model_outputs_at_step]
1047
+
1048
+
1049
+ def log_metrics(
1050
+ metrics: dict[str, float],
1051
+ step: Optional[int] = None,
1052
+ synchronous: Optional[bool] = None,
1053
+ run_id: Optional[str] = None,
1054
+ timestamp: Optional[int] = None,
1055
+ model_id: Optional[str] = None,
1056
+ dataset: Optional["Dataset"] = None,
1057
+ ) -> Optional[RunOperations]:
1058
+ """
1059
+ Log multiple metrics for the current run. If no run is active, this method will create a new
1060
+ active run.
1061
+
1062
+ Args:
1063
+ metrics: Dictionary of metric_name: String -> value: Float. Note that some special
1064
+ values such as +/- Infinity may be replaced by other values depending on
1065
+ the store. For example, sql based store may replace +/- Infinity with
1066
+ max / min float values.
1067
+ step: A single integer step at which to log the specified
1068
+ Metrics. If unspecified, each metric is logged at step zero.
1069
+ synchronous: *Experimental* If True, blocks until the metrics are logged
1070
+ successfully. If False, logs the metrics asynchronously and
1071
+ returns a future representing the logging operation. If None, read from environment
1072
+ variable `MLFLOW_ENABLE_ASYNC_LOGGING`, which defaults to False if not set.
1073
+ run_id: Run ID. If specified, log metrics to the specified run. If not specified, log
1074
+ metrics to the currently active run.
1075
+ timestamp: Time when these metrics were calculated. Defaults to the current system time.
1076
+ model_id: The ID of the model associated with the metric. If not specified, use the current
1077
+ active model ID set by :py:func:`mlflow.set_active_model`. If no active model
1078
+ exists, the models IDs associated with the specified or active run will be used.
1079
+ dataset: The dataset associated with the metrics.
1080
+
1081
+ Returns:
1082
+ When `synchronous=True`, returns None. When `synchronous=False`, returns an
1083
+ :py:class:`mlflow.utils.async_logging.run_operations.RunOperations` instance that
1084
+ represents future for logging operation.
1085
+
1086
+ .. code-block:: python
1087
+ :test:
1088
+ :caption: Example
1089
+
1090
+ import mlflow
1091
+
1092
+ metrics = {"mse": 2500.00, "rmse": 50.00}
1093
+
1094
+ # Log a batch of metrics
1095
+ with mlflow.start_run():
1096
+ mlflow.log_metrics(metrics)
1097
+
1098
+ # Log a batch of metrics in async fashion.
1099
+ with mlflow.start_run():
1100
+ mlflow.log_metrics(metrics, synchronous=False)
1101
+ """
1102
+ run_id = run_id or _get_or_start_run().info.run_id
1103
+ timestamp = timestamp or get_current_time_millis()
1104
+ step = step or 0
1105
+ dataset_name = dataset.name if dataset is not None else None
1106
+ dataset_digest = dataset.digest if dataset is not None else None
1107
+ model_id = model_id or get_active_model_id()
1108
+ model_ids = (
1109
+ [model_id]
1110
+ if model_id is not None
1111
+ else (_get_model_ids_for_new_metric_if_exist(run_id, step) or [None])
1112
+ )
1113
+ metrics_arr = [
1114
+ Metric(
1115
+ key,
1116
+ value,
1117
+ timestamp,
1118
+ step or 0,
1119
+ model_id=model_id,
1120
+ dataset_name=dataset_name,
1121
+ dataset_digest=dataset_digest,
1122
+ run_id=run_id,
1123
+ )
1124
+ for key, value in metrics.items()
1125
+ for model_id in model_ids
1126
+ ]
1127
+ _log_inputs_for_metrics_if_necessary(
1128
+ run_id, metrics_arr, [dataset] if dataset is not None else None
1129
+ )
1130
+ synchronous = synchronous if synchronous is not None else not MLFLOW_ENABLE_ASYNC_LOGGING.get()
1131
+ return MlflowClient().log_batch(
1132
+ run_id=run_id,
1133
+ metrics=metrics_arr,
1134
+ params=[],
1135
+ tags=[],
1136
+ synchronous=synchronous,
1137
+ )
1138
+
1139
+
1140
+ def log_params(
1141
+ params: dict[str, Any], synchronous: Optional[bool] = None, run_id: Optional[str] = None
1142
+ ) -> Optional[RunOperations]:
1143
+ """
1144
+ Log a batch of params for the current run. If no run is active, this method will create a
1145
+ new active run.
1146
+
1147
+ Args:
1148
+ params: Dictionary of param_name: String -> value: (String, but will be string-ified if
1149
+ not)
1150
+ synchronous: *Experimental* If True, blocks until the parameters are logged
1151
+ successfully. If False, logs the parameters asynchronously and
1152
+ returns a future representing the logging operation. If None, read from environment
1153
+ variable `MLFLOW_ENABLE_ASYNC_LOGGING`, which defaults to False if not set.
1154
+ run_id: Run ID. If specified, log params to the specified run. If not specified, log
1155
+ params to the currently active run.
1156
+
1157
+ Returns:
1158
+ When `synchronous=True`, returns None. When `synchronous=False`, returns an
1159
+ :py:class:`mlflow.utils.async_logging.run_operations.RunOperations` instance that
1160
+ represents future for logging operation.
1161
+
1162
+ .. code-block:: python
1163
+ :test:
1164
+ :caption: Example
1165
+
1166
+ import mlflow
1167
+
1168
+ params = {"learning_rate": 0.01, "n_estimators": 10}
1169
+
1170
+ # Log a batch of parameters
1171
+ with mlflow.start_run():
1172
+ mlflow.log_params(params)
1173
+
1174
+ # Log a batch of parameters in async fashion.
1175
+ with mlflow.start_run():
1176
+ mlflow.log_params(params, synchronous=False)
1177
+ """
1178
+ run_id = run_id or _get_or_start_run().info.run_id
1179
+ params_arr = [Param(key, str(value)) for key, value in params.items()]
1180
+ synchronous = synchronous if synchronous is not None else not MLFLOW_ENABLE_ASYNC_LOGGING.get()
1181
+ return MlflowClient().log_batch(
1182
+ run_id=run_id, metrics=[], params=params_arr, tags=[], synchronous=synchronous
1183
+ )
1184
+
1185
+
1186
+ def _create_dataset_input(
1187
+ dataset: Optional["Dataset"],
1188
+ context: Optional[str] = None,
1189
+ tags: Optional[dict[str, str]] = None,
1190
+ ) -> Optional[DatasetInput]:
1191
+ if (context or tags) and dataset is None:
1192
+ raise MlflowException.invalid_parameter_value(
1193
+ "`dataset` must be specified if `context` or `tags` is specified."
1194
+ )
1195
+ tags_to_log = []
1196
+ if tags:
1197
+ tags_to_log = [InputTag(key=key, value=value) for key, value in tags.items()]
1198
+ if context:
1199
+ tags_to_log.append(InputTag(key=MLFLOW_DATASET_CONTEXT, value=context))
1200
+
1201
+ return DatasetInput(dataset=dataset._to_mlflow_entity(), tags=tags_to_log) if dataset else None
1202
+
1203
+
1204
+ def log_input(
1205
+ dataset: Optional["Dataset"] = None,
1206
+ context: Optional[str] = None,
1207
+ tags: Optional[dict[str, str]] = None,
1208
+ model: Optional[LoggedModelInput] = None,
1209
+ ) -> None:
1210
+ """
1211
+ Log a dataset used in the current run.
1212
+
1213
+ Args:
1214
+ dataset: :py:class:`mlflow.data.dataset.Dataset` object to be logged.
1215
+ context: Context in which the dataset is used. For example: "training", "testing".
1216
+ This will be set as an input tag with key `mlflow.data.context`.
1217
+ tags: Tags to be associated with the dataset. Dictionary of tag_key -> tag_value.
1218
+ model: A :py:class:`mlflow.entities.LoggedModelInput` instance to log as as input
1219
+ to the run.
1220
+
1221
+ .. code-block:: python
1222
+ :test:
1223
+ :caption: Example
1224
+
1225
+ import numpy as np
1226
+ import mlflow
1227
+
1228
+ array = np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
1229
+ dataset = mlflow.data.from_numpy(array, source="data.csv")
1230
+
1231
+ # Log an input dataset used for training
1232
+ with mlflow.start_run():
1233
+ mlflow.log_input(dataset, context="training")
1234
+ """
1235
+ run_id = _get_or_start_run().info.run_id
1236
+ datasets = [_create_dataset_input(dataset, context, tags)] if dataset else None
1237
+ models = [model] if model else None
1238
+
1239
+ MlflowClient().log_inputs(run_id=run_id, datasets=datasets, models=models)
1240
+
1241
+
1242
+ def log_inputs(
1243
+ datasets: Optional[list[Optional["Dataset"]]] = None,
1244
+ contexts: Optional[list[Optional[str]]] = None,
1245
+ tags_list: Optional[list[Optional[dict[str, str]]]] = None,
1246
+ models: Optional[list[Optional[LoggedModelInput]]] = None,
1247
+ ) -> None:
1248
+ """
1249
+ Log a batch of datasets used in the current run.
1250
+
1251
+ The lists of `datasets`, `contexts`, `tags_list` must have the same length.
1252
+ The entries in these lists can be ``None``, which represents empty value to the
1253
+ corresponding input.
1254
+
1255
+ Args:
1256
+ datasets: List of :py:class:`mlflow.data.dataset.Dataset` object to be logged.
1257
+ contexts: List of context in which the dataset is used. For example: "training", "testing".
1258
+ This will be set as an input tag with key `mlflow.data.context`.
1259
+ tags_list: List of tags to be associated with the dataset. Dictionary of
1260
+ tag_key -> tag_value.
1261
+ models: List of :py:class:`mlflow.entities.LoggedModelInput` instance to log as input
1262
+ to the run. Currently only Databricks managed MLflow supports this argument.
1263
+
1264
+ .. code-block:: python
1265
+ :test:
1266
+ :caption: Example
1267
+
1268
+ import numpy as np
1269
+ import mlflow
1270
+
1271
+ array = np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
1272
+ dataset = mlflow.data.from_numpy(array, source="data.csv")
1273
+
1274
+ array2 = np.asarray([[-1, 2, 3], [-4, 5, 6]])
1275
+ dataset2 = mlflow.data.from_numpy(array2, source="data2.csv")
1276
+
1277
+ # Log 2 input datasets used for training and test,
1278
+ # the training dataset has no tag.
1279
+ # the test dataset has tags `{"my_tag": "tag_value"}`.
1280
+ with mlflow.start_run():
1281
+ mlflow.log_inputs(
1282
+ [dataset, dataset2],
1283
+ contexts=["training", "test"],
1284
+ tags_list=[None, {"my_tag": "tag_value"}],
1285
+ models=None,
1286
+ )
1287
+ """
1288
+ from mlflow.utils.databricks_utils import is_databricks_uri
1289
+
1290
+ run_id = _get_or_start_run().info.run_id
1291
+
1292
+ datasets = datasets or []
1293
+ contexts = contexts or []
1294
+ tags_list = tags_list or []
1295
+ if not (len(datasets) == len(contexts) == len(tags_list)):
1296
+ raise MlflowException(
1297
+ "`mlflow.log_inputs` requires `datasets`, `contexts`, `tags_list` to be "
1298
+ "non-empty list and have the same length."
1299
+ )
1300
+
1301
+ if models and not is_databricks_uri(mlflow.get_tracking_uri()):
1302
+ raise MlflowException("'models' argument is only supported by Databricks managed MLflow.")
1303
+
1304
+ dataset_inputs = [
1305
+ _create_dataset_input(dataset, context, tags)
1306
+ for dataset, context, tags in zip(datasets, contexts, tags_list)
1307
+ ]
1308
+
1309
+ MlflowClient().log_inputs(run_id=run_id, datasets=dataset_inputs, models=models)
1310
+
1311
+
1312
+ def set_experiment_tags(tags: dict[str, Any]) -> None:
1313
+ """
1314
+ Set tags for the current active experiment.
1315
+
1316
+ Args:
1317
+ tags: Dictionary containing tag names and corresponding values.
1318
+
1319
+ .. code-block:: python
1320
+ :test:
1321
+ :caption: Example
1322
+
1323
+ import mlflow
1324
+
1325
+ tags = {
1326
+ "engineering": "ML Platform",
1327
+ "release.candidate": "RC1",
1328
+ "release.version": "2.2.0",
1329
+ }
1330
+
1331
+ # Set a batch of tags
1332
+ with mlflow.start_run():
1333
+ mlflow.set_experiment_tags(tags)
1334
+ """
1335
+ for key, value in tags.items():
1336
+ set_experiment_tag(key, value)
1337
+
1338
+
1339
+ def set_tags(tags: dict[str, Any], synchronous: Optional[bool] = None) -> Optional[RunOperations]:
1340
+ """
1341
+ Log a batch of tags for the current run. If no run is active, this method will create a
1342
+ new active run.
1343
+
1344
+ Args:
1345
+ tags: Dictionary of tag_name: String -> value: (String, but will be string-ified if
1346
+ not)
1347
+ synchronous: *Experimental* If True, blocks until tags are logged successfully. If False,
1348
+ logs tags asynchronously and returns a future representing the logging operation.
1349
+ If None, read from environment variable `MLFLOW_ENABLE_ASYNC_LOGGING`, which
1350
+ defaults to False if not set.
1351
+
1352
+ Returns:
1353
+ When `synchronous=True`, returns None. When `synchronous=False`, returns an
1354
+ :py:class:`mlflow.utils.async_logging.run_operations.RunOperations` instance that
1355
+ represents future for logging operation.
1356
+
1357
+ .. code-block:: python
1358
+ :test:
1359
+ :caption: Example
1360
+
1361
+ import mlflow
1362
+
1363
+ tags = {
1364
+ "engineering": "ML Platform",
1365
+ "release.candidate": "RC1",
1366
+ "release.version": "2.2.0",
1367
+ }
1368
+
1369
+ # Set a batch of tags
1370
+ with mlflow.start_run():
1371
+ mlflow.set_tags(tags)
1372
+
1373
+ # Set a batch of tags in async fashion.
1374
+ with mlflow.start_run():
1375
+ mlflow.set_tags(tags, synchronous=False)
1376
+ """
1377
+ run_id = _get_or_start_run().info.run_id
1378
+ tags_arr = [RunTag(key, str(value)) for key, value in tags.items()]
1379
+ synchronous = synchronous if synchronous is not None else not MLFLOW_ENABLE_ASYNC_LOGGING.get()
1380
+ return MlflowClient().log_batch(
1381
+ run_id=run_id, metrics=[], params=[], tags=tags_arr, synchronous=synchronous
1382
+ )
1383
+
1384
+
1385
+ def log_artifact(
1386
+ local_path: str, artifact_path: Optional[str] = None, run_id: Optional[str] = None
1387
+ ) -> None:
1388
+ """
1389
+ Log a local file or directory as an artifact of the currently active run. If no run is
1390
+ active, this method will create a new active run.
1391
+
1392
+ Args:
1393
+ local_path: Path to the file to write.
1394
+ artifact_path: If provided, the directory in ``artifact_uri`` to write to.
1395
+ run_id: If specified, log the artifact to the specified run. If not specified, log the
1396
+ artifact to the currently active run.
1397
+
1398
+ .. code-block:: python
1399
+ :test:
1400
+ :caption: Example
1401
+
1402
+ import tempfile
1403
+ from pathlib import Path
1404
+
1405
+ import mlflow
1406
+
1407
+ # Create a features.txt artifact file
1408
+ features = "rooms, zipcode, median_price, school_rating, transport"
1409
+ with tempfile.TemporaryDirectory() as tmp_dir:
1410
+ path = Path(tmp_dir, "features.txt")
1411
+ path.write_text(features)
1412
+ # With artifact_path=None write features.txt under
1413
+ # root artifact_uri/artifacts directory
1414
+ with mlflow.start_run():
1415
+ mlflow.log_artifact(path)
1416
+ """
1417
+ run_id = run_id or _get_or_start_run().info.run_id
1418
+ MlflowClient().log_artifact(run_id, local_path, artifact_path)
1419
+
1420
+
1421
+ def log_artifacts(
1422
+ local_dir: str, artifact_path: Optional[str] = None, run_id: Optional[str] = None
1423
+ ) -> None:
1424
+ """
1425
+ Log all the contents of a local directory as artifacts of the run. If no run is active,
1426
+ this method will create a new active run.
1427
+
1428
+ Args:
1429
+ local_dir: Path to the directory of files to write.
1430
+ artifact_path: If provided, the directory in ``artifact_uri`` to write to.
1431
+ run_id: If specified, log the artifacts to the specified run. If not specified, log the
1432
+ artifacts to the currently active run.
1433
+
1434
+ .. code-block:: python
1435
+ :test:
1436
+ :caption: Example
1437
+
1438
+ import json
1439
+ import tempfile
1440
+ from pathlib import Path
1441
+
1442
+ import mlflow
1443
+
1444
+ # Create some files to preserve as artifacts
1445
+ features = "rooms, zipcode, median_price, school_rating, transport"
1446
+ data = {"state": "TX", "Available": 25, "Type": "Detached"}
1447
+ with tempfile.TemporaryDirectory() as tmp_dir:
1448
+ tmp_dir = Path(tmp_dir)
1449
+ with (tmp_dir / "data.json").open("w") as f:
1450
+ json.dump(data, f, indent=2)
1451
+ with (tmp_dir / "features.json").open("w") as f:
1452
+ f.write(features)
1453
+ # Write all files in `tmp_dir` to root artifact_uri/states
1454
+ with mlflow.start_run():
1455
+ mlflow.log_artifacts(tmp_dir, artifact_path="states")
1456
+ """
1457
+ run_id = run_id or _get_or_start_run().info.run_id
1458
+ MlflowClient().log_artifacts(run_id, local_dir, artifact_path)
1459
+
1460
+
1461
+ def list_artifacts(artifact_path: Optional[str] = None, run_id: Optional[str] = None) -> list:
1462
+ """
1463
+ List the artifacts of the run. If no run is active, this method will list artifacts
1464
+ of the last active run.
1465
+
1466
+ Args:
1467
+ artifact_path: Path to a directory within the artifact storage. If provided,
1468
+ lists the artifacts in this directory.
1469
+ run_id: ID of the run to list artifacts for. If not specified, artifacts
1470
+ will be listed for the currently active run.
1471
+
1472
+ Returns:
1473
+ List of FileInfo objects representing the artifacts.
1474
+ """
1475
+ from mlflow.entities import FileInfo
1476
+ run_id = run_id or _get_or_start_run().info.run_id
1477
+ return MlflowClient().list_artifacts(run_id, artifact_path)
1478
+
1479
+
1480
+ def log_text(text: str, artifact_file: str, run_id: Optional[str] = None) -> None:
1481
+ """
1482
+ Log text as an artifact.
1483
+
1484
+ Args:
1485
+ text: String containing text to log.
1486
+ artifact_file: The run-relative artifact file path in posixpath format to which
1487
+ the text is saved (e.g. "dir/file.txt").
1488
+ run_id: If specified, log the artifact to the specified run. If not specified, log the
1489
+ artifact to the currently active run.
1490
+
1491
+ .. code-block:: python
1492
+ :test:
1493
+ :caption: Example
1494
+
1495
+ import mlflow
1496
+
1497
+ with mlflow.start_run():
1498
+ # Log text to a file under the run's root artifact directory
1499
+ mlflow.log_text("text1", "file1.txt")
1500
+
1501
+ # Log text in a subdirectory of the run's root artifact directory
1502
+ mlflow.log_text("text2", "dir/file2.txt")
1503
+
1504
+ # Log HTML text
1505
+ mlflow.log_text("<h1>header</h1>", "index.html")
1506
+
1507
+ """
1508
+ run_id = run_id or _get_or_start_run().info.run_id
1509
+ MlflowClient().log_text(run_id, text, artifact_file)
1510
+
1511
+
1512
+ def log_dict(dictionary: dict[str, Any], artifact_file: str, run_id: Optional[str] = None) -> None:
1513
+ """
1514
+ Log a JSON/YAML-serializable object (e.g. `dict`) as an artifact. The serialization
1515
+ format (JSON or YAML) is automatically inferred from the extension of `artifact_file`.
1516
+ If the file extension doesn't exist or match any of [".json", ".yml", ".yaml"],
1517
+ JSON format is used.
1518
+
1519
+ Args:
1520
+ dictionary: Dictionary to log.
1521
+ artifact_file: The run-relative artifact file path in posixpath format to which
1522
+ the dictionary is saved (e.g. "dir/data.json").
1523
+ run_id: If specified, log the dictionary to the specified run. If not specified, log the
1524
+ dictionary to the currently active run.
1525
+
1526
+ .. code-block:: python
1527
+ :test:
1528
+ :caption: Example
1529
+
1530
+ import mlflow
1531
+
1532
+ dictionary = {"k": "v"}
1533
+
1534
+ with mlflow.start_run():
1535
+ # Log a dictionary as a JSON file under the run's root artifact directory
1536
+ mlflow.log_dict(dictionary, "data.json")
1537
+
1538
+ # Log a dictionary as a YAML file in a subdirectory of the run's root artifact directory
1539
+ mlflow.log_dict(dictionary, "dir/data.yml")
1540
+
1541
+ # If the file extension doesn't exist or match any of [".json", ".yaml", ".yml"],
1542
+ # JSON format is used.
1543
+ mlflow.log_dict(dictionary, "data")
1544
+ mlflow.log_dict(dictionary, "data.txt")
1545
+
1546
+ """
1547
+ run_id = run_id or _get_or_start_run().info.run_id
1548
+ MlflowClient().log_dict(run_id, dictionary, artifact_file)
1549
+
1550
+
1551
+ def log_figure(
1552
+ figure: Union["matplotlib.figure.Figure", "plotly.graph_objects.Figure"],
1553
+ artifact_file: str,
1554
+ *,
1555
+ save_kwargs: Optional[dict[str, Any]] = None,
1556
+ ) -> None:
1557
+ """
1558
+ Log a figure as an artifact. The following figure objects are supported:
1559
+
1560
+ - `matplotlib.figure.Figure`_
1561
+ - `plotly.graph_objects.Figure`_
1562
+
1563
+ .. _matplotlib.figure.Figure:
1564
+ https://matplotlib.org/api/_as_gen/matplotlib.figure.Figure.html
1565
+
1566
+ .. _plotly.graph_objects.Figure:
1567
+ https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html
1568
+
1569
+ Args:
1570
+ figure: Figure to log.
1571
+ artifact_file: The run-relative artifact file path in posixpath format to which
1572
+ the figure is saved (e.g. "dir/file.png").
1573
+ save_kwargs: Additional keyword arguments passed to the method that saves the figure.
1574
+
1575
+ .. code-block:: python
1576
+ :test:
1577
+ :caption: Matplotlib Example
1578
+
1579
+ import mlflow
1580
+ import matplotlib.pyplot as plt
1581
+
1582
+ fig, ax = plt.subplots()
1583
+ ax.plot([0, 1], [2, 3])
1584
+
1585
+ with mlflow.start_run():
1586
+ mlflow.log_figure(fig, "figure.png")
1587
+
1588
+ .. code-block:: python
1589
+ :test:
1590
+ :caption: Plotly Example
1591
+
1592
+ import mlflow
1593
+ from plotly import graph_objects as go
1594
+
1595
+ fig = go.Figure(go.Scatter(x=[0, 1], y=[2, 3]))
1596
+
1597
+ with mlflow.start_run():
1598
+ mlflow.log_figure(fig, "figure.html")
1599
+ """
1600
+ run_id = _get_or_start_run().info.run_id
1601
+ MlflowClient().log_figure(run_id, figure, artifact_file, save_kwargs=save_kwargs)
1602
+
1603
+
1604
+ def log_image(
1605
+ image: Union["numpy.ndarray", "PIL.Image.Image", "mlflow.Image"],
1606
+ artifact_file: Optional[str] = None,
1607
+ key: Optional[str] = None,
1608
+ step: Optional[int] = None,
1609
+ timestamp: Optional[int] = None,
1610
+ synchronous: Optional[bool] = False,
1611
+ ) -> None:
1612
+ """
1613
+ Logs an image in MLflow, supporting two use cases:
1614
+
1615
+ 1. Time-stepped image logging:
1616
+ Ideal for tracking changes or progressions through iterative processes (e.g.,
1617
+ during model training phases).
1618
+
1619
+ - Usage: :code:`log_image(image, key=key, step=step, timestamp=timestamp)`
1620
+
1621
+ 2. Artifact file image logging:
1622
+ Best suited for static image logging where the image is saved directly as a file
1623
+ artifact.
1624
+
1625
+ - Usage: :code:`log_image(image, artifact_file)`
1626
+
1627
+ The following image formats are supported:
1628
+ - `numpy.ndarray`_
1629
+ - `PIL.Image.Image`_
1630
+
1631
+ .. _numpy.ndarray:
1632
+ https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html
1633
+
1634
+ .. _PIL.Image.Image:
1635
+ https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image
1636
+
1637
+ - :class:`mlflow.Image`: An MLflow wrapper around PIL image for convenient image logging.
1638
+
1639
+ Numpy array support
1640
+ - data types:
1641
+
1642
+ - bool (useful for logging image masks)
1643
+ - integer [0, 255]
1644
+ - unsigned integer [0, 255]
1645
+ - float [0.0, 1.0]
1646
+
1647
+ .. warning::
1648
+
1649
+ - Out-of-range integer values will raise ValueError.
1650
+ - Out-of-range float values will auto-scale with min/max and warn.
1651
+
1652
+ - shape (H: height, W: width):
1653
+
1654
+ - H x W (Grayscale)
1655
+ - H x W x 1 (Grayscale)
1656
+ - H x W x 3 (an RGB channel order is assumed)
1657
+ - H x W x 4 (an RGBA channel order is assumed)
1658
+
1659
+ Args:
1660
+ image: The image object to be logged.
1661
+ artifact_file: Specifies the path, in POSIX format, where the image
1662
+ will be stored as an artifact relative to the run's root directory (for
1663
+ example, "dir/image.png"). This parameter is kept for backward compatibility
1664
+ and should not be used together with `key`, `step`, or `timestamp`.
1665
+ key: Image name for time-stepped image logging. This string may only contain
1666
+ alphanumerics, underscores (_), dashes (-), periods (.), spaces ( ), and
1667
+ slashes (/).
1668
+ step: Integer training step (iteration) at which the image was saved.
1669
+ Defaults to 0.
1670
+ timestamp: Time when this image was saved. Defaults to the current system time.
1671
+ synchronous: *Experimental* If True, blocks until the image is logged successfully.
1672
+
1673
+ .. code-block:: python
1674
+ :caption: Time-stepped image logging numpy example
1675
+
1676
+ import mlflow
1677
+ import numpy as np
1678
+
1679
+ image = np.random.randint(0, 256, size=(100, 100, 3), dtype=np.uint8)
1680
+
1681
+ with mlflow.start_run():
1682
+ mlflow.log_image(image, key="dogs", step=3)
1683
+
1684
+ .. code-block:: python
1685
+ :caption: Time-stepped image logging pillow example
1686
+
1687
+ import mlflow
1688
+ from PIL import Image
1689
+
1690
+ image = Image.new("RGB", (100, 100))
1691
+
1692
+ with mlflow.start_run():
1693
+ mlflow.log_image(image, key="dogs", step=3)
1694
+
1695
+ .. code-block:: python
1696
+ :caption: Time-stepped image logging with mlflow.Image example
1697
+
1698
+ import mlflow
1699
+ from PIL import Image
1700
+
1701
+ # If you have a preexisting saved image
1702
+ Image.new("RGB", (100, 100)).save("image.png")
1703
+
1704
+ image = mlflow.Image("image.png")
1705
+ with mlflow.start_run() as run:
1706
+ mlflow.log_image(run.info.run_id, image, key="dogs", step=3)
1707
+
1708
+ .. code-block:: python
1709
+ :caption: Legacy artifact file image logging numpy example
1710
+
1711
+ import mlflow
1712
+ import numpy as np
1713
+
1714
+ image = np.random.randint(0, 256, size=(100, 100, 3), dtype=np.uint8)
1715
+
1716
+ with mlflow.start_run():
1717
+ mlflow.log_image(image, "image.png")
1718
+
1719
+ .. code-block:: python
1720
+ :caption: Legacy artifact file image logging pillow example
1721
+
1722
+ import mlflow
1723
+ from PIL import Image
1724
+
1725
+ image = Image.new("RGB", (100, 100))
1726
+
1727
+ with mlflow.start_run():
1728
+ mlflow.log_image(image, "image.png")
1729
+ """
1730
+ run_id = _get_or_start_run().info.run_id
1731
+ MlflowClient().log_image(run_id, image, artifact_file, key, step, timestamp, synchronous)
1732
+
1733
+
1734
+ def log_table(
1735
+ data: Union[dict[str, Any], "pandas.DataFrame"],
1736
+ artifact_file: str,
1737
+ run_id: Optional[str] = None,
1738
+ ) -> None:
1739
+ """
1740
+ Log a table to MLflow Tracking as a JSON artifact. If the artifact_file already exists
1741
+ in the run, the data would be appended to the existing artifact_file.
1742
+
1743
+ Args:
1744
+ data: Dictionary or pandas.DataFrame to log.
1745
+ artifact_file: The run-relative artifact file path in posixpath format to which
1746
+ the table is saved (e.g. "dir/file.json").
1747
+ run_id: If specified, log the table to the specified run. If not specified, log the
1748
+ table to the currently active run.
1749
+
1750
+ .. code-block:: python
1751
+ :test:
1752
+ :caption: Dictionary Example
1753
+
1754
+ import mlflow
1755
+
1756
+ table_dict = {
1757
+ "inputs": ["What is MLflow?", "What is Databricks?"],
1758
+ "outputs": ["MLflow is ...", "Databricks is ..."],
1759
+ "toxicity": [0.0, 0.0],
1760
+ }
1761
+ with mlflow.start_run():
1762
+ # Log the dictionary as a table
1763
+ mlflow.log_table(data=table_dict, artifact_file="qabot_eval_results.json")
1764
+
1765
+ .. code-block:: python
1766
+ :test:
1767
+ :caption: Pandas DF Example
1768
+
1769
+ import mlflow
1770
+ import pandas as pd
1771
+
1772
+ table_dict = {
1773
+ "inputs": ["What is MLflow?", "What is Databricks?"],
1774
+ "outputs": ["MLflow is ...", "Databricks is ..."],
1775
+ "toxicity": [0.0, 0.0],
1776
+ }
1777
+ df = pd.DataFrame.from_dict(table_dict)
1778
+ with mlflow.start_run():
1779
+ # Log the df as a table
1780
+ mlflow.log_table(data=df, artifact_file="qabot_eval_results.json")
1781
+ """
1782
+ run_id = run_id or _get_or_start_run().info.run_id
1783
+ MlflowClient().log_table(run_id, data, artifact_file)
1784
+
1785
+
1786
+ def load_table(
1787
+ artifact_file: str,
1788
+ run_ids: Optional[list[str]] = None,
1789
+ extra_columns: Optional[list[str]] = None,
1790
+ ) -> "pandas.DataFrame":
1791
+ """
1792
+ Load a table from MLflow Tracking as a pandas.DataFrame. The table is loaded from the
1793
+ specified artifact_file in the specified run_ids. The extra_columns are columns that
1794
+ are not in the table but are augmented with run information and added to the DataFrame.
1795
+
1796
+ Args:
1797
+ artifact_file: The run-relative artifact file path in posixpath format to which
1798
+ table to load (e.g. "dir/file.json").
1799
+ run_ids: Optional list of run_ids to load the table from. If no run_ids are specified,
1800
+ the table is loaded from all runs in the current experiment.
1801
+ extra_columns: Optional list of extra columns to add to the returned DataFrame
1802
+ For example, if extra_columns=["run_id"], then the returned DataFrame
1803
+ will have a column named run_id.
1804
+
1805
+ Returns:
1806
+ pandas.DataFrame containing the loaded table if the artifact exists
1807
+ or else throw a MlflowException.
1808
+
1809
+ .. code-block:: python
1810
+ :test:
1811
+ :caption: Example with passing run_ids
1812
+
1813
+ import mlflow
1814
+
1815
+ table_dict = {
1816
+ "inputs": ["What is MLflow?", "What is Databricks?"],
1817
+ "outputs": ["MLflow is ...", "Databricks is ..."],
1818
+ "toxicity": [0.0, 0.0],
1819
+ }
1820
+
1821
+ with mlflow.start_run() as run:
1822
+ # Log the dictionary as a table
1823
+ mlflow.log_table(data=table_dict, artifact_file="qabot_eval_results.json")
1824
+ run_id = run.info.run_id
1825
+
1826
+ loaded_table = mlflow.load_table(
1827
+ artifact_file="qabot_eval_results.json",
1828
+ run_ids=[run_id],
1829
+ # Append a column containing the associated run ID for each row
1830
+ extra_columns=["run_id"],
1831
+ )
1832
+
1833
+ .. code-block:: python
1834
+ :test:
1835
+ :caption: Example with passing no run_ids
1836
+
1837
+ # Loads the table with the specified name for all runs in the given
1838
+ # experiment and joins them together
1839
+ import mlflow
1840
+
1841
+ table_dict = {
1842
+ "inputs": ["What is MLflow?", "What is Databricks?"],
1843
+ "outputs": ["MLflow is ...", "Databricks is ..."],
1844
+ "toxicity": [0.0, 0.0],
1845
+ }
1846
+
1847
+ with mlflow.start_run():
1848
+ # Log the dictionary as a table
1849
+ mlflow.log_table(data=table_dict, artifact_file="qabot_eval_results.json")
1850
+
1851
+ loaded_table = mlflow.load_table(
1852
+ "qabot_eval_results.json",
1853
+ # Append the run ID and the parent run ID to the table
1854
+ extra_columns=["run_id"],
1855
+ )
1856
+ """
1857
+ experiment_id = _get_experiment_id()
1858
+ return MlflowClient().load_table(experiment_id, artifact_file, run_ids, extra_columns)
1859
+
1860
+
1861
+ def _record_logged_model(mlflow_model, run_id=None):
1862
+ run_id = run_id or _get_or_start_run().info.run_id
1863
+ MlflowClient()._record_logged_model(run_id, mlflow_model)
1864
+
1865
+
1866
+ def get_experiment(experiment_id: str) -> Experiment:
1867
+ """Retrieve an experiment by experiment_id from the backend store
1868
+
1869
+ Args:
1870
+ experiment_id: The string-ified experiment ID returned from ``create_experiment``.
1871
+
1872
+ Returns:
1873
+ :py:class:`mlflow.entities.Experiment`
1874
+
1875
+ .. code-block:: python
1876
+ :test:
1877
+ :caption: Example
1878
+
1879
+ import mlflow
1880
+
1881
+ experiment = mlflow.get_experiment("0")
1882
+ print(f"Name: {experiment.name}")
1883
+ print(f"Artifact Location: {experiment.artifact_location}")
1884
+ print(f"Tags: {experiment.tags}")
1885
+ print(f"Lifecycle_stage: {experiment.lifecycle_stage}")
1886
+ print(f"Creation timestamp: {experiment.creation_time}")
1887
+
1888
+ .. code-block:: text
1889
+ :caption: Output
1890
+
1891
+ Name: Default
1892
+ Artifact Location: file:///.../mlruns/0
1893
+ Tags: {}
1894
+ Lifecycle_stage: active
1895
+ Creation timestamp: 1662004217511
1896
+ """
1897
+ return MlflowClient().get_experiment(experiment_id)
1898
+
1899
+
1900
+ def get_experiment_by_name(name: str) -> Optional[Experiment]:
1901
+ """
1902
+ Retrieve an experiment by experiment name from the backend store
1903
+
1904
+ Args:
1905
+ name: The case sensitive experiment name.
1906
+
1907
+ Returns:
1908
+ An instance of :py:class:`mlflow.entities.Experiment`
1909
+ if an experiment with the specified name exists, otherwise None.
1910
+
1911
+ .. code-block:: python
1912
+ :test:
1913
+ :caption: Example
1914
+
1915
+ import mlflow
1916
+
1917
+ # Case sensitive name
1918
+ experiment = mlflow.get_experiment_by_name("Default")
1919
+ print(f"Experiment_id: {experiment.experiment_id}")
1920
+ print(f"Artifact Location: {experiment.artifact_location}")
1921
+ print(f"Tags: {experiment.tags}")
1922
+ print(f"Lifecycle_stage: {experiment.lifecycle_stage}")
1923
+ print(f"Creation timestamp: {experiment.creation_time}")
1924
+
1925
+ .. code-block:: text
1926
+ :caption: Output
1927
+
1928
+ Experiment_id: 0
1929
+ Artifact Location: file:///.../mlruns/0
1930
+ Tags: {}
1931
+ Lifecycle_stage: active
1932
+ Creation timestamp: 1662004217511
1933
+ """
1934
+ return MlflowClient().get_experiment_by_name(name)
1935
+
1936
+
1937
+ def search_experiments(
1938
+ view_type: int = ViewType.ACTIVE_ONLY,
1939
+ max_results: Optional[int] = None,
1940
+ filter_string: Optional[str] = None,
1941
+ order_by: Optional[list[str]] = None,
1942
+ ) -> list[Experiment]:
1943
+ """
1944
+ Search for experiments that match the specified search query.
1945
+
1946
+ Args:
1947
+ view_type: One of enum values ``ACTIVE_ONLY``, ``DELETED_ONLY``, or ``ALL``
1948
+ defined in :py:class:`mlflow.entities.ViewType`.
1949
+ max_results: If passed, specifies the maximum number of experiments desired. If not
1950
+ passed, all experiments will be returned.
1951
+ filter_string: Filter query string (e.g., ``"name = 'my_experiment'"``), defaults to
1952
+ searching for all experiments. The following identifiers, comparators, and logical
1953
+ operators are supported.
1954
+
1955
+ Identifiers
1956
+ - ``name``: Experiment name
1957
+ - ``creation_time``: Experiment creation time
1958
+ - ``last_update_time``: Experiment last update time
1959
+ - ``tags.<tag_key>``: Experiment tag. If ``tag_key`` contains
1960
+ spaces, it must be wrapped with backticks (e.g., ``"tags.`extra key`"``).
1961
+
1962
+ Comparators for string attributes and tags
1963
+ - ``=``: Equal to
1964
+ - ``!=``: Not equal to
1965
+ - ``LIKE``: Case-sensitive pattern match
1966
+ - ``ILIKE``: Case-insensitive pattern match
1967
+
1968
+ Comparators for numeric attributes
1969
+ - ``=``: Equal to
1970
+ - ``!=``: Not equal to
1971
+ - ``<``: Less than
1972
+ - ``<=``: Less than or equal to
1973
+ - ``>``: Greater than
1974
+ - ``>=``: Greater than or equal to
1975
+
1976
+ Logical operators
1977
+ - ``AND``: Combines two sub-queries and returns True if both of them are True.
1978
+
1979
+ order_by: List of columns to order by. The ``order_by`` column can contain an optional
1980
+ ``DESC`` or ``ASC`` value (e.g., ``"name DESC"``). The default ordering is ``ASC``,
1981
+ so ``"name"`` is equivalent to ``"name ASC"``. If unspecified, defaults to
1982
+ ``["last_update_time DESC"]``, which lists experiments updated most recently first.
1983
+ The following fields are supported:
1984
+
1985
+ - ``experiment_id``: Experiment ID
1986
+ - ``name``: Experiment name
1987
+ - ``creation_time``: Experiment creation time
1988
+ - ``last_update_time``: Experiment last update time
1989
+
1990
+ Returns:
1991
+ A list of :py:class:`Experiment <mlflow.entities.Experiment>` objects.
1992
+
1993
+ .. code-block:: python
1994
+ :test:
1995
+ :caption: Example
1996
+
1997
+ import mlflow
1998
+
1999
+
2000
+ def assert_experiment_names_equal(experiments, expected_names):
2001
+ actual_names = [e.name for e in experiments if e.name != "Default"]
2002
+ assert actual_names == expected_names, (actual_names, expected_names)
2003
+
2004
+
2005
+ mlflow.set_tracking_uri("sqlite:///:memory:")
2006
+ # Create experiments
2007
+ for name, tags in [
2008
+ ("a", None),
2009
+ ("b", None),
2010
+ ("ab", {"k": "v"}),
2011
+ ("bb", {"k": "V"}),
2012
+ ]:
2013
+ mlflow.create_experiment(name, tags=tags)
2014
+
2015
+ # Search for experiments with name "a"
2016
+ experiments = mlflow.search_experiments(filter_string="name = 'a'")
2017
+ assert_experiment_names_equal(experiments, ["a"])
2018
+ # Search for experiments with name starting with "a"
2019
+ experiments = mlflow.search_experiments(filter_string="name LIKE 'a%'")
2020
+ assert_experiment_names_equal(experiments, ["ab", "a"])
2021
+ # Search for experiments with tag key "k" and value ending with "v" or "V"
2022
+ experiments = mlflow.search_experiments(filter_string="tags.k ILIKE '%v'")
2023
+ assert_experiment_names_equal(experiments, ["bb", "ab"])
2024
+ # Search for experiments with name ending with "b" and tag {"k": "v"}
2025
+ experiments = mlflow.search_experiments(filter_string="name LIKE '%b' AND tags.k = 'v'")
2026
+ assert_experiment_names_equal(experiments, ["ab"])
2027
+ # Sort experiments by name in ascending order
2028
+ experiments = mlflow.search_experiments(order_by=["name"])
2029
+ assert_experiment_names_equal(experiments, ["a", "ab", "b", "bb"])
2030
+ # Sort experiments by ID in descending order
2031
+ experiments = mlflow.search_experiments(order_by=["experiment_id DESC"])
2032
+ assert_experiment_names_equal(experiments, ["bb", "ab", "b", "a"])
2033
+ """
2034
+
2035
+ def pagination_wrapper_func(number_to_get, next_page_token):
2036
+ return MlflowClient().search_experiments(
2037
+ view_type=view_type,
2038
+ max_results=number_to_get,
2039
+ filter_string=filter_string,
2040
+ order_by=order_by,
2041
+ page_token=next_page_token,
2042
+ )
2043
+
2044
+ return get_results_from_paginated_fn(
2045
+ pagination_wrapper_func,
2046
+ SEARCH_MAX_RESULTS_DEFAULT,
2047
+ max_results,
2048
+ )
2049
+
2050
+
2051
+ def create_experiment(
2052
+ name: str,
2053
+ artifact_location: Optional[str] = None,
2054
+ tags: Optional[dict[str, Any]] = None,
2055
+ ) -> str:
2056
+ """
2057
+ Create an experiment.
2058
+
2059
+ Args:
2060
+ name: The experiment name, must be a non-empty unique string.
2061
+ artifact_location: The location to store run artifacts. If not provided, the server picks
2062
+ an appropriate default.
2063
+ tags: An optional dictionary of string keys and values to set as tags on the experiment.
2064
+
2065
+ Returns:
2066
+ String ID of the created experiment.
2067
+
2068
+ .. code-block:: python
2069
+ :test:
2070
+ :caption: Example
2071
+
2072
+ import mlflow
2073
+ from pathlib import Path
2074
+
2075
+ # Create an experiment name, which must be unique and case sensitive
2076
+ experiment_id = mlflow.create_experiment(
2077
+ "Social NLP Experiments",
2078
+ artifact_location=Path.cwd().joinpath("mlruns").as_uri(),
2079
+ tags={"version": "v1", "priority": "P1"},
2080
+ )
2081
+ experiment = mlflow.get_experiment(experiment_id)
2082
+ print(f"Name: {experiment.name}")
2083
+ print(f"Experiment_id: {experiment.experiment_id}")
2084
+ print(f"Artifact Location: {experiment.artifact_location}")
2085
+ print(f"Tags: {experiment.tags}")
2086
+ print(f"Lifecycle_stage: {experiment.lifecycle_stage}")
2087
+ print(f"Creation timestamp: {experiment.creation_time}")
2088
+
2089
+ .. code-block:: text
2090
+ :caption: Output
2091
+
2092
+ Name: Social NLP Experiments
2093
+ Experiment_id: 1
2094
+ Artifact Location: file:///.../mlruns
2095
+ Tags: {'version': 'v1', 'priority': 'P1'}
2096
+ Lifecycle_stage: active
2097
+ Creation timestamp: 1662004217511
2098
+ """
2099
+ return MlflowClient().create_experiment(name, artifact_location, tags)
2100
+
2101
+
2102
+ def delete_experiment(experiment_id: str) -> None:
2103
+ """
2104
+ Delete an experiment from the backend store.
2105
+
2106
+ Args:
2107
+ experiment_id: The string-ified experiment ID returned from ``create_experiment``.
2108
+
2109
+ .. code-block:: python
2110
+ :test:
2111
+ :caption: Example
2112
+
2113
+ import mlflow
2114
+
2115
+ experiment_id = mlflow.create_experiment("New Experiment")
2116
+ mlflow.delete_experiment(experiment_id)
2117
+
2118
+ # Examine the deleted experiment details.
2119
+ experiment = mlflow.get_experiment(experiment_id)
2120
+ print(f"Name: {experiment.name}")
2121
+ print(f"Artifact Location: {experiment.artifact_location}")
2122
+ print(f"Lifecycle_stage: {experiment.lifecycle_stage}")
2123
+ print(f"Last Updated timestamp: {experiment.last_update_time}")
2124
+
2125
+ .. code-block:: text
2126
+ :caption: Output
2127
+
2128
+ Name: New Experiment
2129
+ Artifact Location: file:///.../mlruns/2
2130
+ Lifecycle_stage: deleted
2131
+ Last Updated timestamp: 1662004217511
2132
+
2133
+ """
2134
+ MlflowClient().delete_experiment(experiment_id)
2135
+
2136
+
2137
+ @experimental(version="3.0.0")
2138
+ def initialize_logged_model(
2139
+ name: Optional[str] = None,
2140
+ source_run_id: Optional[str] = None,
2141
+ tags: Optional[dict[str, str]] = None,
2142
+ params: Optional[dict[str, str]] = None,
2143
+ model_type: Optional[str] = None,
2144
+ experiment_id: Optional[str] = None,
2145
+ ) -> LoggedModel:
2146
+ """
2147
+ Initialize a LoggedModel. Creates a LoggedModel with status ``PENDING`` and no artifacts. You
2148
+ must add artifacts to the model and finalize it to the ``READY`` state, for example by calling
2149
+ a flavor-specific ``log_model()`` method such as :py:func:`mlflow.pyfunc.log_model()`.
2150
+
2151
+ Args:
2152
+ name: The name of the model. If not specified, a random name will be generated.
2153
+ source_run_id: The ID of the run that the model is associated with. If unspecified and a
2154
+ run is active, the active run ID will be used.
2155
+ tags: A dictionary of string keys and values to set as tags on the model.
2156
+ params: A dictionary of string keys and values to set as parameters on the model.
2157
+ model_type: The type of the model.
2158
+ experiment_id: The experiment ID of the experiment to which the model belongs.
2159
+
2160
+ Returns:
2161
+ A new :py:class:`mlflow.entities.LoggedModel` object with status ``PENDING``.
2162
+ """
2163
+ model = _create_logged_model(
2164
+ name=name,
2165
+ source_run_id=source_run_id,
2166
+ tags=tags,
2167
+ params=params,
2168
+ model_type=model_type,
2169
+ experiment_id=experiment_id,
2170
+ )
2171
+ _last_logged_model_id.set(model.model_id)
2172
+ return model
2173
+
2174
+
2175
+ @contextlib.contextmanager
2176
+ def _use_logged_model(model: LoggedModel) -> Generator[LoggedModel, None, None]:
2177
+ """
2178
+ Context manager to wrap a LoggedModel and update the model
2179
+ status after the context is exited.
2180
+ If any exception occurs, the model status is set to FAILED.
2181
+ Otherwise, it is set to READY.
2182
+ """
2183
+ try:
2184
+ yield model
2185
+ except Exception:
2186
+ finalize_logged_model(model.model_id, LoggedModelStatus.FAILED)
2187
+ raise
2188
+ else:
2189
+ finalize_logged_model(model.model_id, LoggedModelStatus.READY)
2190
+
2191
+
2192
+ def create_external_model(
2193
+ name: Optional[str] = None,
2194
+ source_run_id: Optional[str] = None,
2195
+ tags: Optional[dict[str, str]] = None,
2196
+ params: Optional[dict[str, str]] = None,
2197
+ model_type: Optional[str] = None,
2198
+ experiment_id: Optional[str] = None,
2199
+ ) -> LoggedModel:
2200
+ """
2201
+ Create a new LoggedModel whose artifacts are stored outside of MLflow. This is useful for
2202
+ tracking parameters and performance data (metrics, traces etc.) for a model, application, or
2203
+ generative AI agent that is not packaged using the MLflow Model format.
2204
+
2205
+ Args:
2206
+ name: The name of the model. If not specified, a random name will be generated.
2207
+ source_run_id: The ID of the run that the model is associated with. If unspecified and a
2208
+ run is active, the active run ID will be used.
2209
+ tags: A dictionary of string keys and values to set as tags on the model.
2210
+ params: A dictionary of string keys and values to set as parameters on the model.
2211
+ model_type: The type of the model. This is a user-defined string that can be used to
2212
+ search and compare related models. For example, setting ``model_type="agent"``
2213
+ enables you to easily search for this model and compare it to other models of
2214
+ type ``"agent"`` in the future.
2215
+ experiment_id: The experiment ID of the experiment to which the model belongs.
2216
+
2217
+ Returns:
2218
+ A new :py:class:`mlflow.entities.LoggedModel` object with status ``READY``.
2219
+ """
2220
+ from mlflow.models.model import MLMODEL_FILE_NAME, Model
2221
+ from mlflow.models.utils import get_external_mlflow_model_spec
2222
+
2223
+ tags = dict(tags) if tags else {}
2224
+ tags[MLFLOW_MODEL_IS_EXTERNAL] = "true"
2225
+
2226
+ client = MlflowClient()
2227
+ model = _create_logged_model(
2228
+ name=name,
2229
+ source_run_id=source_run_id,
2230
+ tags=tags,
2231
+ params=params,
2232
+ model_type=model_type,
2233
+ experiment_id=experiment_id,
2234
+ )
2235
+
2236
+ # If a model is external, its artifacts (code, weights, etc.) are not stored in MLflow.
2237
+ # Accordingly, we finalize the model immediately after creation, since there aren't
2238
+ # any model artifacts for the client to upload to MLflow. Additionally, we create a
2239
+ # dummy MLModel file to ensure that the model can be registered to the Model Registry
2240
+ mlflow_model: Model = get_external_mlflow_model_spec(model)
2241
+ with TempDir() as tmp:
2242
+ mlflow_model.save(tmp.path(MLMODEL_FILE_NAME))
2243
+ MlflowClient().log_model_artifacts(
2244
+ model_id=model.model_id,
2245
+ local_dir=tmp.path(),
2246
+ )
2247
+
2248
+ model = client.finalize_logged_model(model_id=model.model_id, status=LoggedModelStatus.READY)
2249
+ _last_logged_model_id.set(model.model_id)
2250
+
2251
+ return model
2252
+
2253
+
2254
+ def _create_logged_model(
2255
+ name: Optional[str] = None,
2256
+ source_run_id: Optional[str] = None,
2257
+ tags: Optional[dict[str, str]] = None,
2258
+ params: Optional[dict[str, str]] = None,
2259
+ model_type: Optional[str] = None,
2260
+ experiment_id: Optional[str] = None,
2261
+ ) -> LoggedModel:
2262
+ """
2263
+ Create a new LoggedModel in the ``PENDING`` state.
2264
+
2265
+ Args:
2266
+ name: The name of the model. If not specified, a random name will be generated.
2267
+ source_run_id: The ID of the run that the model is associated with. If unspecified and a
2268
+ run is active, the active run ID will be used.
2269
+ tags: A dictionary of string keys and values to set as tags on the model.
2270
+ params: A dictionary of string keys and values to set as parameters on the model.
2271
+ model_type: The type of the model. This is a user-defined string that can be used to
2272
+ search and compare related models. For example, setting ``model_type="agent"``
2273
+ enables you to easily search for this model and compare it to other models of
2274
+ type ``"agent"`` in the future.
2275
+ experiment_id: The experiment ID of the experiment to which the model belongs.
2276
+
2277
+ Returns:
2278
+ A new LoggedModel in the ``PENDING`` state.
2279
+ """
2280
+ if source_run_id is None and (run := active_run()):
2281
+ source_run_id = run.info.run_id
2282
+
2283
+ if experiment_id is None and (run := active_run()):
2284
+ experiment_id = run.info.experiment_id
2285
+ elif experiment_id is None:
2286
+ experiment_id = _get_experiment_id() or (
2287
+ get_run(source_run_id).info.experiment_id if source_run_id else None
2288
+ )
2289
+ resolved_tags = context_registry.resolve_tags(tags)
2290
+ return MlflowClient().create_logged_model(
2291
+ experiment_id=experiment_id,
2292
+ name=name,
2293
+ source_run_id=source_run_id,
2294
+ tags=resolved_tags,
2295
+ params=params,
2296
+ model_type=model_type,
2297
+ )
2298
+
2299
+
2300
+ @experimental(version="3.0.0")
2301
+ def log_model_params(params: dict[str, str], model_id: Optional[str] = None) -> None:
2302
+ """
2303
+ Log params to the specified logged model.
2304
+
2305
+ Args:
2306
+ params: Params to log on the model.
2307
+ model_id: ID of the model. If not specified, use the current active model ID.
2308
+
2309
+ Returns:
2310
+ None
2311
+
2312
+ Example:
2313
+
2314
+ .. code-block:: python
2315
+ :test:
2316
+
2317
+ import mlflow
2318
+
2319
+
2320
+ class DummyModel(mlflow.pyfunc.PythonModel):
2321
+ def predict(self, context, model_input: list[str]) -> list[str]:
2322
+ return model_input
2323
+
2324
+
2325
+ model_info = mlflow.pyfunc.log_model(name="model", python_model=DummyModel())
2326
+ mlflow.log_model_params(params={"param": "value"}, model_id=model_info.model_id)
2327
+ """
2328
+ model_id = model_id or get_active_model_id()
2329
+ MlflowClient().log_model_params(model_id, params)
2330
+
2331
+
2332
+ @experimental(version="3.0.0")
2333
+ def finalize_logged_model(
2334
+ model_id: str, status: Union[Literal["READY", "FAILED"], LoggedModelStatus]
2335
+ ) -> LoggedModel:
2336
+ """
2337
+ Finalize a model by updating its status.
2338
+
2339
+ Args:
2340
+ model_id: ID of the model to finalize.
2341
+ status: Final status to set on the model.
2342
+
2343
+ Returns:
2344
+ The updated model.
2345
+
2346
+ Example:
2347
+
2348
+ .. code-block:: python
2349
+ :test:
2350
+
2351
+ import mlflow
2352
+ from mlflow.entities import LoggedModelStatus
2353
+
2354
+ model = mlflow.initialize_logged_model(name="model")
2355
+ logged_model = mlflow.finalize_logged_model(
2356
+ model_id=model.model_id,
2357
+ status=LoggedModelStatus.READY,
2358
+ )
2359
+ assert logged_model.status == LoggedModelStatus.READY
2360
+
2361
+ """
2362
+ return MlflowClient().finalize_logged_model(model_id, status)
2363
+
2364
+
2365
+ @experimental(version="3.0.0")
2366
+ def get_logged_model(model_id: str) -> LoggedModel:
2367
+ """
2368
+ Get a logged model by ID.
2369
+
2370
+ Args:
2371
+ model_id: The ID of the logged model.
2372
+
2373
+ Returns:
2374
+ The logged model.
2375
+
2376
+ Example:
2377
+
2378
+ .. code-block:: python
2379
+ :test:
2380
+
2381
+ import mlflow
2382
+
2383
+
2384
+ class DummyModel(mlflow.pyfunc.PythonModel):
2385
+ def predict(self, context, model_input: list[str]) -> list[str]:
2386
+ return model_input
2387
+
2388
+
2389
+ model_info = mlflow.pyfunc.log_model(name="model", python_model=DummyModel())
2390
+ logged_model = mlflow.get_logged_model(model_id=model_info.model_id)
2391
+ assert logged_model.model_id == model_info.model_id
2392
+
2393
+ """
2394
+ return MlflowClient().get_logged_model(model_id)
2395
+
2396
+
2397
+ @experimental(version="3.0.0")
2398
+ def last_logged_model() -> Optional[LoggedModel]:
2399
+ """
2400
+ Fetches the most recent logged model in the current session.
2401
+ If no model has been logged, None is returned.
2402
+
2403
+ Returns:
2404
+ The logged model.
2405
+
2406
+
2407
+ .. code-block:: python
2408
+ :test:
2409
+ :caption: Example
2410
+
2411
+ import mlflow
2412
+
2413
+
2414
+ class DummyModel(mlflow.pyfunc.PythonModel):
2415
+ def predict(self, context, model_input: list[str]) -> list[str]:
2416
+ return model_input
2417
+
2418
+
2419
+ model_info = mlflow.pyfunc.log_model(name="model", python_model=DummyModel())
2420
+ last_model = mlflow.last_logged_model()
2421
+ assert last_model.model_id == model_info.model_id
2422
+ """
2423
+ if id := _last_logged_model_id.get():
2424
+ return get_logged_model(id)
2425
+
2426
+
2427
+ @overload
2428
+ def search_logged_models(
2429
+ experiment_ids: Optional[list[str]] = None,
2430
+ filter_string: Optional[str] = None,
2431
+ datasets: Optional[list[dict[str, str]]] = None,
2432
+ max_results: Optional[int] = None,
2433
+ order_by: Optional[list[dict[str, Any]]] = None,
2434
+ output_format: Literal["pandas"] = "pandas",
2435
+ ) -> "pandas.DataFrame": ...
2436
+
2437
+
2438
+ @overload
2439
+ def search_logged_models(
2440
+ experiment_ids: Optional[list[str]] = None,
2441
+ filter_string: Optional[str] = None,
2442
+ datasets: Optional[list[dict[str, str]]] = None,
2443
+ max_results: Optional[int] = None,
2444
+ order_by: Optional[list[dict[str, Any]]] = None,
2445
+ output_format: Literal["list"] = "list",
2446
+ ) -> list[LoggedModel]: ...
2447
+
2448
+
2449
+ @experimental(version="3.0.0")
2450
+ def search_logged_models(
2451
+ experiment_ids: Optional[list[str]] = None,
2452
+ filter_string: Optional[str] = None,
2453
+ datasets: Optional[list[dict[str, str]]] = None,
2454
+ max_results: Optional[int] = None,
2455
+ order_by: Optional[list[dict[str, Any]]] = None,
2456
+ output_format: Literal["pandas", "list"] = "pandas",
2457
+ ) -> Union[list[LoggedModel], "pandas.DataFrame"]:
2458
+ """
2459
+ Search for logged models that match the specified search criteria.
2460
+
2461
+ Args:
2462
+ experiment_ids: List of experiment IDs to search for logged models. If not specified,
2463
+ the active experiment will be used.
2464
+ filter_string: A SQL-like filter string to parse. The filter string syntax supports:
2465
+
2466
+ - Entity specification:
2467
+ - attributes: `attribute_name` (default if no prefix is specified)
2468
+ - metrics: `metrics.metric_name`
2469
+ - parameters: `params.param_name`
2470
+ - tags: `tags.tag_name`
2471
+ - Comparison operators:
2472
+ - For numeric entities (metrics and numeric attributes): <, <=, >, >=, =, !=
2473
+ - For string entities (params, tags, string attributes): =, !=, IN, NOT IN
2474
+ - Multiple conditions can be joined with 'AND'
2475
+ - String values must be enclosed in single quotes
2476
+
2477
+ Example filter strings:
2478
+ - `creation_time > 100`
2479
+ - `metrics.rmse > 0.5 AND params.model_type = 'rf'`
2480
+ - `tags.release IN ('v1.0', 'v1.1')`
2481
+ - `params.optimizer != 'adam' AND metrics.accuracy >= 0.9`
2482
+
2483
+ datasets: List of dictionaries to specify datasets on which to apply metrics filters
2484
+ For example, a filter string with `metrics.accuracy > 0.9` and dataset with name
2485
+ "test_dataset" means we will return all logged models with accuracy > 0.9 on the
2486
+ test_dataset. Metric values from ANY dataset matching the criteria are considered.
2487
+ If no datasets are specified, then metrics across all datasets are considered in
2488
+ the filter. The following fields are supported:
2489
+
2490
+ dataset_name (str):
2491
+ Required. Name of the dataset.
2492
+ dataset_digest (str):
2493
+ Optional. Digest of the dataset.
2494
+ max_results: The maximum number of logged models to return.
2495
+ order_by: List of dictionaries to specify the ordering of the search results. The following
2496
+ fields are supported:
2497
+
2498
+ field_name (str):
2499
+ Required. Name of the field to order by, e.g. "metrics.accuracy".
2500
+ ascending (bool):
2501
+ Optional. Whether the order is ascending or not.
2502
+ dataset_name (str):
2503
+ Optional. If ``field_name`` refers to a metric, this field
2504
+ specifies the name of the dataset associated with the metric. Only metrics
2505
+ associated with the specified dataset name will be considered for ordering.
2506
+ This field may only be set if ``field_name`` refers to a metric.
2507
+ dataset_digest (str):
2508
+ Optional. If ``field_name`` refers to a metric, this field
2509
+ specifies the digest of the dataset associated with the metric. Only metrics
2510
+ associated with the specified dataset name and digest will be considered for
2511
+ ordering. This field may only be set if ``dataset_name`` is also set.
2512
+
2513
+ output_format: The output format of the search results. Supported values are 'pandas'
2514
+ and 'list'.
2515
+
2516
+ Returns:
2517
+ The search results in the specified output format.
2518
+
2519
+ Example:
2520
+
2521
+ .. code-block:: python
2522
+ :test:
2523
+
2524
+ import mlflow
2525
+
2526
+
2527
+ class DummyModel(mlflow.pyfunc.PythonModel):
2528
+ def predict(self, context, model_input: list[str]) -> list[str]:
2529
+ return model_input
2530
+
2531
+
2532
+ model_info = mlflow.pyfunc.log_model(name="model", python_model=DummyModel())
2533
+ another_model_info = mlflow.pyfunc.log_model(
2534
+ name="another_model", python_model=DummyModel()
2535
+ )
2536
+ models = mlflow.search_logged_models(output_format="list")
2537
+ assert [m.name for m in models] == ["another_model", "model"]
2538
+ models = mlflow.search_logged_models(
2539
+ filter_string="name = 'another_model'", output_format="list"
2540
+ )
2541
+ assert [m.name for m in models] == ["another_model"]
2542
+ models = mlflow.search_logged_models(
2543
+ order_by=[{"field_name": "creation_time", "ascending": True}], output_format="list"
2544
+ )
2545
+ assert [m.name for m in models] == ["model", "another_model"]
2546
+ """
2547
+ experiment_ids = experiment_ids or [_get_experiment_id()]
2548
+ client = MlflowClient()
2549
+ models = []
2550
+ page_token = None
2551
+ while True:
2552
+ logged_models_page = client.search_logged_models(
2553
+ experiment_ids=experiment_ids,
2554
+ filter_string=filter_string,
2555
+ datasets=datasets,
2556
+ max_results=max_results,
2557
+ order_by=order_by,
2558
+ page_token=page_token,
2559
+ )
2560
+ models.extend(logged_models_page.to_list())
2561
+ if max_results is not None and len(models) >= max_results:
2562
+ break
2563
+ if not logged_models_page.token:
2564
+ break
2565
+ page_token = logged_models_page.token
2566
+
2567
+ # Only return at most max_results logged models if specified
2568
+ if max_results is not None:
2569
+ models = models[:max_results]
2570
+
2571
+ if output_format == "list":
2572
+ return models
2573
+ elif output_format == "pandas":
2574
+ import pandas as pd
2575
+
2576
+ model_dicts = []
2577
+ for model in models:
2578
+ model_dict = model.to_dictionary()
2579
+ # Convert the status back from int to the enum string
2580
+ model_dict["status"] = LoggedModelStatus.from_int(model_dict["status"])
2581
+ model_dicts.append(model_dict)
2582
+
2583
+ return pd.DataFrame(model_dicts)
2584
+
2585
+ else:
2586
+ raise MlflowException(
2587
+ f"Unsupported output format: {output_format!r}. Supported string values are "
2588
+ "'pandas' or 'list'",
2589
+ INVALID_PARAMETER_VALUE,
2590
+ )
2591
+
2592
+
2593
+ @experimental(version="3.0.0")
2594
+ def log_outputs(models: Optional[list[LoggedModelOutput]] = None):
2595
+ """
2596
+ Log outputs, such as models, to the active run. If there is no active run, a new run will be
2597
+ created.
2598
+
2599
+ Args:
2600
+ models: List of :py:class:`mlflow.entities.LoggedModelOutput` instances to log
2601
+ as outputs to the run.
2602
+
2603
+ Returns:
2604
+ None.
2605
+ """
2606
+ run_id = _get_or_start_run().info.run_id
2607
+ MlflowClient().log_outputs(run_id, models=models)
2608
+
2609
+
2610
+ def delete_run(run_id: str) -> None:
2611
+ """
2612
+ Deletes a run with the given ID.
2613
+
2614
+ Args:
2615
+ run_id: Unique identifier for the run to delete.
2616
+
2617
+ .. code-block:: python
2618
+ :test:
2619
+ :caption: Example
2620
+
2621
+ import mlflow
2622
+
2623
+ with mlflow.start_run() as run:
2624
+ mlflow.log_param("p", 0)
2625
+
2626
+ run_id = run.info.run_id
2627
+ mlflow.delete_run(run_id)
2628
+
2629
+ lifecycle_stage = mlflow.get_run(run_id).info.lifecycle_stage
2630
+ print(f"run_id: {run_id}; lifecycle_stage: {lifecycle_stage}")
2631
+
2632
+ .. code-block:: text
2633
+ :caption: Output
2634
+
2635
+ run_id: 45f4af3e6fd349e58579b27fcb0b8277; lifecycle_stage: deleted
2636
+
2637
+ """
2638
+ MlflowClient().delete_run(run_id)
2639
+
2640
+
2641
+ def set_logged_model_tags(model_id: str, tags: dict[str, Any]) -> None:
2642
+ """
2643
+ Set tags on the specified logged model.
2644
+
2645
+ Args:
2646
+ model_id: ID of the model.
2647
+ tags: Tags to set on the model.
2648
+
2649
+ Returns:
2650
+ None
2651
+
2652
+ Example:
2653
+
2654
+ .. code-block:: python
2655
+ :test:
2656
+
2657
+ import mlflow
2658
+
2659
+
2660
+ class DummyModel(mlflow.pyfunc.PythonModel):
2661
+ def predict(self, context, model_input: list[str]) -> list[str]:
2662
+ return model_input
2663
+
2664
+
2665
+ model_info = mlflow.pyfunc.log_model(name="model", python_model=DummyModel())
2666
+ mlflow.set_logged_model_tags(model_info.model_id, {"key": "value"})
2667
+ model = mlflow.get_logged_model(model_info.model_id)
2668
+ assert model.tags["key"] == "value"
2669
+ """
2670
+ MlflowClient().set_logged_model_tags(model_id, tags)
2671
+
2672
+
2673
+ def delete_logged_model_tag(model_id: str, key: str) -> None:
2674
+ """
2675
+ Delete a tag from the specified logged model.
2676
+
2677
+ Args:
2678
+ model_id: ID of the model.
2679
+ key: Tag key to delete.
2680
+
2681
+ Example:
2682
+
2683
+ .. code-block:: python
2684
+ :test:
2685
+
2686
+ import mlflow
2687
+
2688
+
2689
+ class DummyModel(mlflow.pyfunc.PythonModel):
2690
+ def predict(self, context, model_input: list[str]) -> list[str]:
2691
+ return model_input
2692
+
2693
+
2694
+ model_info = mlflow.pyfunc.log_model(name="model", python_model=DummyModel())
2695
+ mlflow.set_logged_model_tags(model_info.model_id, {"key": "value"})
2696
+ model = mlflow.get_logged_model(model_info.model_id)
2697
+ assert model.tags["key"] == "value"
2698
+ mlflow.delete_logged_model_tag(model_info.model_id, "key")
2699
+ model = mlflow.get_logged_model(model_info.model_id)
2700
+ assert "key" not in model.tags
2701
+ """
2702
+ MlflowClient().delete_logged_model_tag(model_id, key)
2703
+
2704
+
2705
+ def get_artifact_uri(artifact_path: Optional[str] = None) -> str:
2706
+ """
2707
+ Get the absolute URI of the specified artifact in the currently active run.
2708
+
2709
+ If `path` is not specified, the artifact root URI of the currently active
2710
+ run will be returned; calls to ``log_artifact`` and ``log_artifacts`` write
2711
+ artifact(s) to subdirectories of the artifact root URI.
2712
+
2713
+ If no run is active, this method will create a new active run.
2714
+
2715
+ Args:
2716
+ artifact_path: The run-relative artifact path for which to obtain an absolute URI.
2717
+ For example, "path/to/artifact". If unspecified, the artifact root URI
2718
+ for the currently active run will be returned.
2719
+
2720
+ Returns:
2721
+ An *absolute* URI referring to the specified artifact or the currently active run's
2722
+ artifact root. For example, if an artifact path is provided and the currently active
2723
+ run uses an S3-backed store, this may be a uri of the form
2724
+ ``s3://<bucket_name>/path/to/artifact/root/path/to/artifact``. If an artifact path
2725
+ is not provided and the currently active run uses an S3-backed store, this may be a
2726
+ URI of the form ``s3://<bucket_name>/path/to/artifact/root``.
2727
+
2728
+ .. code-block:: python
2729
+ :test:
2730
+ :caption: Example
2731
+
2732
+ import tempfile
2733
+
2734
+ import mlflow
2735
+
2736
+ features = "rooms, zipcode, median_price, school_rating, transport"
2737
+ with tempfile.NamedTemporaryFile("w") as tmp_file:
2738
+ tmp_file.write(features)
2739
+ tmp_file.flush()
2740
+
2741
+ # Log the artifact in a directory "features" under the root artifact_uri/features
2742
+ with mlflow.start_run():
2743
+ mlflow.log_artifact(tmp_file.name, artifact_path="features")
2744
+
2745
+ # Fetch the artifact uri root directory
2746
+ artifact_uri = mlflow.get_artifact_uri()
2747
+ print(f"Artifact uri: {artifact_uri}")
2748
+
2749
+ # Fetch a specific artifact uri
2750
+ artifact_uri = mlflow.get_artifact_uri(artifact_path="features/features.txt")
2751
+ print(f"Artifact uri: {artifact_uri}")
2752
+
2753
+ .. code-block:: text
2754
+ :caption: Output
2755
+
2756
+ Artifact uri: file:///.../0/a46a80f1c9644bd8f4e5dd5553fffce/artifacts
2757
+ Artifact uri: file:///.../0/a46a80f1c9644bd8f4e5dd5553fffce/artifacts/features/features.txt
2758
+ """
2759
+ if not mlflow.active_run():
2760
+ _logger.warning(
2761
+ "No active run found. A new active run will be created. If this is not intended, "
2762
+ "please create a run using `mlflow.start_run()` first."
2763
+ )
2764
+
2765
+ return artifact_utils.get_artifact_uri(
2766
+ run_id=_get_or_start_run().info.run_id, artifact_path=artifact_path
2767
+ )
2768
+
2769
+
2770
+ def search_runs(
2771
+ experiment_ids: Optional[list[str]] = None,
2772
+ filter_string: str = "",
2773
+ run_view_type: int = ViewType.ACTIVE_ONLY,
2774
+ max_results: int = SEARCH_MAX_RESULTS_PANDAS,
2775
+ order_by: Optional[list[str]] = None,
2776
+ output_format: str = "pandas",
2777
+ search_all_experiments: bool = False,
2778
+ experiment_names: Optional[list[str]] = None,
2779
+ ) -> Union[list[Run], "pandas.DataFrame"]:
2780
+ """
2781
+ Search for Runs that fit the specified criteria.
2782
+
2783
+ Args:
2784
+ experiment_ids: List of experiment IDs. Search can work with experiment IDs or
2785
+ experiment names, but not both in the same call. Values other than
2786
+ ``None`` or ``[]`` will result in error if ``experiment_names`` is
2787
+ also not ``None`` or ``[]``. ``None`` will default to the active
2788
+ experiment if ``experiment_names`` is ``None`` or ``[]``.
2789
+ filter_string: Filter query string, defaults to searching all runs.
2790
+ run_view_type: one of enum values ``ACTIVE_ONLY``, ``DELETED_ONLY``, or ``ALL`` runs
2791
+ defined in :py:class:`mlflow.entities.ViewType`.
2792
+ max_results: The maximum number of runs to put in the dataframe. Default is 100,000
2793
+ to avoid causing out-of-memory issues on the user's machine.
2794
+ order_by: List of columns to order by (e.g., "metrics.rmse"). The ``order_by`` column
2795
+ can contain an optional ``DESC`` or ``ASC`` value. The default is ``ASC``.
2796
+ The default ordering is to sort by ``start_time DESC``, then ``run_id``.
2797
+ output_format: The output format to be returned. If ``pandas``, a ``pandas.DataFrame``
2798
+ is returned and, if ``list``, a list of :py:class:`mlflow.entities.Run`
2799
+ is returned.
2800
+ search_all_experiments: Boolean specifying whether all experiments should be searched.
2801
+ Only honored if ``experiment_ids`` is ``[]`` or ``None``.
2802
+ experiment_names: List of experiment names. Search can work with experiment IDs or
2803
+ experiment names, but not both in the same call. Values other
2804
+ than ``None`` or ``[]`` will result in error if ``experiment_ids``
2805
+ is also not ``None`` or ``[]``. ``None`` will default to the active
2806
+ experiment if ``experiment_ids`` is ``None`` or ``[]``.
2807
+
2808
+ Returns:
2809
+ If output_format is ``list``: a list of :py:class:`mlflow.entities.Run`. If
2810
+ output_format is ``pandas``: ``pandas.DataFrame`` of runs, where each metric,
2811
+ parameter, and tag is expanded into its own column named metrics.*, params.*, or
2812
+ tags.* respectively. For runs that don't have a particular metric, parameter, or tag,
2813
+ the value for the corresponding column is (NumPy) ``Nan``, ``None``, or ``None``
2814
+ respectively.
2815
+
2816
+ .. code-block:: python
2817
+ :test:
2818
+ :caption: Example
2819
+
2820
+ import mlflow
2821
+
2822
+ # Create an experiment and log two runs under it
2823
+ experiment_name = "Social NLP Experiments"
2824
+ experiment_id = mlflow.create_experiment(experiment_name)
2825
+ with mlflow.start_run(experiment_id=experiment_id):
2826
+ mlflow.log_metric("m", 1.55)
2827
+ mlflow.set_tag("s.release", "1.1.0-RC")
2828
+ with mlflow.start_run(experiment_id=experiment_id):
2829
+ mlflow.log_metric("m", 2.50)
2830
+ mlflow.set_tag("s.release", "1.2.0-GA")
2831
+ # Search for all the runs in the experiment with the given experiment ID
2832
+ df = mlflow.search_runs([experiment_id], order_by=["metrics.m DESC"])
2833
+ print(df[["metrics.m", "tags.s.release", "run_id"]])
2834
+ print("--")
2835
+ # Search the experiment_id using a filter_string with tag
2836
+ # that has a case insensitive pattern
2837
+ filter_string = "tags.s.release ILIKE '%rc%'"
2838
+ df = mlflow.search_runs([experiment_id], filter_string=filter_string)
2839
+ print(df[["metrics.m", "tags.s.release", "run_id"]])
2840
+ print("--")
2841
+ # Search for all the runs in the experiment with the given experiment name
2842
+ df = mlflow.search_runs(experiment_names=[experiment_name], order_by=["metrics.m DESC"])
2843
+ print(df[["metrics.m", "tags.s.release", "run_id"]])
2844
+
2845
+ .. code-block:: text
2846
+ :caption: Output
2847
+
2848
+ metrics.m tags.s.release run_id
2849
+ 0 2.50 1.2.0-GA 147eed886ab44633902cc8e19b2267e2
2850
+ 1 1.55 1.1.0-RC 5cc7feaf532f496f885ad7750809c4d4
2851
+ --
2852
+ metrics.m tags.s.release run_id
2853
+ 0 1.55 1.1.0-RC 5cc7feaf532f496f885ad7750809c4d4
2854
+ --
2855
+ metrics.m tags.s.release run_id
2856
+ 0 2.50 1.2.0-GA 147eed886ab44633902cc8e19b2267e2
2857
+ 1 1.55 1.1.0-RC 5cc7feaf532f496f885ad7750809c4d4
2858
+ """
2859
+ no_ids = experiment_ids is None or len(experiment_ids) == 0
2860
+ no_names = experiment_names is None or len(experiment_names) == 0
2861
+ no_ids_or_names = no_ids and no_names
2862
+ if not no_ids and not no_names:
2863
+ raise MlflowException(
2864
+ message="Only experiment_ids or experiment_names can be used, but not both",
2865
+ error_code=INVALID_PARAMETER_VALUE,
2866
+ )
2867
+
2868
+ if search_all_experiments and no_ids_or_names:
2869
+ experiment_ids = [
2870
+ exp.experiment_id for exp in search_experiments(view_type=ViewType.ACTIVE_ONLY)
2871
+ ]
2872
+ elif no_ids_or_names:
2873
+ experiment_ids = [_get_experiment_id()]
2874
+ elif not no_names:
2875
+ experiments = []
2876
+ for n in experiment_names:
2877
+ if n is not None:
2878
+ experiment_by_name = get_experiment_by_name(n)
2879
+ if experiment_by_name:
2880
+ experiments.append(experiment_by_name)
2881
+ else:
2882
+ _logger.warning("Cannot retrieve experiment by name %s", n)
2883
+ experiment_ids = [e.experiment_id for e in experiments if e is not None]
2884
+
2885
+ if len(experiment_ids) == 0:
2886
+ runs = []
2887
+ else:
2888
+ # Using an internal function as the linter doesn't like assigning a lambda, and inlining the
2889
+ # full thing is a mess
2890
+ def pagination_wrapper_func(number_to_get, next_page_token):
2891
+ return MlflowClient().search_runs(
2892
+ experiment_ids,
2893
+ filter_string,
2894
+ run_view_type,
2895
+ number_to_get,
2896
+ order_by,
2897
+ next_page_token,
2898
+ )
2899
+
2900
+ runs = get_results_from_paginated_fn(
2901
+ pagination_wrapper_func,
2902
+ NUM_RUNS_PER_PAGE_PANDAS,
2903
+ max_results,
2904
+ )
2905
+
2906
+ if output_format == "list":
2907
+ return runs # List[mlflow.entities.run.Run]
2908
+ elif output_format == "pandas":
2909
+ import numpy as np
2910
+ import pandas as pd
2911
+
2912
+ info = {
2913
+ "run_id": [],
2914
+ "experiment_id": [],
2915
+ "status": [],
2916
+ "artifact_uri": [],
2917
+ "start_time": [],
2918
+ "end_time": [],
2919
+ }
2920
+ params = {}
2921
+ metrics = {}
2922
+ tags = {}
2923
+ PARAM_NULL = None
2924
+ METRIC_NULL = np.nan
2925
+ TAG_NULL = None
2926
+ for i, run in enumerate(runs):
2927
+ info["run_id"].append(run.info.run_id)
2928
+ info["experiment_id"].append(run.info.experiment_id)
2929
+ info["status"].append(run.info.status)
2930
+ info["artifact_uri"].append(run.info.artifact_uri)
2931
+ info["start_time"].append(pd.to_datetime(run.info.start_time, unit="ms", utc=True))
2932
+ info["end_time"].append(pd.to_datetime(run.info.end_time, unit="ms", utc=True))
2933
+
2934
+ # Params
2935
+ param_keys = set(params.keys())
2936
+ for key in param_keys:
2937
+ if key in run.data.params:
2938
+ params[key].append(run.data.params[key])
2939
+ else:
2940
+ params[key].append(PARAM_NULL)
2941
+ new_params = set(run.data.params.keys()) - param_keys
2942
+ for p in new_params:
2943
+ params[p] = [PARAM_NULL] * i # Fill in null values for all previous runs
2944
+ params[p].append(run.data.params[p])
2945
+
2946
+ # Metrics
2947
+ metric_keys = set(metrics.keys())
2948
+ for key in metric_keys:
2949
+ if key in run.data.metrics:
2950
+ metrics[key].append(run.data.metrics[key])
2951
+ else:
2952
+ metrics[key].append(METRIC_NULL)
2953
+ new_metrics = set(run.data.metrics.keys()) - metric_keys
2954
+ for m in new_metrics:
2955
+ metrics[m] = [METRIC_NULL] * i
2956
+ metrics[m].append(run.data.metrics[m])
2957
+
2958
+ # Tags
2959
+ tag_keys = set(tags.keys())
2960
+ for key in tag_keys:
2961
+ if key in run.data.tags:
2962
+ tags[key].append(run.data.tags[key])
2963
+ else:
2964
+ tags[key].append(TAG_NULL)
2965
+ new_tags = set(run.data.tags.keys()) - tag_keys
2966
+ for t in new_tags:
2967
+ tags[t] = [TAG_NULL] * i
2968
+ tags[t].append(run.data.tags[t])
2969
+
2970
+ data = {}
2971
+ data.update(info)
2972
+ for key, value in metrics.items():
2973
+ data["metrics." + key] = value
2974
+ for key, value in params.items():
2975
+ data["params." + key] = value
2976
+ for key, value in tags.items():
2977
+ data["tags." + key] = value
2978
+ return pd.DataFrame(data)
2979
+ else:
2980
+ raise ValueError(
2981
+ f"Unsupported output format: {output_format}. Supported string values are 'pandas' "
2982
+ "or 'list'"
2983
+ )
2984
+
2985
+
2986
+ def _get_or_start_run():
2987
+ active_run_stack = _active_run_stack.get()
2988
+ if len(active_run_stack) > 0:
2989
+ return active_run_stack[-1]
2990
+ return start_run()
2991
+
2992
+
2993
+ def _get_experiment_id_from_env():
2994
+ experiment_name = MLFLOW_EXPERIMENT_NAME.get()
2995
+ experiment_id = MLFLOW_EXPERIMENT_ID.get()
2996
+ if experiment_name is not None:
2997
+ exp = MlflowClient().get_experiment_by_name(experiment_name)
2998
+ if exp:
2999
+ if experiment_id and experiment_id != exp.experiment_id:
3000
+ raise MlflowException(
3001
+ message=f"The provided {MLFLOW_EXPERIMENT_ID} environment variable "
3002
+ f"value `{experiment_id}` does not match the experiment id "
3003
+ f"`{exp.experiment_id}` for experiment name `{experiment_name}`",
3004
+ error_code=INVALID_PARAMETER_VALUE,
3005
+ )
3006
+ else:
3007
+ return exp.experiment_id
3008
+ else:
3009
+ return MlflowClient().create_experiment(name=experiment_name)
3010
+ if experiment_id is not None:
3011
+ try:
3012
+ exp = MlflowClient().get_experiment(experiment_id)
3013
+ return exp.experiment_id
3014
+ except MlflowException as exc:
3015
+ raise MlflowException(
3016
+ message=f"The provided {MLFLOW_EXPERIMENT_ID} environment variable "
3017
+ f"value `{experiment_id}` does not exist in the tracking server. Provide a valid "
3018
+ f"experiment_id.",
3019
+ error_code=INVALID_PARAMETER_VALUE,
3020
+ ) from exc
3021
+
3022
+
3023
+ def _get_experiment_id() -> Optional[str]:
3024
+ if _active_experiment_id:
3025
+ return _active_experiment_id
3026
+ else:
3027
+ return _get_experiment_id_from_env() or default_experiment_registry.get_experiment_id()
3028
+
3029
+
3030
+ @autologging_integration("mlflow")
3031
+ def autolog(
3032
+ log_input_examples: bool = False,
3033
+ log_model_signatures: bool = True,
3034
+ log_models: bool = True,
3035
+ log_datasets: bool = True,
3036
+ log_traces: bool = True,
3037
+ disable: bool = False,
3038
+ exclusive: bool = False,
3039
+ disable_for_unsupported_versions: bool = False,
3040
+ silent: bool = False,
3041
+ extra_tags: Optional[dict[str, str]] = None,
3042
+ exclude_flavors: Optional[list[str]] = None,
3043
+ ) -> None:
3044
+ """
3045
+ Enables (or disables) and configures autologging for all supported integrations.
3046
+
3047
+ The parameters are passed to any autologging integrations that support them.
3048
+
3049
+ See the `tracking docs <../../tracking/autolog.html>`_ for a list of supported autologging
3050
+ integrations.
3051
+
3052
+ Note that framework-specific configurations set at any point will take precedence over
3053
+ any configurations set by this function. For example:
3054
+
3055
+ .. code-block:: python
3056
+ :test:
3057
+
3058
+ import mlflow
3059
+
3060
+ mlflow.autolog(log_models=False, exclusive=True)
3061
+ import sklearn
3062
+
3063
+ would enable autologging for `sklearn` with `log_models=False` and `exclusive=True`,
3064
+ but
3065
+
3066
+ .. code-block:: python
3067
+ :test:
3068
+
3069
+ import mlflow
3070
+
3071
+ mlflow.autolog(log_models=False, exclusive=True)
3072
+
3073
+ import sklearn
3074
+
3075
+ mlflow.sklearn.autolog(log_models=True)
3076
+
3077
+ would enable autologging for `sklearn` with `log_models=True` and `exclusive=False`,
3078
+ the latter resulting from the default value for `exclusive` in `mlflow.sklearn.autolog`;
3079
+ other framework autolog functions (e.g. `mlflow.tensorflow.autolog`) would use the
3080
+ configurations set by `mlflow.autolog` (in this instance, `log_models=False`, `exclusive=True`),
3081
+ until they are explicitly called by the user.
3082
+
3083
+ Args:
3084
+ log_input_examples: If ``True``, input examples from training datasets are collected and
3085
+ logged along with model artifacts during training. If ``False``,
3086
+ input examples are not logged.
3087
+ Note: Input examples are MLflow model attributes
3088
+ and are only collected if ``log_models`` is also ``True``.
3089
+ log_model_signatures: If ``True``,
3090
+ :py:class:`ModelSignatures <mlflow.models.ModelSignature>`
3091
+ describing model inputs and outputs are collected and logged along
3092
+ with model artifacts during training. If ``False``, signatures are
3093
+ not logged. Note: Model signatures are MLflow model attributes
3094
+ and are only collected if ``log_models`` is also ``True``.
3095
+ log_models: If ``True``, trained models are logged as MLflow model artifacts.
3096
+ If ``False``, trained models are not logged.
3097
+ Input examples and model signatures, which are attributes of MLflow models,
3098
+ are also omitted when ``log_models`` is ``False``.
3099
+ log_datasets: If ``True``, dataset information is logged to MLflow Tracking.
3100
+ If ``False``, dataset information is not logged.
3101
+ log_traces: If ``True``, traces are collected for integrations.
3102
+ If ``False``, no trace is collected.
3103
+ disable: If ``True``, disables all supported autologging integrations. If ``False``,
3104
+ enables all supported autologging integrations.
3105
+ exclusive: If ``True``, autologged content is not logged to user-created fluent runs.
3106
+ If ``False``, autologged content is logged to the active fluent run,
3107
+ which may be user-created.
3108
+ disable_for_unsupported_versions: If ``True``, disable autologging for versions of
3109
+ all integration libraries that have not been tested against this version
3110
+ of the MLflow client or are incompatible.
3111
+ silent: If ``True``, suppress all event logs and warnings from MLflow during autologging
3112
+ setup and training execution. If ``False``, show all events and warnings during
3113
+ autologging setup and training execution.
3114
+ extra_tags: A dictionary of extra tags to set on each managed run created by autologging.
3115
+ exclude_flavors: A list of flavor names that are excluded from the auto-logging.
3116
+ e.g. tensorflow, pyspark.ml
3117
+
3118
+ .. code-block:: python
3119
+ :test:
3120
+ :caption: Example
3121
+
3122
+ import numpy as np
3123
+ import mlflow.sklearn
3124
+ from mlflow import MlflowClient
3125
+ from sklearn.linear_model import LinearRegression
3126
+
3127
+
3128
+ def print_auto_logged_info(r):
3129
+ tags = {k: v for k, v in r.data.tags.items() if not k.startswith("mlflow.")}
3130
+ artifacts = [f.path for f in MlflowClient().list_artifacts(r.info.run_id, "model")]
3131
+ print(f"run_id: {r.info.run_id}")
3132
+ print(f"artifacts: {artifacts}")
3133
+ print(f"params: {r.data.params}")
3134
+ print(f"metrics: {r.data.metrics}")
3135
+ print(f"tags: {tags}")
3136
+
3137
+
3138
+ # prepare training data
3139
+ X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
3140
+ y = np.dot(X, np.array([1, 2])) + 3
3141
+
3142
+ # Auto log all the parameters, metrics, and artifacts
3143
+ mlflow.autolog()
3144
+ model = LinearRegression()
3145
+ with mlflow.start_run() as run:
3146
+ model.fit(X, y)
3147
+
3148
+ # fetch the auto logged parameters and metrics for ended run
3149
+ print_auto_logged_info(mlflow.get_run(run_id=run.info.run_id))
3150
+
3151
+ .. code-block:: text
3152
+ :caption: Output
3153
+
3154
+ run_id: fd10a17d028c47399a55ab8741721ef7
3155
+ artifacts: ['model/MLmodel', 'model/conda.yaml', 'model/model.pkl']
3156
+ params: {'copy_X': 'True',
3157
+ 'normalize': 'False',
3158
+ 'fit_intercept': 'True',
3159
+ 'n_jobs': 'None'}
3160
+ metrics: {'training_score': 1.0,
3161
+ 'training_root_mean_squared_error': 4.440892098500626e-16,
3162
+ 'training_r2_score': 1.0,
3163
+ 'training_mean_absolute_error': 2.220446049250313e-16,
3164
+ 'training_mean_squared_error': 1.9721522630525295e-31}
3165
+ tags: {'estimator_class': 'sklearn.linear_model._base.LinearRegression',
3166
+ 'estimator_name': 'LinearRegression'}
3167
+ """
3168
+ locals_copy = locals().items()
3169
+
3170
+ # Mapping of library name to specific autolog function name. We use string like
3171
+ # "tensorflow.autolog" to avoid loading all flavor modules, so we only set autologging for
3172
+ # compatible modules.
3173
+ LIBRARY_TO_AUTOLOG_MODULE = {
3174
+ "tensorflow": "mlflow.tensorflow",
3175
+ "keras": "mlflow.keras",
3176
+ "xgboost": "mlflow.xgboost",
3177
+ "lightgbm": "mlflow.lightgbm",
3178
+ "statsmodels": "mlflow.statsmodels",
3179
+ "sklearn": "mlflow.sklearn",
3180
+ "pyspark": "mlflow.spark",
3181
+ "pyspark.ml": "mlflow.pyspark.ml",
3182
+ # TODO: Broaden this beyond pytorch_lightning as we add autologging support for more
3183
+ # Pytorch frameworks under mlflow.pytorch.autolog
3184
+ "pytorch_lightning": "mlflow.pytorch",
3185
+ "lightning": "mlflow.pytorch",
3186
+ "setfit": "mlflow.transformers",
3187
+ "transformers": "mlflow.transformers",
3188
+ # do not enable langchain autologging by default
3189
+ }
3190
+
3191
+ GENAI_LIBRARY_TO_AUTOLOG_MODULE = {
3192
+ "autogen": "mlflow.ag2",
3193
+ "anthropic": "mlflow.anthropic",
3194
+ "autogen_agentchat": "mlflow.autogen",
3195
+ "openai": "mlflow.openai",
3196
+ "google.genai": "mlflow.gemini",
3197
+ "google.generativeai": "mlflow.gemini",
3198
+ "litellm": "mlflow.litellm",
3199
+ "llama_index.core": "mlflow.llama_index",
3200
+ "langchain": "mlflow.langchain",
3201
+ "dspy": "mlflow.dspy",
3202
+ "crewai": "mlflow.crewai",
3203
+ "smolagents": "mlflow.smolagents",
3204
+ "groq": "mlflow.groq",
3205
+ "boto3": "mlflow.bedrock",
3206
+ "mistralai": "mlflow.mistral",
3207
+ "pydantic_ai": "mlflow.pydantic_ai",
3208
+ }
3209
+
3210
+ # Currently, GenAI libraries are not enabled by `mlflow.autolog` in Databricks,
3211
+ # particularly when disable=False. This is because the function is automatically invoked
3212
+ # by system and we don't want to take the risk of enabling GenAI libraries all at once.
3213
+ # TODO: Remove this logic once a feature flag is implemented in Databricks Runtime init logic.
3214
+ if is_in_databricks_runtime() and (not disable):
3215
+ target_library_and_module = LIBRARY_TO_AUTOLOG_MODULE
3216
+ else:
3217
+ target_library_and_module = LIBRARY_TO_AUTOLOG_MODULE | GENAI_LIBRARY_TO_AUTOLOG_MODULE
3218
+
3219
+ if exclude_flavors:
3220
+ excluded_modules = [f"mlflow.{flavor}" for flavor in exclude_flavors]
3221
+ target_library_and_module = {
3222
+ k: v for k, v in target_library_and_module.items() if v not in excluded_modules
3223
+ }
3224
+
3225
+ def get_autologging_params(autolog_fn):
3226
+ try:
3227
+ needed_params = list(inspect.signature(autolog_fn).parameters.keys())
3228
+ return {k: v for k, v in locals_copy if k in needed_params}
3229
+ except Exception:
3230
+ return {}
3231
+
3232
+ # Note: we need to protect `setup_autologging` with `autologging_conf_lock`,
3233
+ # because `setup_autologging` might be registered as post importing hook
3234
+ # and be executed asynchronously, so that it is out of current active
3235
+ # `autologging_conf_lock` scope.
3236
+ @autologging_conf_lock
3237
+ def setup_autologging(module):
3238
+ try:
3239
+ autologging_params = None
3240
+ autolog_module = importlib.import_module(target_library_and_module[module.__name__])
3241
+ autolog_fn = autolog_module.autolog
3242
+ # Only call integration's autolog function with `mlflow.autolog` configs
3243
+ # if the integration's autolog function has not already been called by the user.
3244
+ # Logic is as follows:
3245
+ # - if a previous_config exists, that means either `mlflow.autolog` or
3246
+ # `mlflow.integration.autolog` was called.
3247
+ # - if the config contains `AUTOLOGGING_CONF_KEY_IS_GLOBALLY_CONFIGURED`, the
3248
+ # configuration was set by `mlflow.autolog`, and so we can safely call `autolog_fn`
3249
+ # with `autologging_params`.
3250
+ # - if the config doesn't contain this key, the configuration was set by an
3251
+ # `mlflow.integration.autolog` call, so we should not call `autolog_fn` with
3252
+ # new configs.
3253
+ prev_config = AUTOLOGGING_INTEGRATIONS.get(autolog_fn.integration_name)
3254
+ if prev_config and not prev_config.get(
3255
+ AUTOLOGGING_CONF_KEY_IS_GLOBALLY_CONFIGURED, False
3256
+ ):
3257
+ return
3258
+
3259
+ autologging_params = get_autologging_params(autolog_fn)
3260
+ autolog_fn(**autologging_params)
3261
+ AUTOLOGGING_INTEGRATIONS[autolog_fn.integration_name][
3262
+ AUTOLOGGING_CONF_KEY_IS_GLOBALLY_CONFIGURED
3263
+ ] = True
3264
+ if not autologging_is_disabled(
3265
+ autolog_fn.integration_name
3266
+ ) and not autologging_params.get("silent", False):
3267
+ _logger.info("Autologging successfully enabled for %s.", module.__name__)
3268
+ except Exception as e:
3269
+ if is_testing():
3270
+ # Raise unexpected exceptions in test mode in order to detect
3271
+ # errors within dependent autologging integrations
3272
+ raise
3273
+ elif autologging_params is None or not autologging_params.get("silent", False):
3274
+ _logger.warning(
3275
+ "Exception raised while enabling autologging for %s: %s",
3276
+ module.__name__,
3277
+ str(e),
3278
+ )
3279
+
3280
+ # for each autolog library (except pyspark), register a post-import hook.
3281
+ # this way, we do not send any errors to the user until we know they are using the library.
3282
+ # the post-import hook also retroactively activates for previously-imported libraries.
3283
+ for library in sorted(set(target_library_and_module) - {"pyspark", "pyspark.ml"}):
3284
+ register_post_import_hook(setup_autologging, library, overwrite=True)
3285
+
3286
+ if is_in_databricks_runtime():
3287
+ # for pyspark, we activate autologging immediately, without waiting for a module import.
3288
+ # this is because on Databricks a SparkSession already exists and the user can directly
3289
+ # interact with it, and this activity should be logged.
3290
+ import pyspark as pyspark_module
3291
+ import pyspark.ml as pyspark_ml_module
3292
+
3293
+ setup_autologging(pyspark_module)
3294
+ setup_autologging(pyspark_ml_module)
3295
+ else:
3296
+ if "pyspark" in target_library_and_module:
3297
+ register_post_import_hook(setup_autologging, "pyspark", overwrite=True)
3298
+ if "pyspark.ml" in target_library_and_module:
3299
+ register_post_import_hook(setup_autologging, "pyspark.ml", overwrite=True)
3300
+
3301
+
3302
+ _active_model_id_env_lock = threading.Lock()
3303
+
3304
+
3305
+ class ActiveModelContext:
3306
+ """
3307
+ The context of the active model.
3308
+
3309
+ Args:
3310
+ model_id: The ID of the active model.
3311
+ set_by_user: Whether the active model was set by the user or not.
3312
+ """
3313
+
3314
+ def __init__(self, model_id: Optional[str] = None, set_by_user: bool = False):
3315
+ # use active model ID from environment variables as the default value for model_id
3316
+ # so that for subprocesses the default _ACTIVE_MODEL_CONTEXT.model_id
3317
+ # is still valid, and we don't need to read from env var.
3318
+ self._set_by_user = set_by_user
3319
+ if is_in_databricks_model_serving_environment():
3320
+ # In Databricks, we set the active model ID to the environment variable
3321
+ # so that it can be used in the main process, since databricks serving
3322
+ # loads model from threads.
3323
+ with _active_model_id_env_lock:
3324
+ self._model_id = model_id or _get_active_model_id_from_env()
3325
+ if self._model_id:
3326
+ _MLFLOW_ACTIVE_MODEL_ID.set(self._model_id)
3327
+ else:
3328
+ self._model_id = model_id or _get_active_model_id_from_env()
3329
+
3330
+ def __repr__(self):
3331
+ return f"ActiveModelContext(model_id={self.model_id}, set_by_user={self.set_by_user})"
3332
+
3333
+ @property
3334
+ def model_id(self) -> Optional[str]:
3335
+ return self._model_id
3336
+
3337
+ @property
3338
+ def set_by_user(self) -> bool:
3339
+ return self._set_by_user
3340
+
3341
+
3342
+ def _get_active_model_id_from_env() -> Optional[str]:
3343
+ """
3344
+ Get the active model ID from environment variables, with proper precedence handling.
3345
+
3346
+ This utility function reads the active model ID from environment variables with the following
3347
+ precedence order:
3348
+ 1. MLFLOW_ACTIVE_MODEL_ID (public variable) - takes precedence if set
3349
+ 2. _MLFLOW_ACTIVE_MODEL_ID (legacy internal variable) - used as fallback
3350
+
3351
+ Historical Context:
3352
+ The _MLFLOW_ACTIVE_MODEL_ID environment variable was originally created for internal MLflow
3353
+ use only. With the introduction of MLFLOW_ACTIVE_MODEL_ID as the public API, we prioritize
3354
+ the public variable to encourage migration to the public interface while maintaining
3355
+ backward compatibility by falling back to the legacy variable when only it is set.
3356
+
3357
+ Returns:
3358
+ The active model ID if found in environment variables, otherwise None.
3359
+ """
3360
+ # Check public variable first to prioritize the public API
3361
+ public_model_id = MLFLOW_ACTIVE_MODEL_ID.get()
3362
+ if public_model_id is not None:
3363
+ return public_model_id
3364
+
3365
+ # Fallback to legacy internal variable for backward compatibility
3366
+ return _MLFLOW_ACTIVE_MODEL_ID.get()
3367
+
3368
+
3369
+ _ACTIVE_MODEL_CONTEXT = ThreadLocalVariable(default_factory=lambda: ActiveModelContext())
3370
+
3371
+
3372
+ class ActiveModel(LoggedModel):
3373
+ """
3374
+ Wrapper around :py:class:`mlflow.entities.LoggedModel` to enable using Python ``with`` syntax.
3375
+ """
3376
+
3377
+ def __init__(self, logged_model: LoggedModel, set_by_user: bool):
3378
+ super().__init__(**logged_model.to_dictionary())
3379
+ self.last_active_model_context = _ACTIVE_MODEL_CONTEXT.get()
3380
+ _set_active_model_id(self.model_id, set_by_user)
3381
+
3382
+ def __enter__(self):
3383
+ return self
3384
+
3385
+ def __exit__(self, exc_type, exc_val, exc_tb):
3386
+ if is_in_databricks_model_serving_environment():
3387
+ # create a new instance of ActiveModelContext to make sure the
3388
+ # environment variable is updated in databricks serving environment
3389
+ _ACTIVE_MODEL_CONTEXT.set(
3390
+ ActiveModelContext(
3391
+ model_id=self.last_active_model_context.model_id,
3392
+ set_by_user=self.last_active_model_context.set_by_user,
3393
+ )
3394
+ )
3395
+ else:
3396
+ _ACTIVE_MODEL_CONTEXT.set(self.last_active_model_context)
3397
+
3398
+
3399
+ # NB: This function is only intended to be used publicly by users to set the
3400
+ # active model ID. MLflow internally should NEVER call this function directly,
3401
+ # since we need to differentiate between user and system set active model IDs.
3402
+ # For MLflow internal usage, use `_set_active_model` instead.
3403
+ def set_active_model(*, name: Optional[str] = None, model_id: Optional[str] = None) -> ActiveModel:
3404
+ """
3405
+ Set the active model with the specified name or model ID, and it will be used for linking
3406
+ traces that are generated during the lifecycle of the model. The return value can be used as
3407
+ a context manager within a ``with`` block; otherwise, you must call ``set_active_model()``
3408
+ to update active model.
3409
+
3410
+ Args:
3411
+ name: The name of the :py:class:`mlflow.entities.LoggedModel` to set as active.
3412
+ If a LoggedModel with the name does not exist, it will be created under the current
3413
+ experiment. If multiple LoggedModels with the name exist, the latest one will be
3414
+ set as active.
3415
+ model_id: The ID of the :py:class:`mlflow.entities.LoggedModel` to set as active.
3416
+ If no LoggedModel with the ID exists, an exception will be raised.
3417
+
3418
+ Returns:
3419
+ :py:class:`mlflow.ActiveModel` object that acts as a context manager wrapping the
3420
+ LoggedModel's state.
3421
+
3422
+ .. code-block:: python
3423
+ :test:
3424
+ :caption: Example
3425
+
3426
+ import mlflow
3427
+
3428
+ # Set the active model by name
3429
+ mlflow.set_active_model(name="my_model")
3430
+
3431
+ # Set the active model by model ID
3432
+ model = mlflow.create_external_model(name="test_model")
3433
+ mlflow.set_active_model(model_id=model.model_id)
3434
+
3435
+ # Use the active model in a context manager
3436
+ with mlflow.set_active_model(name="new_model"):
3437
+ print(mlflow.get_active_model_id())
3438
+
3439
+ # Traces are automatically linked to the active model
3440
+ mlflow.set_active_model(name="my_model")
3441
+
3442
+
3443
+ @mlflow.trace
3444
+ def predict(model_input):
3445
+ return model_input
3446
+
3447
+
3448
+ predict("abc")
3449
+ traces = mlflow.search_traces(model_id=mlflow.get_active_model_id(), return_type="list")
3450
+ assert len(traces) == 1
3451
+ """
3452
+ return _set_active_model(name=name, model_id=model_id, set_by_user=True)
3453
+
3454
+
3455
+ def _set_active_model(
3456
+ *, name: Optional[str] = None, model_id: Optional[str] = None, set_by_user: bool = False
3457
+ ) -> ActiveModel:
3458
+ if name is None and model_id is None:
3459
+ raise MlflowException.invalid_parameter_value(
3460
+ message="Either name or model_id must be provided",
3461
+ )
3462
+
3463
+ if model_id is not None:
3464
+ logged_model = mlflow.get_logged_model(model_id)
3465
+ if name is not None and logged_model.name != name:
3466
+ raise MlflowException.invalid_parameter_value(
3467
+ f"LoggedModel with model_id {model_id!r} has name {logged_model.name!r}, which does"
3468
+ f" not match the provided name {name!r}."
3469
+ )
3470
+ elif name is not None:
3471
+ logged_models = mlflow.search_logged_models(
3472
+ filter_string=f"name='{name}'", max_results=2, output_format="list"
3473
+ )
3474
+ if len(logged_models) > 1:
3475
+ _logger.warning(
3476
+ f"Multiple LoggedModels found with name {name!r}, setting the latest one as active "
3477
+ "model."
3478
+ )
3479
+ if len(logged_models) == 0:
3480
+ _logger.info(f"LoggedModel with name {name!r} does not exist, creating one...")
3481
+ logged_model = mlflow.create_external_model(name=name)
3482
+ else:
3483
+ logged_model = logged_models[0]
3484
+ return ActiveModel(logged_model=logged_model, set_by_user=set_by_user)
3485
+
3486
+
3487
+ def _set_active_model_id(model_id: str, set_by_user: bool = False) -> None:
3488
+ """
3489
+ Set the active model ID in the active model context and update the
3490
+ corresponding environment variable. This should only be used when
3491
+ we know the LoggedModel with the model_id exists.
3492
+ This function should be used inside MLflow to set the active model
3493
+ while not blocking other code execution.
3494
+ """
3495
+ try:
3496
+ _ACTIVE_MODEL_CONTEXT.set(ActiveModelContext(model_id, set_by_user))
3497
+ except Exception as e:
3498
+ _logger.warning(f"Failed to set active model ID to {model_id}, error: {e}")
3499
+ else:
3500
+ _logger.info(f"Active model is set to the logged model with ID: {model_id}")
3501
+ if not set_by_user:
3502
+ _logger.info(
3503
+ "Use `mlflow.set_active_model` to set the active model "
3504
+ "to a different one if needed."
3505
+ )
3506
+
3507
+
3508
+ def _get_active_model_context() -> ActiveModelContext:
3509
+ """
3510
+ Get the active model context. This is used internally by MLflow to manage the active model
3511
+ context.
3512
+ """
3513
+ return _ACTIVE_MODEL_CONTEXT.get()
3514
+
3515
+
3516
+ def get_active_model_id() -> Optional[str]:
3517
+ """
3518
+ Get the active model ID. If no active model is set with ``set_active_model()``, the
3519
+ default active model is set using model ID from the environment variable
3520
+ ``MLFLOW_ACTIVE_MODEL_ID`` or the legacy environment variable ``_MLFLOW_ACTIVE_MODEL_ID``.
3521
+ If neither is set, return None. Note that this function only get the active model ID from the
3522
+ current thread.
3523
+
3524
+ Returns:
3525
+ The active model ID if set, otherwise None.
3526
+ """
3527
+ return _get_active_model_context().model_id
3528
+
3529
+
3530
+ def _get_active_model_id_global() -> Optional[str]:
3531
+ """
3532
+ Get the active model ID from the global context by checking all threads.
3533
+ This is useful when we need to get the active_model_id set by a different thread.
3534
+ """
3535
+ # if the active model ID is set in the current thread, always use it
3536
+ if model_id_in_current_thread := get_active_model_id():
3537
+ _logger.debug(f"Active model ID found in the current thread: {model_id_in_current_thread}")
3538
+ return model_id_in_current_thread
3539
+ model_ids = [
3540
+ ctx.model_id
3541
+ for ctx in _ACTIVE_MODEL_CONTEXT.get_all_thread_values().values()
3542
+ if ctx.model_id is not None
3543
+ ]
3544
+ if model_ids:
3545
+ if len(set(model_ids)) > 1:
3546
+ _logger.debug(
3547
+ "Failed to get one active model id from all threads, multiple active model IDs "
3548
+ f"found: {set(model_ids)}."
3549
+ )
3550
+ return
3551
+ return model_ids[0]
3552
+ _logger.debug("No active model ID found in any thread.")
3553
+
3554
+
3555
+ def clear_active_model() -> None:
3556
+ """
3557
+ Clear the active model. This will clear the active model previously set by
3558
+ :py:func:`mlflow.set_active_model` or via the ``MLFLOW_ACTIVE_MODEL_ID`` environment variable
3559
+ or the ``_MLFLOW_ACTIVE_MODEL_ID`` legacy environment variable.
3560
+
3561
+ from current thread. To temporarily switch
3562
+ the active model, use ``with mlflow.set_active_model(...)`` instead.
3563
+
3564
+ .. code-block:: python
3565
+ :test:
3566
+ :caption: Example
3567
+
3568
+ import mlflow
3569
+
3570
+ # Set the active model by name
3571
+ mlflow.set_active_model(name="my_model")
3572
+
3573
+ # Clear the active model
3574
+ mlflow.clear_active_model()
3575
+ # Check that the active model is None
3576
+ assert mlflow.get_active_model_id() is None
3577
+
3578
+ # If you want to temporarily set the active model,
3579
+ # use `set_active_model` as a context manager instead
3580
+ with mlflow.set_active_model(name="my_model") as active_model:
3581
+ assert mlflow.get_active_model_id() == active_model.model_id
3582
+ assert mlflow.get_active_model_id() is None
3583
+ """
3584
+ # reset the environment variables as well to avoid them being used when creating
3585
+ # ActiveModelContext
3586
+ MLFLOW_ACTIVE_MODEL_ID.unset()
3587
+ _MLFLOW_ACTIVE_MODEL_ID.unset()
3588
+
3589
+ # Reset the active model context to avoid the active model ID set by other threads
3590
+ # to be used when creating a new ActiveModelContext
3591
+ _ACTIVE_MODEL_CONTEXT.reset()
3592
+ # set_by_user is False because this API clears the state of active model
3593
+ # and MLflow might still set the active model in cases like `load_model`
3594
+ _ACTIVE_MODEL_CONTEXT.set(ActiveModelContext(set_by_user=False))
3595
+ _logger.info("Active model is cleared")