Skip to content

Commit

Permalink
Merge branch 'main' of github.com:radicalbit/radicalbit-ai-monitoring…
Browse files Browse the repository at this point in the history
… into feature/ROS-280-add-reference-model-quality-of-multiclass
  • Loading branch information
dtria91 committed Jun 28, 2024
2 parents ed1b634 + be55fac commit f45f7d6
Show file tree
Hide file tree
Showing 123 changed files with 3,866 additions and 223 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/radicalbit-bot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
ORGANIZATION: ${{ secrets.DOCKER_HUB_ORG }}

build-ui:
if: ${{ false && github.event.comment.body == '/build-ui' || github.event.comment.body == '/build-all' }}
if: ${{ github.event.comment.body == '/build-ui' || github.event.comment.body == '/build-all' }}
uses: radicalbit/radicalbit-github-workflows/.github/workflows/docker.yaml@v1
with:
push: false
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
# Radicalbit AI Monitoring

# 👋 Welcome!
The **Radicalbit AI Monitoring Platform** provides a comprehensive solution for monitoring your Artificial Intelligence models in production.
The **Radicalbit AI Monitoring Platform** provides a comprehensive solution for monitoring your Machine Learning and Large Language models in production.

## 🤔 Why Monitor AI Models?
While models often perform well during development and validation, their effectiveness can degrade over time in production due to various factors like data shifts or concept drift. The Radicalbit AI Monitor platform helps you proactively identify and address potential performance issues.

## 🗝️ Key Functionalities
The platform provides extensive monitoring capabilities to ensure optimal performance of your AI models in production. It analyzes both your reference dataset (used for pre-production validation) and the current datasets, allowing you to put under control:
The platform provides extensive monitoring capabilities to ensure optimal performance of your AI models in production. It analyzes both your reference dataset (used for pre-production validation) and the current datasets, allowing you to control:
* **Data Quality**
* **Model Quality**
* **Model Drift**
Expand All @@ -36,7 +36,7 @@ This repository contains all the files and projects to run Radicalbit AI Monitor

## 🚀 Installation using Docker compose

In this repository a docker compose file is available to run the platform in local with a K3s cluster where we can deploy Spark jobs.
This repository provides a Docker Compose file for running the platform locally with a K3s cluster. This setup allows you to deploy Spark jobs.

To run, simply:

Expand All @@ -50,25 +50,25 @@ If the UI is needed:
docker compose --profile ui up
```

After all containers are up & running, you can go to [http://localhost:5173](http://127.0.0.1:5173) to play with the app.
Once all containers are up & running, you can go to [http://localhost:5173](http://127.0.0.1:5173) to play with the app.

### Interacting with K3s cluster

In the compose file is present a [k9s](https://k9scli.io/) container that can be used to monitor the K3s cluster.
The compose file includes a [k9s](https://k9scli.io/) container that can be used to monitor the K3s cluster.

```bash
docker compose up k9s -d && docker attach radicalbit-ai-monitoring-k9s-1
```

#### Other tools

In order to connect and interact with the K3s cluster from the local machine (for example with Lens or `kubectl`) is necessary to create another file starting from `./docker/k3s_data/kubeconfig/kubeconfig.yaml` (that is automatically generated when the docker compose is up and running).
In order to connect and interact with the K3s cluster from the local machine (for example with Lens or `kubectl`), it is necessary to create another file starting from `./docker/k3s_data/kubeconfig/kubeconfig.yaml` (that is automatically generated when the docker compose is up and running).

Copy the above file and modify `https://k3s:6443` with `https://127.0.0.1:6443` and use this new file to interact with the cluster from the local machine

### Real AWS

In order to use a real AWS instead of Minio is necessary to modify the environment variables of the api container, putting real `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION` and `S3_BUCKET_NAME` and removing `S3_ENDPOINT_URL`.
In order to use a real AWS instead of Minio it is necessary to modify the environment variables of the api container, putting real `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION` and `S3_BUCKET_NAME` and removing `S3_ENDPOINT_URL`.

