mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-12-22 17:14:22 +00:00
Pass default config values to instance dialog
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -43,3 +43,6 @@ llamactl.dev.yaml
|
|||||||
|
|
||||||
# Debug files
|
# Debug files
|
||||||
__debug*
|
__debug*
|
||||||
|
|
||||||
|
# Binary
|
||||||
|
llamactl-*
|
||||||
@@ -14,7 +14,7 @@ import ParseCommandDialog from "@/components/ParseCommandDialog";
|
|||||||
import InstanceSettingsCard from "@/components/instance/InstanceSettingsCard";
|
import InstanceSettingsCard from "@/components/instance/InstanceSettingsCard";
|
||||||
import BackendConfigurationCard from "@/components/instance/BackendConfigurationCard";
|
import BackendConfigurationCard from "@/components/instance/BackendConfigurationCard";
|
||||||
import { Upload } from "lucide-react";
|
import { Upload } from "lucide-react";
|
||||||
import { useInstanceDefaults } from "@/contexts/ConfigContext";
|
import { useInstanceDefaults, useBackendSettings } from "@/contexts/ConfigContext";
|
||||||
|
|
||||||
interface InstanceDialogProps {
|
interface InstanceDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -38,6 +38,10 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
|
|||||||
const [showParseDialog, setShowParseDialog] = useState(false);
|
const [showParseDialog, setShowParseDialog] = useState(false);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(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
|
// Reset form when dialog opens/closes or when instance changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -55,20 +59,32 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
|
|||||||
restart_delay: instanceDefaults?.restartDelay,
|
restart_delay: instanceDefaults?.restartDelay,
|
||||||
on_demand_start: instanceDefaults?.onDemandStart,
|
on_demand_start: instanceDefaults?.onDemandStart,
|
||||||
backend_type: BackendType.LLAMA_CPP, // Default backend type
|
backend_type: BackendType.LLAMA_CPP, // Default backend type
|
||||||
|
docker_enabled: llamaCppSettings?.dockerEnabled ?? false,
|
||||||
backend_options: {},
|
backend_options: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setNameError(""); // Reset any name errors
|
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) => {
|
const handleFieldChange = (key: keyof CreateInstanceOptions, value: unknown) => {
|
||||||
setFormData((prev) => {
|
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) {
|
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 {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
backend_type: value as CreateInstanceOptions['backend_type'],
|
backend_type: value as CreateInstanceOptions['backend_type'],
|
||||||
|
docker_enabled: dockerEnabled,
|
||||||
backend_options: {}, // Clear backend options when backend type changes
|
backend_options: {}, // Clear backend options when backend type changes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ const EnvVarsInput: React.FC<EnvVarsInputProps> = (props) => {
|
|||||||
keyPlaceholder="Variable name"
|
keyPlaceholder="Variable name"
|
||||||
valuePlaceholder="Variable value"
|
valuePlaceholder="Variable value"
|
||||||
addButtonText="Add Variable"
|
addButtonText="Add Variable"
|
||||||
helperText="Environment variables that will be passed to the backend process"
|
|
||||||
allowEmptyValues={false}
|
allowEmptyValues={false}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Terminal, ChevronDown, ChevronRight } from 'lucide-react'
|
|||||||
import { getBasicBackendFields, getAdvancedBackendFields } from '@/lib/zodFormUtils'
|
import { getBasicBackendFields, getAdvancedBackendFields } from '@/lib/zodFormUtils'
|
||||||
import BackendFormField from '@/components/BackendFormField'
|
import BackendFormField from '@/components/BackendFormField'
|
||||||
import SelectInput from '@/components/form/SelectInput'
|
import SelectInput from '@/components/form/SelectInput'
|
||||||
|
import ExecutionContextSection from '@/components/instance/ExecutionContextSection'
|
||||||
|
|
||||||
interface BackendConfigurationCardProps {
|
interface BackendConfigurationCardProps {
|
||||||
formData: CreateInstanceOptions
|
formData: CreateInstanceOptions
|
||||||
@@ -59,6 +60,12 @@ const BackendConfigurationCard: React.FC<BackendConfigurationCardProps> = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Execution Context Section */}
|
||||||
|
<ExecutionContextSection
|
||||||
|
formData={formData}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Basic Backend Options */}
|
{/* Basic Backend Options */}
|
||||||
{basicBackendFields.length > 0 && (
|
{basicBackendFields.length > 0 && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
76
webui/src/components/instance/ExecutionContextSection.tsx
Normal file
76
webui/src/components/instance/ExecutionContextSection.tsx
Normal file
@@ -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<ExecutionContextSectionProps> = ({
|
||||||
|
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 (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h3 className="text-md font-medium">Execution Context</h3>
|
||||||
|
|
||||||
|
{/* Docker Mode Toggle - only for backends that support Docker */}
|
||||||
|
{formData.backend_type !== BackendType.MLX_LM && (
|
||||||
|
<CheckboxInput
|
||||||
|
id="docker_enabled"
|
||||||
|
label="Enable Docker"
|
||||||
|
value={formData.docker_enabled}
|
||||||
|
onChange={(value) => 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) && (
|
||||||
|
<TextInput
|
||||||
|
id="command_override"
|
||||||
|
label="Command Override"
|
||||||
|
value={formData.command_override || ''}
|
||||||
|
onChange={(value) => onChange('command_override', value)}
|
||||||
|
placeholder={getCommandPlaceholder()}
|
||||||
|
description="Custom path to backend executable (leave empty to use config default)"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<EnvVarsInput
|
||||||
|
id="environment"
|
||||||
|
label="Environment Variables"
|
||||||
|
value={formData.environment}
|
||||||
|
onChange={(value) => onChange('environment', value)}
|
||||||
|
description="Custom environment variables for the instance"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExecutionContextSection
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
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 { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import AutoRestartConfiguration from '@/components/instance/AutoRestartConfiguration'
|
import AutoRestartConfiguration from '@/components/instance/AutoRestartConfiguration'
|
||||||
import NumberInput from '@/components/form/NumberInput'
|
import NumberInput from '@/components/form/NumberInput'
|
||||||
import CheckboxInput from '@/components/form/CheckboxInput'
|
import CheckboxInput from '@/components/form/CheckboxInput'
|
||||||
import EnvVarsInput from '@/components/form/EnvVarsInput'
|
|
||||||
import SelectInput from '@/components/form/SelectInput'
|
import SelectInput from '@/components/form/SelectInput'
|
||||||
import TextInput from '@/components/form/TextInput'
|
|
||||||
import { nodesApi, type NodesMap } from '@/lib/api'
|
import { nodesApi, type NodesMap } from '@/lib/api'
|
||||||
|
|
||||||
interface InstanceSettingsCardProps {
|
interface InstanceSettingsCardProps {
|
||||||
@@ -106,48 +104,6 @@ const InstanceSettingsCard: React.FC<InstanceSettingsCardProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Execution Context */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h3 className="text-lg font-medium">Execution Context</h3>
|
|
||||||
|
|
||||||
{/* Docker Mode Toggle - only for backends that support Docker */}
|
|
||||||
{formData.backend_type !== BackendType.MLX_LM && (
|
|
||||||
<CheckboxInput
|
|
||||||
id="docker_enabled"
|
|
||||||
label="Enable Docker"
|
|
||||||
value={formData.docker_enabled}
|
|
||||||
onChange={(value) => 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) && (
|
|
||||||
<TextInput
|
|
||||||
id="command_override"
|
|
||||||
label="Command Override"
|
|
||||||
value={formData.command_override || ''}
|
|
||||||
onChange={(value) => 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"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<EnvVarsInput
|
|
||||||
id="environment"
|
|
||||||
label="Environment Variables"
|
|
||||||
value={formData.environment}
|
|
||||||
onChange={(value) => onChange('environment', value)}
|
|
||||||
description="Custom environment variables for the instance"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Auto Restart Configuration */}
|
{/* Auto Restart Configuration */}
|
||||||
<AutoRestartConfiguration
|
<AutoRestartConfiguration
|
||||||
formData={formData}
|
formData={formData}
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import { type ReactNode, createContext, useCallback, useContext, useEffect, useState } from 'react'
|
import { type ReactNode, createContext, useContext, useEffect, useState, useRef } from 'react'
|
||||||
import { serverApi } from '@/lib/api'
|
import { serverApi } from '@/lib/api'
|
||||||
import type { AppConfig } from '@/types/config'
|
import type { AppConfig } from '@/types/config'
|
||||||
|
import { useAuth } from './AuthContext'
|
||||||
|
|
||||||
interface ConfigContextState {
|
interface ConfigContextType {
|
||||||
config: AppConfig | null
|
config: AppConfig | null
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
error: string | null
|
error: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConfigContextActions {
|
|
||||||
refetchConfig: () => Promise<void>
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigContextType = ConfigContextState & ConfigContextActions
|
|
||||||
|
|
||||||
const ConfigContext = createContext<ConfigContextType | undefined>(undefined)
|
const ConfigContext = createContext<ConfigContextType | undefined>(undefined)
|
||||||
|
|
||||||
interface ConfigProviderProps {
|
interface ConfigProviderProps {
|
||||||
@@ -21,44 +16,38 @@ interface ConfigProviderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ConfigProvider = ({ children }: ConfigProviderProps) => {
|
export const ConfigProvider = ({ children }: ConfigProviderProps) => {
|
||||||
|
const { isAuthenticated } = useAuth()
|
||||||
const [config, setConfig] = useState<AppConfig | null>(null)
|
const [config, setConfig] = useState<AppConfig | null>(null)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(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(() => {
|
useEffect(() => {
|
||||||
void fetchConfig()
|
if (!isAuthenticated || loadedRef.current) {
|
||||||
}, [fetchConfig])
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const refetchConfig = useCallback(async () => {
|
loadedRef.current = true
|
||||||
await fetchConfig()
|
|
||||||
}, [fetchConfig])
|
|
||||||
|
|
||||||
const value: ConfigContextType = {
|
const loadConfig = async () => {
|
||||||
config,
|
try {
|
||||||
isLoading,
|
const data = await serverApi.getConfig()
|
||||||
error,
|
setConfig(data)
|
||||||
refetchConfig,
|
} 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 (
|
return (
|
||||||
<ConfigContext.Provider value={value}>
|
<ConfigContext.Provider value={{ config, isLoading, error }}>
|
||||||
{children}
|
{children}
|
||||||
</ConfigContext.Provider>
|
</ConfigContext.Provider>
|
||||||
)
|
)
|
||||||
@@ -76,7 +65,7 @@ export const useConfig = (): ConfigContextType => {
|
|||||||
export const useInstanceDefaults = () => {
|
export const useInstanceDefaults = () => {
|
||||||
const { config } = useConfig()
|
const { config } = useConfig()
|
||||||
|
|
||||||
if (!config) {
|
if (!config || !config.instances) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,13 +77,36 @@ export const useInstanceDefaults = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper hook to get backend settings from config
|
// Helper hook to get specific backend settings by backend type
|
||||||
export const useBackendConfig = () => {
|
export const useBackendSettings = (backendType: string | undefined) => {
|
||||||
const { config } = useConfig()
|
const { config } = useConfig()
|
||||||
|
|
||||||
if (!config) {
|
if (!config || !config.backends || !backendType) {
|
||||||
return null
|
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 || '',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user