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 || '',
+ }
}