### Teardown

Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ services:
profiles: ["ui"]
build:
context: ./ui
target: build
target: dev
command: yarn start:local
develop:
watch:
Expand Down
75 changes: 70 additions & 5 deletions docs/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,88 @@
:root {
--ifm-color-primary: #3695D9;
--ifm-color-primary-dark: #0A71BB;
--ifm-color-primary-darker: #182336;
--ifm-color-primary-darkest: #182336;
--ifm-color-primary-darker: #252329;
--ifm-color-primary-darkest: #252329;
--ifm-color-primary-light: #fff;
--ifm-color-primary-lighter: #fff;
--ifm-color-primary-lightest: #fff;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
--ifm-menu-color-background-active: transparent;
--ifm-menu-color-background-hover: transparent;
--ifm-breadcrumb-item-background-active: transparent;

--ifm-font-family-base: 'Open Sans', Helvetica, Arial, Lucida, sans-serif;
--ifm-footer-padding-vertical: 8rem;

--rdb-font-family-headers: 'Montserrat', Helvetica, Arial, Lucida, sans-serif;
}

/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
--ifm-color-primary: #fff;
--ifm-color-primary-dark: #182336;
--ifm-color-primary-darker: #182336;
--ifm-color-primary-darkest: #182336;
--ifm-color-primary-dark: #3E3E3E;
--ifm-color-primary-darker: #252329;
--ifm-color-primary-darkest: #252329;
--ifm-color-primary-light: #73B2E0;
--ifm-color-primary-lighter: #73B2E0;
--ifm-color-primary-lightest: #73B2E0;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}


/* Radicalbit corporate elements */

@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');

h1, h2, h3 {
font-family: var(--rdb-font-family-headers);
font-weight: normal;
color: var(--ifm-color-primary);
}

.breadcrumbs__link {
padding: 0;
}

.navbar__title {
font-family: var(--rdb-font-family-headers);
}

.pagination-nav__link {
border: 0;
}

.theme-doc-sidebar-container {
background-image: linear-gradient(180deg, rgba(54, 149, 217, 0.18) 0%, transparent 80%) !important;
}

.footer {
background-image: linear-gradient(191deg, rgba(115, 177, 222, 1) 0%, #3a92d0 24%, #1171c2 46%, #095bbf 69%, #053189 100%) !important;
position: relative;
font-size: .8rem;
}

.footer::before {
background-image: url(img/rdb-page-divisor--light.svg);
background-size: cover;
top: 0;
left: 0;
height: 94px;
width: 100%;
z-index: 1;
content: ' ';
position: absolute;
}

.footer__copyright {
border-top: 1px solid var(--docusaurus-highlighted-code-line-bg);
padding-top: 1rem;
margin-top: 2rem;
font-size: .8em;
}

[data-theme='dark'] .footer::before {
background-image: url(img/rdb-page-divisor--dark.svg);
}
5 changes: 5 additions & 0 deletions docs/src/css/img/rdb-page-divisor--dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/src/css/img/rdb-page-divisor--light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions ui/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_BASE_URL=APP_BASE_URL
20 changes: 15 additions & 5 deletions ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
FROM node:20.10.0-bullseye AS build
FROM node:20.10.0-bullseye AS dev

WORKDIR /app

COPY . .

RUN npm install --global --force [email protected] && \
yarn install && \
yarn build:dev
yarn install

FROM node:20.10.0-bullseye AS build

WORKDIR /app

COPY . .
COPY --from=dev /app/node_modules /app/node_modules

RUN yarn build:prod

FROM nginx:alpine3.19

EXPOSE 80

COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY env.sh /docker-entrypoint.d/
RUN chmod +x /docker-entrypoint.d/env.sh
10 changes: 10 additions & 0 deletions ui/env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#! /bin/sh

for i in $(env | grep "APP_"); do
key=$(echo "$i" | cut -d '=' -f 1)
value=$(echo "$i" | cut -d '=' -f 2-)

echo "$key=$value"

find "/usr/share/nginx/html" -type f -exec sed -i "s|${key}|${value}|g" '{}' +
done
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"start:dev": "NODE_ENV=development && vite --host 0.0.0.0",
"build:local": "NODE_ENV=local && vite build",
"build:dev": "NODE_ENV=development && vite build",
"build:prod": "vite build --mode production",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0 --fix",
"preview": "vite preview"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { DataTypeEnum, ModelTypeEnum } from '@State/models/constants';
import useFormbit from '@radicalbit/formbit';
import {
createContext,
useContext,
useMemo,
useState,
} from 'react';
import { DataTypeEnum, ModelTypeEnum } from '@State/models/constants';
import schemaStepFour from './step-four/schema';
import schemaStepOne from './step-one/schema';
import schemaStepThree from './step-three/schema';
Expand Down
60 changes: 41 additions & 19 deletions ui/src/components/modals/add-new-model/step-four/form-fields.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { FormField, Select, Tooltip } from '@radicalbit/radicalbit-design-system';
import {
FormField,
Select,
Tooltip,
} from '@radicalbit/radicalbit-design-system';
import { ModelTypeEnum } from '@Src/store/state/models/constants';
import { useModalContext } from '../modal-context-provider';

Expand Down Expand Up @@ -261,46 +265,64 @@ function Probability() {
);
}

