From 0bdba54ba24a14e7dc2cfab64084452756bccce7 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Mon, 22 Dec 2025 16:59:04 +0200 Subject: feat: Add user settings to disable auto tagging/summarization (#2275) * feat: Add per-user settings to disable auto-tagging and auto-summarization This commit adds user-level controls for AI features when they are enabled on the server. Users can now toggle auto-tagging and auto-summarization on/off from the AI Settings page. Changes: - Added autoTaggingEnabled and autoSummarizationEnabled fields to user table - Updated user settings schemas and API endpoints to handle new fields - Modified inference workers to check user preferences before processing - Added toggle switches to AI Settings page (only visible when server has features enabled) - Generated database migration for new fields - Exposed enableAutoTagging and enableAutoSummarization in client config The settings default to null (use server default). When explicitly set to false, the user's bookmarks will skip the respective AI processing. * revert migration * i18n --------- Co-authored-by: Claude --- apps/web/components/settings/AISettings.tsx | 108 ++++++++++++++++++++++++++ apps/web/lib/clientConfig.tsx | 2 + apps/web/lib/i18n/locales/en/translation.json | 5 ++ apps/web/lib/userSettings.tsx | 2 + 4 files changed, 117 insertions(+) (limited to 'apps/web') diff --git a/apps/web/components/settings/AISettings.tsx b/apps/web/components/settings/AISettings.tsx index beaa93dc..d8adcb76 100644 --- a/apps/web/components/settings/AISettings.tsx +++ b/apps/web/components/settings/AISettings.tsx @@ -4,8 +4,10 @@ import { ActionButton } from "@/components/ui/action-button"; import { Form, FormControl, + FormDescription, FormField, FormItem, + FormLabel, FormMessage, } from "@/components/ui/form"; import { FullPageSpinner } from "@/components/ui/full-page-spinner"; @@ -18,15 +20,18 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; import { toast } from "@/components/ui/use-toast"; import { useClientConfig } from "@/lib/clientConfig"; import { useTranslation } from "@/lib/i18n/client"; import { api } from "@/lib/trpc"; +import { useUserSettings } from "@/lib/userSettings"; import { zodResolver } from "@hookform/resolvers/zod"; import { Plus, Save, Trash2 } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; +import { useUpdateUserSettings } from "@karakeep/shared-react/hooks/users"; import { buildImagePrompt, buildSummaryPromptUntruncated, @@ -37,6 +42,108 @@ import { ZPrompt, zUpdatePromptSchema, } from "@karakeep/shared/types/prompts"; +import { zUpdateUserSettingsSchema } from "@karakeep/shared/types/users"; + +export function AIPreferences() { + const { t } = useTranslation(); + const clientConfig = useClientConfig(); + const settings = useUserSettings(); + + const { mutate: updateSettings } = useUpdateUserSettings({ + onSuccess: () => { + toast({ + description: "Settings updated successfully!", + }); + }, + onError: () => { + toast({ + description: "Failed to update settings", + variant: "destructive", + }); + }, + }); + + const form = useForm>({ + resolver: zodResolver(zUpdateUserSettingsSchema), + values: settings + ? { + autoTaggingEnabled: settings.autoTaggingEnabled, + autoSummarizationEnabled: settings.autoSummarizationEnabled, + } + : undefined, + }); + + const showAutoTagging = clientConfig.inference.enableAutoTagging; + const showAutoSummarization = clientConfig.inference.enableAutoSummarization; + + // Don't show the section if neither feature is enabled on the server + if (!showAutoTagging && !showAutoSummarization) { + return null; + } + + return ( +
+

+ {t("settings.ai.ai_preferences_description")} +

+
+ + {showAutoTagging && ( + ( + +
+ {t("settings.ai.auto_tagging")} + + {t("settings.ai.auto_tagging_description")} + +
+ + { + field.onChange(checked); + updateSettings({ autoTaggingEnabled: checked }); + }} + /> + +
+ )} + /> + )} + + {showAutoSummarization && ( + ( + +
+ {t("settings.ai.auto_summarization")} + + {t("settings.ai.auto_summarization_description")} + +
+ + { + field.onChange(checked); + updateSettings({ autoSummarizationEnabled: checked }); + }} + /> + +
+ )} + /> + )} + + +
+ ); +} export function PromptEditor() { const { t } = useTranslation(); @@ -353,6 +460,7 @@ export default function AISettings() {
{t("settings.ai.ai_settings")}
+ diff --git a/apps/web/lib/clientConfig.tsx b/apps/web/lib/clientConfig.tsx index 9331a7af..ab367be0 100644 --- a/apps/web/lib/clientConfig.tsx +++ b/apps/web/lib/clientConfig.tsx @@ -14,6 +14,8 @@ export const ClientConfigCtx = createContext({ inference: { isConfigured: false, inferredTagLang: "english", + enableAutoTagging: false, + enableAutoSummarization: false, }, serverVersion: undefined, disableNewReleaseCheck: true, diff --git a/apps/web/lib/i18n/locales/en/translation.json b/apps/web/lib/i18n/locales/en/translation.json index d05ca702..08dc33e4 100644 --- a/apps/web/lib/i18n/locales/en/translation.json +++ b/apps/web/lib/i18n/locales/en/translation.json @@ -222,6 +222,11 @@ }, "ai": { "ai_settings": "AI Settings", + "ai_preferences_description": "Control which AI features are enabled for your account.", + "auto_tagging": "Auto-tagging", + "auto_tagging_description": "Automatically generate tags for your bookmarks using AI.", + "auto_summarization": "Auto-summarization", + "auto_summarization_description": "Automatically generate summaries for your bookmarks using AI.", "tagging_rules": "Tagging Rules", "tagging_rule_description": "Prompts that you add here will be included as rules to the model during tag generation. You can view the final prompts in the prompt preview section.", "prompt_preview": "Prompt Preview", diff --git a/apps/web/lib/userSettings.tsx b/apps/web/lib/userSettings.tsx index 2bb7c8a5..d35c9e56 100644 --- a/apps/web/lib/userSettings.tsx +++ b/apps/web/lib/userSettings.tsx @@ -16,6 +16,8 @@ export const UserSettingsContext = createContext({ readerFontSize: null, readerLineHeight: null, readerFontFamily: null, + autoTaggingEnabled: null, + autoSummarizationEnabled: null, }); export function UserSettingsContextProvider({ -- cgit v1.2.3-70-g09d2