Skip to content

Support mlflow.genai.to_predict_fn for app invocation endpoints#19779

Merged
jennsun merged 15 commits intomlflow:masterfrom
jennsun:ML-57538-app-to-predict-fn
Jan 15, 2026
Merged

Support mlflow.genai.to_predict_fn for app invocation endpoints#19779
jennsun merged 15 commits intomlflow:masterfrom
jennsun:ML-57538-app-to-predict-fn

Conversation

@jennsun
Copy link
Collaborator

@jennsun jennsun commented Jan 6, 2026

🛠 DevTools 🛠

Open in GitHub Codespaces

Install mlflow from this PR

# mlflow
pip install git+https://github.com/mlflow/mlflow.git@refs/pull/19779/merge
# mlflow-skinny
pip install git+https://github.com/mlflow/mlflow.git@refs/pull/19779/merge#subdirectory=libs/skinny

For Databricks, use the following command:

%sh curl -LsSf https://raw.githubusercontent.com/mlflow/mlflow/HEAD/dev/install-skinny.sh | sh -s pull/19779/merge

Related Issues/PRs

#xxx

What changes are proposed in this pull request?

This PR updates genai.to_predict_fn (https://mlflow.org/docs/latest/api_reference/python_api/mlflow.genai.html#mlflow.genai.to_predict_fn) to support calls to app invocation endpoints

Where a user can create a predict function by using

predict_fn = to_predict_fn(f"apps:/{APP_NAME}")

adding an additional schema to the existing group (used to only support model serving endpoints with schema endpoints)

Under the hood, an oauth token for the databricks workspace will be used to send a post request to the app's invocation endpoint to be passed in as the prediction function for mlflow e2e eval flow

How is this PR tested?

  • Existing unit/integration tests
  • New unit/integration tests
  • Manual tests

Tested notebook with mlflow version of this PR [NOTE: app invocations requires a fix for notebook oauth tokens]:
https://eng-ml-agent-platform.staging.cloud.databricks.com/editor/notebooks/2722550516530718?o=2850744067564480
image

Tested locally:
image

With mlflow evaluation framework, verifying traces are visible in experiments:

image image image

Does this PR require documentation update?

  • No. You can skip the rest of this section.
  • Yes. I've updated:
    • Examples
    • API references
    • Instructions

Release Notes

Is this a user-facing change?

  • No. You can skip the rest of this section.
  • Yes. Give a description of this change to be included in the release notes for MLflow users.

What component(s), interfaces, languages, and integrations does this PR affect?

Components

  • area/tracking: Tracking Service, tracking client APIs, autologging
  • area/models: MLmodel format, model serialization/deserialization, flavors
  • area/model-registry: Model Registry service, APIs, and the fluent client calls for Model Registry
  • area/scoring: MLflow Model server, model deployment tools, Spark UDFs
  • area/evaluation: MLflow model evaluation features, evaluation metrics, and evaluation workflows
  • area/gateway: MLflow AI Gateway client APIs, server, and third-party integrations
  • area/prompts: MLflow prompt engineering features, prompt templates, and prompt management
  • area/tracing: MLflow Tracing features, tracing APIs, and LLM tracing functionality
  • area/projects: MLproject format, project running backends
  • area/uiux: Front-end, user experience, plotting, JavaScript, JavaScript dev server
  • area/build: Build and test infrastructure for MLflow
  • area/docs: MLflow documentation pages

How should the PR be classified in the release notes? Choose one:

  • rn/none - No description will be included. The PR will be mentioned only by the PR number in the "Small Bugfixes and Documentation Updates" section
  • rn/breaking-change - The PR will be mentioned in the "Breaking Changes" section
  • [ X ] rn/feature - A new user-facing feature worth mentioning in the release notes
  • rn/bug-fix - A user-facing bug fix worth mentioning in the release notes
  • rn/documentation - A user-facing documentation change worth mentioning in the release notes

Should this PR be included in the next patch release?

Yes should be selected for bug fixes, documentation updates, and other small changes. No should be selected for new features and larger changes. If you're unsure about the release classification of this PR, leave this unchecked to let the maintainers decide.

What is a minor/patch release?
  • Minor release: a release that increments the second part of the version number (e.g., 1.2.0 -> 1.3.0).
    Bug fixes, doc updates and new features usually go into minor releases.
  • Patch release: a release that increments the third part of the version number (e.g., 1.2.0 -> 1.2.1).
    Bug fixes and doc updates usually go into patch releases.
  • Yes (this PR will be cherry-picked and included in the next patch release)
  • No (this PR will be included in the next minor release)

@jennsun jennsun changed the title [draft] apps to_predict_fn Support mlflow.genai.to_predict_fn for app invocation endpoints Jan 7, 2026
@jennsun jennsun marked this pull request as ready for review January 7, 2026 02:08
@github-actions github-actions bot added the rn/feature Mention under Features in Changelogs. label Jan 7, 2026
@jennsun jennsun marked this pull request as draft January 7, 2026 02:08
@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2026

Documentation preview for 6e11bbc is available at:

More info
  • Ignore this comment if this PR does not change the documentation.
  • The preview is updated when a new commit is pushed to this PR.
  • This comment was created by this workflow run.
  • The documentation was built by this workflow run.

@jennsun jennsun marked this pull request as ready for review January 7, 2026 17:18
@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2026

@jennsun Thank you for the contribution! Could you fix the following issue(s)?

⚠ DCO check

The DCO check failed. Please sign off your commit(s) by following the instructions here. See https://github.com/mlflow/mlflow/blob/master/CONTRIBUTING.md#sign-your-work for more details.

@jennsun jennsun force-pushed the ML-57538-app-to-predict-fn branch from 7297367 to a108b51 Compare January 7, 2026 17:22
@bbqiu bbqiu self-requested a review January 7, 2026 19:15

schema, path = _parse_model_uri(endpoint_uri)

if schema == "apps":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we separate this function into 2 helper functions?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, let's use case match for schema and route to different functions

mock_get_experiment_id.assert_called_once()


# ========== Databricks Apps Tests ==========
Copy link
Collaborator

@bbqiu bbqiu Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the manual test, can we do a whole e2e flow for eval?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


if schema == "apps":
try:
config = get_databricks_workspace_client_config("databricks", scopes=["all-apis"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we leave a comment here describing why this will be okay?

Copy link
Collaborator

@bbqiu bbqiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great work! left a few comments

you might have to rebase and sign all of your commits btw

in the future you can avoid by running git commit -sm .. to sign each commit

@jennsun jennsun force-pushed the ML-57538-app-to-predict-fn branch from a108b51 to 9c9dfb2 Compare January 9, 2026 00:37
Signed-off-by: Jenny <jenny.sun@databricks.com>
@jennsun jennsun force-pushed the ML-57538-app-to-predict-fn branch from 9c9dfb2 to a2a3319 Compare January 9, 2026 00:38
@jennsun
Copy link
Collaborator Author

jennsun commented Jan 9, 2026

Note: upon further testing with notebook oauth it looks like the notebook oauth tokens are failing to query the invocations endpoint for apps in notebook environment due to oauth2proxy failures:

[2026/01/09 01:22:38] [databricks_apps.go:536] ActorID not found in session user, will attempt to get from current user API
[2026/01/09 01:22:38] [jwt_session.go:52] Error retrieving session from token in Authorization header: [unable to verify bearer token, could not check roles: error getting current user: {"error_description":"Invalid certificate confirmation","error":"access_denied"} [ReqId: 12713ec0-415b-4568-9564-a1c6dfbe4e74]]
[2026/01/09 01:22:38] [logger.go:777] [req=5d1377e7-cd0e-4a04-8c36-77fc1ed94c5e] No valid authentication in request. Initiating login.

This happens because the notebook oauth tokens contain a CNF/X.509 Fingerprint (SHA-256) claim which binds the token to a specific client certificate (intended for notebook usage only). oauth2proxy is not presenting the right certificate when making API calls, so when it tries to use this token without presenting the certificate, the request is rejected and the endpoint redirects to sign-in page.

@jennsun jennsun requested a review from serena-ruan January 11, 2026 16:28

schema, path = _parse_model_uri(endpoint_uri)

if schema == "apps":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, let's use case match for schema and route to different functions

Signed-off-by: Jenny <jenny.sun@databricks.com>
@jennsun jennsun requested a review from serena-ruan January 12, 2026 23:13
Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jenny.sun@databricks.com>
Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>
Signed-off-by: Jenny <jennysunnjm@gmail.com>
Comment on lines +847 to +853
except TypeError as e:
if "scopes" in str(e):
raise MlflowException.invalid_parameter_value(
"The 'scopes' parameter requires databricks-sdk>=0.74.0. "
"Please upgrade with: pip install --upgrade databricks-sdk",
) from e
raise
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I didn't make it clear in previous comment, could we check databricks sdk version instead of capturing this error message, which is not reliable? It may capture other errors with scopes but not related to version.
Let's create a function like:

def databricks_workspace_client_supports_scopes:
    if Version(databricks.sdk.xxx) < Version("0.74.0"):
        raise ...

Then we can reuse this check here and line382 in models/evaluation/base.py

Copy link
Collaborator

@serena-ruan serena-ruan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM! BTW we pushed RC to tomorrow, so you can address the comment before then :)

Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jenny.sun@databricks.com>
Raises:
MlflowException: If databricks-sdk version is < 0.74.0
"""
from packaging.version import Version
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this to top

from databricks.sdk import WorkspaceClient

if scopes is not None:
check_databricks_sdk_supports_scopes()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use kwargs = {"scopes": scopes} here, and pass below to avoid breaking old databricks-sdk

Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jenny.sun@databricks.com>

# Append /invocations to endpoint
return DatabricksAppConfig(
app_url=f"{app.url}/invocations",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we change this to be app_invocation_url?


def check_databricks_sdk_supports_scopes():
"""
Check if the installed databricks-sdk version supports the 'scopes' parameter.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: specify param for workspaceclient

Returns:
A predict function that invokes the endpoint
"""
from mlflow.deployments import get_deploy_client
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: move imports up

Copy link
Collaborator

@bbqiu bbqiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jenny.sun@databricks.com>
@jennsun jennsun added this pull request to the merge queue Jan 15, 2026
Merged via the queue into mlflow:master with commit 7d256d2 Jan 15, 2026
48 of 50 checks passed
@jennsun jennsun deleted the ML-57538-app-to-predict-fn branch January 15, 2026 02:20
ridgupta26 pushed a commit to ridgupta26/mlflow-ridz that referenced this pull request Jan 29, 2026
…ow#19779)

Signed-off-by: Jenny <jenny.sun@databricks.com>
Signed-off-by: Jenny <jennysunnjm@gmail.com>
Co-authored-by: Serena Ruan <82044803+serena-ruan@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rn/feature Mention under Features in Changelogs. v3.9.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants