Drift Detection for Edge And Multi-cloud Model Deployments
Table of Contents
Edge Model Drift Detection with Model Insights via the Wallaroo Dashboard
Model Drift Detection with Model Insights via the Wallaroo Dashboard allows organizations to create assays that monitor inference requests and results against an established baseline.
The following focuses on model drift detection for model deployment on edge locations. For full details on managing assays and model drift detection through the Wallaroo Dashboard, see Model Drift Detection with Model Insights.
Prerequisites
To monitor inference requests and results on edge locations, the following prerequisites must be met:
- Edge Observability Enabled: Edge observability must be enabled in the Wallaroo Ops instance.
- Deployed Edge Locations: Edge locations are added to a published pipeline and deployed on edge devices. For full details, see Wallaroo SDK Essentials Guide: Pipeline Edge Publication.
Access the Model Insights Page
Assays created through the Wallaroo Dashboard are accessed through the Pipeline Dashboard through the following process.
- Log into the Wallaroo Dashboard.
- Select the workspace containing the pipeline with the models being monitored from the Change Current Workspace and Workspace Management drop down.
- Select View Pipelines.
- Select the pipeline containing the models being monitored.
- Select Insights.
The Wallaroo Assay Dashboard contains the following elements. For more details of each configuration type, see the Model Insights and Assays Introduction.
IMPORTANT NOTE
Assays are only displayed in the version they were created in. For new installations of Wallaroo 2024.4, Wallaroo Assays default to V2. If Wallaroo is configured for Assays V1, then only assays created under Assay V1 will be displayed. If Wallaroo is configured for Assays V2, only assays created for Assays V2 are displayed.
Pipeline Name and Identifier (A): The pipeline’s assigned name and unique identifier in UUID format.
Filter Edges (B): By default, all locations are displayed. Filter Edges provides a list of available edge deployments are displayed. Selecting one or more filters from the list limits the available metrics and logs displayed to only the selected locations.

Status (C): The status of the pipeline. The status only applies to the pipeline’s status in the Wallaroo Ops instance.. Options are:
- Active: The pipeline is deployed.
- Inactive: The pipeline is not deployed.
Tags (D): Any tags applied to the pipeline.
Inference Urls (E): The internal and external inference URLs for the deployed pipeline in the Wallaroo Ops instance.
+ Create Assay (F): Create a new assay.
Deploy/Undeploy the Pipeline (G): Deploy an inactive pipeline, or deploy an active pipeline in the Wallaroo Ops instance.
Filter Assays (H): Filter the assays by Name or Status, and Sort.
- Status filters are:
- All Status: All statuses.
- Active: Active assays.
- Paused: Assays that are paused.
- Drift Detected: Assays where model drift is detected.
- Sort filters are:
- Creation Date
- Last Assay Run
- Status filters are:
Assay Details View (I): A chart of the assay analyses. This is filtered by the selected dates and time ranges.
Activity (J): Comments left by users.
Assay Details View

The following details are visible by selecting the Assay View Details icon:
- (A) Assay Name: The name of the assay displayed.
- (B) Input / Output: The input or output and the index of the element being monitored.
- (C) Baseline: The time period used to generate the baseline. For baselines generated from a file, the baseline displayed
Uploaded File. - (D) Last Run: The date and time the assay was last run.
- (E) Next Run: The future date and time the assay will be run again. NOTE: If the assay is paused, then it will not run at the scheduled time. When unpaused, the date will be updated to the next date and time that the assay will be run.
- (F) Aggregation Type: The aggregation type used with the assay.
- (G) Threshold: The threshold value used for the assay.
- (H) Metric: The metric type used for the assay.
- (I) Number of Bins: The number of bins used for the assay.
- (J) Bin Weight: The weight applied to each bin.
- (K) Bin Mode: The type of bin node applied to each bin.
View Assay Alert Details
To view details on an assay alert:
- Select the data with available alert data.
- Mouse hover of a specific Assay Event Alert to view the data and time of the event and the alert value.
- Select the Assay Event Alert to view the Baseline and Window details of the alert including the left_outlier and right_outlier.
Hover over a bar chart graph to view additional details.
- Select the ⊗ symbol to exit the Assay Event Alert details and return to the Assay View.
Filter By Locations
Edge deployments are filtered with the Filter Edges (B) option.

By default, all locations are displayed. Filter Edges provides a list of available edge deployments are displayed. Selecting one or more filters from the list limits the available metrics and logs displayed to only the selected locations.

