Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion label_studio/frontend/src/config/ApiConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export const API_CONFIG = {
deleteMLBackend: "DELETE:/ml/:pk",
trainMLBackend: "POST:/ml/:pk/train",
predictWithML: "POST:/ml/:pk/predict",
modelVersions: "/projects/:pk/model-versions",
projectModelVersions: "/projects/:pk/model-versions",
modelVersions: "/ml/:pk/versions",
mlInteractive: "POST:/ml/:pk/interactive-annotating",

// Export
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,21 @@ import { Description } from '../../../components/Description/Description';
import { Divider } from '../../../components/Divider/Divider';
import { ErrorWrapper } from '../../../components/Error/Error';
import { InlineError } from '../../../components/Error/InlineError';
import { Form, Input, Label, Select, TextArea, Toggle } from '../../../components/Form';
import { FormResponseContext } from '../../../components/Form/FormContext';
import { Form, Input, Label, TextArea, Toggle } from '../../../components/Form';
import { modal } from '../../../components/Modal/Modal';
import { useAPI } from '../../../providers/ApiProvider';
import { ProjectContext } from '../../../providers/ProjectProvider';
import { MachineLearningList } from './MachineLearningList';
import { ProjectModelVersionSelector } from './ProjectModelVersionSelector';
import { ModelVersionSelector } from './ModelVersionSelector';
import { FF_DEV_1682, isFF } from '../../../utils/feature-flags';
import './MachineLearningSettings.styl';

export const MachineLearningSettings = () => {
const api = useAPI();
const { project, fetchProject, updateProject } = useContext(ProjectContext);
const { project, fetchProject } = useContext(ProjectContext);
const [mlError, setMLError] = useState();
const [backends, setBackends] = useState([]);
const [versions, setVersions] = useState([]);

const resetMLVersion = useCallback(async (e) => {
e.preventDefault();
e.stopPropagation();

await updateProject({
model_version: null,
});
}, [api, project]);

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


if (models) setBackends(models);
}, [api, project, setBackends]);

const fetchMLVersions = useCallback(async () => {
const modelVersions = await api.callApi("modelVersions", {
params: {
pk: project.id,
},
});

var versions = [];

for (const [key, value] of Object.entries(modelVersions)) {
versions.push({
value: key,
label: key + " (" + value + " predictions)",
});
}

setVersions(versions);
}, [api, project.id]);

