From c04c9522936ec69a31fcf5490f92fc91208a258b Mon Sep 17 00:00:00 2001 From: LordMathis Date: Fri, 14 Nov 2025 23:07:30 +0100 Subject: [PATCH] Pass default config values to instance dialog --- .gitignore | 5 +- webui/src/components/InstanceDialog.tsx | 22 ++++- webui/src/components/form/EnvVarsInput.tsx | 1 - .../instance/BackendConfigurationCard.tsx | 7 ++ .../instance/ExecutionContextSection.tsx | 76 +++++++++++++++ .../instance/InstanceSettingsCard.tsx | 46 +-------- webui/src/contexts/ConfigContext.tsx | 96 +++++++++++-------- 7 files changed, 161 insertions(+), 92 deletions(-) create mode 100644 webui/src/components/instance/ExecutionContextSection.tsx diff --git a/.gitignore b/.gitignore index 4075d71..d96fc8c 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,7 @@ site/ llamactl.dev.yaml # Debug files -__debug* \ No newline at end of file +__debug* + +# Binary +llamactl-* \ No newline at end of file diff --git a/webui/src/components/InstanceDialog.tsx b/webui/src/components/InstanceDialog.tsx index d88b2a3..58432a7 100644 --- a/webui/src/components/InstanceDialog.tsx +++ b/webui/src/components/InstanceDialog.tsx @@ -14,7 +14,7 @@ import ParseCommandDialog from "@/components/ParseCommandDialog"; import InstanceSettingsCard from "@/components/instance/InstanceSettingsCard"; import BackendConfigurationCard from "@/components/instance/BackendConfigurationCard"; import { Upload } from "lucide-react"; -import { useInstanceDefaults } from "@/contexts/ConfigContext"; +import { useInstanceDefaults, useBackendSettings } from "@/contexts/ConfigContext"; interface InstanceDialogProps { open: boolean; @@ -38,6 +38,10 @@ const InstanceDialog: React.FC = ({ const [showParseDialog, setShowParseDialog] = useState(false); const fileInputRef = useRef(null); + // Get backend settings for all backends (we'll use this to update docker_enabled on backend type change) + const llamaCppSettings = useBackendSettings(BackendType.LLAMA_CPP); + const vllmSettings = useBackendSettings(BackendType.VLLM); + const mlxSettings = useBackendSettings(BackendType.MLX_LM); // Reset form when dialog opens/closes or when instance changes useEffect(() => { @@ -55,20 +59,32 @@ const InstanceDialog: React.FC = ({ restart_delay: instanceDefaults?.restartDelay, on_demand_start: instanceDefaults?.onDemandStart, backend_type: BackendType.LLAMA_CPP, // Default backend type + docker_enabled: llamaCppSettings?.dockerEnabled ?? false, backend_options: {}, }); } setNameError(""); // Reset any name errors } - }, [open, instance, instanceDefaults]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [open, instance]); const handleFieldChange = (key: keyof CreateInstanceOptions, value: unknown) => { setFormData((prev) => { - // If backend_type is changing, clear backend_options + // If backend_type is changing, update docker_enabled default and clear backend_options if (key === 'backend_type' && prev.backend_type !== value) { + let dockerEnabled = false; + if (value === BackendType.LLAMA_CPP) { + dockerEnabled = llamaCppSettings?.dockerEnabled ?? false; + } else if (value === BackendType.VLLM) { + dockerEnabled = vllmSettings?.dockerEnabled ?? false; + } else if (value === BackendType.MLX_LM) { + dockerEnabled = mlxSettings?.dockerEnabled ?? false; + } + return { ...prev, backend_type: value as CreateInstanceOptions['backend_type'], + docker_enabled: dockerEnabled, backend_options: {}, // Clear backend options when backend type changes }; } diff --git a/webui/src/components/form/EnvVarsInput.tsx b/webui/src/components/form/EnvVarsInput.tsx index 476a98a..5c77433 100644 --- a/webui/src/components/form/EnvVarsInput.tsx +++ b/webui/src/components/form/EnvVarsInput.tsx @@ -18,7 +18,6 @@ const EnvVarsInput: React.FC = (props) => { keyPlaceholder="Variable name" valuePlaceholder="Variable value" addButtonText="Add Variable" - helperText="Environment variables that will be passed to the backend process" allowEmptyValues={false} /> ) diff --git a/webui/src/components/instance/BackendConfigurationCard.tsx b/webui/src/components/instance/BackendConfigurationCard.tsx index 799ea2b..00fa371 100644 --- a/webui/src/components/instance/BackendConfigurationCard.tsx +++ b/webui/src/components/instance/BackendConfigurationCard.tsx @@ -6,6 +6,7 @@ import { Terminal, ChevronDown, ChevronRight } from 'lucide-react' import { getBasicBackendFields, getAdvancedBackendFields } from '@/lib/zodFormUtils' import BackendFormField from '@/components/BackendFormField' import SelectInput from '@/components/form/SelectInput' +import ExecutionContextSection from '@/components/instance/ExecutionContextSection' interface BackendConfigurationCardProps { formData: CreateInstanceOptions @@ -59,6 +60,12 @@ const BackendConfigurationCard: React.FC = ({

