Skip to content
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
40f9e05
init
safoinme Sep 27, 2025
cd81c4e
mypy
safoinme Sep 27, 2025
b420555
update sorting behavious
safoinme Sep 27, 2025
2014113
fix display order sorting
safoinme Sep 27, 2025
3376266
fixes
safoinme Sep 27, 2025
7139581
few more fixes
safoinme Sep 27, 2025
2ca3897
fix endpoint
safoinme Sep 27, 2025
1718b58
fix
safoinme Sep 29, 2025
4bc726a
docstring
safoinme Sep 29, 2025
e907e4f
Merge branch 'develop' into feature/deployment-custom-visualizations
safoinme Sep 29, 2025
74002e7
add migrations file
safoinme Sep 29, 2025
43677a0
mypy
safoinme Sep 29, 2025
f05e1ee
Update visualization parameters to disable metadata and resources
safoinme Oct 5, 2025
6c09a15
Merge branch 'develop' into feature/deployment-custom-visualizations
safoinme Oct 5, 2025
291c536
curated visualizations
safoinme Oct 9, 2025
c25ba31
Merge branch 'develop' into feature/deployment-custom-visualizations
safoinme Oct 9, 2025
4d3f259
more fixes
safoinme Oct 11, 2025
98eb22c
Merge branch 'develop' into feature/deployment-custom-visualizations
safoinme Oct 11, 2025
b4ae2d3
review fix and adding size
safoinme Oct 12, 2025
7f78e36
revert deloyment
safoinme Oct 13, 2025
2fc0553
add other schema support
safoinme Oct 13, 2025
f17f849
Rename 'size' to 'layout_size' in visualizations
safoinme Oct 13, 2025
2928bc0
Update src/zenml/models/v2/core/curated_visualization.py
safoinme Oct 14, 2025
f83b35e
Update src/zenml/models/v2/core/curated_visualization.py
safoinme Oct 14, 2025
318f5f5
renaming artifact_vertsion_id
safoinme Oct 14, 2025
347ac87
format
safoinme Oct 14, 2025
15b26c4
fix enum
safoinme Oct 14, 2025
5ba7c9e
apply stefan reviews
safoinme Oct 14, 2025
dae9fa5
delete migration outdated file
safoinme Oct 15, 2025
4166124
update migration file
safoinme Oct 15, 2025
978a1b0
docstring
safoinme Oct 15, 2025
22192c9
docstring
safoinme Oct 15, 2025
7d53da9
Update src/zenml/zen_stores/schemas/curated_visualization_schemas.py
safoinme Oct 15, 2025
e718e43
Update src/zenml/zen_stores/schemas/curated_visualization_schemas.py
safoinme Oct 15, 2025
176a838
Update src/zenml/models/v2/core/curated_visualization.py
safoinme Oct 15, 2025
6c7906b
Update src/zenml/models/v2/core/curated_visualization.py
safoinme Oct 15, 2025
b8ddcf9
Update src/zenml/zen_stores/schemas/pipeline_run_schemas.py
safoinme Oct 15, 2025
4e77acd
Merge branch 'feature/deployment-custom-visualizations' of https://gi…
safoinme Oct 15, 2025
efc8eff
final round of review
safoinme Oct 15, 2025
b4e7b44
docstring
safoinme Oct 15, 2025
2ded931
Merge branch 'develop' into feature/deployment-custom-visualizations
safoinme Oct 15, 2025
24f920e
update migration
safoinme Oct 15, 2025
18fee07
Update src/zenml/zen_server/routers/curated_visualization_endpoints.py
safoinme Oct 16, 2025
ba3d5fb
Update src/zenml/zen_server/routers/curated_visualization_endpoints.py
safoinme Oct 16, 2025
960f354
Update src/zenml/zen_server/routers/curated_visualization_endpoints.py
safoinme Oct 16, 2025
c58b0b2
Update migration file description for visualizations
safoinme Oct 17, 2025
e827520
Merge branch 'develop' into feature/deployment-custom-visualizations
safoinme Oct 17, 2025
cfd35b0
renaming of visualisation
safoinme Oct 17, 2025
a41ffa1
migration
safoinme Oct 17, 2025
4a958d6
add model rebuild
safoinme Oct 17, 2025
edde4b6
update tests
safoinme Oct 19, 2025
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
154 changes: 153 additions & 1 deletion docs/book/how-to/artifacts/visualizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,158 @@ There are three ways how you can add custom visualizations to the dashboard:
* If you are already handling HTML, Markdown, CSV or JSON data in one of your steps, you can have them visualized in just a few lines of code by casting them to a [special class](#visualization-via-special-return-types) inside your step.
* If you want to automatically extract visualizations for all artifacts of a certain data type, you can define type-specific visualization logic by [building a custom materializer](#visualization-via-materializers).

### Curated Visualizations Across Resources

Curated visualizations let you surface a specific artifact visualization across multiple ZenML resources. Each curated visualization links to exactly one resource—for example, a model performance report that appears on the model detail page, or a deployment health dashboard that shows up in the deployment view.

Curated visualizations currently support the following resources:

- **Projects** – high-level dashboards and KPIs that summarize the state of a project.
- **Deployments** – monitoring pages for deployed pipelines.
- **Models** – evaluation dashboards and health views for registered models.
- **Pipelines** – reusable visual documentation attached to pipeline definitions.
- **Pipeline Runs** – detailed diagnostics for specific executions.
- **Pipeline Snapshots** – configuration/version comparisons for snapshot history.

You can create a curated visualization programmatically by linking an artifact visualization to a single resource. The example below shows how to create separate visualizations for different resource types:

```python
from uuid import UUID

from zenml.client import Client
from zenml.enums import (
CuratedVisualizationSize,
VisualizationResourceTypes,
)
from zenml.models import CuratedVisualizationResource

client = Client()
artifact_version_id = UUID("<ARTIFACT_VERSION_ID>")
project = client.active_project
pipeline = client.list_pipelines().items[0]
pipeline_run = pipeline.runs()[0]
snapshot = pipeline_run.snapshot()
deployment = client.list_deployments().items[0]
model = client.list_models().items[0]

# Create a visualization for the model
model_viz = client.create_curated_visualization(
artifact_version_id=artifact_version_id,
visualization_index=0,
resource=CuratedVisualizationResource(
id=model.id,
type=VisualizationResourceTypes.MODEL
),
display_name="Model performance dashboard",
layout_size=CuratedVisualizationSize.FULL_WIDTH,
)

# Create a visualization for the deployment
deployment_viz = client.create_curated_visualization(
artifact_version_id=artifact_version_id,
visualization_index=0,
resource=CuratedVisualizationResource(
id=deployment.id,
type=VisualizationResourceTypes.DEPLOYMENT
),
display_name="Deployment health dashboard",
layout_size=CuratedVisualizationSize.HALF_WIDTH,
)

# Create a visualization for the project
project_viz = client.create_curated_visualization(
artifact_version_id=artifact_version_id,
visualization_index=0,
resource=CuratedVisualizationResource(
id=project.id,
type=VisualizationResourceTypes.PROJECT
),
display_name="Project overview dashboard",
layout_size=CuratedVisualizationSize.FULL_WIDTH,
)
```

After creation, the returned response includes the visualization ID. You can retrieve a specific visualization later with `Client.get_curated_visualization`:

```python
retrieved = client.get_curated_visualization(model_viz.id, hydrate=True)
print(retrieved.display_name)
print(retrieved.resource.type)
print(retrieved.resource.id)
```

Curated visualizations are tied to their parent resources and automatically surface in the ZenML dashboard wherever those resources appear, so keep track of the IDs returned by `create_curated_visualization` if you need to reference them later.

#### Updating curated visualizations

Once you've created a curated visualization, you can update its display name, order, or tile size using `Client.update_curated_visualization`:

```python
from uuid import UUID

client.update_curated_visualization(
visualization_id=UUID("<CURATED_VISUALIZATION_ID>"),
display_name="Updated Dashboard Title",
display_order=10,
layout_size=CuratedVisualizationSize.HALF_WIDTH,
)
```

When a visualization is no longer relevant, you can remove it entirely:

```python
client.delete_curated_visualization(visualization_id=UUID("<CURATED_VISUALIZATION_ID>"))
```

#### Controlling display order and size

The optional `display_order` field determines how visualizations are sorted when displayed. Visualizations with lower order values appear first, while those with `None` (the default) appear at the end in creation order.

When setting display orders, consider leaving gaps between values (e.g., 10, 20, 30 instead of 1, 2, 3) to make it easier to insert new visualizations later without renumbering everything:

```python
# Leave gaps for future insertions
visualization_a = client.create_curated_visualization(
artifact_version_id=artifact_version_id,
visualization_index=0,
resource=CuratedVisualizationResource(
id=model.id,
type=VisualizationResourceTypes.MODEL
),
display_order=10, # Primary dashboard
layout_size=CuratedVisualizationSize.FULL_WIDTH,
)

visualization_b = client.create_curated_visualization(
artifact_version_id=artifact_version_id,
visualization_index=1,
resource=CuratedVisualizationResource(
id=model.id,
type=VisualizationResourceTypes.MODEL
),
display_order=20, # Secondary metrics
layout_size=CuratedVisualizationSize.HALF_WIDTH, # Compact chart beside the primary tile
)

# Later, easily insert between them
visualization_c = client.create_curated_visualization(
artifact_version_id=artifact_version_id,
visualization_index=2,
resource=CuratedVisualizationResource(
id=model.id,
type=VisualizationResourceTypes.MODEL
),
display_order=15, # Now appears between A and B
layout_size=CuratedVisualizationSize.HALF_WIDTH,
)
```

#### RBAC visibility

Curated visualizations respect the access permissions of the resource they're linked to. A user can only see a curated visualization if they have read access to the specific resource it targets. If a user lacks permission for the linked resource, the visualization will be hidden from their view.

For example, if you create a visualization linked to a specific deployment, only users with read access to that deployment will see the visualization. If you need the same visualization to appear in different contexts with different access controls (e.g., on both a project page and a deployment page), create separate curated visualizations for each resource. This ensures that visualizations never inadvertently expose information from resources a user shouldn't access, while giving you fine-grained control over visibility.

### Visualization via Special Return Types

If you already have HTML, Markdown, CSV or JSON data available as a string inside your step, you can simply cast them to one of the following types and return them from your step:
Expand Down Expand Up @@ -257,4 +409,4 @@ steps:

Visualizing artifacts is a powerful way to gain insights from your ML pipelines. ZenML's built-in visualization capabilities make it easy to understand your data and model outputs, identify issues, and communicate results.

By leveraging these visualization tools, you can better understand your ML workflows, debug problems more effectively, and make more informed decisions about your models.
By leveraging these visualization tools, you can better understand your ML workflows, debug problems more effectively, and make more informed decisions about your models.
101 changes: 101 additions & 0 deletions src/zenml/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
from zenml.enums import (
ArtifactType,
ColorVariants,
CuratedVisualizationSize,
DeploymentStatus,
LogicalOperators,
ModelStages,
Expand Down Expand Up @@ -108,6 +109,10 @@
ComponentRequest,
ComponentResponse,
ComponentUpdate,
CuratedVisualizationRequest,
CuratedVisualizationResource,
CuratedVisualizationResponse,
CuratedVisualizationUpdate,
DeploymentFilter,
DeploymentResponse,
EventSourceFilter,
Expand Down Expand Up @@ -3739,6 +3744,102 @@ def get_deployment(
hydrate=hydrate,
)

def create_curated_visualization(
self,
artifact_version_id: UUID,
visualization_index: int,
*,
resource: CuratedVisualizationResource,
project_id: Optional[UUID] = None,
display_name: Optional[str] = None,
display_order: Optional[int] = None,
layout_size: CuratedVisualizationSize = CuratedVisualizationSize.FULL_WIDTH,
) -> CuratedVisualizationResponse:
"""Create a curated visualization associated with a resource.

Curated visualizations can be attached to any of the following
ZenML resource types to provide contextual dashboards throughout the ML
lifecycle:

- **Deployments** (VisualizationResourceTypes.DEPLOYMENT): Surface on
deployment monitoring dashboards
- **Pipelines** (VisualizationResourceTypes.PIPELINE): Associate with
pipeline definitions
- **Pipeline Runs** (VisualizationResourceTypes.PIPELINE_RUN): Attach to
specific execution runs
- **Pipeline Snapshots** (VisualizationResourceTypes.PIPELINE_SNAPSHOT):
Link to captured pipeline configurations

Each visualization is linked to exactly one resource.

Args:
artifact_version_id: The ID of the artifact version containing the visualization.
visualization_index: The index of the visualization within the artifact version.
resource: The resource to associate with the visualization.
Should be a `CuratedVisualizationResource` containing
the resource ID and type (e.g., DEPLOYMENT, PIPELINE, PIPELINE_RUN,
PIPELINE_SNAPSHOT).
project_id: The ID of the project to associate with the visualization.
display_name: The display name of the visualization.
display_order: The display order of the visualization.
layout_size: The layout size of the visualization in the dashboard.

Returns:
The created curated visualization.

Raises:
ValueError: If resource is not provided.
"""
request = CuratedVisualizationRequest(
project=project_id or self.active_project.id,
artifact_version_id=artifact_version_id,
visualization_index=visualization_index,
display_name=display_name,
display_order=display_order,
layout_size=layout_size,
resource=resource,
)
return self.zen_store.create_curated_visualization(request)

def update_curated_visualization(
self,
visualization_id: UUID,
*,
display_name: Optional[str] = None,
display_order: Optional[int] = None,
layout_size: Optional[CuratedVisualizationSize] = None,
) -> CuratedVisualizationResponse:
"""Update display metadata for a curated visualization.

Args:
visualization_id: The ID of the curated visualization to update.
display_name: New display name for the visualization.
display_order: New display order for the visualization.
layout_size: Updated layout size for the visualization.

Returns:
The updated deployment visualization.
"""
update_model = CuratedVisualizationUpdate(
display_name=display_name,
display_order=display_order,
layout_size=layout_size,
)
return self.zen_store.update_curated_visualization(
visualization_id=visualization_id,
visualization_update=update_model,
)

def delete_curated_visualization(self, visualization_id: UUID) -> None:
"""Delete a curated visualization.

Args:
visualization_id: The ID of the curated visualization to delete.
"""
self.zen_store.delete_curated_visualization(
visualization_id=visualization_id
)

def list_deployments(
self,
sort_by: str = "created",
Expand Down
1 change: 1 addition & 0 deletions src/zenml/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ def handle_int_env_var(var: str, default: int = 0) -> int:
PIPELINE_CONFIGURATION = "/pipeline-configuration"
PIPELINE_DEPLOYMENTS = "/pipeline_deployments"
DEPLOYMENTS = "/deployments"
CURATED_VISUALIZATIONS = "/curated_visualizations"
PIPELINE_SNAPSHOTS = "/pipeline_snapshots"
PIPELINES = "/pipelines"
PIPELINE_SPEC = "/pipeline-spec"
Expand Down
35 changes: 35 additions & 0 deletions src/zenml/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,41 @@ class MetadataResourceTypes(StrEnum):
SCHEDULE = "schedule"


class VisualizationResourceTypes(StrEnum):
"""Resource types that support curated visualizations.

Curated visualizations can be attached to these ZenML resources to provide
contextual dashboards and visual insights throughout the ML lifecycle:

- **DEPLOYMENT**: Server-side pipeline deployments - surface visualizations
on deployment monitoring dashboards and status pages
- **MODEL**: ZenML model entities - surface model evaluation dashboards and
performance summaries directly on the model detail pages
- **PIPELINE**: Pipeline definitions - associate visualizations with pipeline
configurations for reusable visual documentation
- **PIPELINE_RUN**: Pipeline execution runs - attach visualizations to specific
run results for detailed analysis and debugging
- **PIPELINE_SNAPSHOT**: Pipeline snapshots - link visualizations to captured
pipeline configurations for version comparison and historical analysis
- **PROJECT**: Project-level overviews - provide high-level project dashboards
and KPI visualizations for cross-pipeline insights
"""

DEPLOYMENT = "deployment" # Server-side pipeline deployments
MODEL = "model" # ZenML models
PIPELINE = "pipeline" # Pipeline definitions
PIPELINE_RUN = "pipeline_run" # Execution runs
PIPELINE_SNAPSHOT = "pipeline_snapshot" # Snapshot configurations
PROJECT = "project" # Project-level dashboards


class CuratedVisualizationSize(StrEnum):
"""Layout size options for curated visualizations."""

FULL_WIDTH = "full_width"
HALF_WIDTH = "half_width"


class SecretResourceTypes(StrEnum):
"""All possible resource types for adding secrets."""

Expand Down
18 changes: 18 additions & 0 deletions src/zenml/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@
DeploymentResponseMetadata,
DeploymentResponseResources,
)
from zenml.models.v2.core.curated_visualization import (
CuratedVisualizationRequest,
CuratedVisualizationResponse,
CuratedVisualizationResponseBody,
CuratedVisualizationResponseMetadata,
CuratedVisualizationResponseResources,
CuratedVisualizationUpdate,
)
from zenml.models.v2.core.device import (
OAuthDeviceUpdate,
OAuthDeviceFilter,
Expand Down Expand Up @@ -422,6 +430,9 @@
RunMetadataEntry,
RunMetadataResource,
)
from zenml.models.v2.misc.curated_visualization import (
CuratedVisualizationResource,
)
from zenml.models.v2.misc.server_models import (
ServerModel,
ServerDatabaseType,
Expand Down Expand Up @@ -653,6 +664,12 @@
"DeploymentResponseBody",
"DeploymentResponseMetadata",
"DeploymentResponseResources",
"CuratedVisualizationRequest",
"CuratedVisualizationResponse",
"CuratedVisualizationResponseBody",
"CuratedVisualizationResponseMetadata",
"CuratedVisualizationResponseResources",
"CuratedVisualizationUpdate",
"EventSourceFlavorResponse",
"EventSourceFlavorResponseBody",
"EventSourceFlavorResponseMetadata",
Expand Down Expand Up @@ -871,6 +888,7 @@
"ResourcesInfo",
"RunMetadataEntry",
"RunMetadataResource",
"CuratedVisualizationResource",
"ProjectStatistics",
"PipelineRunDAG",
"ExceptionInfo",
Expand Down
Loading
Loading