Build a Edge Location Filtered Assay Through the Pipeline Dashboard
To create a new assay through the Wallaroo Pipeline Dashboard:
- Log into the Wallaroo Dashboard.
- Select the workspace containing the pipeline with the models being monitored from the Change Current Workspace and Workspace Management drop down.
- Select View Pipelines.
- Select the pipeline containing the models being monitored.
- Select Insights.
- Select Filter Edges and select the edge locations to include. By default, assays include all locations. By specifying edges in the filter, this restricts the inference inputs used for the assay baseline and assay analysis results to the specified locations.
- Select +Create Assay.
- On the Create Assay module, enter the following:
On the Assay Name section, enter the following:

Assay Name (A): The name of the new assay.
Monitor output data or Monitor input data (B): Select whether to monitor input or output data.
Select an output/input to monitor (C): Select the input or output to monitor.
- Named Field: The name of the field to monitor.
- Index: The index of the monitored field.
On the Specify Baseline section, select one of the following options:
- (D) Select the data to use for the baseline. This can either be set with a preset recent time period (last 30 seconds, last 60 seconds, etc) or with a custom date range.

(E) Upload an assay baseline file as either a CSV or TXT file. These assay baselines must be a list of numpy (aka float) values that are comma and newline separated, terminating at the last record with no additional commas or returns.

For example:
684577.200, 921561.500, 705013.440, 725875.900, 684577.200, 379398.300, 266405.600, 256630.310
Once selected, a preview graph of the baseline values will be displayed (C). Note that this may take a few seconds to generate.
Select Next to continue.
- On the Settings Module:
Set the date and time range to view values generated by the assay. This can either be set with a preset recent time period (last 30 seconds, last 60 seconds, etc) or with a custom date range.
New assays are configured to run a new analysis for every 24 hours starting at the end of the baseline period. For information on how to adjust the scheduling period and other settings for the assay scheduling window, see the SDK section on how to Schedule Assay.
Set the following Advanced Settings.