const targetValidTypes = ['int', 'float', 'double'];
const targetValidTypes = {
[ModelTypeEnum.BINARY_CLASSIFICATION]: ['int', 'float', 'double'],
[ModelTypeEnum.MULTI_CLASSIFICATION]: ['int', 'float', 'double'],
[ModelTypeEnum.REGRESSION]: ['int', 'float', 'double'],
};
const useGetTargets = () => {
const { useFormbit } = useModalContext();
const { useFormbit, useFormbitStepOne } = useModalContext();
const { form } = useFormbit;

return form.features.filter(({ type }) => targetValidTypes.includes(type));
const { form: formStepOne } = useFormbitStepOne;
const { modelType } = formStepOne;

return form.features.filter(({ type }) => targetValidTypes[modelType].includes(type));
};

const predictionValidTypes = ['int', 'float', 'double'];
const predictionValidTypes = {
[ModelTypeEnum.BINARY_CLASSIFICATION]: ['int', 'float', 'double'],
[ModelTypeEnum.MULTI_CLASSIFICATION]: ['int', 'float', 'double', 'string'],
[ModelTypeEnum.REGRESSION]: ['int', 'float', 'double'],
};
const useGetPredictions = () => {
const { useFormbit } = useModalContext();
const { useFormbit, useFormbitStepOne } = useModalContext();
const { form } = useFormbit;

return form.outputs.filter(({ type }) => predictionValidTypes.includes(type));
const { form: formStepOne } = useFormbitStepOne;
const { modelType } = formStepOne;

return form.outputs.filter(({ type }) => predictionValidTypes[modelType].includes(type));
};

const binaryClassificationProbabilityValidTypes = ['float', 'double'];
const binaryClassificationProbabilityValidTypes = {
[ModelTypeEnum.BINARY_CLASSIFICATION]: ['float', 'double'],
[ModelTypeEnum.MULTI_CLASSIFICATION]: ['float', 'double'],
[ModelTypeEnum.REGRESSION]: ['float', 'double'],
};
const useGetProbabilities = () => {
const { useFormbitStepOne, useFormbit } = useModalContext();
const { form } = useFormbit;
const { form: formStepOne } = useFormbitStepOne;

if (formStepOne.modelType === ModelTypeEnum.BINARY_CLASSIFICATION) {
return form.outputs.filter(({ type }) => binaryClassificationProbabilityValidTypes.includes(type));
}
const { form: formStepOne } = useFormbitStepOne;
const { modelType } = formStepOne;

return form.outputs;
return form.outputs.filter(({ type }) => binaryClassificationProbabilityValidTypes[modelType].includes(type));
};

const timestampValidTypes = ['datetime'];
const timestampValidTypes = {
[ModelTypeEnum.BINARY_CLASSIFICATION]: ['datetime'],
[ModelTypeEnum.MULTI_CLASSIFICATION]: ['datetime'],
[ModelTypeEnum.REGRESSION]: ['datetime'],
};
const useGetTimestapValidFeatures = () => {
const { useFormbitStepOne, useFormbit } = useModalContext();
const { form } = useFormbit;
const { form: formStepOne } = useFormbitStepOne;

if (formStepOne.modelType === ModelTypeEnum.BINARY_CLASSIFICATION) {
return form?.features.filter(({ type }) => timestampValidTypes.includes(type));
}
const { form: formStepOne } = useFormbitStepOne;
const { modelType } = formStepOne;

return form.outputs;
return form?.features.filter(({ type }) => timestampValidTypes[modelType].includes(type));
};

export {
Expand Down
Loading

0 comments on commit f45f7d6

Please sign in to comment.