Store gateway<>scorer binding correctly#20176
Conversation
Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com>
🛠 DevTools 🛠
Install mlflow from this PRFor Databricks, use the following command: |
| class GatewayResourceType(str, Enum): | ||
| """Valid MLflow resource types that can use gateway endpoints.""" | ||
|
|
||
| SCORER_JOB = "scorer_job" |
There was a problem hiding this comment.
renamed scorer_job to scorer since endpoint id is linked to a registered scorer instead of each scorer execution job
There was a problem hiding this comment.
Pull request overview
This PR corrects the resource type terminology for gateway-scorer bindings from "scorer_job" to "scorer" to better reflect the actual relationship being tracked. The change updates the enum value, tests, documentation, and UI labels across Python, TypeScript/JavaScript, and Java codebases. Additionally, new integration tests verify that endpoint bindings are correctly created when scorers are registered with gateway endpoints and properly cleaned up when scorers are deleted.
Changes:
- Updated
GatewayResourceTypeenum fromSCORER_JOB = "scorer_job"toSCORER = "scorer" - Added endpoint binding creation logic in
register_scorer()and cleanup logic indelete_scorer() - Updated all tests and UI components to use the new "scorer" resource type terminology
- Added comprehensive integration tests for endpoint binding lifecycle
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| mlflow/entities/gateway_endpoint.py | Changed enum value from SCORER_JOB to SCORER |
| mlflow/store/tracking/sqlalchemy_store.py | Added binding creation/deletion logic and imported GatewayResourceType |
| mlflow/store/tracking/gateway/*.py | Updated documentation examples from "scorer_job" to "scorer" |
| tests/genai/scorers/test_scorer_CRUD.py | Added new integration tests for endpoint binding creation and deletion |
| tests/tracking/test_rest_tracking.py | Updated all test assertions to use new resource type |
| tests/store/tracking/test_rest_store.py | Updated mock responses and test data to use "scorer" |
| tests/store/tracking/test_gateway_sql_store.py | Updated all binding tests to use "scorer" |
| tests/entities/test_gateway_endpoint.py | Updated binding tests and enum assertions |
| mlflow/server/js/src/gateway/types.ts | Changed ResourceType from 'scorer_job' to 'scorer' |
| mlflow/server/js/src/gateway/components/*.tsx | Updated UI labels from "Scorer Job" to "Scorer" |
| mlflow/server/js/src/gateway/hooks/*.test.tsx | Updated test fixtures to use 'scorer' |
| mlflow/protos/service.proto | Updated documentation comments to use "scorer" example |
| mlflow/java/client/src/main/java/org/mlflow/api/proto/Service.java | Updated generated Java code with new documentation |
| mlflow/store/db_migrations/alembic.ini | Changed sqlalchemy.url from empty to hardcoded SQLite path |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Verify the endpoint exists in the database before creating binding | ||
| if endpoint_id is not None: | ||
| endpoint_exists = ( | ||
| session.query(SqlGatewayEndpoint) | ||
| .filter(SqlGatewayEndpoint.endpoint_id == endpoint_id) | ||
| .first() | ||
| ) | ||
| if endpoint_exists is not None: | ||
| # Delete any existing binding for this scorer (in case of re-registration) | ||
| session.query(SqlGatewayEndpointBinding).filter( | ||
| SqlGatewayEndpointBinding.resource_type == GatewayResourceType.SCORER.value, | ||
| SqlGatewayEndpointBinding.resource_id == name, | ||
| ).delete() | ||
|
|
||
| binding = SqlGatewayEndpointBinding( | ||
| endpoint_id=endpoint_id, | ||
| resource_type=GatewayResourceType.SCORER.value, | ||
| resource_id=name, | ||
| created_at=get_current_time_millis(), | ||
| last_updated_at=get_current_time_millis(), | ||
| ) | ||
| session.add(binding) | ||
|
|
There was a problem hiding this comment.
The endpoint existence check on line 2185-2189 is redundant since the endpoint was already retrieved on line 2134 via get_gateway_endpoint, which would have raised an exception if the endpoint didn't exist. This additional database query is unnecessary and could be removed to improve performance. If the intent is to handle the case where the endpoint was deleted between line 2134 and this point, the session transaction would prevent that scenario.
| # Verify the endpoint exists in the database before creating binding | |
| if endpoint_id is not None: | |
| endpoint_exists = ( | |
| session.query(SqlGatewayEndpoint) | |
| .filter(SqlGatewayEndpoint.endpoint_id == endpoint_id) | |
| .first() | |
| ) | |
| if endpoint_exists is not None: | |
| # Delete any existing binding for this scorer (in case of re-registration) | |
| session.query(SqlGatewayEndpointBinding).filter( | |
| SqlGatewayEndpointBinding.resource_type == GatewayResourceType.SCORER.value, | |
| SqlGatewayEndpointBinding.resource_id == name, | |
| ).delete() | |
| binding = SqlGatewayEndpointBinding( | |
| endpoint_id=endpoint_id, | |
| resource_type=GatewayResourceType.SCORER.value, | |
| resource_id=name, | |
| created_at=get_current_time_millis(), | |
| last_updated_at=get_current_time_millis(), | |
| ) | |
| session.add(binding) | |
| if endpoint_id is not None: | |
| # Delete any existing binding for this scorer (in case of re-registration) | |
| session.query(SqlGatewayEndpointBinding).filter( | |
| SqlGatewayEndpointBinding.resource_type == GatewayResourceType.SCORER.value, | |
| SqlGatewayEndpointBinding.resource_id == name, | |
| ).delete() | |
| binding = SqlGatewayEndpointBinding( | |
| endpoint_id=endpoint_id, | |
| resource_type=GatewayResourceType.SCORER.value, | |
| resource_id=name, | |
| created_at=get_current_time_millis(), | |
| last_updated_at=get_current_time_millis(), | |
| ) | |
| session.add(binding) |
|
Documentation preview for c84b2ab is available at: More info
|
Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com>
006958f to
25d640d
Compare
- Use scorer_id instead of scorer name as resource_id for endpoint bindings to ensure globally unique identification across experiments - Add display_name field to endpoint bindings for showing human-readable scorer names in the UI (instead of UUIDs) - Update frontend to display display_name when available, falling back to resource_id for backwards compatibility 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com>
| if endpoint_exists is not None: | ||
| # Delete any existing binding for this scorer (in case of re-registration) | ||
| # Use scorer_id for globally unique identification across experiments | ||
| session.query(SqlGatewayEndpointBinding).filter( | ||
| SqlGatewayEndpointBinding.resource_type == GatewayResourceType.SCORER.value, | ||
| SqlGatewayEndpointBinding.resource_id == scorer.scorer_id, | ||
| ).delete() |
There was a problem hiding this comment.
Why do we need this? The scorer can only be registered once right?
There was a problem hiding this comment.
Scorers can have multiple versions and this is needed to clean up the binding of older versions.
serena-ruan
left a comment
There was a problem hiding this comment.
LGTM once https://github.com/mlflow/mlflow/pull/20176/files#r2715910365 is resolved
- Use scorer_id instead of scorer name as resource_id for endpoint bindings to ensure globally unique identification across experiments - Add display_name field to endpoint bindings for showing human-readable scorer names in the UI (instead of UUIDs) - Update frontend to display display_name when available, falling back to resource_id for backwards compatibility 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com>
…flow into fix/scorer/gateway-binding
…flow into fix/scorer/gateway-binding
…flow into fix/scorer/gateway-binding
Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
Related Issues/PRs
n/a
What changes are proposed in this pull request?
This PR makes sure that the binding record is created between gateway and scorer when a gateway endpoint is used by a registered scorer.
How is this PR tested?
Does this PR require documentation update?
Release Notes
Is this a user-facing change?
What component(s), interfaces, languages, and integrations does this PR affect?
Components
area/tracking: Tracking Service, tracking client APIs, autologgingarea/models: MLmodel format, model serialization/deserialization, flavorsarea/model-registry: Model Registry service, APIs, and the fluent client calls for Model Registryarea/scoring: MLflow Model server, model deployment tools, Spark UDFsarea/evaluation: MLflow model evaluation features, evaluation metrics, and evaluation workflowsarea/gateway: MLflow AI Gateway client APIs, server, and third-party integrationsarea/prompts: MLflow prompt engineering features, prompt templates, and prompt managementarea/tracing: MLflow Tracing features, tracing APIs, and LLM tracing functionalityarea/projects: MLproject format, project running backendsarea/uiux: Front-end, user experience, plotting, JavaScript, JavaScript dev serverarea/build: Build and test infrastructure for MLflowarea/docs: MLflow documentation pagesHow 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" sectionrn/breaking-change- The PR will be mentioned in the "Breaking Changes" sectionrn/feature- A new user-facing feature worth mentioning in the release notesrn/bug-fix- A user-facing bug fix worth mentioning in the release notesrn/documentation- A user-facing documentation change worth mentioning in the release notesShould this PR be included in the next patch release?
Yesshould be selected for bug fixes, documentation updates, and other small changes.Noshould 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?
Bug fixes, doc updates and new features usually go into minor releases.
Bug fixes and doc updates usually go into patch releases.