+ {/* Execution Context Section */} + + {/* Basic Backend Options */} {basicBackendFields.length > 0 && (
diff --git a/webui/src/components/instance/ExecutionContextSection.tsx b/webui/src/components/instance/ExecutionContextSection.tsx new file mode 100644 index 0000000..7f4ddb1 --- /dev/null +++ b/webui/src/components/instance/ExecutionContextSection.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import { BackendType, type CreateInstanceOptions } from '@/types/instance' +import CheckboxInput from '@/components/form/CheckboxInput' +import TextInput from '@/components/form/TextInput' +import EnvVarsInput from '@/components/form/EnvVarsInput' +import { useBackendSettings } from '@/contexts/ConfigContext' + +interface ExecutionContextSectionProps { + formData: CreateInstanceOptions + onChange: (key: keyof CreateInstanceOptions, value: unknown) => void +} + +const ExecutionContextSection: React.FC = ({ + formData, + onChange +}) => { + const backendSettings = useBackendSettings(formData.backend_type) + + // Get placeholder for command override based on backend type and config + const getCommandPlaceholder = () => { + if (backendSettings?.command) { + return backendSettings.command + } + + // Fallback placeholders if config is not loaded + switch (formData.backend_type) { + case BackendType.LLAMA_CPP: + return "llama-server" + case BackendType.VLLM: + return "vllm" + case BackendType.MLX_LM: + return "mlx_lm.server" + default: + return "" + } + } + + return ( +
+

Execution Context

+ + {/* Docker Mode Toggle - only for backends that support Docker */} + {formData.backend_type !== BackendType.MLX_LM && ( + onChange('docker_enabled', value)} + description="Run backend in Docker container" + /> + )} + + {/* Command Override - only shown when Docker is disabled or backend is MLX */} + {(formData.backend_type === BackendType.MLX_LM || formData.docker_enabled !== true) && ( + onChange('command_override', value)} + placeholder={getCommandPlaceholder()} + description="Custom path to backend executable (leave empty to use config default)" + /> + )} + + onChange('environment', value)} + description="Custom environment variables for the instance" + /> +
+ ) +} + +export default ExecutionContextSection diff --git a/webui/src/components/instance/InstanceSettingsCard.tsx b/webui/src/components/instance/InstanceSettingsCard.tsx index d1ba4c0..b5f945c 100644 --- a/webui/src/components/instance/InstanceSettingsCard.tsx +++ b/webui/src/components/instance/InstanceSettingsCard.tsx @@ -1,14 +1,12 @@ import React, { useState, useEffect } from 'react' -import { BackendType, type CreateInstanceOptions } from '@/types/instance' +import { type CreateInstanceOptions } from '@/types/instance' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' import AutoRestartConfiguration from '@/components/instance/AutoRestartConfiguration' import NumberInput from '@/components/form/NumberInput' import CheckboxInput from '@/components/form/CheckboxInput' -import EnvVarsInput from '@/components/form/EnvVarsInput' import SelectInput from '@/components/form/SelectInput' -import TextInput from '@/components/form/TextInput' import { nodesApi, type NodesMap } from '@/lib/api' interface InstanceSettingsCardProps { @@ -106,48 +104,6 @@ const InstanceSettingsCard: React.FC = ({ /> )} - {/* Execution Context */} -
-