- (A) Preview Date Range: The date and times to for the preview chart.
- (B) Preview: A preview of the assay results will be displayed based on the settings below.
- (C) Scheduling: Set the Frequency (Daily, Every Minute, Hourly, Weekly, Default: Daily) and the Time (increments of one hour Default: 1:00 AM).
- (D) Group Results: How the results are grouped: Daily (Default), Every Minute, Weekly, or Monthly.
- (E) Aggregation Type: Density or Cumulative.
- (F) Threshold:
- Default: 0.1
- (G) Metric:
- Default: Population Stability Index
- Maximum Difference of Bins
- Sum of the Difference of Bins
- (H) Number of Bins: From 5 to 14. Default: 5
- (F) Bin Mode:
- Equally Spaced
- Default: Quantile
- (I) Bin Weights: The bin weights:
- Equally Weighted (Default)
- Custom: Users can assign their own bin weights as required.
Review the preview chart to verify the settings are correct.
Select Build to complete the process and build the new assay.
Once created, it may take a few minutes for the assay to complete compiling data.
Model Insights via the Wallaroo SDK
Assays generated through the Wallaroo SDK can be previewed, configured, and uploaded to the Wallaroo Ops instance. The following is a condensed version of this process. For full details see the Wallaroo SDK Essentials Guide: Assays Management guide.
Model drift detection with assays using the Wallaroo SDK follows this general process.
- Define the Baseline: From either historical inference data for a specific model in a pipeline, or from a pre-determine array of data, a baseline is formed.
- Assay Preview: Once the baseline is formed, we preview the assay and configure the different options until we have the the best method of detecting environment or model drift.
- Create Assay: With the previews and configuration complete, we upload the assay. The assay will perform an analysis on a regular scheduled based on the configuration.
- Get Assay Results: Retrieve the analyses and use them to detect model drift and possible sources.
- Pause/Resume Assay: Pause or restart an assay as needed.
Define the Baseline
Assay baselines are defined with the wallaroo.client.build_assay method. Through this process we define the baseline from either a range of dates or pre-generated values.
wallaroo.client.build_assay take the following parameters:
| Parameter | Type | Description |
|---|---|---|
| assay_name | String (Required) - required | The name of the assay. Assay names must be unique across the Wallaroo instance. |
| pipeline | wallaroo.pipeline.Pipeline (Required) | The pipeline the assay is monitoring. |
| model_name | String (Optional) / None | The name of the model to monitor. This field should only be used to track the inputs/outputs for a specific model step in a pipeline. If no model_name is to be included, then the parameters must be passed a named parameters not positional ones. |
| iopath | String (Required) | The iopath of the assay in the format "input/output {field_name} {field_index}", where field_index is required for list values and omitted for scalar values. For example, to monitor the output list field house_price at index 0, the iopath is output house_price 0. For scalar and numpy nan support, these data types are only supported in Wallaroo Assay Version V2.For data that are scalar values, no index is passed. For example, the iopath for the input scalar field number_of_houses as an input is 'input number_of_houses'. For more details of scalar and nan support, see Iopath Format and NaN Support. |
| baseline_start | datetime.datetime (Optional) | The start time for the inferences to use as the baseline. Must be included with baseline_end. Cannot be included with baseline_data. |
| baseline_end | datetime.datetime (Optional) | The end time of the baseline window. the baseline. Windows start immediately after the baseline window and are run at regular intervals continuously until the assay is deactivated or deleted. Must be included with baseline_start. Cannot be included with baseline_data.. |
| baseline_data | numpy.array (Optional) | The baseline data in numpy array format. Cannot be included with either baseline_start or baseline_data. |
Note that model_name is an optional parameters when parameters are named. For example:
assay_builder_from_dates = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
or:
assay_builder_from_dates = wl.build_assay("assays from date baseline",
mainpipeline,
None, ## since we are using positional parameters, `None` must be included for the model parameter
"output variable 0",
assay_baseline_start,
assay_baseline_end)
Baselines are created in one of two mutually exclusive methods:
- Date Range: The
baseline_startandbaseline_endretrieves the inference requests and results for the pipeline from the start and end period. This data is summarized and used to create the baseline. For our examples, we’re using the variablesassay_baseline_startandassay_baseline_endto represent a range of dates, withassay_baseline_startbeing set beforeassay_baseline_end. - Numpy Values: The
baseline_datasets the baseline from a provided numpy array. This allows assay baselines to be created without first performing inferences in Wallaroo.
Iopath Format
The Input/Output Path (iopath) uses the following format.
- Input/Output: Whether the data is input for model, or is model’s output.
- Field: The specific field to monitor.
- Index: The field index to monitor.
- For Assays V2 only, scalar values are represented by passing no index.
The following shows setting the iopath via the Wallaroo SDK for List values and scalars.
| List | Scalar (Assays V2 only) |
|---|---|
| |
- List: Supported by Wallaroo Assays V1 and V2 and it determined by.
- Scalar:
NaN Support
For List(numpy.ndarray) data that includes nan aka “Not a Number” (NaN), nan values are treated in Wallaroo assays as follows:
- Visualizations:
nanvalues are not discretely represented in visualizations. These include methods including Analysis Chart. - SDK Calls:
nanvalues in requests and responses are handled as null values. These include the following scenarios:- Baselines imports.
- Querying baseline data.
- Querying assays results in preview mode.
- Querying assay results after assay upload and analysis.
- Baseline metrics: Baseline metrics created from imported values (aka numpy) and date based baselines from the Wallaroo Dashboard and the Wallaroo SDK are treated as follows:
- Baseline stats ignore
nanwhen calculating:mean,median,std dev,min,max. - Baseline stats include
nanwhen calculatingcount.
- Baseline stats ignore
- Baseline binning includes
nanfor:- Score Metrics of
PSI,SUMDIFFandMAXDIFFfor both preview aka “interactive run” and post assay upload analysis factornanvalues into the window score calculation and the associated binning scheme. - NaN’s are treated as negative infinity but included in the left most bin.
- Score Metrics of
Baseline Upload with NaN
When uploading baseline data via the Wallaroo Dashboard, verify that the scalar field is set to None and the imported baseline csv nan values must be one of the following:
- empty double quotes “”
- empty single quotes ‘’
- empty values
The following are examples of NaN within baseline imported data.
nan values as empty values. | nan represented as double quotes: | nan represented as single quotes: |
|---|---|---|
| | |
Define the Baseline by Location Example
This example shows the assay defined from the date ranges from the inferences performed earlier.
By default, all locations are included in the assay location filters. For our example we use wallaroo.assay_config.WindowBuilder.add_location_filter to specify location_01 for our baseline comparison results.
# edge locations
location_01 = "houseprice-edge-demonstration-01"
location_02 = "houseprice-edge-demonstration-02"
# Build the assay, based on the start and end of our baseline time,
# and tracking the output variable index 0
display(assay_baseline_start)
display(assay_baseline_end)
assay_baseline_from_dates = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# set the location to the edge location
assay_baseline_from_dates.window_builder().add_location_filter([location_01])
# create the baseline from the dates
assay_baseline_run_from_dates = assay_baseline_from_dates.build().interactive_baseline_run()
datetime.datetime(2025, 7, 15, 20, 4, 47, 902345, tzinfo=datetime.timezone.utc)
datetime.datetime(2025, 7, 15, 20, 5, 53, 416806, tzinfo=datetime.timezone.utc)
Baseline DataFrame
The method wallaroo.assay_config.AssayBuilder.baseline_dataframe returns a DataFrame of the assay baseline generated from the provided parameters. This includes:
metadata: The inference metadata with the model information, inference time, and other related factors.indata: Each input field assigned with the labelin.{input field name}.outdata: Each output field assigned with the labelout.{output field name}
Note that for assays generated from numpy values, there is only the out data based on the supplied baseline data.
In the following example, the baseline DataFrame is retrieved. Note that in this example, the partition is set to houseprice-edge-demonstration-01, the location specified when we set the location when creating the baseline.
display(assay_baseline_from_dates.baseline_dataframe())
| time | metadata | input_tensor_0 | input_tensor_1 | input_tensor_2 | input_tensor_3 | input_tensor_4 | input_tensor_5 | input_tensor_6 | input_tensor_7 | input_tensor_8 | input_tensor_9 | input_tensor_10 | input_tensor_11 | input_tensor_12 | input_tensor_13 | input_tensor_14 | input_tensor_15 | input_tensor_16 | input_tensor_17 | output_variable_0 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 3.0 | 2.50 | 1730.0 | 7442.0 | 2.0 | 0.0 | 0.0 | 3.0 | 7.0 | 1730.0 | 0.0 | 47.350700 | -122.178001 | 1630.0 | 6458.0 | 27.0 | 0.0 | 0.0 | 285253.15625 |
| 1 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 3.0 | 1.00 | 1670.0 | 4005.0 | 1.5 | 0.0 | 0.0 | 4.0 | 7.0 | 1170.0 | 500.0 | 47.687801 | -122.379997 | 1240.0 | 4005.0 | 75.0 | 0.0 | 0.0 | 557391.25000 |
| 2 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 3.0 | 1.75 | 1540.0 | 9154.0 | 1.0 | 0.0 | 0.0 | 3.0 | 8.0 | 1540.0 | 0.0 | 47.620701 | -122.042000 | 1990.0 | 10273.0 | 31.0 | 0.0 | 0.0 | 555231.93750 |
| 3 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 1.0 | 1.00 | 1230.0 | 3774.0 | 1.0 | 0.0 | 0.0 | 4.0 | 6.0 | 830.0 | 400.0 | 47.688599 | -122.353996 | 1300.0 | 3774.0 | 90.0 | 0.0 | 0.0 | 448627.71875 |
| 4 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 2.0 | 1.00 | 930.0 | 6098.0 | 1.0 | 0.0 | 0.0 | 4.0 | 6.0 | 930.0 | 0.0 | 47.528900 | -122.029999 | 1730.0 | 9000.0 | 95.0 | 0.0 | 0.0 | 246901.15625 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 495 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 4.0 | 1.50 | 1800.0 | 6750.0 | 1.5 | 0.0 | 0.0 | 4.0 | 7.0 | 1800.0 | 0.0 | 47.686798 | -122.285004 | 1420.0 | 5900.0 | 64.0 | 0.0 | 0.0 | 557391.25000 |
| 496 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 5.0 | 3.50 | 3450.0 | 28324.0 | 1.0 | 0.0 | 0.0 | 5.0 | 8.0 | 2350.0 | 1100.0 | 47.699100 | -122.195999 | 2640.0 | 14978.0 | 42.0 | 0.0 | 0.0 | 782434.43750 |
| 497 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 3.0 | 1.75 | 2300.0 | 41900.0 | 1.0 | 0.0 | 0.0 | 4.0 | 8.0 | 1310.0 | 990.0 | 47.477001 | -122.292000 | 1160.0 | 8547.0 | 76.0 | 0.0 | 0.0 | 430252.40625 |
| 498 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 3.0 | 1.75 | 1520.0 | 12282.0 | 1.0 | 0.0 | 0.0 | 3.0 | 8.0 | 1220.0 | 300.0 | 47.751598 | -122.299004 | 1860.0 | 13500.0 | 36.0 | 0.0 | 0.0 | 424966.46875 |
| 499 | 1752609953329 | {'last_model': '{"model_name":"house-price-estimator","model_sha":"e22a0831aafd9917f3cc87a15ed267797f80e2afa12ad7d8810ca58f173b8cc6"}', 'pipeline_version': '1ff19772-f41f-42fb-b0d1-f82130bf5801', 'elapsed': [3638412, 5155007], 'dropped': [], 'partition': 'houseprice-edge-demonstration-01'} | 2.0 | 1.00 | 1060.0 | 7868.0 | 1.0 | 0.0 | 0.0 | 3.0 | 7.0 | 1060.0 | 0.0 | 47.741402 | -122.294998 | 1530.0 | 10728.0 | 63.0 | 0.0 | 0.0 | 340764.53125 |
500 rows × 21 columns
Baseline Stats
The method wallaroo.assay.AssayAnalysis.baseline_stats() returns a pandas.core.frame.DataFrame of the baseline stats.
The baseline stats are displayed in the sample below.
assay_baseline_run_from_dates.baseline_stats()
| Baseline | |
|---|---|
| count | 500 |
| min | 236238.65625 |
| max | 1489624.5 |
| mean | 497609.064031 |
| median | 445851.0 |
| std | 223975.59375 |
| start | None |
| end | None |
Baseline Bins
The method wallaroo.assay.AssayAnalysis.baseline_bins a simple dataframe to with the edge/bin data for a baseline.
assay_baseline_run_from_dates.baseline_bins()
| b_edges | b_edge_names | b_aggregated_values | b_aggregation | |
|---|---|---|---|---|
| 0 | 305617.46875 | < 20% | 0.200 | Aggregation.DENSITY |
| 1 | 420370.65625 | 20% - 40% | 0.200 | Aggregation.DENSITY |
| 2 | 466051.34375 | 40% - 60% | 0.200 | Aggregation.DENSITY |
| 3 | 682284.625 | 60% - 80% | 0.208 | Aggregation.DENSITY |
| 4 | INFINITY | > 80% | 0.192 | Aggregation.DENSITY |
Baseline Histogram Chart
The method wallaroo.assay_config.AssayBuilder.baseline_histogram returns a histogram chart of the assay baseline generated from the provided parameters.
assay_baseline_from_dates.baseline_histogram()

