Skip to content

Commit 9f7ba6c

Browse files
authored
feat: DEV-1682: per model backend model version selector (#2476)
* feat: DEV-1682: add model version selector to ml backend edit form * remove project based model version settings * feat: DEV-1682: add toggle switch for ml_backend auto-update with FF implementation * feat: DEV-1682: moving the original project model version settings to where its supposed to be
1 parent 148b523 commit 9f7ba6c

File tree

5 files changed

+187
-64
lines changed

5 files changed

+187
-64
lines changed

label_studio/frontend/src/config/ApiConfig.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ export const API_CONFIG = {
4949
deleteMLBackend: "DELETE:/ml/:pk",
5050
trainMLBackend: "POST:/ml/:pk/train",
5151
predictWithML: "POST:/ml/:pk/predict",
52-
modelVersions: "/projects/:pk/model-versions",
52+
projectModelVersions: "/projects/:pk/model-versions",
53+
modelVersions: "/ml/:pk/versions",
5354
mlInteractive: "POST:/ml/:pk/interactive-annotating",
5455

5556
// Export

label_studio/frontend/src/pages/Settings/MachineLearningSettings/MachineLearningSettings.js

Lines changed: 30 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,21 @@ import { Description } from '../../../components/Description/Description';
44
import { Divider } from '../../../components/Divider/Divider';
55
import { ErrorWrapper } from '../../../components/Error/Error';
66
import { InlineError } from '../../../components/Error/InlineError';
7-
import { Form, Input, Label, Select, TextArea, Toggle } from '../../../components/Form';
8-
import { FormResponseContext } from '../../../components/Form/FormContext';
7+
import { Form, Input, Label, TextArea, Toggle } from '../../../components/Form';
98
import { modal } from '../../../components/Modal/Modal';
109
import { useAPI } from '../../../providers/ApiProvider';
1110
import { ProjectContext } from '../../../providers/ProjectProvider';
1211
import { MachineLearningList } from './MachineLearningList';
12+
import { ProjectModelVersionSelector } from './ProjectModelVersionSelector';
13+
import { ModelVersionSelector } from './ModelVersionSelector';
14+
import { FF_DEV_1682, isFF } from '../../../utils/feature-flags';
1315
import './MachineLearningSettings.styl';
1416

1517
export const MachineLearningSettings = () => {
1618
const api = useAPI();
17-
const { project, fetchProject, updateProject } = useContext(ProjectContext);
19+
const { project, fetchProject } = useContext(ProjectContext);
1820
const [mlError, setMLError] = useState();
1921
const [backends, setBackends] = useState([]);
20-
const [versions, setVersions] = useState([]);
21-
22-
const resetMLVersion = useCallback(async (e) => {
23-
e.preventDefault();
24-
e.stopPropagation();
25-
26-
await updateProject({
27-
model_version: null,
28-
});
29-
}, [api, project]);
3022

3123
const fetchBackends = useCallback(async () => {
3224
const models = await api.callApi('mlBackends', {
@@ -35,33 +27,12 @@ export const MachineLearningSettings = () => {
3527
},
3628
});
3729

38-
3930
if (models) setBackends(models);
4031
}, [api, project, setBackends]);
4132

42-
const fetchMLVersions = useCallback(async () => {
43-
const modelVersions = await api.callApi("modelVersions", {
44-
params: {
45-
pk: project.id,
46-
},
47-
});
48-
49-
var versions = [];
50-
51-
for (const [key, value] of Object.entries(modelVersions)) {
52-
versions.push({
53-
value: key,
54-
label: key + " (" + value + " predictions)",
55-
});
56-
}
57-
58-
setVersions(versions);
59-
}, [api, project.id]);
60-
6133
const showMLFormModal = useCallback((backend) => {
6234
const action = backend ? "updateMLBackend" : "addMLBackend";
6335

64-
console.log({ backend });
6536
const modalProps = {
6637
title: `${backend ? 'Edit' : 'Add'} model`,
6738
style: { width: 760 },
@@ -89,7 +60,22 @@ export const MachineLearningSettings = () => {
8960
<TextArea name="description" label="Description" style={{ minHeight: 120 }}/>
9061
</Form.Row>
9162

92-
<Form.Row columnCount={1}>
63+
<Form.Row columnCount={isFF(FF_DEV_1682) ? 3 : 1}>
64+
{isFF(FF_DEV_1682) && (
65+
<>
66+
<ModelVersionSelector
67+
object={backend}
68+
apiName="modelVersions"
69+
valueName="version"
70+
label="Version"
71+
/>
72+
73+
<Toggle
74+
name="auto_update"
75+
label="Allow version auto-update"
76+
/>
77+
</>
78+
)}
9379
<Toggle
9480
name="is_interactive"
9581
label="Use for interactive preannotations"
@@ -126,7 +112,6 @@ export const MachineLearningSettings = () => {
126112
useEffect(() => {
127113
if (project.id) {
128114
fetchBackends();
129-
fetchMLVersions();
130115
}
131116
}, [project]);
132117

@@ -177,34 +162,16 @@ export const MachineLearningSettings = () => {
177162
</div>
178163
</Form.Row>
179164

180-
{versions.length > 1 && (
181-
<Form.Row columnCount={1}>
182-
<Label
183-
text="Model Version"
184-
description="Model version allows you to specify which prediction will be shown to the annotators."
185-
style={{ marginTop: 16 }}
186-
large
187-
/>
188-
189-
<div style={{ display: 'flex', alignItems: 'center', width: 400, paddingLeft: 16 }}>
190-
<div style={{ flex: 1, paddingRight: 16 }}>
191-
<Select
192-
name="model_version"
193-
defaultValue={null}
194-
options={[
195-
...versions,
196-
]}
197-
placeholder="No model version selected"
198-
/>
199-
</div>
200-
201-
<Button onClick={resetMLVersion}>
202-
Reset
203-
</Button>
204-
</div>
205-
206-
</Form.Row>
165+
{!isFF(FF_DEV_1682) && (
166+
<ProjectModelVersionSelector />
207167
)}
168+
169+
<Form.Actions>
170+
<Form.Indicator>
171+
<span case="success">Saved!</span>
172+
</Form.Indicator>
173+
<Button type="submit" look="primary" style={{ width: 120 }}>Save</Button>
174+
</Form.Actions>
208175
</Form>
209176

210177
<MachineLearningList
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useCallback, useEffect, useState } from 'react';
2+
import { useAPI } from '../../../providers/ApiProvider';
3+
import { Select } from '../../../components/Form';
4+
5+
export const ModelVersionSelector = ({
6+
name = "model_version",
7+
valueName = "model_version",
8+
apiName = "modelVersions",
9+
placeholder = "No model version selected",
10+
object,
11+
...props
12+
}) => {
13+
const api = useAPI();
14+
const [versions, setVersions] = useState([]);
15+
16+
const fetchMLVersions = useCallback(async () => {
17+
const pk = object?.id;
18+
19+
if (!pk) return;
20+
21+
const modelVersions = await api.callApi(apiName, {
22+
params: {
23+
pk,
24+
},
25+
});
26+
27+
if (!modelVersions) return;
28+
29+
setVersions(Object.entries(modelVersions).reduce((v, [key, value]) => [...v, {
30+
value: key,
31+
label: key + " (" + value + " predictions)",
32+
}], []));
33+
}, [api, object?.id, apiName]);
34+
35+
useEffect(fetchMLVersions, []);
36+
37+
return (
38+
<Select
39+
name={name}
40+
disabled={!versions.length}
41+
defaultValue={object?.[valueName] || null}
42+
options={versions}
43+
placeholder={placeholder}
44+
{...props}
45+
/>
46+
);
47+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { useCallback, useContext, useEffect, useState } from 'react';
2+
import { useAPI } from '../../../providers/ApiProvider';
3+
import { Button } from '../../../components';
4+
import { Form, Label, Select } from '../../../components/Form';
5+
import { ProjectContext } from '../../../providers/ProjectProvider';
6+
7+
export const ProjectModelVersionSelector = ({
8+
name = "model_version",
9+
valueName = "model_version",
10+
apiName = "projecModelVersions",
11+
placeholder = "No model version selected",
12+
...props
13+
}) => {
14+
const api = useAPI();
15+
const { project, updateProject } = useContext(ProjectContext);
16+
const [versions, setVersions] = useState([]);
17+
18+
const resetMLVersion = useCallback(async (e) => {
19+
e.preventDefault();
20+
e.stopPropagation();
21+
22+
await updateProject({
23+
model_version: null,
24+
});
25+
}, [updateProject]);
26+
27+
const fetchMLVersions = useCallback(async () => {
28+
const pk = project?.id;
29+
30+
if (!pk) return;
31+
32+
const modelVersions = await api.callApi(apiName, {
33+
params: {
34+
pk,
35+
},
36+
});
37+
38+
if (!modelVersions) return;
39+
40+
setVersions(project.entries(modelVersions).reduce((v, [key, value]) => [...v, {
41+
value: key,
42+
label: key + " (" + value + " predictions)",
43+
}], []));
44+
}, [api, project?.id, apiName]);
45+
46+
useEffect(fetchMLVersions, []);
47+
48+
return (
49+
<Form.Row columnCount={1}>
50+
<Label
51+
text="Model Version"
52+
description={(
53+
<>
54+
Model version allows you to specify which prediction will be shown to the annotators.
55+
{project.model_version && (
56+
<>
57+
<br />
58+
<b>Current project model version: {project.model_version}</b>
59+
</>
60+
)}
61+
</>
62+
)}
63+
style={{ marginTop: 16 }}
64+
large
65+
/>
66+
67+
<div style={{ display: 'flex', alignItems: 'center', width: 400, paddingLeft: 16 }}>
68+
<div style={{ flex: 1, paddingRight: 16 }}>
69+
<Select
70+
name={name}
71+
disabled={!versions.length}
72+
defaultValue={project?.[valueName] || null}
73+
options={versions}
74+
placeholder={placeholder}
75+
{...props}
76+
/>
77+
</div>
78+
79+
<Button onClick={resetMLVersion}>
80+
Reset
81+
</Button>
82+
</div>
83+
</Form.Row>
84+
);
85+
};
86+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const FEATURE_FLAGS = window.APP_SETTINGS?.feature_flags || {};
2+
3+
// Fix displaying of created_at in the review mode
4+
export const FF_DEV_1480 = "ff_front_dev_1480_created_on_in_review_180122_short";
5+
// Fix avatar blinking and stuck on organization page
6+
export const FF_DEV_1495 = "ff_front_dev_1495_avatar_mess_210122_short";
7+
// Notifications
8+
export const FF_DEV_1658 = "ff_front_dev_1658_notification_center_170222_short";
9+
// Add rejected icon on the cards
10+
export const FF_DEV_1614 = "ff_back_1614_rejected_queue_17022022_short";
11+
// Model version selector per model backend
12+
export const FF_DEV_1682 = "ff_front_dev_1682_model_version_dropdown_070622_short";
13+
14+
export function isFF(id) {
15+
if (id in FEATURE_FLAGS) {
16+
return FEATURE_FLAGS[id] === true;
17+
}
18+
else {
19+
return window.APP_SETTINGS?.feature_flags_default_value === true;
20+
}
21+
}
22+

0 commit comments

Comments
 (0)