Skip to content

WIP: Add workspace CRUD support#11

Closed
mprahl wants to merge 1 commit into
opendatahub-io:orgnization-supportfrom
mprahl:workspace-crud-api
Closed

WIP: Add workspace CRUD support#11
mprahl wants to merge 1 commit into
opendatahub-io:orgnization-supportfrom
mprahl:workspace-crud-api

Conversation

@mprahl

@mprahl mprahl commented Nov 21, 2025

Copy link
Copy Markdown
🛠 DevTools 🛠

Open in GitHub Codespaces

Install mlflow from this PR

# mlflow
pip install git+https://github.com/mlflow/mlflow.git@refs/pull/11/merge
# mlflow-skinny
pip install git+https://github.com/mlflow/mlflow.git@refs/pull/11/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/11/merge

Related Issues/PRs

#xxx

What changes are proposed in this pull request?

How is this PR tested?

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

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
  • 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)

Summary by CodeRabbit

  • New Features

    • Workspace management via MlflowClient: list/create/get/update/delete workspaces and get_workspace_uri.
    • CLI/server options to enable workspaces and set workspace store URI; env vars to control workspace behavior and URI.
    • New workspace providers: SQL-backed and REST-backed store implementations; provider registry and client for resolving workspace URIs.
    • Workspace serialization: Workspace.to_dict().
  • Tests

    • Comprehensive tests for workspace stores, registry, utils, name validation, and client behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Nov 21, 2025

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds workspace multi-tenancy: new environment variables and CLI flags, server endpoints and helpers, workspace store registry and providers (SQLAlchemy/REST), MlflowClient workspace methods, Workspace.to_dict(), name validation, utilities for workspace URI resolution, DB schema integration, and comprehensive tests.

Changes