Assay Preview
Now that the baseline is defined, we look at different configuration options and view how the assay baseline and results changes. Once we determine what gives us the best method of determining model drift, we can create the assay.
The following examples show different methods of previewing the assay, then how to configure the assay by collecting data from different locations.
Analysis List Chart Scores
Analysis List scores show the assay scores for each assay result interval in one chart. Values that are outside of the alert threshold are colored red, while scores within the alert threshold are green.
Assay chart scores are displayed with the method wallaroo.assay.AssayAnalysisList.chart_scores(title: Optional[str] = None), with ability to display an optional title with the chart.
The following example shows retrieving the assay results and displaying the chart scores for each window interval for location_01
# Create the assay baseline
assay_baseline = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# Set the assay parameters
# set the location to the edge location
assay_baseline.window_builder().add_location_filter([location_01])
# The end date to gather inference results
assay_baseline.add_run_until(datetime.datetime.now())
# Set the interval and window to one minute each, set the start date for gathering inference results
assay_baseline.window_builder().add_width(minutes=1).add_interval(minutes=1).add_start(assay_window_start)
# build the assay configuration
assay_config = assay_baseline.build()
# perform an interactive run and collect inference data
assay_results = assay_config.interactive_run()
# Preview the assay analyses
assay_results.chart_scores()

