From efbe7e0a21e99d6ae4bc384d33c2431825871ad8 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 29 Mar 2026 22:42:05 +0300 Subject: [PATCH] feat(llm): add provider config in options --- .../src/translations/en/translation.json | 18 ++- .../src/widgets/type_widgets/options/llm.tsx | 94 ++++++++++++++++ .../options/llm/AddProviderModal.tsx | 106 ++++++++++++++++++ apps/server/src/routes/api/options.ts | 3 +- packages/commons/src/lib/options_interface.ts | 4 + 5 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 apps/client/src/widgets/type_widgets/options/llm/AddProviderModal.tsx diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 1270064087..d48872abc8 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1632,7 +1632,9 @@ "tokens_used_with_model": "{{model}}: {{prompt}} prompt + {{completion}} completion = {{total}} tokens", "tokens_used_with_model_and_cost": "{{model}}: {{prompt}} prompt + {{completion}} completion = {{total}} tokens (~${{cost}})", "tokens": "tokens", - "context_used": "{{percentage}}% used" + "context_used": "{{percentage}}% used", + "note_context_enabled": "Click to disable note context: {{title}}", + "note_context_disabled": "Click to include current note in context" }, "sidebar_chat": { "title": "AI Chat", @@ -2267,6 +2269,18 @@ }, "llm": { "settings_title": "AI / LLM", - "settings_description": "Configure AI and Large Language Model integrations." + "settings_description": "Configure AI and Large Language Model integrations.", + "add_provider": "Add Provider", + "add_provider_title": "Add AI Provider", + "configured_providers": "Configured Providers", + "no_providers_configured": "No providers configured yet.", + "provider_name": "Name", + "provider_type": "Provider", + "actions": "Actions", + "delete_provider": "Delete", + "delete_provider_confirmation": "Are you sure you want to delete the provider \"{{name}}\"?", + "api_key": "API Key", + "api_key_placeholder": "Enter your API key", + "cancel": "Cancel" } } diff --git a/apps/client/src/widgets/type_widgets/options/llm.tsx b/apps/client/src/widgets/type_widgets/options/llm.tsx index 0446b85885..caa867f5fd 100644 --- a/apps/client/src/widgets/type_widgets/options/llm.tsx +++ b/apps/client/src/widgets/type_widgets/options/llm.tsx @@ -1,10 +1,104 @@ +import { useCallback, useMemo, useState } from "preact/hooks"; import { t } from "../../../services/i18n"; +import Button from "../../react/Button"; import OptionsSection from "./components/OptionsSection"; +import AddProviderModal, { type LlmProviderConfig, PROVIDER_TYPES } from "./llm/AddProviderModal"; +import ActionButton from "../../react/ActionButton"; +import dialog from "../../../services/dialog"; +import { useTriliumOption } from "../../react/hooks"; export default function LlmSettings() { + const [providersJson, setProvidersJson] = useTriliumOption("llmProviders"); + const providers = useMemo(() => { + try { + return providersJson ? JSON.parse(providersJson) : []; + } catch { + return []; + } + }, [providersJson]); + const setProviders = useCallback((newProviders: LlmProviderConfig[]) => { + setProvidersJson(JSON.stringify(newProviders)); + }, [setProvidersJson]); + const [showAddModal, setShowAddModal] = useState(false); + + const handleAddProvider = useCallback((newProvider: LlmProviderConfig) => { + setProviders([...providers, newProvider]); + }, [providers, setProviders]); + + const handleDeleteProvider = useCallback(async (providerId: string, providerName: string) => { + if (!(await dialog.confirm(t("llm.delete_provider_confirmation", { name: providerName })))) { + return; + } + setProviders(providers.filter(p => p.id !== providerId)); + }, [providers, setProviders]); + return (

{t("llm.settings_description")}

+ + + + + } + > + + + + + + + + , + document.body + ); +} diff --git a/apps/server/src/routes/api/options.ts b/apps/server/src/routes/api/options.ts index bb6ffb00d6..f2e44a3e2e 100644 --- a/apps/server/src/routes/api/options.ts +++ b/apps/server/src/routes/api/options.ts @@ -104,7 +104,8 @@ const ALLOWED_OPTIONS = new Set([ "experimentalFeatures", "newLayout", "mfaEnabled", - "mfaMethod" + "mfaMethod", + "llmProviders" ]); function getOptions() { diff --git a/packages/commons/src/lib/options_interface.ts b/packages/commons/src/lib/options_interface.ts index 5582df79d2..334a8bd7a4 100644 --- a/packages/commons/src/lib/options_interface.ts +++ b/packages/commons/src/lib/options_interface.ts @@ -140,6 +140,10 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions