Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE]Main based model registry #389

Draft
wants to merge 75 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
539cd48
Revert "feat: remove model registry related code and dependencies (#63)"
ruanyl Jan 31, 2023
b5ef886
Feat add show my models in owner filter (#29)
wanglam Jan 30, 2023
3a60008
bring in-app navigation bar back (#72)
ruanyl Jan 31, 2023
7048cfc
feat: add more actions in model list (#77)
raintygao Feb 1, 2023
67a8066
populate register form with existing model version (#82)
raintygao Feb 2, 2023
110dc64
Feature/add model upload logic (#83)
wanglam Feb 8, 2023
f8c0f57
feat: update register model form ui according to the new design (#85)
ruanyl Feb 8, 2023
115752f
update help button location and flyout content according to updated d…
raintygao Feb 9, 2023
04fad6f
feat: add multiple validation rules on tag field (#93)
ruanyl Feb 13, 2023
c16f225
feat: align ui with the latest design changes (#95)
ruanyl Feb 13, 2023
1eb4688
add metrics validation (#97)
raintygao Feb 14, 2023
52f873d
feat: add register form submission footer (#99)
ruanyl Feb 14, 2023
622a31c
feat: show notifications if form submit success or fail (#110)
ruanyl Feb 20, 2023
0be9346
feat: add model_register_button to model_list (#80)
xyinshen Feb 21, 2023
531b603
feat: add upload callout (#113)
wanglam Feb 22, 2023
a302d48
fix: update-register-form-hearder-descriptions (#114)
xyinshen Feb 22, 2023
a493f78
feat: upload file after register form submitted (#117)
ruanyl Feb 22, 2023
e0bca75
feat: update artifact file validation rules (#118)
ruanyl Feb 24, 2023
9509ceb
feat: display notification when upload model by URL (#126)
ruanyl Feb 27, 2023
ea5d5ec
Feature/add model name unique verification (#129)
wanglam Mar 7, 2023
2f46e80
Feature/rename annotation and remove model details (#133)
wanglam Mar 7, 2023
3083a8b
Feature/fetch pre trained model list (#131)
wanglam Mar 8, 2023
d22c759
fix: revert legacy pagination methods
ruanyl Mar 9, 2023
ed81610
feat: init model group page (#134)
ruanyl Mar 10, 2023
22ebc9c
Featuer/fill pre trained model data to register form (#135)
wanglam Mar 10, 2023
7654fcd
Feature file version title and configuration description (#130)
xyinshen Mar 10, 2023
3ff81c1
fix: tweak test mocks (#137)
ruanyl Mar 13, 2023
2ba8108
Feature update description max width 725 (#140)
xyinshen Mar 13, 2023
c24d55d
feat: disallow user to type if text exceed max length (#138)
ruanyl Mar 14, 2023
8a7ed88
Feature/update model register tags logic (#142)
wanglam Mar 16, 2023
8787bd4
feat: add form error call-out (#141)
ruanyl Mar 16, 2023
88e983c
feat: add model file format select (#143)
ruanyl Mar 17, 2023
69a6823
feat: tweaks form section titles per new design (#144)
ruanyl Mar 20, 2023
95918d1
Feature change register form max width to 1000px and make it centered…
xyinshen Mar 22, 2023
33705dc
test: initiate the use of MSW for API mocking (#147)
ruanyl Mar 24, 2023
7b1fea3
Feature/add model version detail mock page (#153)
wanglam Apr 6, 2023
60b1129
feat: support for adding tag types (#161)
ruanyl Apr 19, 2023
9121c4d
Feature/replace model list stage filter with deployment toggle (#163)
wanglam Apr 20, 2023
cb0c0e8
Feature/update tag filter (#162)
wanglam Apr 23, 2023
03e554b
Feature/update model detail page layout (#166)
wanglam Apr 26, 2023
a9c1fd6
Feature/update global breadcrumbs (#164)
wanglam Apr 27, 2023
067a7be
Feature/add model loading empty failed screen for model list (#165)
wanglam May 6, 2023
4fab379
Feature/add versions table in model group detail (#170)
wanglam May 9, 2023
635dffe
Feature/add id column for model versions table (#177)
wanglam May 11, 2023
76f0422
Version details page mockup (#179)
ruanyl May 12, 2023
b6e4dae
Feature/add details tab content in model group page (#176)
wanglam May 12, 2023
ec603ba
feat(ui): version information edit component (#180)
ruanyl May 17, 2023
4df7a51
Feature/add tags tab content model group page (#181)
wanglam May 18, 2023
9aa8391
feat: version tag edit (#182)
ruanyl May 22, 2023
e119ebb
feat(ui): artifact and configuration edit (#187)
ruanyl May 23, 2023
b7289bd
Feature/add model version loading empty error screens (#185)
wanglam May 24, 2023
8ab8a3c
Feature/add deploy confirmation modal (#186)
wanglam May 24, 2023
edec198
Feature/create model group before register (#192)
wanglam May 26, 2023
100af2e
Feature/jump to model detail page with correct (#193)
wanglam May 26, 2023
c0831d5
Feature/update model list with model group (#197)
wanglam May 30, 2023
59d0d04
feat: deploy and undeploy api integration (#198)
ruanyl May 31, 2023
52d1e4c
fix: map model_group_id to model_id (#207)
ruanyl Jun 12, 2023
7bf1a7a
Feature/add model version delete modal (#199)
wanglam Jun 12, 2023
6bd7184
Feature/import model by name (#218)
wanglam Jun 19, 2023
9361ee6
Feat add basic model delete (#219)
wanglam Jun 19, 2023
9d7851e
feat: refresh model version data after deploy or undeploy complete (#…
wanglam Jun 20, 2023
3ba16ab
fix: fix error when search for model name when index hasn't created (…
ruanyl Jun 20, 2023
42d76cd
build: add experimental release action (#221)
ruanyl Jun 20, 2023
a2531e8
Fix owner and transport missing
wanglam Jan 3, 2025
e767c0c
Hide model registry entrances
wanglam Jan 3, 2025
d74398a
Replace response factory with response
wanglam Jan 3, 2025
93f17d6
Renaming model version service test cases
wanglam Jan 3, 2025
0e85397
Rename model version utils
wanglam Jan 3, 2025
1409e0c
Renaming model version router
wanglam Jan 3, 2025
eca4234
Hide global breadcrumbs model registry unit tests
wanglam Jan 3, 2025
e1e8670
Fix model version table unit tests
wanglam Jan 3, 2025
d9faa0d
Fix invalid import path for model util
wanglam Jan 6, 2025
717cbad
Fix failed UT in monitoring page
wanglam Jan 6, 2025
f392d31
Fix failed cases in useMonitoring
wanglam Jan 6, 2025
64bc340
Change back waitFor
wanglam Jan 6, 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
Prev Previous commit
Next Next commit
add metrics validation (#97)
* feat: add metrics validation

Signed-off-by: raintygao <tygao@amazon.com>

* test: add UT

Signed-off-by: raintygao <tygao@amazon.com>

---------

Signed-off-by: raintygao <tygao@amazon.com>
Signed-off-by: Lin Wang <wonglam@amazon.com>
raintygao authored and wanglam committed Dec 25, 2024
commit 1eb46886b73e9d1860eaf4f3e5e641130c917374
Original file line number Diff line number Diff line change
@@ -59,13 +59,14 @@ describe('<RegisterModel /> Evaluation Metrics', () => {
expect(onSubmitMock).toHaveBeenCalled();
});

it('should submit the form if metric name is selected but metric value are empty', async () => {
it('should NOT submit the form if metric name is selected but metric value are empty and error message in screen', async () => {
const result = await setup();
await result.user.click(screen.getByLabelText(/^metric$/i));
await result.user.click(screen.getByText('Metric 1'));
await result.user.click(result.submitButton);

expect(onSubmitMock).toHaveBeenCalled();
expect(onSubmitMock).not.toHaveBeenCalled();
expect(screen.getByText('At least one value is required. Enter a value')).toBeInTheDocument();
});

it('should submit the form if metric name and all metric value are selected', async () => {
@@ -117,4 +118,13 @@ describe('<RegisterModel /> Evaluation Metrics', () => {

expect(onSubmitMock).not.toHaveBeenCalled();
});

it('should keep metric value not more than 2 decimal point', async () => {
const result = await setup();
await result.user.click(screen.getByLabelText(/^metric$/i));
await result.user.click(screen.getByText('Metric 1'));

await result.user.type(screen.getByLabelText(/training value/i), '1.111');
expect(screen.getByLabelText(/training value/i)).toHaveValue(1.11);
});
});
94 changes: 73 additions & 21 deletions public/components/register_model/evaluation_metrics.tsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import {
EuiFormRow,
EuiTitle,
@@ -15,51 +15,75 @@ import {
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { useController, useFormContext } from 'react-hook-form';
import { useController, useFormContext, useWatch } from 'react-hook-form';

import type { ModelFileFormData, ModelUrlFormData } from './register_model.types';
import { useMetricNames } from './register_model.hooks';
import { fixTwoDecimalPoint } from '../../utils';

const METRIC_VALUE_STEP = 0.01;
const MAX_METRIC_NAME_LENGTH = 50;

export const EvaluationMetricsPanel = () => {
const { control } = useFormContext<ModelFileFormData | ModelUrlFormData>();
const { trigger, control } = useFormContext<ModelFileFormData | ModelUrlFormData>();
const [isRequiredValueText, setIsRequiredValueText] = useState(false);
const [metricNamesLoading, metricNames] = useMetricNames();

// TODO: this has to be hooked with data from BE API
const options = useMemo(() => {
return metricNames.map((n) => ({ label: n }));
}, [metricNames]);

const metricFieldController = useController({
name: 'metricName',
const metricKeyController = useController({
name: 'metric.key',
control,
});

const metric = useWatch({
control,
name: 'metric',
});

const valueValidateFn = () => {
if (metric) {
const { trainingValue, validationValue, testingValue, key } = metric;
if (key && !trainingValue && !validationValue && !testingValue) {
setIsRequiredValueText(true);
return false;
} else {
setIsRequiredValueText(false);
return true;
}
}
return true;
};
const trainingMetricFieldController = useController({
name: 'trainingMetricValue',
name: 'metric.trainingValue',
control,
rules: {
max: 1,
min: 0,
validate: valueValidateFn,
},
});

const validationMetricFieldController = useController({
name: 'validationMetricValue',
name: 'metric.validationValue',
control,
rules: {
max: 1,
min: 0,
validate: valueValidateFn,
},
});

const testingMetricFieldController = useController({
name: 'testingMetricValue',
name: 'metric.testingValue',
control,
rules: {
max: 1,
min: 0,
validate: valueValidateFn,
},
});

@@ -69,13 +93,13 @@ export const EvaluationMetricsPanel = () => {
trainingMetricFieldController.field.onChange('');
validationMetricFieldController.field.onChange('');
testingMetricFieldController.field.onChange('');
metricFieldController.field.onChange('');
metricKeyController.field.onChange('');
} else {
metricFieldController.field.onChange(data[0].label);
metricKeyController.field.onChange(data[0].label);
}
},
[
metricFieldController,
metricKeyController,
trainingMetricFieldController,
validationMetricFieldController,
testingMetricFieldController,
@@ -84,9 +108,12 @@ export const EvaluationMetricsPanel = () => {

const onCreateMetricName = useCallback(
(metricName: string) => {
metricFieldController.field.onChange(metricName);
if (metricName.length > MAX_METRIC_NAME_LENGTH) {
return;
}
metricKeyController.field.onChange(metricName);
},
[metricFieldController]
[metricKeyController.field]
);

const metricValueFields = [
@@ -95,8 +122,25 @@ export const EvaluationMetricsPanel = () => {
{ label: 'Testing value', controller: testingMetricFieldController },
];

const onBlur = useCallback(
(e: React.FocusEvent<HTMLDivElement>) => {
// The blur could happen when selecting combo box dropdown
// But we don't want to trigger form validation in this case
if (
(e.relatedTarget?.getAttribute('role') === 'option' &&
e.relatedTarget?.tagName === 'BUTTON') ||
e.relatedTarget?.getAttribute('role') === 'textbox'
) {
return;
}
// Validate the form only when the current tag row blurred
trigger('metric');
},
[trigger]
);

return (
<div style={{ width: 475 }}>
<div style={{ width: 475 }} onBlur={onBlur}>
<EuiTitle size="s">
<h3>
Evaluation Metrics - <i style={{ fontWeight: 300 }}>optional</i>
@@ -111,22 +155,23 @@ export const EvaluationMetricsPanel = () => {
<EuiFormRow
fullWidth
label="Metric"
isInvalid={Boolean(metricFieldController.fieldState.error)}
isInvalid={Boolean(metricKeyController.fieldState.error)}
>
<EuiComboBox
fullWidth
isLoading={metricNamesLoading}
isInvalid={Boolean(metricKeyController.fieldState.error)}
placeholder='Select or add a metric, like "Accuracy"'
singleSelection={{ asPlainText: true }}
options={options}
selectedOptions={
metricFieldController.field.value ? [{ label: metricFieldController.field.value }] : []
metricKeyController.field.value ? [{ label: metricKeyController.field.value }] : []
}
onChange={onMetricNameChange}
onCreateOption={onCreateMetricName}
customOptionText="Add {searchValue} as new metric name"
onBlur={metricFieldController.field.onBlur}
inputRef={metricFieldController.field.ref}
customOptionText={`Add {searchValue} as new metric name. (${MAX_METRIC_NAME_LENGTH} characters allowed)`}
onBlur={metricKeyController.field.onBlur}
inputRef={metricKeyController.field.ref}
/>
</EuiFormRow>
<EuiSpacer />
@@ -141,18 +186,25 @@ export const EvaluationMetricsPanel = () => {
<EuiFieldNumber
placeholder="Enter a value"
isInvalid={Boolean(controller.fieldState.error)}
disabled={!metricFieldController.field.value}
disabled={!metricKeyController.field.value}
step={METRIC_VALUE_STEP}
name={controller.field.name}
value={controller.field.value ?? ''}
onChange={controller.field.onChange}
onChange={(value) =>
controller.field.onChange(fixTwoDecimalPoint(value.target.value))
}
onBlur={controller.field.onBlur}
inputRef={controller.field.ref}
/>
</EuiFormRow>
</EuiFlexItem>
))}
</EuiFlexGroup>
{isRequiredValueText && (
<EuiText color="danger" size="xs">
At least one value is required. Enter a value
</EuiText>
)}
</div>
);
};
12 changes: 8 additions & 4 deletions public/components/register_model/register_model.types.ts
Original file line number Diff line number Diff line change
@@ -8,16 +8,20 @@ export interface Tag {
value: string;
}

interface Metric {
key: string;
trainingValue: string;
validationValue: string;
testingValue: string;
}

interface ModelFormBase {
name: string;
version: string;
description: string;
annotations?: string;
configuration: string;
metricName?: string;
trainingMetricValue?: string;
validationMetricValue?: string;
testingMetricValue?: string;
metric?: Metric;
tags?: Tag[];
}

1 change: 1 addition & 0 deletions public/utils/index.ts
Original file line number Diff line number Diff line change
@@ -5,3 +5,4 @@

export * from './table';
export * from './version';
export * from './number';
8 changes: 8 additions & 0 deletions public/utils/number.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export const fixTwoDecimalPoint = (value: string) => {
return value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');
};