Implement UI for managing multiple models (#24023)

* Implement hardware detection and UI management

* Cleanup Frigate+ detection

* Don't count model as changed

* Fixes for audio map error

* Add descriptions

* Enforce that all model must exist

* Fix hardware picking

* Docs fixes

* WebUI cleanup

* Cleanup handling of scenes

* UI refinement

* Cleanup recommended UI

* test fixews
This commit is contained in:
Nicolas Mowen
2026-09-12 07:30:04 -06:00
parent 3f20209339
commit 27a40a507b
59 changed files with 2554 additions and 2587 deletions
@@ -110,6 +110,21 @@ const detect: SectionConfigOverrides = {
return Math.abs(newRatio - savedRatio) > 0.01;
},
},
{
key: "detect-scene-without-model",
field: "scene",
position: "after",
messageKey: "configMessages.detect.sceneWithoutModel",
severity: "warning",
docLink: "/configuration/object_detectors#running-more-than-one-model",
condition: (ctx) => {
const scene = ctx.formData?.scene as string | undefined;
if (!scene || scene === "all") return false;
const models = ctx.fullConfig?.models;
if (!models) return false;
return !models.some((model) => model.scene === scene);
},
},
{
key: "fps-greater-than-five",
field: "fps",
@@ -153,6 +168,7 @@ const detect: SectionConfigOverrides = {
],
fieldOrder: [
"enabled",
"scene",
"width",
"height",
"fps",
@@ -170,6 +186,11 @@ const detect: SectionConfigOverrides = {
tracking: ["min_initialized", "max_disappeared"],
},
uiSchema: {
scene: {
"ui:options": {
enumI18nPrefix: "detectionModels.scenes",
},
},
annotation_offset: {
"ui:options": {
signed: true,
@@ -186,6 +207,7 @@ const detect: SectionConfigOverrides = {
},
global: {
restartRequired: [
"scene",
"fps",
"width",
"height",
@@ -195,6 +217,7 @@ const detect: SectionConfigOverrides = {
},
camera: {
restartRequired: [
"scene",
"fps",
"width",
"height",
@@ -211,6 +234,7 @@ const detect: SectionConfigOverrides = {
hiddenFields: [
"enabled",
"enabled_in_config",
"scene",
"min_initialized",
"max_disappeared",
"annotation_offset",
@@ -1,28 +0,0 @@
import type { SectionConfigOverrides } from "./types";
const detectorHiddenFields = [
"*.model.labelmap",
"*.model.attributes_map",
"*.model",
"*.model_path",
];
const detectors: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/object_detectors",
fieldOrder: [],
advancedFields: [],
hiddenFields: detectorHiddenFields,
uiSchema: {
"ui:field": "DetectorHardwareField",
"ui:options": {
multiInstanceTypes: ["cpu", "onnx", "openvino", "edgetpu"],
typeOrder: ["onnx", "openvino", "edgetpu"],
hiddenByType: {},
hiddenFields: detectorHiddenFields,
},
},
},
};
export default detectors;
@@ -1,8 +1,24 @@
import type { SectionConfigOverrides } from "./types";
const model: SectionConfigOverrides = {
// scene and devices are rendered by ModelsField itself; the rest of each model
// is delegated back to the schema form
const modelFields = [
"path",
"labelmap_path",
"width",
"height",
"input_pixel_format",
"input_tensor",
"input_dtype",
"model_type",
];
const models: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/object_detectors#model",
sectionDocs: "/configuration/object_detectors",
// the default-model rule must be enforced as the list is edited, not
// only when the form is submitted
liveValidate: true,
fieldMessages: [
{
key: "model-optimized-for-320",
@@ -36,51 +52,46 @@ const model: SectionConfigOverrides = {
},
},
],
// every model field takes effect only when the detection processes restart
restartRequired: [
"path",
"labelmap_path",
"width",
"height",
"scene",
"devices",
...modelFields,
"labelmap",
"attributes_map",
"input_tensor",
"input_pixel_format",
"input_dtype",
"model_type",
],
fieldOrder: [
"path",
"labelmap_path",
"width",
"height",
"input_pixel_format",
"input_tensor",
"input_dtype",
"model_type",
],
advancedFields: [
"input_pixel_format",
"input_tensor",
"input_dtype",
"model_type",
],
].map((field) => `*.${field}`),
hiddenFields: [
"labelmap",
"attributes_map",
"colormap",
"all_attributes",
"non_logo_attributes",
"plus",
"*.labelmap",
"*.attributes_map",
"*.colormap",
"*.all_attributes",
"*.non_logo_attributes",
"*.plus",
],
uiSchema: {
path: {
"ui:options": { size: "md" },
},
labelmap_path: {
"ui:options": { size: "md" },
"ui:field": "ModelsField",
items: {
path: {
"ui:options": { size: "md" },
},
labelmap_path: {
"ui:options": { size: "md" },
},
input_pixel_format: {
"ui:options": { advanced: true, size: "xs" },
},
input_tensor: {
"ui:options": { advanced: true, size: "xs" },
},
input_dtype: {
"ui:options": { advanced: true, size: "xs" },
},
model_type: {
"ui:options": { advanced: true, size: "xs" },
},
},
},
},
};
export default model;
export default models;