# Create the assay baseline
assay_baseline = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# Set the assay parameters
# set the location to the edge location
assay_baseline.window_builder().add_location_filter([location_01, location_02])
# The end date to gather inference results
assay_baseline.add_run_until(datetime.datetime.now())
# Set the interval and window to one minute each, set the start date for gathering inference results
assay_baseline.window_builder().add_width(minutes=1).add_interval(minutes=1).add_start(assay_window_start)
# build the assay configuration
assay_config = assay_baseline.build()
# perform an interactive run and collect inference data
assay_results = assay_config.interactive_run()
# Preview the assay analyses
assay_results.chart_scores()

Analysis Chart
The method wallaroo.assay.AssayAnalysis.chart() displays a comparison between the baseline and an interval of inference data.
This is compared to the Chart Scores, which is a list of all of the inference data split into intervals, while the Analysis Chart shows the breakdown of one set of inference data against the baseline.
Score from the Analysis List Chart Scores and each element from the Analysis List DataFrame generates
The following fields are included.
| Field | Type | Description |
|---|---|---|
| baseline mean | Float | The mean of the baseline values. |
| window mean | Float | The mean of the window values. |
| baseline median | Float | The median of the baseline values. |
| window median | Float | The median of the window values. |
| bin_mode | String | The binning mode used for the assay. |
| aggregation | String | The aggregation mode used for the assay. |
| metric | String | The metric mode used for the assay. |
| weighted | Bool | Whether the bins were manually weighted. |
| score | Float | The score from the assay window. |
| scores | List(Float) | The score from each assay window bin. |
| index | Integer/None | The window index. Interactive assay runs are None. |
# Create the assay baseline
assay_baseline = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# Set the assay parameters
# set the location to the edge location
assay_baseline.window_builder().add_location_filter([location_01])
# The end date to gather inference results
assay_baseline.add_run_until(datetime.datetime.now())
# Set the interval and window to one minute each, set the start date for gathering inference results
assay_baseline.window_builder().add_width(minutes=1).add_interval(minutes=1).add_start(assay_window_start)
# build the assay configuration
assay_config = assay_baseline.build()
# perform an interactive run and collect inference data
assay_results = assay_config.interactive_run()
# Preview the assay analyses
assay_results[0].chart()