const showMLFormModal = useCallback((backend) => {
const action = backend ? "updateMLBackend" : "addMLBackend";

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

<Form.Row columnCount={1}>
<Form.Row columnCount={isFF(FF_DEV_1682) ? 3 : 1}>
{isFF(FF_DEV_1682) && (
<>
<ModelVersionSelector
object={backend}
apiName="modelVersions"
valueName="version"
label="Version"
/>

<Toggle
name="auto_update"
label="Allow version auto-update"
/>
</>
)}
<Toggle
name="is_interactive"
label="Use for interactive preannotations"
Expand Down Expand Up @@ -126,7 +112,6 @@ export const MachineLearningSettings = () => {
useEffect(() => {
if (project.id) {
fetchBackends();
fetchMLVersions();
}
}, [project]);

Expand Down Expand Up @@ -177,34 +162,16 @@ export const MachineLearningSettings = () => {
</div>
</Form.Row>

{versions.length > 1 && (
<Form.Row columnCount={1}>
<Label
text="Model Version"
description="Model version allows you to specify which prediction will be shown to the annotators."
style={{ marginTop: 16 }}
large
/>

<div style={{ display: 'flex', alignItems: 'center', width: 400, paddingLeft: 16 }}>
<div style={{ flex: 1, paddingRight: 16 }}>
<Select
name="model_version"
defaultValue={null}
options={[
...versions,
]}
placeholder="No model version selected"
/>
</div>

<Button onClick={resetMLVersion}>
Reset
</Button>
</div>

</Form.Row>
{!isFF(FF_DEV_1682) && (
<ProjectModelVersionSelector />
)}

<Form.Actions>
<Form.Indicator>
<span case="success">Saved!</span>
</Form.Indicator>
<Button type="submit" look="primary" style={{ width: 120 }}>Save</Button>
</Form.Actions>
</Form>

<MachineLearningList
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useCallback, useEffect, useState } from 'react';
import { useAPI } from '../../../providers/ApiProvider';
import { Select } from '../../../components/Form';

export const ModelVersionSelector = ({
name = "model_version",
valueName = "model_version",
apiName = "modelVersions",
placeholder = "No model version selected",
object,
...props
}) => {
const api = useAPI();
const [versions, setVersions] = useState([]);

const fetchMLVersions = useCallback(async () => {
const pk = object?.id;

if (!pk) return;

const modelVersions = await api.callApi(apiName, {
params: {
pk,
},
});

if (!modelVersions) return;

setVersions(Object.entries(modelVersions).reduce((v, [key, value]) => [...v, {
value: key,
label: key + " (" + value + " predictions)",
}], []));
}, [api, object?.id, apiName]);

useEffect(fetchMLVersions, []);

return (
<Select
name={name}
disabled={!versions.length}
defaultValue={object?.[valueName] || null}
options={versions}
placeholder={placeholder}
{...props}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useCallback, useContext, useEffect, useState } from 'react';
import { useAPI } from '../../../providers/ApiProvider';
import { Button } from '../../../components';
import { Form, Label, Select } from '../../../components/Form';
import { ProjectContext } from '../../../providers/ProjectProvider';

export const ProjectModelVersionSelector = ({
name = "model_version",
valueName = "model_version",
apiName = "projecModelVersions",
placeholder = "No model version selected",
...props
}) => {
const api = useAPI();
const { project, updateProject } = useContext(ProjectContext);
const [versions, setVersions] = useState([]);

const resetMLVersion = useCallback(async (e) => {
e.preventDefault();
e.stopPropagation();

await updateProject({
model_version: null,
});
}, [updateProject]);

const fetchMLVersions = useCallback(async () => {
const pk = project?.id;

if (!pk) return;

const modelVersions = await api.callApi(apiName, {
params: {
pk,
},
});

if (!modelVersions) return;

setVersions(project.entries(modelVersions).reduce((v, [key, value]) => [...v, {
value: key,
label: key + " (" + value + " predictions)",
}], []));
}, [api, project?.id, apiName]);

useEffect(fetchMLVersions, []);

return (
<Form.Row columnCount={1}>
<Label
text="Model Version"
description={(
<>
Model version allows you to specify which prediction will be shown to the annotators.
{project.model_version && (
<>
<br />
<b>Current project model version: {project.model_version}</b>
</>
)}
</>
)}
style={{ marginTop: 16 }}
large
/>

<div style={{ display: 'flex', alignItems: 'center', width: 400, paddingLeft: 16 }}>
<div style={{ flex: 1, paddingRight: 16 }}>
<Select
name={name}
disabled={!versions.length}
defaultValue={project?.[valueName] || null}
options={versions}
placeholder={placeholder}
{...props}
/>
</div>

<Button onClick={resetMLVersion}>
Reset
</Button>
</div>
</Form.Row>
);
};

22 changes: 22 additions & 0 deletions label_studio/frontend/src/utils/feature-flags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const FEATURE_FLAGS = window.APP_SETTINGS?.feature_flags || {};

// Fix displaying of created_at in the review mode
export const FF_DEV_1480 = "ff_front_dev_1480_created_on_in_review_180122_short";
// Fix avatar blinking and stuck on organization page
export const FF_DEV_1495 = "ff_front_dev_1495_avatar_mess_210122_short";
// Notifications
export const FF_DEV_1658 = "ff_front_dev_1658_notification_center_170222_short";
// Add rejected icon on the cards
export const FF_DEV_1614 = "ff_back_1614_rejected_queue_17022022_short";
// Model version selector per model backend
export const FF_DEV_1682 = "ff_front_dev_1682_model_version_dropdown_070622_short";

export function isFF(id) {
if (id in FEATURE_FLAGS) {
return FEATURE_FLAGS[id] === true;
}
else {
return window.APP_SETTINGS?.feature_flags_default_value === true;
}
}