Cohort / File(s) Summary
Configuration & CLI
mlflow/environment_variables.py, mlflow/cli/__init__.py
Add MLFLOW_ENABLE_WORKSPACES and MLFLOW_WORKSPACE_URI env vars; add --workspace-store-uri and --enable-workspaces CLI options with env binding and conditional behavior/warnings.
Server handlers & helpers
mlflow/server/handlers.py, mlflow/server/workspace_helpers.py
Add workspace HTTP handlers (list/create/get/update/delete) guarded by feature flag; add _get_workspace_store with memoization, URI resolution, and feature-flag enforcement.
Workspace store API & exports
mlflow/store/workspace/abstract_store.py, mlflow/store/workspace/__init__.py
Introduce AbstractStore interface, WorkspaceNameValidator, and package exports for workspace store components.
Workspace stores (backends)
mlflow/store/workspace/sqlalchemy_store.py, mlflow/store/workspace/rest_store.py
Implement SqlAlchemyStore (DB-backed CRUD, default-protection, sessions) and RestWorkspaceStore (HTTP CRUD mapping).
DB schema integration
mlflow/store/db/utils.py
Include workspace table (SqlWorkspace.__tablename__) in internal table-existence checks.
Client & registry
mlflow/tracking/_workspace/client.py, mlflow/tracking/_workspace/registry.py, mlflow/tracking/_workspace/utils.py, mlflow/tracking/_workspace/__init__.py
Add WorkspaceProviderClient, WorkspaceStoreRegistry (scheme-based resolution, caching, entry-point support), and workspace URI override/resolve utilities; expose package-level API.
MlflowClient surface
mlflow/tracking/client.py
Add optional workspace_uri param, get_workspace_uri, and workspace CRUD proxy methods with lazy provider initialization and explicit error mapping for unsupported URIs.
Entity serialization
mlflow/entities/workspace.py
Add Workspace.to_dict() returning name/description dict.
Utilities
mlflow/utils/workspace_utils.py
Update module docstring (constants unchanged).
Public docs
docs/api_reference/api_inventory.txt
Register new MlflowClient workspace methods and Workspace.to_dict in API inventory.
Tests
tests/store/workspace/*, tests/tracking/*
Add tests for SQLAlchemy and REST stores, workspace name validator, registry resolution (SQL/REST/unsupported), workspace utils, and MlflowClient workspace URI resolution and integration.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant MlflowClient
    participant WorkspaceProviderClient
    participant Registry as WorkspaceStoreRegistry
    participant Store as WorkspaceStore
    participant Backend

    User->>MlflowClient: list_workspaces()
    MlflowClient->>MlflowClient: resolve workspace_uri
    MlflowClient->>WorkspaceProviderClient: ensure provider (cached)
    WorkspaceProviderClient->>Registry: get_workspace_store(workspace_uri)
    Registry->>Registry: resolve scheme & cache
    alt SQL scheme
        Registry->>Store: instantiate SqlAlchemyStore
    else HTTP(s) scheme
        Registry->>Store: instantiate RestWorkspaceStore
    end
    WorkspaceProviderClient->>Store: list_workspaces()
    Store->>Backend: query or HTTP request
    Backend-->>Store: workspaces
    Store-->>WorkspaceProviderClient: Workspace objects
    WorkspaceProviderClient-->>MlflowClient: Workspace objects
    MlflowClient-->>User: return list
Loading
sequenceDiagram
    autonumber
    participant Client as MLflow Server
    participant Handler as handlers.py
    participant Helper as workspace_helpers.py
    participant Registry as WorkspaceStoreRegistry
    participant Store as WorkspaceStore

    Client->>Handler: GET /api/2.0/mlflow/workspaces
    Handler->>Handler: check feature flag
    alt disabled
        Handler-->>Client: FEATURE_DISABLED error
    else enabled
        Handler->>Helper: _get_workspace_store()
        Helper->>Registry: get_workspace_store(resolved_uri)
        Registry-->>Helper: WorkspaceStore
        Handler->>Store: list_workspaces()
        Store-->>Handler: workspaces
        Handler-->>Client: JSON response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • mlflow/tracking/_workspace/registry.py — thread-safety, caching, entry-point loading, supported-schemes mapping.
  • mlflow/store/workspace/sqlalchemy_store.py — session/transaction handling, integrity error mapping, default-workspace protection.
  • mlflow/tracking/client.py & mlflow/tracking/_workspace/client.py — workspace URI resolution precedence, lazy initialization, and clear error messages for unsupported/missing URIs.
  • mlflow/server/handlers.py & mlflow/server/workspace_helpers.py — feature-flag gating, memoization, and consistent error responses.
  • Tests that initialize or seed DB schema (ensure initialization and teardown are robust across DB backends).

Poem

🐇 I hopped through schemas, REST and SQL,
I nudged the CLI, set flags to "enable",
Names validated, registries kept stable,
Workspaces bloom where URIs lead the way,
A cheerful warren for code to play.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete. It contains only DevTools/installation instructions and unfilled template placeholders for all required sections (related issues, proposed changes, testing approach, documentation updates, release notes classification, and patch release classification). Complete the PR description by filling out all required template sections: describe proposed changes, specify testing approach (existing or new tests), indicate documentation updates, and select appropriate release notes classification.
Title check ❓ Inconclusive The PR title 'WIP: Add workspace CRUD support' is vague and generic. While it references workspace CRUD operations, it lacks specificity about the scope and primary changes, uses 'WIP' indicating work-in-progress status, and does not convey the breadth of the changeset. Replace 'WIP:' prefix and clarify the primary change. Consider: 'Add workspace CRUD API support to MlflowClient and server' or similar to better reflect the scope.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mprahl mprahl force-pushed the workspace-crud-api branch from 4796b92 to ad8dca3 Compare November 21, 2025 14:05
@mprahl

mprahl commented Nov 21, 2025

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 21, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mprahl mprahl force-pushed the workspace-crud-api branch 4 times, most recently from 17c0a79 to b9fabb9 Compare November 21, 2025 16:04
@mprahl

mprahl commented Nov 21, 2025

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 21, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mlflow/server/handlers.py (1)

31-65: Use an HTTP status code rather than databricks_pb2.FEATURE_DISABLED in the guard

The review comment is accurate. _disable_if_workspaces_disabled passes databricks_pb2.FEATURE_DISABLED (which equals 1005) directly as the HTTP status code to Response(). HTTP status codes must be in the range 100–599; 1005 is invalid and will produce a malformed HTTP response.

In contrast, the existing _disable_unless_serve_artifacts guard (lines 751–761) correctly uses 503. Additionally, the helper _workspace_not_supported (line 805) demonstrates the correct pattern by raising MlflowException(message, FEATURE_DISABLED), letting the error handler convert the Protobuf error code to a proper HTTP status.

The decorator should either:

  • Return Response(..., 503) directly, or
  • Raise MlflowException(..., error_code=FEATURE_DISABLED) to leverage the existing exception-to-HTTP-response mapping.
🧹 Nitpick comments (13)
mlflow/tracking/_workspace/client.py (1)

35-36: Potentially unnecessary list conversion.

The return type annotation list[Workspace] suggests the provider already returns a list, making list(...) conversion potentially redundant. If list_workspaces() returns an iterator or generator, this is correct; otherwise, it's unnecessary overhead.

Verify whether the provider returns a list or an iterator. If it's already a list, simplify:

-    return list(self.provider.list_workspaces())
+    return self.provider.list_workspaces()
tests/tracking/test_client_workspace.py (1)

1-52: Good focused coverage of workspace URI resolution in MlflowClient

This test cleanly validates the three key paths (env var, explicit workspace_uri, and fallback to tracking URI) by patching the internal clients and resolvers and recording constructor arguments. It’s isolated from real backends and should be robust to internal changes as long as WorkspaceProviderClient is still constructed with a workspace_uri. If you want to tighten it further, you could also assert recorded["tracking_uri"] in each scenario to guard against regressions in tracking URI resolution, but that’s optional.

mlflow/server/workspace_helpers.py (2)

15-40: Clarify singleton caching semantics for _get_workspace_store

_get_workspace_store memoizes the first resolved workspace store in _workspace_store and returns it for all subsequent calls, ignoring later workspace_uri / tracking_uri arguments. This is perfectly fine if the server is designed to use a single workspace store per process (which seems to be the intent), but it makes the parameters effectively “first-call only”. Consider documenting this in the function docstring or renaming the helper to make the singleton nature explicit, so future callers don’t assume that passing a different URI will have an effect.


15-15: Confirm Python version compatibility for PEP 604 union type hints

The signature def _get_workspace_store(workspace_uri: str | None = None, tracking_uri: str | None = None) uses the X | Y union syntax. Without from __future__ import annotations in this module, that requires Python ≥ 3.10 to import successfully. If MLflow still supports older Python versions (e.g., 3.8/3.9) in this branch, you’ll need to either add from __future__ import annotations at the top of the file or switch to Optional[str] from typing. Please confirm against the project’s supported Python versions.

mlflow/tracking/client.py (1)

45-46: Workspace client wiring and URI resolution look coherent

  • Importing Workspace and wiring WorkspaceProviderClient via workspace_utils.resolve_workspace_uri() gives a clear, consistent precedence: explicit arg → global / env → tracking URI.
  • Lazy initialization in _get_workspace_client() with a specialized UnsupportedWorkspaceStoreURIException translated to a MlflowException with FEATURE_DISABLED matches the existing registry client pattern and should produce good UX.
  • The new get_workspace_uri() helper and the CRUD methods (list_workspace*, create_*, get_*, update_*, delete_*) are thin, type-annotated pass-throughs, which is exactly what you want at this layer.

Unless you plan to treat workspaces as experimental, these additions look ready as-is. If workspaces are still considered preview, consider decorating the new public methods with @experimental for consistency, but that’s optional and policy-driven.

Please confirm whether workspace APIs are meant to be GA or should be annotated as experimental to match other new surfaces (e.g., datasets, prompts).

Also applies to: 110-113, 203-208, 211-237, 242-244, 250-257, 295-339

mlflow/tracking/_workspace/registry.py (1)

18-29: Workspace store registry is solid; consider aligning error code with other Unsupported exceptions*

  • The registry design (entry-point group, default DB/REST registrations, LRU-cached get_store with a build lock) is consistent with existing MLflow store registries and looks correct.
  • UnsupportedWorkspaceStoreURIException provides a clear message and exposes supported_uri_schemes, which is useful for callers like MlflowClient._get_workspace_client().

One small consistency suggestion: other “unsupported store URI” paths (e.g., model registry) typically use FEATURE_DISABLED as the error code. Here the custom exception uses INVALID_PARAMETER_VALUE, while callers re-wrap to FEATURE_DISABLED. If you expect external callers to catch UnsupportedWorkspaceStoreURIException directly, you might want to switch its error_code to FEATURE_DISABLED to match the rest of the API surface; otherwise, current behavior is acceptable.

Also applies to: 31-63, 66-79, 82-92, 94-112

mlflow/cli/__init__.py (1)

23-28: Workspace server flags and envvar wiring are consistent; minor UX edge case

  • Importing MLFLOW_ENABLE_WORKSPACES / MLFLOW_WORKSPACE_URI and exposing --enable-workspaces / --workspace-store-uri via their .name attributes matches the existing experiment CLI patterns.
  • The server() logic correctly:
    • Forces MLFLOW_ENABLE_WORKSPACES=true when workspaces are enabled, and
    • Propagates workspace_store_uri into MLFLOW_WORKSPACE_URI only when workspaces are enabled, otherwise warning and ignoring it.

One UX edge case: when MLFLOW_WORKSPACE_URI is set only via environment variable (and MLFLOW_ENABLE_WORKSPACES is left false), workspace_store_uri will still be populated by click and you’ll emit the “Ignoring --workspace-store-uri” message even though the user didn’t pass the flag explicitly. If that’s undesirable, you could check ctx.get_parameter_source("workspace_store_uri") to distinguish CLI vs envvar and adjust the message accordingly, but behavior is otherwise sound.

Also applies to: 466-483, 506-508, 553-563

mlflow/tracking/_workspace/utils.py (1)

5-18: Tighten resolve_workspace_uri truthiness checks to match type hints

The override/envvar handling looks good and the precedence is clear. One small refinement: resolve_workspace_uri() currently uses truthiness checks:

  • if workspace_uri:
  • if configured_uri:

Given the annotated types are str | None, it’s safer and less surprising to treat an empty string as an explicit (albeit likely invalid) value rather than falling through to the tracking URI. You can switch to explicit is not None checks:

 def resolve_workspace_uri(
     workspace_uri: str | None = None, tracking_uri: str | None = None
 ) -> str | None:
@@
-    if workspace_uri:
+    if workspace_uri is not None:
         return workspace_uri
@@
-    configured_uri = get_workspace_uri()
-    if configured_uri:
-        return configured_uri
+    configured_uri = get_workspace_uri()
+    if configured_uri is not None:
+        return configured_uri

This better matches the type hints and avoids subtle fallback behavior if an empty string is ever passed in.

Also applies to: 21-29, 31-52

mlflow/store/workspace/rest_store.py (1)

12-98: REST workspace store behavior looks consistent; note 201/empty-body fallback semantics

The overall implementation (list/get/create/update/delete/get_default) is consistent with other REST stores and correctly uses http_request + verify_rest_response, URL-escapes workspace names, and tolerates empty JSON bodies by fabricating a Workspace from the request data on 201 responses.

Only behavioral nuance to be aware of: for create_workspace and update_workspace, if the server returns a 2xx with an empty body, the store will fall back to the client-side Workspace values instead of failing. That seems reasonable, but if the REST provider later adds extra server-populated fields, they won’t be surfaced in that edge case.

tests/store/workspace/test_sqlalchemy_store.py (1)

13-160: Good coverage and realistic expectations for SQLAlchemy workspace store

The fixture and tests exercise the full CRUD surface plus default handling and resolve_artifact_root:

  • Fixture correctly initializes full MLflow schema, seeds the default workspace, and disposes both engines.
  • Tests validate both the error messages and error_code values (RESOURCE_DOES_NOT_EXIST, RESOURCE_ALREADY_EXISTS, INVALID_STATE), which tightly couples to SqlAlchemyStore behavior.
  • test_resolve_artifact_root_returns_default explicitly codifies that resolve_artifact_root ignores workspace_name and just returns (default_root, True); this is an intentional contract you’ll want to remember if you later add per-workspace artifact roots.

Looks solid overall.

mlflow/store/workspace/abstract_store.py (1)

12-129: Abstract interface and name validator are well-structured; consider DRY’ing validation

The AbstractStore API surface is clear, and the default resolve_artifact_root plus get_default_workspace contract is straightforward and aligns with the SQL/REST implementations.

WorkspaceNameValidator enforces length, pattern, and reserved names cleanly and exposes both is_valid and validate. There is some duplication between the two (the same checks are reimplemented), so if this grows further it may be worth centralizing the logic (e.g., have is_valid call a shared helper and validate wrap that with error messages). Not urgent as-is.

mlflow/server/handlers.py (1)

808-877: Workspace CRUD handlers are correct; consider minor polish on validation and responses

The workspace handlers follow existing patterns:

  • All are wrapped in @catch_mlflow_exception and gated by _disable_if_workspaces_disabled, so store-level MlflowExceptions get serialized consistently.
  • _create_workspace_handler:
    • Validates presence of "name" and runs WorkspaceNameValidator.validate(name).
    • Correctly maps NotImplementedError from the store to a FEATURE_DISABLED-coded MlflowException via _workspace_not_supported.
    • Returns 201 with a JSON body of workspace.to_dict().
  • _update_workspace_handler:
    • Rejects empty payloads and unknown keys.
    • Enforces that only "description" is allowed and safely accesses payload["description"] after that check.
  • _delete_workspace_handler and _get_workspace_handler simply delegate to the store, with NotImplementedError from delete mapped to feature-disabled errors.

Two optional tweaks you might consider:

  • Apply WorkspaceNameValidator.validate(workspace_name) in the path-parameter handlers (_get_workspace_handler, _update_workspace_handler, _delete_workspace_handler) for symmetry with create, unless you expect to support legacy names that don’t pass the validator.
  • For create, optionally add a Location header pointing to /api/2.0/mlflow/workspaces/<workspace_name> (and its AJAX variant), which can simplify client code.
mlflow/store/workspace/sqlalchemy_store.py (1)

24-100: SQL workspace store implementation is solid; import path difference is the only thing to double-check

The SqlAlchemyStore implementation looks correct overall:

  • Engine/session management:
    • Uses create_sqlalchemy_engine_with_retry and a managed session maker from db_utils, consistent with other MLflow SQL stores.
  • CRUD semantics:
    • list_workspaces orders by name and converts ORM rows via to_mlflow_entity().
    • get_workspace and _get_workspace centralize not-found behavior and raise MlflowException("Workspace '<name>' not found", RESOURCE_DOES_NOT_EXIST), matching the tests.
    • create_workspace catches IntegrityError and maps it to RESOURCE_ALREADY_EXISTS with a clear message.
    • update_workspace only mutates description, flushes, logs, and returns the updated entity.
    • delete_workspace correctly rejects deletion of DEFAULT_WORKSPACE_NAME with INVALID_STATE before touching the DB.
    • get_default_workspace and resolve_artifact_root line up with the tests’ expectations.

The only thing worth double-checking is the import:

from mlflow.store.workspace.dbmodels import SqlWorkspace

whereas tests import SqlWorkspace from mlflow.store.workspace.dbmodels.models. If dbmodels/__init__.py does not re-export SqlWorkspace, this will raise an ImportError. If it does, then everything is fine, but it’d be good to keep the import style consistent across modules.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b68933 and b9fabb9.

📒 Files selected for processing (22)
  • docs/api_reference/api_inventory.txt (6 hunks)
  • mlflow/cli/__init__.py (4 hunks)
  • mlflow/entities/workspace.py (1 hunks)
  • mlflow/environment_variables.py (2 hunks)
  • mlflow/server/handlers.py (7 hunks)
  • mlflow/server/workspace_helpers.py (1 hunks)
  • mlflow/store/db/utils.py (2 hunks)
  • mlflow/store/workspace/__init__.py (1 hunks)
  • mlflow/store/workspace/abstract_store.py (1 hunks)
  • mlflow/store/workspace/rest_store.py (1 hunks)
  • mlflow/store/workspace/sqlalchemy_store.py (1 hunks)
  • mlflow/tracking/_workspace/__init__.py (1 hunks)
  • mlflow/tracking/_workspace/client.py (1 hunks)
  • mlflow/tracking/_workspace/registry.py (1 hunks)
  • mlflow/tracking/_workspace/utils.py (1 hunks)
  • mlflow/tracking/client.py (5 hunks)
  • mlflow/utils/workspace_utils.py (1 hunks)
  • tests/store/workspace/test_sqlalchemy_store.py (1 hunks)
  • tests/store/workspace/test_workspace_validator.py (1 hunks)
  • tests/tracking/test_client_workspace.py (1 hunks)
  • tests/tracking/test_workspace_registry.py (1 hunks)
  • tests/tracking/test_workspace_utils.py (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: windows (1)
  • GitHub Check: windows (4)
  • GitHub Check: windows (3)
  • GitHub Check: pyfunc (3)
  • GitHub Check: pyfunc (1)
  • GitHub Check: pyfunc (2)
  • GitHub Check: windows (2)
  • GitHub Check: pyfunc (4)
  • GitHub Check: evaluate (2)
  • GitHub Check: evaluate (1)
  • GitHub Check: build (dev)
  • GitHub Check: genai
  • GitHub Check: build
  • GitHub Check: python (2)
  • GitHub Check: python (1)
  • GitHub Check: python (3)
  • GitHub Check: database
  • GitHub Check: models (2)
  • GitHub Check: models (1)
  • GitHub Check: python-skinny
  • GitHub Check: flavors
  • GitHub Check: java
🔇 Additional comments (12)
docs/api_reference/api_inventory.txt (1)

43-43: LGTM!

The API inventory correctly documents the new workspace management methods on MlflowClient and the Workspace.to_dict serialization method. The additions are consistent with the broader workspace CRUD feature.

Also applies to: 65-65, 89-90, 99-99, 154-154, 538-538

mlflow/utils/workspace_utils.py (1)

1-1: LGTM!

The updated docstring "Workspace-related utility constants" is clearer and more accurate than the previous description.

mlflow/store/db/utils.py (1)

68-68: LGTM!

The workspace table integration follows the established pattern for schema validation. The import and table name addition properly extend the canonical schema to include workspace support.

Also applies to: 109-109

mlflow/entities/workspace.py (1)

15-16: LGTM!

The to_dict method provides straightforward serialization of the Workspace entity. The return type correctly reflects that description can be None.

tests/store/workspace/test_workspace_validator.py (1)

7-41: LGTM!

The test coverage for WorkspaceNameValidator is comprehensive, covering:

  • Valid cases: length boundaries, allowed patterns, numeric names
  • Invalid cases: length violations, reserved words, case sensitivity, invalid patterns
  • Both is_valid and validate code paths

The parameterized approach makes the tests clear and maintainable.

tests/tracking/test_workspace_registry.py (1)

14-40: LGTM!

The workspace store registry tests provide good coverage:

  • Proper test isolation via cache-clearing fixture
  • SQLAlchemy, REST, and error cases are validated
  • Resource cleanup with store._engine.dispose() prevents test pollution
tests/tracking/test_workspace_utils.py (1)

7-38: LGTM!

The workspace URI resolution tests are well-structured:

  • Proper isolation with autouse fixture clearing both state and environment
  • Complete coverage of the resolution precedence chain
  • Clean use of monkeypatch for environment variable testing
mlflow/environment_variables.py (2)

109-112: Workspace URI environment variable is correctly introduced

MLFLOW_WORKSPACE_URI follows existing env-var patterns (public name, str type, None default) and the docstring matches the expected “fallback to tracking URI” behavior implemented in the resolver utilities. No changes needed.


442-445: Feature flag for workspaces is consistent with usage

MLFLOW_ENABLE_WORKSPACES is defined as a boolean env var with a sensible default (False) and is used to gate server-side workspace APIs (see mlflow.server.workspace_helpers._get_workspace_store). This aligns with the PR’s multi‑tenancy feature-flagging goals.

mlflow/store/workspace/__init__.py (1)

1-11: Workspace store facade re-exports look correct

The re-exports cleanly surface Workspace, AbstractStore, and RestWorkspaceStore, and __all__ matches the imported symbols. This is a good, minimal public API aggregation point.

mlflow/tracking/_workspace/__init__.py (1)

1-19: Workspace tracking API aggregation is consistent and minimal

The module re-exports the key workspace tracking primitives (WorkspaceProviderClient, registry, store getter, and URI helpers) and __all__ matches them exactly. This keeps callers from depending on deeper module paths and looks like the right public surface.

mlflow/server/handlers.py (1)

880-892: Endpoint registration for workspace routes is consistent with existing routing

_workspace_endpoints correctly:

  • Uses _get_paths("/mlflow/workspaces") and _get_paths("/mlflow/workspaces/<workspace_name>") so both REST (/api/2.0/...) and AJAX (/ajax-api/...) paths are registered.
  • Associates:
    • GET/POST with the collection path, and
    • GET/PATCH/DELETE with the item path.

And get_endpoints simply appends _workspace_endpoints() to the existing service endpoint lists, matching the pattern used elsewhere in this module.

This wiring looks correct and should make the new workspace APIs available through both REST and AJAX routes without interfering with protobuf-derived endpoints.

Also applies to: 3939-3946

Comment thread mlflow/tracking/_workspace/client.py
@mprahl mprahl force-pushed the workspace-crud-api branch 5 times, most recently from 65bd3d6 to 93b4ac7 Compare November 21, 2025 18:59
@mprahl

mprahl commented Nov 21, 2025

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 21, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mprahl mprahl force-pushed the workspace-crud-api branch 3 times, most recently from f38013a to 70edd1d Compare November 21, 2025 19:40
- Introduce the workspace store abstraction (SQLAlchemy + REST), name validation, and registry/URI utilities with CLI flags and env vars.
- Expose workspace CRUD endpoints in the Flask server with proper guards/feature flag wiring plus MlflowClient surface methods.
- Add the Workspace entity, tests for the stores/registry/client, and docs entries so the new multi-tenant provider can be exercised end-to-end.

Signed-off-by: mprahl <mprahl@users.noreply.github.com>
@mprahl mprahl force-pushed the workspace-crud-api branch from 70edd1d to 3d850ff Compare November 21, 2025 20:42
@mprahl mprahl closed this Nov 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant