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
+154 -28
View File
@@ -129,19 +129,19 @@ const openPage = async (frigateApp: {
test.describe("Detection models settings @high", () => {
test("renders a card per configured model", async ({ frigateApp }) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["cpu"] },
{ scene: "default", devices: ["cpu"] },
{ scene: "outdoor", devices: ["edgetpu:pci:0"] },
]);
await openPage(frigateApp);
const root = frigateApp.page.locator("#pageRoot");
await expect(root).toContainText("All cameras");
await expect(root).toContainText("Outdoor");
await expect(root).toContainText("Default");
await expect(root).toContainText("outdoor");
});
test("unlimited hardware offers a detector count", async ({ frigateApp }) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["openvino:GPU.0"] },
{ scene: "default", devices: ["openvino:GPU.0"] },
]);
await openPage(frigateApp);
@@ -169,7 +169,7 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["openvino:GPU.0", "openvino:GPU.0"] },
{ scene: "default", devices: ["openvino:GPU.0", "openvino:GPU.0"] },
]);
await openPage(frigateApp);
@@ -190,7 +190,7 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["edgetpu:pci:0"] },
{ scene: "default", devices: ["edgetpu:pci:0"] },
]);
await openPage(frigateApp);
@@ -206,7 +206,7 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["edgetpu:pci:0"] },
{ scene: "default", devices: ["edgetpu:pci:0"] },
{ scene: "outdoor", devices: ["edgetpu:pci:1"] },
]);
await openPage(frigateApp);
@@ -221,7 +221,7 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["openvino:GPU.0"] },
{ scene: "default", devices: ["openvino:GPU.0"] },
{ scene: "outdoor", devices: ["openvino:GPU.1"] },
]);
await openPage(frigateApp);
@@ -234,23 +234,141 @@ test.describe("Detection models settings @high", () => {
);
});
test("adding a model appends a card with an unused scene", async ({
test("adding a model appends a card with a scene to name", async ({
frigateApp,
}) => {
await installRoutes(frigateApp.page, [{ scene: "all", devices: ["cpu"] }]);
await installRoutes(frigateApp.page, [
{ scene: "default", devices: ["cpu"] },
]);
await openPage(frigateApp);
await frigateApp.page.getByRole("button", { name: "Add model" }).click();
// "all" is taken, so the new card takes the next available scene
await expect(frigateApp.page.locator("#pageRoot")).toContainText("Indoor");
// the new card's scene starts empty, and a scene is required to save
const scene = frigateApp.page.locator("#models-1-scene");
await expect(scene).toHaveValue("");
await expect(
frigateApp.page.getByRole("button", { name: /^Save$/ }),
).toBeDisabled();
// the card keeps focus while its scene is typed
await scene.fill("thermal");
await expect(scene).toHaveValue("thermal");
await expect(scene).toBeFocused();
});
test("one model file cannot be split across scenes", async ({
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{
scene: "default",
devices: ["openvino:GPU.0"],
path: "/config/model_cache/yolo.onnx",
},
{
scene: "driveway",
devices: ["openvino:GPU.1"],
path: "/config/model_cache/other.onnx",
},
]);
await openPage(frigateApp);
await frigateApp.page
.locator("#root_1_path")
.fill("/config/model_cache/yolo.onnx");
await expect(frigateApp.page.locator("#pageRoot")).toContainText(
"The Default and driveway models use the same model file",
);
await expect(
frigateApp.page.getByRole("button", { name: /^Save$/ }),
).toBeDisabled();
});
test("two models without a path share the detector's default model", async ({
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "default", devices: ["cpu"] },
{ scene: "driveway", devices: ["cpu"] },
]);
await openPage(frigateApp);
await expect(frigateApp.page.locator("#pageRoot")).toContainText(
"The Default and driveway models use the same model file",
);
});
test("another spelling of the same path is the same model", async ({
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{
scene: "default",
devices: ["openvino:GPU.0"],
path: "/config/model_cache/yolo.onnx",
},
{
scene: "driveway",
devices: ["openvino:GPU.1"],
path: "/config//model_cache/./tmp/../yolo.onnx",
},
]);
await openPage(frigateApp);
await expect(frigateApp.page.locator("#pageRoot")).toContainText(
"The Default and driveway models use the same model file",
);
});
test("one model file may run on different detectors", async ({
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{
scene: "default",
devices: ["openvino:GPU.0"],
path: "/config/model_cache/yolo.onnx",
},
{
scene: "driveway",
devices: ["onnx"],
path: "/config/model_cache/yolo.onnx",
},
]);
await openPage(frigateApp);
await expect(frigateApp.page.locator("#pageRoot")).toContainText(
"driveway",
);
await expect(frigateApp.page.locator("#pageRoot")).not.toContainText(
"models use the same model file",
);
});
test("two models cannot share a scene", async ({ frigateApp }) => {
await installRoutes(frigateApp.page, [
{ scene: "default", devices: ["cpu"] },
{ scene: "thermal", devices: ["edgetpu:pci:0"] },
]);
await openPage(frigateApp);
await frigateApp.page.locator("#models-1-scene").fill("default");
await expect(frigateApp.page.locator("#pageRoot")).toContainText(
"Each model must use a different scene",
);
await expect(
frigateApp.page.getByRole("button", { name: /^Save$/ }),
).toBeDisabled();
});
test("hardware is summarized rather than listed device by device", async ({
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["openvino:GPU.0", "openvino:GPU.0"] },
{ scene: "default", devices: ["openvino:GPU.0", "openvino:GPU.0"] },
]);
await openPage(frigateApp);
@@ -269,7 +387,7 @@ test.describe("Detection models settings @high", () => {
frigateApp.page,
[
{
scene: "all",
scene: "default",
devices: ["openvino:GPU.0"],
path: "plus://abc123",
plus: PLUS_MODEL,
@@ -294,7 +412,7 @@ test.describe("Detection models settings @high", () => {
frigateApp.page,
[
{
scene: "all",
scene: "default",
devices: ["openvino:GPU.0"],
path: "/config/custom.onnx",
},
@@ -325,7 +443,7 @@ test.describe("Detection models settings @high", () => {
frigateApp.page,
[
{
scene: "all",
scene: "default",
devices: ["openvino:GPU.0"],
path: "plus://abc123",
plus: PLUS_MODEL,
@@ -359,7 +477,7 @@ test.describe("Detection models settings @high", () => {
}) => {
await installRoutes(frigateApp.page, [
{
scene: "all",
scene: "default",
devices: ["openvino:GPU.0"],
path: "plus://abc123",
width: 320,
@@ -383,7 +501,13 @@ test.describe("Detection models settings @high", () => {
// says nothing; which device it was built for is what the user picks on
await installRoutes(
frigateApp.page,
[{ scene: "all", devices: ["hailo:PCIe"], path: "/config/custom.hef" }],
[
{
scene: "default",
devices: ["hailo:PCIe"],
path: "/config/custom.hef",
},
],
true,
HAILO_PLUS_MODELS,
true,
@@ -413,7 +537,7 @@ test.describe("Detection models settings @high", () => {
// that must not read as an edit.
await installRoutes(frigateApp.page, [
{
scene: "all",
scene: "default",
devices: ["openvino:GPU.0", "openvino:GPU.0"],
path: "/config/model_cache/abc123",
width: 320,
@@ -438,12 +562,14 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["openvino:GPU.0"] },
{ scene: "default", devices: ["openvino:GPU.0"] },
]);
await openPage(frigateApp);
const root = frigateApp.page.locator("#pageRoot");
await expect(root).toContainText("The environment this model is for");
await expect(root).toContainText(
"A name for the cameras this model is for",
);
await expect(root).toContainText(
"The hardware this model runs its detection on",
);
@@ -454,7 +580,7 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["edgetpu:pci:0"] },
{ scene: "default", devices: ["edgetpu:pci:0"] },
]);
await openPage(frigateApp);
@@ -465,10 +591,10 @@ test.describe("Detection models settings @high", () => {
});
test("removing the default model blocks saving", async ({ frigateApp }) => {
// a camera that names no scene runs the "all" model, so deleting it would
// a camera that names no scene runs the default model, so deleting it would
// leave those cameras with nothing to fall back to
await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["cpu"] },
{ scene: "default", devices: ["cpu"] },
{ scene: "outdoor", devices: ["edgetpu:pci:0"] },
]);
await openPage(frigateApp);
@@ -479,7 +605,7 @@ test.describe("Detection models settings @high", () => {
.click();
await expect(frigateApp.page.locator("#pageRoot")).toContainText(
"One model must use a scene of 'All cameras'",
"One model must use the default scene",
);
await expect(
frigateApp.page.getByRole("button", { name: /^Save$/ }),
@@ -490,7 +616,7 @@ test.describe("Detection models settings @high", () => {
// shareable hardware can report several addressable units; every one of
// them must be reachable, not just the first
const saves = await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["openvino:GPU.0"] },
{ scene: "default", devices: ["openvino:GPU.0"] },
]);
await openPage(frigateApp);
@@ -508,7 +634,7 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
const saves = await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["openvino:GPU.0", "openvino:GPU.1"] },
{ scene: "default", devices: ["openvino:GPU.0", "openvino:GPU.1"] },
]);
await openPage(frigateApp);
@@ -531,7 +657,7 @@ test.describe("Detection models settings @high", () => {
frigateApp,
}) => {
const saves = await installRoutes(frigateApp.page, [
{ scene: "all", devices: ["edgetpu:pci:0"] },
{ scene: "default", devices: ["edgetpu:pci:0"] },
]);
await openPage(frigateApp);