Analysis List DataFrame
wallaroo.assay.AssayAnalysisList.to_dataframe() returns a DataFrame showing the assay results for each window aka individual analysis. This DataFrame contains the following fields:
| Field | Type | Description |
|---|---|---|
| assay_id | Integer/None | The assay id. Only provided from uploaded and executed assays. |
| name | String/None | The name of the assay. Only provided from uploaded and executed assays. |
| iopath | String/None | The iopath of the assay. Only provided from uploaded and executed assays. |
| score | Float | The assay score. |
| start | DateTime | The DateTime start of the assay window. |
| min | Float | The minimum value in the assay window. |
| max | Float | The maximum value in the assay window. |
| mean | Float | The mean value in the assay window. |
| median | Float | The median value in the assay window. |
| std | Float | The standard deviation value in the assay window. |
| warning_threshold | Float/None | The warning threshold of the assay window. |
| alert_threshold | Float/None | The alert threshold of the assay window. |
| status | String | The assay window status. Values are:
|
For this example, the assay analysis list DataFrame is listed.
From this tutorial, we should have 2 windows of dta to look at, each one minute apart.
# Create the assay baseline
assay_baseline = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# Set the assay parameters
# set the location to the edge location
assay_baseline.window_builder().add_location_filter([location_01])
# The end date to gather inference results
assay_baseline.add_run_until(datetime.datetime.now())
# Set the interval and window to one minute each, set the start date for gathering inference results
assay_baseline.window_builder().add_width(minutes=1).add_interval(minutes=1).add_start(assay_window_start)
# build the assay configuration
assay_config = assay_baseline.build()
# perform an interactive run and collect inference data
assay_results = assay_config.interactive_run()
# Preview the assay analyses
assay_results.to_dataframe()
| id | assay_id | assay_name | iopath | pipeline_id | pipeline_name | workspace_id | workspace_name | score | start | min | max | mean | median | std | warning_threshold | alert_threshold | status | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3f478cbf-73da-4f72-aa0e-2fe1e6197ccc | assay-demonstration-tutorial assay | out.variable.0 | 40 | assay-demonstration-tutorial | 12 | run-anywhere-assay-demonstration-tutorial | 0.030046 | 2025-07-15 20:06:58.424304+00:00 | 236238.65625 | 2016006.0 | 539489.484203 | 451046.9375 | 264051.03125 | None | 0.25 | Ok |
Configure Assays
Before creating the assay, configure the assay and continue to preview it until the best method for detecting drift is set.
Location Filter
This tutorial focuses on the assay configuration method wallaroo.assay_config.WindowBuilder.add_location_filter.
Location Filter Parameters
add_location_filter takes the following parameters.
| Parameter | Type | Description |
|---|---|---|
| locations | List(String) | The list of model deployment locations for the assay. |
Location Filter Example
By default, the locations parameter includes all locations as part of the pipeline. This is seen in the default where no location filter is set, and of the inference data is shown.
For our examples, we will show different locations and how the assay changes. For the first example, we set the location to location_01 which was used to create the baseline, and included inferences that were likely to not trigger a model drift detection alert.
# Create the assay baseline
assay_baseline = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# Set the assay parameters
# set the location to the edge location
assay_baseline.window_builder().add_location_filter([location_01])
# The end date to gather inference results
assay_baseline.add_run_until(datetime.datetime.now())
# Set the interval and window to one minute each, set the start date for gathering inference results
assay_baseline.window_builder().add_width(minutes=1).add_interval(minutes=1).add_start(assay_window_start)
# build the assay configuration
assay_config = assay_baseline.build()
# perform an interactive run and collect inference data
assay_results = assay_config.interactive_run()
# Preview the assay analyses
assay_results.chart_scores()
assay_results.to_dataframe()

