feat(tagger): Settings picker to switch tagging model (WD / JoyTag)

Adds a "Tagging model" control to Settings → AI Workspace that calls
get/set_tagger_model, alongside the existing acceleration/threshold/batch
controls. Switching refreshes the model status so the download/ready row and
the model name/description reflect the selected model, and the threshold hint
shows the model-appropriate default (0.4 for JoyTag, 0.35 for WD).

Frontend only; mirrors the existing acceleration toggle pattern. End-to-end
JoyTag tagging still needs the model files on disk to verify.
This commit is contained in:
2026-06-29 09:51:30 +01:00
parent 52d54d2404
commit 705f8c2e56
2 changed files with 106 additions and 5 deletions
+28
View File
@@ -51,6 +51,7 @@ export type SearchCommand = "filename" | "semantic" | "tag";
export type CaptionAcceleration = "auto" | "cpu" | "directml";
export type CaptionDetail = "short" | "detailed" | "paragraph";
export type TaggerAcceleration = "auto" | "cpu" | "directml";
export type TaggerModel = "wd" | "joytag";
export type AiRating = "general" | "sensitive" | "questionable" | "explicit";
export type TaggingQueueScope = "all" | "selected";
export type SimilarScope = "all_media" | "current_folder" | "current_album";
@@ -424,6 +425,7 @@ interface GalleryState {
taggerModelPreparing: boolean;
taggerModelError: string | null;
taggerModelProgress: TaggerModelProgress | null;
taggerModel: TaggerModel;
taggerAcceleration: TaggerAcceleration;
taggerThreshold: number;
taggerBatchSize: number;
@@ -551,6 +553,8 @@ interface GalleryState {
deleteTaggerModel: () => Promise<void>;
loadTaggerAcceleration: () => Promise<void>;
setTaggerAcceleration: (acceleration: TaggerAcceleration) => Promise<void>;
loadTaggerModel: () => Promise<void>;
setTaggerModel: (model: TaggerModel) => Promise<void>;
loadTaggerThreshold: () => Promise<void>;
setTaggerThreshold: (threshold: number) => Promise<void>;
loadTaggerBatchSize: () => Promise<void>;
@@ -902,6 +906,7 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
taggerModelPreparing: false,
taggerModelError: null,
taggerModelProgress: null,
taggerModel: "wd",
taggerAcceleration: "auto",
taggerThreshold: 0.35,
taggerBatchSize: 8,
@@ -2158,6 +2163,29 @@ export const useGalleryStore = create<GalleryState>((set, get) => ({
set({ taggerAcceleration, taggerRuntimeProbe: null });
},
loadTaggerModel: async () => {
try {
const taggerModel = await invoke<TaggerModel>("get_tagger_model");
set({ taggerModel });
} catch (error) {
set({ taggerModelError: String(error) });
}
},
setTaggerModel: async (model) => {
const taggerModel = await invoke<TaggerModel>("set_tagger_model", {
params: { model },
});
// Switching models changes which files are required on disk, so refresh the
// status too — the download/ready UI must reflect the newly-selected model.
try {
const taggerModelStatus = await invoke<TaggerModelStatus>("get_tagger_model_status");
set({ taggerModel, taggerModelStatus, taggerModelError: null, taggerRuntimeProbe: null });
} catch (error) {
set({ taggerModel, taggerRuntimeProbe: null, taggerModelError: String(error) });
}
},
loadTaggerThreshold: async () => {
try {
const taggerThreshold = await invoke<number>("get_tagger_threshold");