"use client" import { useEffect, useMemo, useState } from "react" import { RefreshCw } from "lucide-react" import { AiAuthorizedModelPicker } from "@/components/admin/settings/sections/ai-authorized-model-picker" import { UltiAiToolsCard } from "@/components/admin/settings/sections/ultiai-tools-card" import { OrgSettingsSection } from "@/components/admin/settings/org-settings-form" import { DeployLockedHint } from "@/components/admin/settings/deploy-locked-hint" import { useDeployFieldLocked } from "@/components/admin/settings/deploy-locked-hint" import { AdminOrgLlmPolicyCard } from "@/components/admin/settings/sections/admin-org-llm-providers-panel" import { LlmProvidersEditor } from "@/components/llm/llm-providers-editor" import { normalizeLlmProvider } from "@/lib/llm/llm-provider-catalog" import { AutomationTabMasonry } from "@/components/gmail/settings/automation/automation-tab-masonry" import { SettingsCard, SettingsField, SettingsGrid, SettingsHint, SettingsToggleRow, } from "@/components/settings/settings-kit" import { useOrgSettingsStore } from "@/lib/admin-settings/org-settings-store" import type { AiModelCatalogEntry } from "@/lib/admin-settings/org-settings-types" import { useDiscoverOrgLLMModels } from "@/lib/api/hooks/use-admin-llm" import { Switch } from "@/components/ui/switch" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Badge } from "@/components/ui/badge" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" export function AiAssistantSection() { const aiAssistant = useOrgSettingsStore((s) => s.aiAssistant) const setAiAssistant = useOrgSettingsStore((s) => s.setAiAssistant) const setPlugins = useOrgSettingsStore((s) => s.setPlugins) const plugins = useOrgSettingsStore((s) => s.plugins) const llm = useOrgSettingsStore((s) => s.llm) const setLlm = useOrgSettingsStore((s) => s.setLlm) const secrets = useOrgSettingsStore((s) => s.meta?.secrets) const effective = useOrgSettingsStore((s) => s.meta?.effective.ai_assistant) const enabledLocked = useDeployFieldLocked("ai_assistant", "enabled") const publicPathLocked = useDeployFieldLocked("ai_assistant", "public_path") const openwebuiLocked = useDeployFieldLocked("ai_assistant", "openwebui_internal_url") const pluginEnabled = plugins.find((p) => p.id === "ai-assistant")?.enabled ?? false const runtimeEnabled = effective?.enabled ?? false const orgEnabled = aiAssistant.enabled || pluginEnabled const [llmDraft, setLlmDraft] = useState(llm) useEffect(() => { setLlmDraft({ ...llm, providers: (llm.providers ?? []).map(normalizeLlmProvider), }) }, [llm]) const [discoverProviderId, setDiscoverProviderId] = useState(llm.default_provider_id) const [discoveredModels, setDiscoveredModels] = useState([]) const discoverProvider = useMemo( () => llmDraft.providers.find((p) => p.id === discoverProviderId) ?? llmDraft.providers[0], [discoverProviderId, llmDraft.providers], ) const discoverModels = useDiscoverOrgLLMModels() const defaultModelOptions = useMemo(() => { const ids = new Set() if (aiAssistant.models.length > 0) { for (const entry of aiAssistant.models) { if (entry.enabled && entry.model_id.trim()) ids.add(entry.model_id.trim()) } } else { for (const provider of llmDraft.providers) { if (provider.default_model?.trim()) ids.add(provider.default_model.trim()) } for (const modelId of discoveredModels) { if (modelId.trim()) ids.add(modelId.trim()) } } if (aiAssistant.default_model.trim()) ids.add(aiAssistant.default_model.trim()) return Array.from(ids).sort((a, b) => a.localeCompare(b)) }, [aiAssistant.models, aiAssistant.default_model, llmDraft.providers, discoveredModels]) useEffect(() => { if (!discoverProvider?.id) return let cancelled = false void discoverModels .mutateAsync(discoverProvider.id) .then((result) => { if (!cancelled) setDiscoveredModels(result.models ?? []) }) .catch(() => { if (!cancelled) setDiscoveredModels([]) }) return () => { cancelled = true } }, [discoverProvider?.id]) async function handleDiscoverModels() { if (!discoverProvider?.id) return setDiscoveredModels([]) try { const result = await discoverModels.mutateAsync(discoverProvider.id) setDiscoveredModels(result.models ?? []) } catch { setDiscoveredModels([]) } } const orgLlmProviderSecrets = secrets?.llm_providers as | Record | undefined function setAuthorizedModels(models: AiModelCatalogEntry[]) { setAiAssistant({ models }) } function setUltiAIEnabled(enabled: boolean) { setAiAssistant({ enabled }) setPlugins( plugins.map((plugin) => plugin.id === "ai-assistant" ? { ...plugin, enabled } : plugin, ), ) } return ( setLlm(llmDraft)} > } badges={ <> Politique org. {orgEnabled ? "activée" : "désactivée"} Runtime Compose {runtimeEnabled ? "actif" : "inactif"} } hint={ <> {enabledLocked ? : null} {!orgEnabled && !runtimeEnabled ? ( Activez le plugin UltiAI dans Administration → Plugins, ou définissez{" "} AI_ASSISTANT_ENABLED=true dans le déploiement, puis redémarrez le backend et OpenWebUI. ) : null} } > } > setAiAssistant({ public_path: e.target.value })} placeholder="/ai" disabled={publicPathLocked} /> } > setAiAssistant({ openwebui_internal_url: e.target.value })} placeholder="http://openwebui:8080" disabled={openwebuiLocked} /> {defaultModelOptions.length > 0 ? ( ) : ( setAiAssistant({ default_model: e.target.value })} placeholder="gpt-4o-mini" /> )} setAiAssistant({ chat_nc_path: e.target.value })} placeholder="/.ultimail/ai/chats" /> setAiAssistant({ embed_default_temporary: v })} /> setAiAssistant({ chat_sync_enabled: v })} />
setLlmDraft((prev) => ({ ...prev, providers })) } onDefaultProviderIdChange={(default_provider_id) => setLlmDraft((prev) => ({ ...prev, default_provider_id })) } />
setAiAssistant({ enabled_tools })} webSearchSettingsHref="/admin/settings/search" /> {llmDraft.providers.length === 0 ? (

Configurez d'abord un fournisseur LLM dans la section ci-dessus.

) : (
)} {discoverModels.isError ? ( {discoverModels.error instanceof Error ? discoverModels.error.message : "Impossible de lister les modèles sur ce fournisseur. Enregistrez d'abord le fournisseur LLM avec une clé API valide."} ) : null} {llmDraft.providers.length > 0 ? ( ) : null}
) }