"use client"; import { useAtomValue } from "jotai"; import { AlertCircle, Bot, ChevronRight, Globe, Shuffle, User, X, Zap } from "lucide-react"; import { AnimatePresence, motion } from "motion/react"; import { useCallback, useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { toast } from "sonner"; import { createNewLLMConfigMutationAtom, updateLLMPreferencesMutationAtom, updateNewLLMConfigMutationAtom, } from "@/atoms/new-llm-config/new-llm-config-mutation.atoms"; import { LLMConfigForm, type LLMConfigFormData } from "@/components/shared/llm-config-form"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import type { GlobalNewLLMConfig, NewLLMConfigPublic, } from "@/contracts/types/new-llm-config.types"; import { cn } from "@/lib/utils"; interface ModelConfigSidebarProps { open: boolean; onOpenChange: (open: boolean) => void; config: NewLLMConfigPublic | GlobalNewLLMConfig | null; isGlobal: boolean; searchSpaceId: number; mode: "create" | "edit" | "view"; } export function ModelConfigSidebar({ open, onOpenChange, config, isGlobal, searchSpaceId, mode, }: ModelConfigSidebarProps) { const [isSubmitting, setIsSubmitting] = useState(false); const [mounted, setMounted] = useState(false); // Handle SSR - only render portal on client useEffect(() => { setMounted(true); }, []); // Mutations - use mutateAsync from the atom value const { mutateAsync: createConfig } = useAtomValue(createNewLLMConfigMutationAtom); const { mutateAsync: updateConfig } = useAtomValue(updateNewLLMConfigMutationAtom); const { mutateAsync: updatePreferences } = useAtomValue(updateLLMPreferencesMutationAtom); // Handle escape key useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === "Escape" && open) { onOpenChange(false); } }; window.addEventListener("keydown", handleEscape); return () => window.removeEventListener("keydown", handleEscape); }, [open, onOpenChange]); // Check if this is Auto mode const isAutoMode = config && "is_auto_mode" in config && config.is_auto_mode; // Get title based on mode const getTitle = () => { if (mode === "create") return "Add New Configuration"; if (isAutoMode) return "Auto Mode (Fastest)"; if (isGlobal) return "View Global Configuration"; return "Edit Configuration"; }; // Handle form submit const handleSubmit = useCallback( async (data: LLMConfigFormData) => { setIsSubmitting(true); try { if (mode === "create") { // Create new config const result = await createConfig({ ...data, search_space_id: searchSpaceId, }); // Assign the new config to the agent role if (result?.id) { await updatePreferences({ search_space_id: searchSpaceId, data: { agent_llm_id: result.id, }, }); } toast.success("Configuration created and assigned!"); onOpenChange(false); } else if (!isGlobal && config) { // Update existing user config await updateConfig({ id: config.id, data: { name: data.name, description: data.description, provider: data.provider, custom_provider: data.custom_provider, model_name: data.model_name, api_key: data.api_key, api_base: data.api_base, litellm_params: data.litellm_params, system_instructions: data.system_instructions, use_default_system_instructions: data.use_default_system_instructions, citations_enabled: data.citations_enabled, }, }); toast.success("Configuration updated!"); onOpenChange(false); } } catch (error) { console.error("Failed to save configuration:", error); toast.error("Failed to save configuration"); } finally { setIsSubmitting(false); } }, [ mode, isGlobal, config, searchSpaceId, createConfig, updateConfig, updatePreferences, onOpenChange, ] ); // Handle "Use this model" for global configs const handleUseGlobalConfig = useCallback(async () => { if (!config || !isGlobal) return; setIsSubmitting(true); try { await updatePreferences({ search_space_id: searchSpaceId, data: { agent_llm_id: config.id, }, }); toast.success(`Now using ${config.name}`); onOpenChange(false); } catch (error) { console.error("Failed to set model:", error); toast.error("Failed to set model"); } finally { setIsSubmitting(false); } }, [config, isGlobal, searchSpaceId, updatePreferences, onOpenChange]); if (!mounted) return null; const sidebarContent = ( {open && ( <> {/* Backdrop */} onOpenChange(false)} /> {/* Sidebar Panel */} {/* Header */}
{isAutoMode ? ( ) : ( )}

{getTitle()}

{isAutoMode ? ( Recommended ) : isGlobal ? ( Global ) : mode !== "create" ? ( Custom ) : null} {config && !isAutoMode && ( {config.model_name} )}
{/* Content - use overflow-y-auto instead of ScrollArea for better compatibility */}
{/* Auto mode info banner */} {isAutoMode && ( Auto mode automatically distributes requests across all available LLM providers to optimize performance and avoid rate limits. )} {/* Global config notice */} {isGlobal && !isAutoMode && mode !== "create" && ( Global configurations are read-only. To customize settings, create a new configuration based on this template. )} {/* Form */} {mode === "create" ? ( onOpenChange(false)} isSubmitting={isSubmitting} mode="create" submitLabel="Create & Use" /> ) : isAutoMode && config ? ( // Special view for Auto mode
{/* Auto Mode Features */}
How It Works

{config.description}

Key Benefits

Automatic (Fastest)

Distributes requests across all configured LLM providers

Rate Limit Protection

Automatically handles rate limits with cooldowns and retries

Automatic Failover

Falls back to other providers if one becomes unavailable

{/* Action Buttons */}
) : isGlobal && config ? ( // Read-only view for global configs
{/* Config Details */}
Configuration Name

{config.name}

{config.description && (
Description

{config.description}

)}
Provider

{config.provider}

Model

{config.model_name}

Citations
{config.citations_enabled ? "Enabled" : "Disabled"}
{config.system_instructions && ( <>
System Instructions

{config.system_instructions}

)}
{/* Action Buttons */}
) : config ? ( // Edit form for user configs onOpenChange(false)} isSubmitting={isSubmitting} mode="edit" submitLabel="Save Changes" /> ) : null}
)} ); return typeof document !== "undefined" ? createPortal(sidebarContent, document.body) : null; }