Refactor model scene definitions (#24508)
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s

* Refactor model scene definitions

* Cleanup

* Validate model paths in the UI

* Handle form validation
This commit is contained in:
Nicolas Mowen
2026-09-30 06:02:14 -06:00
committed by GitHub
parent 5e87d101da
commit 1bb61eb808
30 changed files with 862 additions and 227 deletions
+2 -4
View File
@@ -10,6 +10,7 @@ import type { FrigateConfig } from "@/types/frigateConfig";
import type { HealthProblem } from "@/types/health";
import { getSectionConfig } from "@/utils/configUtil";
import { activeCameras } from "@/utils/health";
import { getSceneLabel } from "@/utils/modelUtil";
import { resolveMessageKey } from "@/utils/runtimeOverrides";
function healthMessages(
@@ -119,10 +120,7 @@ export function evaluateConfigHealth(
section === "models" && Array.isArray(sectionData)
? sectionData.map((model, index) => ({
formData: model as ConfigSectionData,
scope: t(
`detectionModels.scenes.${(model as { scene?: string }).scene || "all"}`,
{ ns: "views/settings" },
),
scope: getSceneLabel(t, (model as { scene?: string }).scene),
idSuffix: `model${index}`,
}))
: [
+2 -3
View File
@@ -11,6 +11,7 @@ import type {
import type { EmbeddingsStats, FrigateStats, GpuVendor } from "@/types/stats";
import { EmbeddingThreshold, InferenceThreshold } from "@/types/graph";
import { summarizeDevices } from "@/utils/detectionHardware";
import { getSceneLabel } from "@/utils/modelUtil";
import { isReplayCamera } from "@/utils/cameraUtil";
import { resolveCameraName } from "@/hooks/use-camera-friendly-name";
@@ -142,9 +143,7 @@ export function detectionRows({
const modelRunners = names.slice(cursor, cursor + model.devices.length);
cursor += model.devices.length;
const label = t(`detectionModels.scenes.${model.scene || "all"}`, {
ns: "views/settings",
});
const label = getSceneLabel(t, model.scene);
const id = `detection:${index}`;
const detail = probeFailed
? t("health.hardware.probeUnavailable", {
+42 -3
View File
@@ -1,10 +1,49 @@
import type { TFunction } from "i18next";
import type { HiddenFieldContext } from "@/types/configForm";
import { DetectionModelConfig, FrigateConfig } from "@/types/frigateConfig";
/** The scene of the model used by cameras that don't name one. */
export const DEFAULT_SCENE = "default";
/** Display name for a scene; custom scenes are shown as the user named them. */
export function getSceneLabel(t: TFunction, scene: string | undefined): string {
if (!scene || scene === DEFAULT_SCENE) {
return t("detectionModels.scenes.default", { ns: "views/settings" });
}
return scene;
}
/**
* The scenes a detect section can choose from: those of the configured models,
* default first when a default model exists, plus the saved scene when no model
* uses it, so it can still be seen and changed.
*/
export function getSceneChoices(
ctx: Pick<HiddenFieldContext, "fullConfig" | "fullCameraConfig" | "level">,
): string[] {
const scenes = [
...new Set(
ctx.fullConfig.models?.map((model) => model.scene || DEFAULT_SCENE),
),
].sort((a, b) => Number(b === DEFAULT_SCENE) - Number(a === DEFAULT_SCENE));
const saved =
(ctx.level !== "global"
? ctx.fullCameraConfig?.detect?.scene
: undefined) ?? ctx.fullConfig.detect?.scene;
if (saved && !scenes.includes(saved)) {
scenes.push(saved);
}
return scenes;
}
/**
* The model a camera runs on, matched by the camera's detect scene.
*
* Falls back to the model for every scene, then to the only configured model,
* which is what the backend does when a camera does not name a scene.
* Falls back to the default model, then to the only configured model, which
* is what the backend does when a camera does not name a scene.
*/
export function getModelForCamera(
config?: FrigateConfig,
@@ -26,7 +65,7 @@ export function getModelForCamera(
}
}
return models.find((model) => model.scene == "all") ?? models[0];
return models.find((model) => model.scene == DEFAULT_SCENE) ?? models[0];
}
/** The model used when the question is not about a specific camera. */