Refactor detector and model management (#23995)

* Refactor detector and model management

* Fix model resolution field
This commit is contained in:
Nicolas Mowen
2026-09-12 07:30:04 -06:00
parent fd76eb6c6f
commit 8fe35ace3b
63 changed files with 2052 additions and 1152 deletions
@@ -49,6 +49,7 @@ import {
import { ConfigSectionTemplate } from "@/components/config-form/sections";
import { ConfigMessageBanner } from "@/components/config-form/ConfigMessageBanner";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { getPrimaryModel } from "@/utils/modelUtil";
import {
buildHiddenFieldContext,
getSectionConfig,
@@ -115,8 +116,9 @@ const STATUS_BAR_KEY = "detectors_and_model";
const EMPTY_PENDING: Record<string, ConfigSectionData> = {};
const deriveInitialState = (config: FrigateConfig): PageState => {
const plusModelId = config.model?.plus?.id;
const modelPath = config.model?.path;
const primaryModel = getPrimaryModel(config);
const plusModelId = primaryModel?.plus?.id;
const modelPath = primaryModel?.path;
const plusEnabled = Boolean(config.plus?.enabled);
// The reliable signal that a Plus model is currently active is the
@@ -136,10 +138,12 @@ const deriveInitialState = (config: FrigateConfig): PageState => {
modelTab = "custom";
}
const { plus: _plus, ...modelWithoutPlus } = (config.model ?? {}) as Record<
string,
unknown
>;
const {
plus: _plus,
scene: _scene,
devices: _devices,
...modelWithoutPlus
} = (primaryModel ?? {}) as Record<string, unknown>;
// If a Plus model is active, the resolved `model.path` is auto-derived from
// `plus.id` — drop it so the Custom tab starts clean and doesn't silently
// re-save the same Plus model when the user thinks they switched modes.
@@ -148,7 +152,7 @@ const deriveInitialState = (config: FrigateConfig): PageState => {
}
return {
detectors: (config.detectors ?? {}) as ConfigSectionData,
detectors: { devices: primaryModel?.devices ?? [] } as ConfigSectionData,
modelTab,
plusModelId: plusModelId ?? undefined,
customModel: modelWithoutPlus as ConfigSectionData,
@@ -17,6 +17,7 @@ import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel";
import { FrigateConfig } from "@/types/frigateConfig";
import { isReplayCamera } from "@/utils/cameraUtil";
import type { SettingsPageProps } from "@/views/settings/SingleSectionPage";
import { getPrimaryModel } from "@/utils/modelUtil";
export default function FrigatePlusSettingsView(_props: SettingsPageProps) {
const { t } = useTranslation("views/settings");
@@ -51,7 +52,7 @@ export default function FrigatePlusSettingsView(_props: SettingsPageProps) {
description={
<>
<p>{t("frigatePlus.apiKey.desc")}</p>
{!config?.model.plus && (
{!getPrimaryModel(config)?.plus && (
<div className="mt-2 flex items-center text-primary-variant">
<Link
to="https://frigate.video/plus"
@@ -85,7 +86,7 @@ export default function FrigatePlusSettingsView(_props: SettingsPageProps) {
{config?.plus?.enabled && (
<FrigatePlusCurrentModelSummary
plusModel={config.model.plus}
plusModel={getPrimaryModel(config)?.plus}
action={
<Button
size="sm"
@@ -34,6 +34,7 @@ import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
import { AudioLevelGraph } from "@/components/audio/AudioLevelGraph";
import { useWs } from "@/api/ws";
import { cn } from "@/lib/utils";
import { getPrimaryModel } from "@/utils/modelUtil";
type ObjectSettingsViewProps = {
selectedCamera?: string;
@@ -172,11 +173,10 @@ export default function ObjectSettingsView({
<div className="mb-5 space-y-3 text-sm text-muted-foreground">
<p>
{t("debug.detectorDesc", {
detectors: config
? Object.keys(config?.detectors)
.map((detector) => capitalizeFirstLetter(detector))
.join(",")
: "",
detectors: (config?.models ?? [])
.flatMap((model) => model.devices ?? [])
.map((device) => capitalizeFirstLetter(device))
.join(","),
})}
</p>
<p>{t("debug.desc")}</p>
@@ -380,7 +380,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
return;
}
return config.model?.colormap;
return getPrimaryModel(config)?.colormap;
}, [config]);
const getColorForObjectName = useCallback(
@@ -3,11 +3,11 @@ import {
SettingsGroupCard,
SplitCardRow,
} from "@/components/card/SettingsGroupCard";
import type { FrigateConfig } from "@/types/frigateConfig";
import type { DetectionModelConfig } from "@/types/frigateConfig";
import { useTranslation } from "react-i18next";
type FrigatePlusCurrentModelSummaryProps = {
plusModel: FrigateConfig["model"]["plus"];
plusModel: DetectionModelConfig["plus"];
action?: ReactNode;
};