| id | assay_id | assay_name | iopath | pipeline_id | pipeline_name | workspace_id | workspace_name | score | start | min | max | mean | median | std | warning_threshold | alert_threshold | status | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | e3695ad8-8a79-4462-a424-f5f4c88e536f | assay-demonstration-tutorial assay | out.variable.0 | 40 | assay-demonstration-tutorial | 12 | run-anywhere-assay-demonstration-tutorial | 0.030046 | 2025-07-15 20:06:58.424304+00:00 | 236238.65625 | 2016006.0 | 539489.484203 | 451046.9375 | 264051.03125 | None | 0.25 | Ok |
The next example includes location_01 and location_02. Since they were performed in distinct times, the model insights scores for each location is seen in the chart.
# Create the assay baseline
assay_baseline = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# Set the assay parameters
# set the location to the edge location
assay_baseline.window_builder().add_location_filter([location_01, location_02])
# The end date to gather inference results
assay_baseline.add_run_until(datetime.datetime.now())
# Set the interval and window to one minute each, set the start date for gathering inference results
assay_baseline.window_builder().add_width(minutes=1).add_interval(minutes=1).add_start(assay_window_start)
# build the assay configuration
assay_config = assay_baseline.build()
# perform an interactive run and collect inference data
assay_results = assay_config.interactive_run()
# Preview the assay analyses
assay_results.chart_scores()
assay_results.to_dataframe()

| id | assay_id | assay_name | iopath | pipeline_id | pipeline_name | workspace_id | workspace_name | score | start | min | max | mean | median | std | warning_threshold | alert_threshold | status | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | c1211c1a-5cea-46fe-8287-e5cb6801613d | assay-demonstration-tutorial assay | out.variable.0 | 40 | assay-demonstration-tutorial | 12 | run-anywhere-assay-demonstration-tutorial | 0.030046 | 2025-07-15 20:06:58.424304+00:00 | 2.362387e+05 | 2016006.0 | 5.394895e+05 | 4.510469e+05 | 264051.03125 | None | 0.25 | Ok |
| 1 | 0 | c1211c1a-5cea-46fe-8287-e5cb6801613d | assay-demonstration-tutorial assay | out.variable.0 | 40 | assay-demonstration-tutorial | 12 | run-anywhere-assay-demonstration-tutorial | 0.030046 | 2025-07-15 20:07:58.424304+00:00 | 2.362387e+05 | 2016006.0 | 5.394895e+05 | 4.510469e+05 | 264051.03125 | None | 0.25 | Ok |
| 2 | 0 | c1211c1a-5cea-46fe-8287-e5cb6801613d | assay-demonstration-tutorial assay | out.variable.0 | 40 | assay-demonstration-tutorial | 12 | run-anywhere-assay-demonstration-tutorial | 4.248209 | 2025-07-15 20:08:58.424304+00:00 | 1.514080e+06 | 2016006.0 | 1.871636e+06 | 1.946437e+06 | 163849.53125 | None | 0.25 | Alert |
Create Assay
With the assay previewed and configuration options determined, we officially create it by uploading it to the Wallaroo instance.
Once it is uploaded, the assay runs an analysis based on the window width, interval, and the other settings configured.
Assays are uploaded with the wallaroo.assay_config.upload() method. This uploads the assay into the Wallaroo database with the configurations applied and returns the assay id. Note that assay names must be unique across the Wallaroo instance; attempting to upload an assay with the same name as an existing one will return an error.
wallaroo.assay_config.upload() returns the assay id for the assay.
Typically we would just call wallaroo.assay_config.upload() after configuring the assay. For the example below, we will perform the complete configuration in one window to show all of the configuration steps at once before creating the assay, and narrow the locations to location_01 and location_02. By default, all locations associated with a pipeline are included in the assay results unless the add_location_filter method is applied to specify location(s).
# Create the assay baseline
assay_baseline = wl.build_assay(assay_name="assays from date baseline",
pipeline=mainpipeline,
iopath="output variable 0",
baseline_start=assay_baseline_start,
baseline_end=assay_baseline_end)
# Set the assay parameters
# set the location to the edge location
assay_baseline.window_builder().add_location_filter([location_01, location_02])
# The end date to gather inference results
assay_baseline.add_run_until(datetime.datetime.now())
# Set the interval and window to one minute each, set the start date for gathering inference results
assay_baseline.window_builder().add_width(minutes=1).add_interval(minutes=1).add_start(assay_window_start)
assay_id = assay_baseline.upload()
The assay is now visible through the Wallaroo UI by selecting the workspace, then the pipeline, then Insights. The following is an example of another assay in the Wallaroo Dashboard.

