From 602c851880924b53d465b607713a8190477667e1 Mon Sep 17 00:00:00 2001 From: Eyal-Danieli Date: Tue, 16 Sep 2025 13:44:54 +0300 Subject: [PATCH 1/4] add count_events module --- modules/src/count_events/count_events.py | 24 ++++++ modules/src/count_events/item.yaml | 14 ++++ modules/src/count_events/requirements.txt | 3 + modules/src/count_events/test_count_events.py | 75 +++++++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 modules/src/count_events/count_events.py create mode 100644 modules/src/count_events/item.yaml create mode 100644 modules/src/count_events/requirements.txt create mode 100644 modules/src/count_events/test_count_events.py diff --git a/modules/src/count_events/count_events.py b/modules/src/count_events/count_events.py new file mode 100644 index 000000000..054d0256e --- /dev/null +++ b/modules/src/count_events/count_events.py @@ -0,0 +1,24 @@ +from mlrun.model_monitoring.applications import ( + ModelMonitoringApplicationBase, ModelMonitoringApplicationMetric, +) +import mlrun.model_monitoring.applications.context as mm_context + + +class CountApp(ModelMonitoringApplicationBase): + def do_tracking( + self, monitoring_context: mm_context.MonitoringApplicationContext + ) -> ModelMonitoringApplicationMetric: + sample_df = monitoring_context.sample_df + monitoring_context.logger.debug("Sample data-frame", sample_df=sample_df) + count = len(sample_df) + monitoring_context.logger.info( + "Counted events for model endpoint window", + model_endpoint_name=monitoring_context.model_endpoint.metadata.name, + count=count, + start=monitoring_context.start_infer_time, + end=monitoring_context.end_infer_time, + ) + return ModelMonitoringApplicationMetric( + name="count", + value=count, + ) \ No newline at end of file diff --git a/modules/src/count_events/item.yaml b/modules/src/count_events/item.yaml new file mode 100644 index 000000000..c12000f7f --- /dev/null +++ b/modules/src/count_events/item.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: monitoring_application +categories: +- model-serving +description: Count events in each time window +example: '' +generationDate: 2025-09-16:12-25 +hidden: false +labels: + author: iguazio +mlrunVersion: 1.10.0-rc27 +name: count_events +requirements: +version: 1.0.0 \ No newline at end of file diff --git a/modules/src/count_events/requirements.txt b/modules/src/count_events/requirements.txt new file mode 100644 index 000000000..89741402a --- /dev/null +++ b/modules/src/count_events/requirements.txt @@ -0,0 +1,3 @@ +mlrun==1.10.0-rc27 +pandas==2.1.4 +pytest~=8.2 diff --git a/modules/src/count_events/test_count_events.py b/modules/src/count_events/test_count_events.py new file mode 100644 index 000000000..66a94c932 --- /dev/null +++ b/modules/src/count_events/test_count_events.py @@ -0,0 +1,75 @@ +# Copyright 2025 Iguazio +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +from mlrun.model_monitoring.applications import ModelMonitoringApplicationMetric +import mlrun.model_monitoring.applications.context as mm_context + +from count_events import CountApp + +from unittest.mock import Mock +from datetime import datetime +import pandas as pd +import pytest + +class TestCountApp: + """Test suite for CountApp class.""" + + def setup_method(self): + """Set up test fixtures before each test method.""" + self.count_app = CountApp() + @staticmethod + def _create_mock_monitoring_context(sample_df, model_endpoint_name="test-model"): + """Helper method to create a mock monitoring context.""" + mock_context = Mock(spec=mm_context.MonitoringApplicationContext) + + # Mock the sample dataframe + mock_context.sample_df = sample_df + + # Mock the logger + mock_logger = Mock() + mock_context.logger = mock_logger + + # Mock the model endpoint + mock_model_endpoint = Mock() + mock_model_endpoint.metadata.name = model_endpoint_name + mock_context.model_endpoint = mock_model_endpoint + + # Mock time attributes + mock_context.start_infer_time = datetime(2025, 1, 1, 10, 0, 0) + mock_context.end_infer_time = datetime(2025, 1, 1, 11, 0, 0) + + return mock_context + + + @pytest.mark.parametrize("df_size", [0, 1, 10, 100, 1000]) + def test_do_tracking_with_various_dataframe_sizes(self, df_size): + """Test do_tracking with various dataframe sizes using parametrized test.""" + # Arrange + if df_size == 0: + test_df = pd.DataFrame() + else: + test_df = pd.DataFrame({"col1": range(df_size)}) + + mock_context = self._create_mock_monitoring_context(test_df) + + # Act + result = self.count_app.do_tracking(mock_context) + + # Assert + assert isinstance(result, ModelMonitoringApplicationMetric) + assert result.value == df_size + assert result.name == "count" + From cf5961bf4d1aef2cd1f1636a8feac271b24d56b0 Mon Sep 17 00:00:00 2001 From: Eyal-Danieli Date: Tue, 16 Sep 2025 15:49:39 +0300 Subject: [PATCH 2/4] adding cw --- modules/src/count_events/count_events.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/modules/src/count_events/count_events.py b/modules/src/count_events/count_events.py index 054d0256e..c2d6444e4 100644 --- a/modules/src/count_events/count_events.py +++ b/modules/src/count_events/count_events.py @@ -1,3 +1,18 @@ +# Copyright 2025 Iguazio +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + from mlrun.model_monitoring.applications import ( ModelMonitoringApplicationBase, ModelMonitoringApplicationMetric, ) From 3f5561865cbd093f91c771fdfe8ba4077e4fa3b3 Mon Sep 17 00:00:00 2001 From: Eyal-Danieli Date: Tue, 16 Sep 2025 16:25:51 +0300 Subject: [PATCH 3/4] remove gitkeep and fix item yaml --- modules/src/.gitkeep | 0 modules/src/count_events/item.yaml | 9 ++++++--- 2 files changed, 6 insertions(+), 3 deletions(-) delete mode 100644 modules/src/.gitkeep diff --git a/modules/src/.gitkeep b/modules/src/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/modules/src/count_events/item.yaml b/modules/src/count_events/item.yaml index c12000f7f..a11d45fc7 100644 --- a/modules/src/count_events/item.yaml +++ b/modules/src/count_events/item.yaml @@ -1,14 +1,17 @@ apiVersion: v1 -kind: monitoring_application categories: - model-serving description: Count events in each time window -example: '' +example: generationDate: 2025-09-16:12-25 hidden: false labels: author: iguazio mlrunVersion: 1.10.0-rc27 name: count_events -requirements: +spec: + filename: count_events.py + image: mlrun/mlrun + kind: monitoring_application + requirements: version: 1.0.0 \ No newline at end of file From 16c1e84ed72f51432579ae75d2f249269e4b7645 Mon Sep 17 00:00:00 2001 From: Eyal-Danieli Date: Tue, 16 Sep 2025 17:00:36 +0300 Subject: [PATCH 4/4] add empty nb for CI --- modules/src/count_events/count_events.ipynb | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 modules/src/count_events/count_events.ipynb diff --git a/modules/src/count_events/count_events.ipynb b/modules/src/count_events/count_events.ipynb new file mode 100644 index 000000000..54f657bb0 --- /dev/null +++ b/modules/src/count_events/count_events.ipynb @@ -0,0 +1,37 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "initial_id", + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}