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,1224 @@
1
+ """
2
+ Usage
3
+ -----
4
+
5
+ .. code-block:: bash
6
+
7
+ mlflow server --app-name basic-auth
8
+ """
9
+
10
+ import functools
11
+ import importlib
12
+ import logging
13
+ import re
14
+ from typing import Any, Callable, Optional, Union
15
+
16
+ import sqlalchemy
17
+ from flask import (
18
+ Flask,
19
+ Request,
20
+ Response,
21
+ flash,
22
+ jsonify,
23
+ make_response,
24
+ render_template_string,
25
+ request,
26
+ )
27
+ from werkzeug.datastructures import Authorization
28
+
29
+ from mlflow import MlflowException
30
+ from mlflow.entities import Experiment
31
+ from mlflow.entities.logged_model import LoggedModel
32
+ from mlflow.entities.model_registry import RegisteredModel
33
+ from mlflow.environment_variables import MLFLOW_FLASK_SERVER_SECRET_KEY
34
+ from mlflow.protos.databricks_pb2 import (
35
+ BAD_REQUEST,
36
+ INTERNAL_ERROR,
37
+ INVALID_PARAMETER_VALUE,
38
+ RESOURCE_DOES_NOT_EXIST,
39
+ ErrorCode,
40
+ )
41
+ from mlflow.protos.model_registry_pb2 import (
42
+ CreateModelVersion,
43
+ CreateRegisteredModel,
44
+ DeleteModelVersion,
45
+ DeleteModelVersionTag,
46
+ DeleteRegisteredModel,
47
+ DeleteRegisteredModelAlias,
48
+ DeleteRegisteredModelTag,
49
+ GetLatestVersions,
50
+ GetModelVersion,
51
+ GetModelVersionByAlias,
52
+ GetModelVersionDownloadUri,
53
+ GetRegisteredModel,
54
+ RenameRegisteredModel,
55
+ SearchRegisteredModels,
56
+ SetModelVersionTag,
57
+ SetRegisteredModelAlias,
58
+ SetRegisteredModelTag,
59
+ TransitionModelVersionStage,
60
+ UpdateModelVersion,
61
+ UpdateRegisteredModel,
62
+ )
63
+ from mlflow.protos.service_pb2 import (
64
+ CreateExperiment,
65
+ # Routes for logged models
66
+ CreateLoggedModel,
67
+ CreateRun,
68
+ DeleteExperiment,
69
+ DeleteLoggedModel,
70
+ DeleteLoggedModelTag,
71
+ DeleteRun,
72
+ DeleteTag,
73
+ FinalizeLoggedModel,
74
+ GetExperiment,
75
+ GetExperimentByName,
76
+ GetLoggedModel,
77
+ GetMetricHistory,
78
+ GetRun,
79
+ ListArtifacts,
80
+ LogBatch,
81
+ LogLoggedModelParamsRequest,
82
+ LogMetric,
83
+ LogModel,
84
+ LogParam,
85
+ RestoreExperiment,
86
+ RestoreRun,
87
+ SearchExperiments,
88
+ SearchLoggedModels,
89
+ SetExperimentTag,
90
+ SetLoggedModelTags,
91
+ SetTag,
92
+ UpdateExperiment,
93
+ UpdateRun,
94
+ )
95
+ from mlflow.server import app
96
+ from mlflow.server.auth.config import read_auth_config
97
+ from mlflow.server.auth.logo import MLFLOW_LOGO
98
+ from mlflow.server.auth.permissions import MANAGE, Permission, get_permission
99
+ from mlflow.server.auth.routes import (
100
+ CREATE_EXPERIMENT_PERMISSION,
101
+ CREATE_REGISTERED_MODEL_PERMISSION,
102
+ CREATE_USER,
103
+ CREATE_USER_UI,
104
+ DELETE_EXPERIMENT_PERMISSION,
105
+ DELETE_REGISTERED_MODEL_PERMISSION,
106
+ DELETE_USER,
107
+ GET_EXPERIMENT_PERMISSION,
108
+ GET_REGISTERED_MODEL_PERMISSION,
109
+ GET_USER,
110
+ HOME,
111
+ SIGNUP,
112
+ UPDATE_EXPERIMENT_PERMISSION,
113
+ UPDATE_REGISTERED_MODEL_PERMISSION,
114
+ UPDATE_USER_ADMIN,
115
+ UPDATE_USER_PASSWORD,
116
+ )
117
+ from mlflow.server.auth.sqlalchemy_store import SqlAlchemyStore
118
+ from mlflow.server.handlers import (
119
+ _get_model_registry_store,
120
+ _get_request_message,
121
+ _get_tracking_store,
122
+ catch_mlflow_exception,
123
+ get_endpoints,
124
+ )
125
+ from mlflow.store.entities import PagedList
126
+ from mlflow.utils.proto_json_utils import message_to_json, parse_dict
127
+ from mlflow.utils.rest_utils import _REST_API_PATH_PREFIX
128
+ from mlflow.utils.search_utils import SearchUtils
129
+
130
+ try:
131
+ from flask_wtf.csrf import CSRFProtect
132
+ except ImportError as e:
133
+ raise ImportError(
134
+ "The MLflow basic auth app requires the Flask-WTF package to perform CSRF "
135
+ "validation. Please run `pip install mlflow[auth]` to install it."
136
+ ) from e
137
+
138
+ _logger = logging.getLogger(__name__)
139
+
140
+ auth_config = read_auth_config()
141
+ store = SqlAlchemyStore()
142
+
143
+
144
+ def is_unprotected_route(path: str) -> bool:
145
+ return path.startswith(("/static", "/favicon.ico", "/health"))
146
+
147
+
148
+ def make_basic_auth_response() -> Response:
149
+ res = make_response(
150
+ "You are not authenticated. Please see "
151
+ "https://www.mlflow.org/docs/latest/auth/index.html#authenticating-to-mlflow "
152
+ "on how to authenticate."
153
+ )
154
+ res.status_code = 401
155
+ res.headers["WWW-Authenticate"] = 'Basic realm="mlflow"'
156
+ return res
157
+
158
+
159
+ def make_forbidden_response() -> Response:
160
+ res = make_response("Permission denied")
161
+ res.status_code = 403
162
+ return res
163
+
164
+
165
+ def _get_request_param(param: str) -> str:
166
+ if request.method == "GET":
167
+ args = request.args
168
+ elif request.method in ("POST", "PATCH"):
169
+ args = request.json
170
+ elif request.method == "DELETE":
171
+ args = request.json if request.is_json else request.args
172
+ else:
173
+ raise MlflowException(
174
+ f"Unsupported HTTP method '{request.method}'",
175
+ BAD_REQUEST,
176
+ )
177
+
178
+ args = args | (request.view_args or {})
179
+ if param not in args:
180
+ # Special handling for run_id
181
+ if param == "run_id":
182
+ return _get_request_param("run_uuid")
183
+ raise MlflowException(
184
+ f"Missing value for required parameter '{param}'. "
185
+ "See the API docs for more information about request parameters.",
186
+ INVALID_PARAMETER_VALUE,
187
+ )
188
+ return args[param]
189
+
190
+
191
+ def _get_permission_from_store_or_default(store_permission_func: Callable[[], str]) -> Permission:
192
+ """
193
+ Attempts to get permission from store,
194
+ and returns default permission if no record is found.
195
+ """
196
+ try:
197
+ perm = store_permission_func()
198
+ except MlflowException as e:
199
+ if e.error_code == ErrorCode.Name(RESOURCE_DOES_NOT_EXIST):
200
+ perm = auth_config.default_permission
201
+ else:
202
+ raise
203
+ return get_permission(perm)
204
+
205
+
206
+ def _get_permission_from_experiment_id() -> Permission:
207
+ experiment_id = _get_request_param("experiment_id")
208
+ username = authenticate_request().username
209
+ return _get_permission_from_store_or_default(
210
+ lambda: store.get_experiment_permission(experiment_id, username).permission
211
+ )
212
+
213
+
214
+ _EXPERIMENT_ID_PATTERN = re.compile(r"^(\d+)/")
215
+
216
+
217
+ def _get_experiment_id_from_view_args():
218
+ if artifact_path := request.view_args.get("artifact_path"):
219
+ if m := _EXPERIMENT_ID_PATTERN.match(artifact_path):
220
+ return m.group(1)
221
+ return None
222
+
223
+
224
+ def _get_permission_from_experiment_id_artifact_proxy() -> Permission:
225
+ if experiment_id := _get_experiment_id_from_view_args():
226
+ username = authenticate_request().username
227
+ return _get_permission_from_store_or_default(
228
+ lambda: store.get_experiment_permission(experiment_id, username).permission
229
+ )
230
+ return get_permission(auth_config.default_permission)
231
+
232
+
233
+ def _get_permission_from_experiment_name() -> Permission:
234
+ experiment_name = _get_request_param("experiment_name")
235
+ store_exp = _get_tracking_store().get_experiment_by_name(experiment_name)
236
+ if store_exp is None:
237
+ raise MlflowException(
238
+ f"Could not find experiment with name {experiment_name}",
239
+ error_code=RESOURCE_DOES_NOT_EXIST,
240
+ )
241
+ username = authenticate_request().username
242
+ return _get_permission_from_store_or_default(
243
+ lambda: store.get_experiment_permission(store_exp.experiment_id, username).permission
244
+ )
245
+
246
+
247
+ def _get_permission_from_run_id() -> Permission:
248
+ # run permissions inherit from parent resource (experiment)
249
+ # so we just get the experiment permission
250
+ run_id = _get_request_param("run_id")
251
+ run = _get_tracking_store().get_run(run_id)
252
+ experiment_id = run.info.experiment_id
253
+ username = authenticate_request().username
254
+ return _get_permission_from_store_or_default(
255
+ lambda: store.get_experiment_permission(experiment_id, username).permission
256
+ )
257
+
258
+
259
+ def _get_permission_from_model_id() -> Permission:
260
+ # logged model permissions inherit from parent resource (experiment)
261
+ model_id = _get_request_param("model_id")
262
+ model = _get_tracking_store().get_logged_model(model_id)
263
+ experiment_id = model.experiment_id
264
+ username = authenticate_request().username
265
+ return _get_permission_from_store_or_default(
266
+ lambda: store.get_experiment_permission(experiment_id, username).permission
267
+ )
268
+
269
+
270
+ def _get_permission_from_registered_model_name() -> Permission:
271
+ name = _get_request_param("name")
272
+ username = authenticate_request().username
273
+ return _get_permission_from_store_or_default(
274
+ lambda: store.get_registered_model_permission(name, username).permission
275
+ )
276
+
277
+
278
+ def validate_can_read_experiment():
279
+ return _get_permission_from_experiment_id().can_read
280
+
281
+
282
+ def validate_can_read_experiment_by_name():
283
+ return _get_permission_from_experiment_name().can_read
284
+
285
+
286
+ def validate_can_update_experiment():
287
+ return _get_permission_from_experiment_id().can_update
288
+
289
+
290
+ def validate_can_delete_experiment():
291
+ return _get_permission_from_experiment_id().can_delete
292
+
293
+
294
+ def validate_can_manage_experiment():
295
+ return _get_permission_from_experiment_id().can_manage
296
+
297
+
298
+ def validate_can_read_experiment_artifact_proxy():
299
+ return _get_permission_from_experiment_id_artifact_proxy().can_read
300
+
301
+
302
+ def validate_can_update_experiment_artifact_proxy():
303
+ return _get_permission_from_experiment_id_artifact_proxy().can_update
304
+
305
+
306
+ def validate_can_delete_experiment_artifact_proxy():
307
+ return _get_permission_from_experiment_id_artifact_proxy().can_manage
308
+
309
+
310
+ # Runs
311
+ def validate_can_read_run():
312
+ return _get_permission_from_run_id().can_read
313
+
314
+
315
+ def validate_can_update_run():
316
+ return _get_permission_from_run_id().can_update
317
+
318
+
319
+ def validate_can_delete_run():
320
+ return _get_permission_from_run_id().can_delete
321
+
322
+
323
+ def validate_can_manage_run():
324
+ return _get_permission_from_run_id().can_manage
325
+
326
+
327
+ # Logged models
328
+ def validate_can_read_logged_model():
329
+ return _get_permission_from_model_id().can_read
330
+
331
+
332
+ def validate_can_update_logged_model():
333
+ return _get_permission_from_model_id().can_update
334
+
335
+
336
+ def validate_can_delete_logged_model():
337
+ return _get_permission_from_model_id().can_delete
338
+
339
+
340
+ def validate_can_manage_logged_model():
341
+ return _get_permission_from_model_id().can_manage
342
+
343
+
344
+ # Registered models
345
+ def validate_can_read_registered_model():
346
+ return _get_permission_from_registered_model_name().can_read
347
+
348
+
349
+ def validate_can_update_registered_model():
350
+ return _get_permission_from_registered_model_name().can_update
351
+
352
+
353
+ def validate_can_delete_registered_model():
354
+ return _get_permission_from_registered_model_name().can_delete
355
+
356
+
357
+ def validate_can_manage_registered_model():
358
+ return _get_permission_from_registered_model_name().can_manage
359
+
360
+
361
+ def sender_is_admin():
362
+ """Validate if the sender is admin"""
363
+ username = authenticate_request().username
364
+ return store.get_user(username).is_admin
365
+
366
+
367
+ def username_is_sender():
368
+ """Validate if the request username is the sender"""
369
+ username = _get_request_param("username")
370
+ sender = authenticate_request().username
371
+ return username == sender
372
+
373
+
374
+ def validate_can_read_user():
375
+ return username_is_sender()
376
+
377
+
378
+ def validate_can_create_user():
379
+ # only admins can create user, but admins won't reach this validator
380
+ return False
381
+
382
+
383
+ def validate_can_update_user_password():
384
+ return username_is_sender()
385
+
386
+
387
+ def validate_can_update_user_admin():
388
+ # only admins can update, but admins won't reach this validator
389
+ return False
390
+
391
+
392
+ def validate_can_delete_user():
393
+ # only admins can delete, but admins won't reach this validator
394
+ return False
395
+
396
+
397
+ BEFORE_REQUEST_HANDLERS = {
398
+ # Routes for experiments
399
+ GetExperiment: validate_can_read_experiment,
400
+ GetExperimentByName: validate_can_read_experiment_by_name,
401
+ DeleteExperiment: validate_can_delete_experiment,
402
+ RestoreExperiment: validate_can_delete_experiment,
403
+ UpdateExperiment: validate_can_update_experiment,
404
+ SetExperimentTag: validate_can_update_experiment,
405
+ # Routes for runs
406
+ CreateRun: validate_can_update_experiment,
407
+ GetRun: validate_can_read_run,
408
+ DeleteRun: validate_can_delete_run,
409
+ RestoreRun: validate_can_delete_run,
410
+ UpdateRun: validate_can_update_run,
411
+ LogMetric: validate_can_update_run,
412
+ LogBatch: validate_can_update_run,
413
+ LogModel: validate_can_update_run,
414
+ SetTag: validate_can_update_run,
415
+ DeleteTag: validate_can_update_run,
416
+ LogParam: validate_can_update_run,
417
+ GetMetricHistory: validate_can_read_run,
418
+ ListArtifacts: validate_can_read_run,
419
+ # Routes for model registry
420
+ GetRegisteredModel: validate_can_read_registered_model,
421
+ DeleteRegisteredModel: validate_can_delete_registered_model,
422
+ UpdateRegisteredModel: validate_can_update_registered_model,
423
+ RenameRegisteredModel: validate_can_update_registered_model,
424
+ GetLatestVersions: validate_can_read_registered_model,
425
+ CreateModelVersion: validate_can_update_registered_model,
426
+ GetModelVersion: validate_can_read_registered_model,
427
+ DeleteModelVersion: validate_can_delete_registered_model,
428
+ UpdateModelVersion: validate_can_update_registered_model,
429
+ TransitionModelVersionStage: validate_can_update_registered_model,
430
+ GetModelVersionDownloadUri: validate_can_read_registered_model,
431
+ SetRegisteredModelTag: validate_can_update_registered_model,
432
+ DeleteRegisteredModelTag: validate_can_update_registered_model,
433
+ SetModelVersionTag: validate_can_update_registered_model,
434
+ DeleteModelVersionTag: validate_can_delete_registered_model,
435
+ SetRegisteredModelAlias: validate_can_update_registered_model,
436
+ DeleteRegisteredModelAlias: validate_can_delete_registered_model,
437
+ GetModelVersionByAlias: validate_can_read_registered_model,
438
+ }
439
+
440
+
441
+ def get_before_request_handler(request_class):
442
+ return BEFORE_REQUEST_HANDLERS.get(request_class)
443
+
444
+
445
+ def _re_compile_path(path: str) -> re.Pattern:
446
+ """
447
+ Convert a path with angle brackets to a regex pattern. For example,
448
+ "/api/2.0/experiments/<experiment_id>" becomes "/api/2.0/experiments/([^/]+)".
449
+ """
450
+ return re.compile(re.sub(r"<([^>]+)>", r"([^/]+)", path))
451
+
452
+
453
+ BEFORE_REQUEST_VALIDATORS = {
454
+ (http_path, method): handler
455
+ for http_path, handler, methods in get_endpoints(get_before_request_handler)
456
+ for method in methods
457
+ }
458
+
459
+ BEFORE_REQUEST_VALIDATORS.update(
460
+ {
461
+ (SIGNUP, "GET"): validate_can_create_user,
462
+ (GET_USER, "GET"): validate_can_read_user,
463
+ (CREATE_USER, "POST"): validate_can_create_user,
464
+ (UPDATE_USER_PASSWORD, "PATCH"): validate_can_update_user_password,
465
+ (UPDATE_USER_ADMIN, "PATCH"): validate_can_update_user_admin,
466
+ (DELETE_USER, "DELETE"): validate_can_delete_user,
467
+ (GET_EXPERIMENT_PERMISSION, "GET"): validate_can_manage_experiment,
468
+ (CREATE_EXPERIMENT_PERMISSION, "POST"): validate_can_manage_experiment,
469
+ (UPDATE_EXPERIMENT_PERMISSION, "PATCH"): validate_can_manage_experiment,
470
+ (DELETE_EXPERIMENT_PERMISSION, "DELETE"): validate_can_manage_experiment,
471
+ (GET_REGISTERED_MODEL_PERMISSION, "GET"): validate_can_manage_registered_model,
472
+ (CREATE_REGISTERED_MODEL_PERMISSION, "POST"): validate_can_manage_registered_model,
473
+ (UPDATE_REGISTERED_MODEL_PERMISSION, "PATCH"): validate_can_manage_registered_model,
474
+ (DELETE_REGISTERED_MODEL_PERMISSION, "DELETE"): validate_can_manage_registered_model,
475
+ }
476
+ )
477
+
478
+
479
+ LOGGED_MODEL_BEFORE_REQUEST_HANDLERS = {
480
+ CreateLoggedModel: validate_can_update_experiment,
481
+ GetLoggedModel: validate_can_read_logged_model,
482
+ DeleteLoggedModel: validate_can_delete_logged_model,
483
+ FinalizeLoggedModel: validate_can_update_logged_model,
484
+ DeleteLoggedModelTag: validate_can_delete_logged_model,
485
+ SetLoggedModelTags: validate_can_update_logged_model,
486
+ LogLoggedModelParamsRequest: validate_can_update_logged_model,
487
+ }
488
+
489
+
490
+ def get_logged_model_before_request_handler(request_class):
491
+ return LOGGED_MODEL_BEFORE_REQUEST_HANDLERS.get(request_class)
492
+
493
+
494
+ LOGGED_MODEL_BEFORE_REQUEST_VALIDATORS = {
495
+ # Paths for logged models contains path parameters (e.g. /mlflow/logged-models/<model_id>)
496
+ (_re_compile_path(http_path), method): handler
497
+ for http_path, handler, methods in get_endpoints(get_logged_model_before_request_handler)
498
+ for method in methods
499
+ }
500
+
501
+
502
+ def _is_proxy_artifact_path(path: str) -> bool:
503
+ return path.startswith(f"{_REST_API_PATH_PREFIX}/mlflow-artifacts/artifacts/")
504
+
505
+
506
+ def _get_proxy_artifact_validator(
507
+ method: str, view_args: Optional[dict[str, Any]]
508
+ ) -> Optional[Callable[[], bool]]:
509
+ if view_args is None:
510
+ return validate_can_read_experiment_artifact_proxy # List
511
+
512
+ return {
513
+ "GET": validate_can_read_experiment_artifact_proxy, # Download
514
+ "PUT": validate_can_update_experiment_artifact_proxy, # Upload
515
+ "DELETE": validate_can_delete_experiment_artifact_proxy, # Delete
516
+ }.get(method)
517
+
518
+
519
+ def authenticate_request() -> Union[Authorization, Response]:
520
+ """Use configured authorization function to get request authorization."""
521
+ auth_func = get_auth_func(auth_config.authorization_function)
522
+ return auth_func()
523
+
524
+
525
+ @functools.lru_cache(maxsize=None)
526
+ def get_auth_func(authorization_function: str) -> Callable[[], Union[Authorization, Response]]:
527
+ """
528
+ Import and return the specified authorization function.
529
+
530
+ Args:
531
+ authorization_function: A string of the form "module.submodule:auth_func"
532
+ """
533
+ mod_name, fn_name = authorization_function.split(":", 1)
534
+ module = importlib.import_module(mod_name)
535
+ return getattr(module, fn_name)
536
+
537
+
538
+ def authenticate_request_basic_auth() -> Union[Authorization, Response]:
539
+ """Authenticate the request using basic auth."""
540
+ if request.authorization is None:
541
+ return make_basic_auth_response()
542
+
543
+ username = request.authorization.username
544
+ password = request.authorization.password
545
+ if store.authenticate_user(username, password):
546
+ return request.authorization
547
+ else:
548
+ # let user attempt login again
549
+ return make_basic_auth_response()
550
+
551
+
552
+ def _find_validator(req: Request) -> Optional[Callable[[], bool]]:
553
+ """
554
+ Finds the validator matching the request path and method.
555
+ """
556
+ if "/mlflow/logged-models" in req.path:
557
+ # logged model routes are not registered in the app
558
+ # so we need to check them manually
559
+ return next(
560
+ (
561
+ v
562
+ for (pat, method), v in LOGGED_MODEL_BEFORE_REQUEST_VALIDATORS.items()
563
+ if pat.fullmatch(req.path) and method == req.method
564
+ ),
565
+ None,
566
+ )
567
+ else:
568
+ return BEFORE_REQUEST_VALIDATORS.get((req.path, req.method))
569
+
570
+
571
+ @catch_mlflow_exception
572
+ def _before_request():
573
+ if is_unprotected_route(request.path):
574
+ return
575
+
576
+ authorization = authenticate_request()
577
+ if isinstance(authorization, Response):
578
+ return authorization
579
+ elif not isinstance(authorization, Authorization):
580
+ raise MlflowException(
581
+ f"Unsupported result type from {auth_config.authorization_function}: "
582
+ f"'{type(authorization).__name__}'",
583
+ INTERNAL_ERROR,
584
+ )
585
+
586
+ # admins don't need to be authorized
587
+ if sender_is_admin():
588
+ return
589
+
590
+ # authorization
591
+ if validator := _find_validator(request):
592
+ if not validator():
593
+ return make_forbidden_response()
594
+ elif _is_proxy_artifact_path(request.path):
595
+ if validator := _get_proxy_artifact_validator(request.method, request.view_args):
596
+ if not validator():
597
+ return make_forbidden_response()
598
+
599
+
600
+ def set_can_manage_experiment_permission(resp: Response):
601
+ response_message = CreateExperiment.Response()
602
+ parse_dict(resp.json, response_message)
603
+ experiment_id = response_message.experiment_id
604
+ username = authenticate_request().username
605
+ store.create_experiment_permission(experiment_id, username, MANAGE.name)
606
+
607
+
608
+ def set_can_manage_registered_model_permission(resp: Response):
609
+ response_message = CreateRegisteredModel.Response()
610
+ parse_dict(resp.json, response_message)
611
+ name = response_message.registered_model.name
612
+ username = authenticate_request().username
613
+ store.create_registered_model_permission(name, username, MANAGE.name)
614
+
615
+
616
+ def delete_can_manage_registered_model_permission(resp: Response):
617
+ """
618
+ Delete registered model permission when the model is deleted.
619
+
620
+ We need to do this because the primary key of the registered model is the name,
621
+ unlike the experiment where the primary key is experiment_id (UUID). Therefore,
622
+ we have to delete the permission record when the model is deleted otherwise it
623
+ conflicts with the new model registered with the same name.
624
+ """
625
+ # Get model name from request context because it's not available in the response
626
+ name = request.get_json(force=True, silent=True)["name"]
627
+ username = authenticate_request().username
628
+ store.delete_registered_model_permission(name, username)
629
+
630
+
631
+ def filter_search_experiments(resp: Response):
632
+ if sender_is_admin():
633
+ return
634
+
635
+ response_message = SearchExperiments.Response()
636
+ parse_dict(resp.json, response_message)
637
+
638
+ # fetch permissions
639
+ username = authenticate_request().username
640
+ perms = store.list_experiment_permissions(username)
641
+ can_read = {p.experiment_id: get_permission(p.permission).can_read for p in perms}
642
+ default_can_read = get_permission(auth_config.default_permission).can_read
643
+
644
+ # filter out unreadable
645
+ for e in list(response_message.experiments):
646
+ if not can_read.get(e.experiment_id, default_can_read):
647
+ response_message.experiments.remove(e)
648
+
649
+ # re-fetch to fill max results
650
+ request_message = _get_request_message(SearchExperiments())
651
+ while (
652
+ len(response_message.experiments) < request_message.max_results
653
+ and response_message.next_page_token != ""
654
+ ):
655
+ refetched: PagedList[Experiment] = _get_tracking_store().search_experiments(
656
+ view_type=request_message.view_type,
657
+ max_results=request_message.max_results,
658
+ order_by=request_message.order_by,
659
+ filter_string=request_message.filter,
660
+ page_token=response_message.next_page_token,
661
+ )
662
+ refetched = refetched[: request_message.max_results - len(response_message.experiments)]
663
+ if len(refetched) == 0:
664
+ response_message.next_page_token = ""
665
+ break
666
+
667
+ refetched_readable_proto = [
668
+ e.to_proto() for e in refetched if can_read.get(e.experiment_id, default_can_read)
669
+ ]
670
+ response_message.experiments.extend(refetched_readable_proto)
671
+
672
+ # recalculate next page token
673
+ start_offset = SearchUtils.parse_start_offset_from_page_token(
674
+ response_message.next_page_token
675
+ )
676
+ final_offset = start_offset + len(refetched)
677
+ response_message.next_page_token = SearchUtils.create_page_token(final_offset)
678
+
679
+ resp.data = message_to_json(response_message)
680
+
681
+
682
+ def filter_search_logged_models(resp: Response) -> None:
683
+ """
684
+ Filter out unreadable logged models from the search results.
685
+ """
686
+ from mlflow.utils.search_utils import SearchLoggedModelsPaginationToken as Token
687
+
688
+ if sender_is_admin():
689
+ return
690
+
691
+ response_proto = SearchLoggedModels.Response()
692
+ parse_dict(resp.json, response_proto)
693
+
694
+ # fetch permissions
695
+ username = authenticate_request().username
696
+ perms = store.list_experiment_permissions(username)
697
+ can_read = {p.experiment_id: get_permission(p.permission).can_read for p in perms}
698
+ default_can_read = get_permission(auth_config.default_permission).can_read
699
+
700
+ # Remove unreadable models
701
+ for m in list(response_proto.models):
702
+ if not can_read.get(m.info.experiment_id, default_can_read):
703
+ response_proto.models.remove(m)
704
+
705
+ request_proto = _get_request_message(SearchLoggedModels())
706
+ max_results = request_proto.max_results
707
+ # These parameters won't change in the loop
708
+ params = {
709
+ "experiment_ids": list(request_proto.experiment_ids),
710
+ "filter_string": request_proto.filter or None,
711
+ "order_by": (
712
+ [
713
+ {
714
+ "field_name": ob.field_name,
715
+ "ascending": ob.ascending,
716
+ "dataset_name": ob.dataset_name,
717
+ "dataset_digest": ob.dataset_digest,
718
+ }
719
+ for ob in request_proto.order_by
720
+ ]
721
+ if request_proto.order_by
722
+ else None
723
+ ),
724
+ }
725
+ next_page_token = response_proto.next_page_token or None
726
+ tracking_store = _get_tracking_store()
727
+ while len(response_proto.models) < max_results and next_page_token is not None:
728
+ batch: PagedList[LoggedModel] = tracking_store.search_logged_models(
729
+ max_results=max_results, page_token=next_page_token, **params
730
+ )
731
+ is_last_page = batch.token is None
732
+ offset = Token.decode(next_page_token).offset if next_page_token else 0
733
+ last_index = len(batch) - 1
734
+ for index, model in enumerate(batch):
735
+ if not can_read.get(model.experiment_id, default_can_read):
736
+ continue
737
+ response_proto.models.append(model.to_proto())
738
+ if len(response_proto.models) >= max_results:
739
+ next_page_token = (
740
+ None
741
+ if is_last_page and index == last_index
742
+ else Token(offset=offset + index + 1, **params).encode()
743
+ )
744
+ break
745
+ else:
746
+ # If we reach here, it means we have not reached the max results.
747
+ next_page_token = (
748
+ None if is_last_page else Token(offset=offset + max_results, **params).encode()
749
+ )
750
+
751
+ if next_page_token:
752
+ response_proto.next_page_token = next_page_token
753
+ resp.data = message_to_json(response_proto)
754
+
755
+
756
+ def filter_search_registered_models(resp: Response):
757
+ if sender_is_admin():
758
+ return
759
+
760
+ response_message = SearchRegisteredModels.Response()
761
+ parse_dict(resp.json, response_message)
762
+
763
+ # fetch permissions
764
+ username = authenticate_request().username
765
+ perms = store.list_registered_model_permissions(username)
766
+ can_read = {p.name: get_permission(p.permission).can_read for p in perms}
767
+ default_can_read = get_permission(auth_config.default_permission).can_read
768
+
769
+ # filter out unreadable
770
+ for rm in list(response_message.registered_models):
771
+ if not can_read.get(rm.name, default_can_read):
772
+ response_message.registered_models.remove(rm)
773
+
774
+ # re-fetch to fill max results
775
+ request_message = _get_request_message(SearchRegisteredModels())
776
+ while (
777
+ len(response_message.registered_models) < request_message.max_results
778
+ and response_message.next_page_token != ""
779
+ ):
780
+ refetched: PagedList[RegisteredModel] = (
781
+ _get_model_registry_store().search_registered_models(
782
+ filter_string=request_message.filter,
783
+ max_results=request_message.max_results,
784
+ order_by=request_message.order_by,
785
+ page_token=response_message.next_page_token,
786
+ )
787
+ )
788
+ refetched = refetched[
789
+ : request_message.max_results - len(response_message.registered_models)
790
+ ]
791
+ if len(refetched) == 0:
792
+ response_message.next_page_token = ""
793
+ break
794
+
795
+ refetched_readable_proto = [
796
+ rm.to_proto() for rm in refetched if can_read.get(rm.name, default_can_read)
797
+ ]
798
+ response_message.registered_models.extend(refetched_readable_proto)
799
+
800
+ # recalculate next page token
801
+ start_offset = SearchUtils.parse_start_offset_from_page_token(
802
+ response_message.next_page_token
803
+ )
804
+ final_offset = start_offset + len(refetched)
805
+ response_message.next_page_token = SearchUtils.create_page_token(final_offset)
806
+
807
+ resp.data = message_to_json(response_message)
808
+
809
+
810
+ def rename_registered_model_permission(resp: Response):
811
+ """
812
+ A model registry can be assigned to multiple users with different permissions.
813
+
814
+ Changing the model registry name must be propagated to all users.
815
+ """
816
+ # get registry model name before update
817
+ data = request.get_json(force=True, silent=True)
818
+ store.rename_registered_model_permissions(data.get("name"), data.get("new_name"))
819
+
820
+
821
+ AFTER_REQUEST_PATH_HANDLERS = {
822
+ CreateExperiment: set_can_manage_experiment_permission,
823
+ CreateRegisteredModel: set_can_manage_registered_model_permission,
824
+ DeleteRegisteredModel: delete_can_manage_registered_model_permission,
825
+ SearchExperiments: filter_search_experiments,
826
+ SearchLoggedModels: filter_search_logged_models,
827
+ SearchRegisteredModels: filter_search_registered_models,
828
+ RenameRegisteredModel: rename_registered_model_permission,
829
+ }
830
+
831
+
832
+ def get_after_request_handler(request_class):
833
+ return AFTER_REQUEST_PATH_HANDLERS.get(request_class)
834
+
835
+
836
+ AFTER_REQUEST_HANDLERS = {
837
+ (http_path, method): handler
838
+ for http_path, handler, methods in get_endpoints(get_after_request_handler)
839
+ for method in methods
840
+ if handler is not None and "/graphql" not in http_path
841
+ }
842
+
843
+
844
+ @catch_mlflow_exception
845
+ def _after_request(resp: Response):
846
+ if 400 <= resp.status_code < 600:
847
+ return resp
848
+
849
+ if handler := AFTER_REQUEST_HANDLERS.get((request.path, request.method)):
850
+ handler(resp)
851
+ return resp
852
+
853
+
854
+ def create_admin_user(username, password):
855
+ if not store.has_user(username):
856
+ try:
857
+ store.create_user(username, password, is_admin=True)
858
+ _logger.info(
859
+ f"Created admin user '{username}'. "
860
+ "It is recommended that you set a new password as soon as possible "
861
+ f"on {UPDATE_USER_PASSWORD}."
862
+ )
863
+ except MlflowException as e:
864
+ if isinstance(e.__cause__, sqlalchemy.exc.IntegrityError):
865
+ # When multiple workers are starting up at the same time, it's possible
866
+ # that they try to create the admin user at the same time and one of them
867
+ # will succeed while the others will fail with an IntegrityError.
868
+ return
869
+ raise
870
+
871
+
872
+ def alert(href: str):
873
+ return render_template_string(
874
+ r"""
875
+ <script type = "text/javascript">
876
+ {% with messages = get_flashed_messages() %}
877
+ {% if messages %}
878
+ {% for message in messages %}
879
+ alert("{{ message }}");
880
+ {% endfor %}
881
+ {% endif %}
882
+ {% endwith %}
883
+ window.location.href = "{{ href }}";
884
+ </script>
885
+ """,
886
+ href=href,
887
+ )
888
+
889
+
890
+ def signup():
891
+ return render_template_string(
892
+ r"""
893
+ <style>
894
+ form {
895
+ background-color: #F5F5F5;
896
+ border: 1px solid #CCCCCC;
897
+ border-radius: 4px;
898
+ padding: 20px;
899
+ max-width: 400px;
900
+ margin: 0 auto;
901
+ font-family: Arial, sans-serif;
902
+ font-size: 14px;
903
+ line-height: 1.5;
904
+ }
905
+
906
+ input[type=text], input[type=password] {
907
+ width: 100%;
908
+ padding: 10px;
909
+ margin-bottom: 10px;
910
+ border: 1px solid #CCCCCC;
911
+ border-radius: 4px;
912
+ box-sizing: border-box;
913
+ }
914
+ input[type=submit] {
915
+ background-color: rgb(34, 114, 180);
916
+ color: #FFFFFF;
917
+ border: none;
918
+ border-radius: 4px;
919
+ padding: 10px 20px;
920
+ cursor: pointer;
921
+ font-size: 16px;
922
+ font-weight: bold;
923
+ }
924
+
925
+ input[type=submit]:hover {
926
+ background-color: rgb(14, 83, 139);
927
+ }
928
+
929
+ .logo-container {
930
+ display: flex;
931
+ align-items: center;
932
+ justify-content: center;
933
+ margin-bottom: 10px;
934
+ }
935
+
936
+ .logo {
937
+ max-width: 150px;
938
+ margin-right: 10px;
939
+ }
940
+ </style>
941
+
942
+ <form action="{{ users_route }}" method="post">
943
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
944
+ <div class="logo-container">
945
+ {% autoescape false %}
946
+ {{ mlflow_logo }}
947
+ {% endautoescape %}
948
+ </div>
949
+ <label for="username">Username:</label>
950
+ <br>
951
+ <input type="text" id="username" name="username" minlength="4">
952
+ <br>
953
+ <label for="password">Password:</label>
954
+ <br>
955
+ <input type="password" id="password" name="password" minlength="12">
956
+ <br>
957
+ <br>
958
+ <input type="submit" value="Sign up">
959
+ </form>
960
+ """,
961
+ mlflow_logo=MLFLOW_LOGO,
962
+ users_route=CREATE_USER_UI,
963
+ )
964
+
965
+
966
+ @catch_mlflow_exception
967
+ def create_user_ui(csrf):
968
+ csrf.protect()
969
+ content_type = request.headers.get("Content-Type")
970
+ if content_type == "application/x-www-form-urlencoded":
971
+ username = request.form["username"]
972
+ password = request.form["password"]
973
+
974
+ if not username or not password:
975
+ message = "Username and password cannot be empty."
976
+ return make_response(message, 400)
977
+
978
+ if store.has_user(username):
979
+ flash(f"Username has already been taken: {username}")
980
+ return alert(href=SIGNUP)
981
+
982
+ store.create_user(username, password)
983
+ flash(f"Successfully signed up user: {username}")
984
+ return alert(href=HOME)
985
+ else:
986
+ message = "Invalid content type. Must be application/x-www-form-urlencoded"
987
+ return make_response(message, 400)
988
+
989
+
990
+ @catch_mlflow_exception
991
+ def create_user():
992
+ content_type = request.headers.get("Content-Type")
993
+ if content_type == "application/json":
994
+ username = _get_request_param("username")
995
+ password = _get_request_param("password")
996
+
997
+ if not username or not password:
998
+ message = "Username and password cannot be empty."
999
+ return make_response(message, 400)
1000
+
1001
+ user = store.create_user(username, password)
1002
+ return jsonify({"user": user.to_json()})
1003
+ else:
1004
+ message = "Invalid content type. Must be application/json"
1005
+ return make_response(message, 400)
1006
+
1007
+
1008
+ @catch_mlflow_exception
1009
+ def get_user():
1010
+ username = _get_request_param("username")
1011
+ user = store.get_user(username)
1012
+ return jsonify({"user": user.to_json()})
1013
+
1014
+
1015
+ @catch_mlflow_exception
1016
+ def update_user_password():
1017
+ username = _get_request_param("username")
1018
+ password = _get_request_param("password")
1019
+ store.update_user(username, password=password)
1020
+ return make_response({})
1021
+
1022
+
1023
+ @catch_mlflow_exception
1024
+ def update_user_admin():
1025
+ username = _get_request_param("username")
1026
+ is_admin = _get_request_param("is_admin")
1027
+ store.update_user(username, is_admin=is_admin)
1028
+ return make_response({})
1029
+
1030
+
1031
+ @catch_mlflow_exception
1032
+ def delete_user():
1033
+ username = _get_request_param("username")
1034
+ store.delete_user(username)
1035
+ return make_response({})
1036
+
1037
+
1038
+ @catch_mlflow_exception
1039
+ def create_experiment_permission():
1040
+ experiment_id = _get_request_param("experiment_id")
1041
+ username = _get_request_param("username")
1042
+ permission = _get_request_param("permission")
1043
+ ep = store.create_experiment_permission(experiment_id, username, permission)
1044
+ return jsonify({"experiment_permission": ep.to_json()})
1045
+
1046
+
1047
+ @catch_mlflow_exception
1048
+ def get_experiment_permission():
1049
+ experiment_id = _get_request_param("experiment_id")
1050
+ username = _get_request_param("username")
1051
+ ep = store.get_experiment_permission(experiment_id, username)
1052
+ return make_response({"experiment_permission": ep.to_json()})
1053
+
1054
+
1055
+ @catch_mlflow_exception
1056
+ def update_experiment_permission():
1057
+ experiment_id = _get_request_param("experiment_id")
1058
+ username = _get_request_param("username")
1059
+ permission = _get_request_param("permission")
1060
+ store.update_experiment_permission(experiment_id, username, permission)
1061
+ return make_response({})
1062
+
1063
+
1064
+ @catch_mlflow_exception
1065
+ def delete_experiment_permission():
1066
+ experiment_id = _get_request_param("experiment_id")
1067
+ username = _get_request_param("username")
1068
+ store.delete_experiment_permission(experiment_id, username)
1069
+ return make_response({})
1070
+
1071
+
1072
+ @catch_mlflow_exception
1073
+ def create_registered_model_permission():
1074
+ name = _get_request_param("name")
1075
+ username = _get_request_param("username")
1076
+ permission = _get_request_param("permission")
1077
+ rmp = store.create_registered_model_permission(name, username, permission)
1078
+ return make_response({"registered_model_permission": rmp.to_json()})
1079
+
1080
+
1081
+ @catch_mlflow_exception
1082
+ def get_registered_model_permission():
1083
+ name = _get_request_param("name")
1084
+ username = _get_request_param("username")
1085
+ rmp = store.get_registered_model_permission(name, username)
1086
+ return make_response({"registered_model_permission": rmp.to_json()})
1087
+
1088
+
1089
+ @catch_mlflow_exception
1090
+ def update_registered_model_permission():
1091
+ name = _get_request_param("name")
1092
+ username = _get_request_param("username")
1093
+ permission = _get_request_param("permission")
1094
+ store.update_registered_model_permission(name, username, permission)
1095
+ return make_response({})
1096
+
1097
+
1098
+ @catch_mlflow_exception
1099
+ def delete_registered_model_permission():
1100
+ name = _get_request_param("name")
1101
+ username = _get_request_param("username")
1102
+ store.delete_registered_model_permission(name, username)
1103
+ return make_response({})
1104
+
1105
+
1106
+ def create_app(app: Flask = app):
1107
+ """
1108
+ A factory to enable authentication and authorization for the MLflow server.
1109
+
1110
+ Args:
1111
+ app: The Flask app to enable authentication and authorization for.
1112
+
1113
+ Returns:
1114
+ The app with authentication and authorization enabled.
1115
+ """
1116
+ _logger.warning(
1117
+ "This feature is still experimental and may change in a future release without warning"
1118
+ )
1119
+
1120
+ # a secret key is required for flashing, and also for
1121
+ # CSRF protection. it's important that this is a static key,
1122
+ # otherwise CSRF validation won't work across workers.
1123
+ secret_key = MLFLOW_FLASK_SERVER_SECRET_KEY.get()
1124
+ if not secret_key:
1125
+ raise MlflowException(
1126
+ "A static secret key needs to be set for CSRF protection. Please set the "
1127
+ "`MLFLOW_FLASK_SERVER_SECRET_KEY` environment variable before starting the "
1128
+ "server. For example:\n\n"
1129
+ "export MLFLOW_FLASK_SERVER_SECRET_KEY='my-secret-key'\n\n"
1130
+ "If you are using multiple servers, please ensure this key is consistent between "
1131
+ "them, in order to prevent validation issues."
1132
+ )
1133
+ app.secret_key = secret_key
1134
+
1135
+ # we only need to protect the CREATE_USER_UI route, since that's
1136
+ # the only browser-accessible route. the rest are client / REST
1137
+ # APIs that do not have access to the CSRF token for validation
1138
+ app.config["WTF_CSRF_CHECK_DEFAULT"] = False
1139
+ csrf = CSRFProtect()
1140
+ csrf.init_app(app)
1141
+
1142
+ store.init_db(auth_config.database_uri)
1143
+ create_admin_user(auth_config.admin_username, auth_config.admin_password)
1144
+
1145
+ app.add_url_rule(
1146
+ rule=SIGNUP,
1147
+ view_func=signup,
1148
+ methods=["GET"],
1149
+ )
1150
+ app.add_url_rule(
1151
+ rule=CREATE_USER_UI,
1152
+ view_func=lambda: create_user_ui(csrf),
1153
+ methods=["POST"],
1154
+ )
1155
+ app.add_url_rule(
1156
+ rule=CREATE_USER,
1157
+ view_func=create_user,
1158
+ methods=["POST"],
1159
+ )
1160
+ app.add_url_rule(
1161
+ rule=GET_USER,
1162
+ view_func=get_user,
1163
+ methods=["GET"],
1164
+ )
1165
+ app.add_url_rule(
1166
+ rule=UPDATE_USER_PASSWORD,
1167
+ view_func=update_user_password,
1168
+ methods=["PATCH"],
1169
+ )
1170
+ app.add_url_rule(
1171
+ rule=UPDATE_USER_ADMIN,
1172
+ view_func=update_user_admin,
1173
+ methods=["PATCH"],
1174
+ )
1175
+ app.add_url_rule(
1176
+ rule=DELETE_USER,
1177
+ view_func=delete_user,
1178
+ methods=["DELETE"],
1179
+ )
1180
+ app.add_url_rule(
1181
+ rule=CREATE_EXPERIMENT_PERMISSION,
1182
+ view_func=create_experiment_permission,
1183
+ methods=["POST"],
1184
+ )
1185
+ app.add_url_rule(
1186
+ rule=GET_EXPERIMENT_PERMISSION,
1187
+ view_func=get_experiment_permission,
1188
+ methods=["GET"],
1189
+ )
1190
+ app.add_url_rule(
1191
+ rule=UPDATE_EXPERIMENT_PERMISSION,
1192
+ view_func=update_experiment_permission,
1193
+ methods=["PATCH"],
1194
+ )
1195
+ app.add_url_rule(
1196
+ rule=DELETE_EXPERIMENT_PERMISSION,
1197
+ view_func=delete_experiment_permission,
1198
+ methods=["DELETE"],
1199
+ )
1200
+ app.add_url_rule(
1201
+ rule=CREATE_REGISTERED_MODEL_PERMISSION,
1202
+ view_func=create_registered_model_permission,
1203
+ methods=["POST"],
1204
+ )
1205
+ app.add_url_rule(
1206
+ rule=GET_REGISTERED_MODEL_PERMISSION,
1207
+ view_func=get_registered_model_permission,
1208
+ methods=["GET"],
1209
+ )
1210
+ app.add_url_rule(
1211
+ rule=UPDATE_REGISTERED_MODEL_PERMISSION,
1212
+ view_func=update_registered_model_permission,
1213
+ methods=["PATCH"],
1214
+ )
1215
+ app.add_url_rule(
1216
+ rule=DELETE_REGISTERED_MODEL_PERMISSION,
1217
+ view_func=delete_registered_model_permission,
1218
+ methods=["DELETE"],
1219
+ )
1220
+
1221
+ app.before_request(_before_request)
1222
+ app.after_request(_after_request)
1223
+
1224
+ return app