Get Assay Info
Assay information is retrieved with the wallaroo.client.get_assay_info() which takes the following parameters.
| Parameter | Type | Description |
|---|---|---|
| assay_id | Integer (Required) | The numerical id of the assay. |
This returns the following:
| Parameter | Type | Description |
|---|---|---|
| id | Integer | The numerical id of the assay. |
| name | String | The name of the assay. |
| active | Boolean | True: The assay is active and generates analyses based on its configuration. False: The assay is disabled and will not generate new analyses. |
| pipeline_name | String | The name of the pipeline the assay references. |
| last_run | DateTime | The date and time the assay last ran. |
| next_run | DateTime | THe date and time the assay analysis will next run. |
| alert_threshold | Float | The alert threshold setting for the assay. |
| baseline | Dict | The baseline and settings as set from the assay configuration. |
| iopath | String | The iopath setting for the assay. |
| metric | String | The metric setting for the assay. |
| num_bins | Integer | The number of bins for the assay. |
| bin_weights | List/None | The bin weights used if any. |
| bin_mode | String | The binning mode used. |
display(wl.get_assay_info(assay_id))
| Field | Value |
|---|---|
| ID | bdf36039-d2ed-4455-8653-803d22554bd5 |
| Name | assays from date baseline |
| Active | True |
| Pipeline | assay-demonstration-tutorial |
| Workspace ID | 12 |
| Workspace Name | run-anywhere-assay-demonstration-tutorial |
| Monitoring | ['out.variable.0'] |
| Window Width | 60 seconds |
| First Run | 2025-15-Jul 20:07:58 |
| End Run | 2025-15-Jul 20:26:33 |
| Run Frequency | 1 Minute |
| Bin Mode | 5 Quantile bins |
| Aggregation | Density |
| Metric | PSI |
| Last Run | 2025-15-Jul 20:25:58 |
| Next Run | 2025-15-Jul 20:26:58 |
| Created At | 2025-15-Jul 20:26:34 |
| Updated At | 2025-15-Jul 20:26:34 |
Troubleshooting
Timeout Issues
If latency between the model’s edge deployment and the Wallaroo Ops Instance is higher than 0.5, the connection may time out and the inference results are not transmitted back to the Wallaroo Ops Center. To check the latency between the deployed model and the Wallaroo Ops Center, use the following command from the edge device:
curl -o /dev/null -s -w "%{time_total}\n" {Wallaroo Domain}:8443
For example, if the Wallaroo Domain of the Wallaroo Ops Center is example.wallaroo.ai, the command is:
curl -o /dev/null -s -w "%{time_total}\n" example.wallaroo.ai:8443
0.089316
For docker run deployments, increase the timeout with the option -e MTLS_CONNECTION_TIMEOUT=3.0. Increase the MTLS_CONNECTION_TIMEOUT further as needed for extreme low latency scenarios.
For example:
docker run -p 8080:8080 \
-v ./data:/persist \
-e DEBUG=true \
-e MTLS_CONNECTION_TIMEOUT=3.0 \
-e OCI_REGISTRY=$OCI_REGISTRY \
-e EDGE_BUNDLE=abc123 \
-e CONFIG_CPUS=1 \
-e OCI_USERNAME=$OCI_USERNAME \
-e OCI_PASSWORD=$OCI_PASSWORD \
-e PIPELINE_URL=example.registry.io/wallaroolabs/doc-samples/pipelines/edge-observability-pipeline:3b49fba8-94d8-4fca-aecc-c75257fd16c6 \
example.registry.io/wallaroolabs/doc-samples/engines/proxy/wallaroo/ghcr.io/wallaroolabs/standalone-mini:v2023.4.0-main-4079