Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions functions/src/mlflow_utils/function.yaml
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
kind: serving
verbose: false
metadata:
categories:
- model-serving
- utilities
name: mlflow-utils
tag: ''
spec:
command: ''
source: ''
default_class: MLFlowModelServer
image: mlrun/mlrun
function_kind: serving_v2
disable_auto_mount: false
max_replicas: 4
min_replicas: 1
function_handler: mlflow-utils-nuclio:handler
build:
functionSourceCode: aW1wb3J0IHppcGZpbGUKZnJvbSB0eXBpbmcgaW1wb3J0IEFueSwgRGljdAppbXBvcnQgbWxmbG93CmZyb20gbWxydW4uc2VydmluZy52Ml9zZXJ2aW5nIGltcG9ydCBWMk1vZGVsU2VydmVyCmltcG9ydCBwYW5kYXMgYXMgcGQKCgpjbGFzcyBNTEZsb3dNb2RlbFNlcnZlcihWMk1vZGVsU2VydmVyKToKICAgICIiIgogICAgTUxGbG93IHRyYWNrZXIgTW9kZWwgc2VydmluZyBjbGFzcywgaW5oZXJpdGluZyB0aGUgVjJNb2RlbFNlcnZlciBjbGFzcyBmb3IgYmVpbmcgaW5pdGlhbGl6ZWQgYXV0b21hdGljYWxseSBieSB0aGUgbW9kZWwKICAgIHNlcnZlciBhbmQgYmUgYWJsZSB0byBydW4gbG9jYWxseSBhcyBwYXJ0IG9mIGEgbnVjbGlvIHNlcnZlcmxlc3MgZnVuY3Rpb24sIG9yIGFzIHBhcnQgb2YgYSByZWFsLXRpbWUgcGlwZWxpbmUuCiAgICAiIiIKCiAgICBkZWYgbG9hZChzZWxmKToKICAgICAgICAiIiIKICAgICAgICBsb2FkcyBhIG1vZGVsIHRoYXQgd2FzIGxvZ2dlZCBieSB0aGUgTUxGbG93IHRyYWNrZXIgbW9kZWwKICAgICAgICAiIiIKICAgICAgICAjIFVuemlwIHRoZSBtb2RlbCBkaXIgYW5kIHRoZW4gdXNlIG1sZmxvdydzIGxvYWQgZnVuY3Rpb24KICAgICAgICBtb2RlbF9maWxlLCBfID0gc2VsZi5nZXRfbW9kZWwoIi56aXAiKQogICAgICAgIG1vZGVsX3BhdGhfdW56aXAgPSBtb2RlbF9maWxlLnJlcGxhY2UoIi56aXAiLCAiIikKCiAgICAgICAgd2l0aCB6aXBmaWxlLlppcEZpbGUobW9kZWxfZmlsZSwgInIiKSBhcyB6aXBfcmVmOgogICAgICAgICAgICB6aXBfcmVmLmV4dHJhY3RhbGwobW9kZWxfcGF0aF91bnppcCkKCiAgICAgICAgc2VsZi5tb2RlbCA9IG1sZmxvdy5weWZ1bmMubG9hZF9tb2RlbChtb2RlbF9wYXRoX3VuemlwKQoKICAgIGRlZiBwcmVkaWN0KHNlbGYsIHJlcXVlc3Q6IERpY3Rbc3RyLCBBbnldKSAtPiBsaXN0OgogICAgICAgICIiIgogICAgICAgIEluZmVyIHRoZSBpbnB1dHMgdGhyb3VnaCB0aGUgbW9kZWwuIFRoZSBpbmZlcnJlZCBkYXRhIHdpbGwKICAgICAgICBiZSByZWFkIGZyb20gdGhlICJpbnB1dHMiIGtleSBvZiB0aGUgcmVxdWVzdC4KCiAgICAgICAgOnBhcmFtIHJlcXVlc3Q6IFRoZSByZXF1ZXN0IHRvIHRoZSBtb2RlbCB1c2luZyB4Z2Jvb3N0J3MgcHJlZGljdC4KICAgICAgICAgICAgICAgIFRoZSBpbnB1dCB0byB0aGUgbW9kZWwgd2lsbCBiZSByZWFkIGZyb20gdGhlICJpbnB1dHMiIGtleS4KCiAgICAgICAgOnJldHVybjogVGhlIG1vZGVsJ3MgcHJlZGljdGlvbiBvbiB0aGUgZ2l2ZW4gaW5wdXQuCiAgICAgICAgIiIiCgogICAgICAgICMgR2V0IHRoZSBpbnB1dHMgYW5kIHNldCB0byBhY2NlcHRlZCB0eXBlOgogICAgICAgIGlucHV0cyA9IHBkLkRhdGFGcmFtZShyZXF1ZXN0WyJpbnB1dHMiXSkKCiAgICAgICAgIyBQcmVkaWN0IHVzaW5nIHRoZSBtb2RlbCdzIHByZWRpY3QgZnVuY3Rpb246CiAgICAgICAgcHJlZGljdGlvbnMgPSBzZWxmLm1vZGVsLnByZWRpY3QoaW5wdXRzKQoKICAgICAgICAjIFJldHVybiBhcyBsaXN0OgogICAgICAgIHJldHVybiBwcmVkaWN0aW9ucy50b2xpc3QoKQoKZnJvbSBtbHJ1bi5ydW50aW1lcyBpbXBvcnQgbnVjbGlvX2luaXRfaG9vawpkZWYgaW5pdF9jb250ZXh0KGNvbnRleHQpOgogICAgbnVjbGlvX2luaXRfaG9vayhjb250ZXh0LCBnbG9iYWxzKCksICdzZXJ2aW5nX3YyJykKCmRlZiBoYW5kbGVyKGNvbnRleHQsIGV2ZW50KToKICAgIHJldHVybiBjb250ZXh0Lm1scnVuX2hhbmRsZXIoY29udGV4dCwgZXZlbnQpCg==
requirements:
- mlflow==2.12.2
- lightgbm
- xgboost
code_origin: ''
- mlflow~=3.5
origin_filename: ''
image: mlrun/mlrun
code_origin: ''
description: Mlflow model server, and additional utils.
command: ''
base_image_pull: false
default_class: MLFlowModelServer
source: ''
default_handler: ''
max_replicas: 4
disable_auto_mount: false
min_replicas: 1
description: Mlflow model server, and additional utils.
function_handler: mlflow-utils-nuclio:handler
env:
- name: MLRUN_HTTPDB__NUCLIO__EXPLICIT_ACK
value: enabled
metadata:
categories:
- model-serving
- utilities
name: mlflow-utils
tag: ''
kind: serving
8 changes: 3 additions & 5 deletions functions/src/mlflow_utils/item.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ labels:
author: Iguazio
maintainers: []
marketplaceType: ''
mlrunVersion: 1.8.0
mlrunVersion: 1.10.0
name: mlflow_utils
platformVersion: ''
spec:
Expand All @@ -23,8 +23,6 @@ spec:
image: mlrun/mlrun
kind: serving
requirements:
- mlflow~=2.22
- lightgbm
- xgboost
- mlflow~=3.5
url: ''
version: 1.1.0
version: 1.2.0
2 changes: 1 addition & 1 deletion functions/src/mlflow_utils/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mlflow~=2.22
mlflow~=3.5
lightgbm
xgboost
38 changes: 33 additions & 5 deletions functions/src/mlflow_utils/test_mlflow_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
#
import tempfile