Execution Context

- - {/* Docker Mode Toggle - only for backends that support Docker */} - {formData.backend_type !== BackendType.MLX_LM && ( - onChange('docker_enabled', value)} - description="Run backend in Docker container (overrides config default)" - /> - )} - - {/* Command Override - only shown when Docker is disabled or backend is MLX */} - {(formData.backend_type === BackendType.MLX_LM || formData.docker_enabled !== true) && ( - onChange('command_override', value)} - placeholder={ - formData.backend_type === BackendType.LLAMA_CPP - ? "/usr/local/bin/llama-server" - : formData.backend_type === BackendType.VLLM - ? "/usr/local/bin/vllm" - : "/usr/local/bin/mlx_lm.server" - } - description="Custom path to backend executable" - /> - )} - - onChange('environment', value)} - description="Custom environment variables for the instance" - /> -
- {/* Auto Restart Configuration */} Promise -} - -type ConfigContextType = ConfigContextState & ConfigContextActions - const ConfigContext = createContext(undefined) interface ConfigProviderProps { @@ -21,44 +16,38 @@ interface ConfigProviderProps { } export const ConfigProvider = ({ children }: ConfigProviderProps) => { + const { isAuthenticated } = useAuth() const [config, setConfig] = useState(null) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) + const loadedRef = useRef(false) - const fetchConfig = useCallback(async () => { - setIsLoading(true) - setError(null) - - try { - const data = await serverApi.getConfig() - setConfig(data) - } catch (err) { - const errorMessage = err instanceof Error ? err.message : 'Failed to load configuration' - setError(errorMessage) - console.error('Error loading config:', err) - } finally { - setIsLoading(false) - } - }, []) - - // Load config on mount useEffect(() => { - void fetchConfig() - }, [fetchConfig]) + if (!isAuthenticated || loadedRef.current) { + setIsLoading(false) + return + } - const refetchConfig = useCallback(async () => { - await fetchConfig() - }, [fetchConfig]) + loadedRef.current = true - const value: ConfigContextType = { - config, - isLoading, - error, - refetchConfig, - } + const loadConfig = async () => { + try { + const data = await serverApi.getConfig() + setConfig(data) + } catch (err) { + const errorMessage = err instanceof Error ? err.message : 'Failed to load configuration' + setError(errorMessage) + console.error('Error loading config:', err) + } finally { + setIsLoading(false) + } + } + + void loadConfig() + }, [isAuthenticated]) return ( - + {children} ) @@ -76,7 +65,7 @@ export const useConfig = (): ConfigContextType => { export const useInstanceDefaults = () => { const { config } = useConfig() - if (!config) { + if (!config || !config.instances) { return null } @@ -88,13 +77,36 @@ export const useInstanceDefaults = () => { } } -// Helper hook to get backend settings from config -export const useBackendConfig = () => { +// Helper hook to get specific backend settings by backend type +export const useBackendSettings = (backendType: string | undefined) => { const { config } = useConfig() - if (!config) { + if (!config || !config.backends || !backendType) { return null } - return config.backends + // Map backend type to config key + const backendKey = backendType === 'llama_cpp' + ? 'llama-cpp' + : backendType === 'mlx_lm' + ? 'mlx' + : backendType === 'vllm' + ? 'vllm' + : null + + if (!backendKey) { + return null + } + + const backendConfig = config.backends[backendKey as keyof typeof config.backends] + + if (!backendConfig) { + return null + } + + return { + command: backendConfig.command || '', + dockerEnabled: backendConfig.docker?.enabled ?? false, + dockerImage: backendConfig.docker?.image || '', + } }