import shutil
import lightgbm as lgb
import mlflow
import mlflow.environment_variables
Expand Down Expand Up @@ -132,7 +132,9 @@ def test_track_run_with_experiment_name(handler):
# Set the mlflow experiment name
mlflow.environment_variables.MLFLOW_EXPERIMENT_NAME.set(f"{handler}_test_track")
with tempfile.TemporaryDirectory() as test_directory:
mlflow.set_tracking_uri(test_directory) # Tell mlflow where to save logged data
# Use SQLite backend instead of filesystem (filesystem will be deprecated in Feb 2026)
db_uri = f"sqlite:///{os.path.join(test_directory, 'mlflow.db')}"
mlflow.set_tracking_uri(db_uri) # Tell mlflow where to save logged data

# Create a project for this tester:
project = mlrun.get_or_create_project(name="default", context=test_directory)
Expand All @@ -149,17 +151,43 @@ def test_track_run_with_experiment_name(handler):
trainer_run = func.run(
local=True,
handler=handler,
artifact_path=test_directory,
output_path=test_directory,
)

# Find the MLflow logged model and prepare it for serving
# Note: In MLflow 2.24+, we must dynamically discover model paths since MLflow changed
# its directory structure from predictable paths (e.g., experiment_name/0/model/) to
# UUID-based paths (e.g., experiment_id/run_uuid/artifacts/model/).

# Create MLflow client to query the tracking server
mlflow_client = mlflow.tracking.MlflowClient(tracking_uri=db_uri)

# Get the experiment by name to obtain its ID
experiment = mlflow_client.get_experiment_by_name(f"{handler}_test_track")

# Search for runs in this experiment and get the run ID
# (There should only be one run from our training above)
run_id = mlflow_client.search_runs(experiment_ids=[experiment.experiment_id])[0].info.run_id

# Find all models logged in this run
logged_models = mlflow.search_logged_models(filter_string=f"source_run_id = '{run_id}'")

# Extract the artifact location and remove the "file://" prefix
model_artifacts_dir = logged_models["artifact_location"].tolist()[0].replace("file://", "")

# Package the model artifacts as a zip file for MLFlowModelServer
# Note: MLFlowModelServer requires models to be packaged as zip archives
# rather than loose directories for deployment
model_path = os.path.join(test_directory, f"{handler}-model-serving")
os.makedirs(model_path, exist_ok=True)
shutil.make_archive(os.path.join(model_path, "model"), 'zip', model_artifacts_dir)

serving_func = project.set_function(
func=os.path.abspath("function.yaml"),
name=f"{handler}-server",
)
model_name = f"{handler}-model"
# Add the model
upper_handler = handler.replace("_", "-")
model_path = test_directory + f"/{upper_handler}-test-{upper_handler}/0/model/"
serving_func.add_model(
model_name,
class_name="MLFlowModelServer",
Expand Down