mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-12-22 09:04:22 +00:00
Pass default config values to instance dialog
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -42,4 +42,7 @@ site/
|
||||
llamactl.dev.yaml
|
||||
|
||||
# Debug files
|
||||
__debug*
|
||||
__debug*
|
||||
|
||||
# Binary
|
||||
llamactl-*
|
||||
@@ -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<InstanceDialogProps> = ({
|
||||
const [showParseDialog, setShowParseDialog] = useState(false);
|
||||
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
|
||||
useEffect(() => {
|
||||
@@ -55,20 +59,32 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ const EnvVarsInput: React.FC<EnvVarsInputProps> = (props) => {
|
||||
keyPlaceholder="Variable name"
|
||||
valuePlaceholder="Variable value"
|
||||
addButtonText="Add Variable"
|
||||
helperText="Environment variables that will be passed to the backend process"
|
||||
allowEmptyValues={false}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -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<BackendConfigurationCardProps> = ({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Execution Context Section */}
|
||||
<ExecutionContextSection
|
||||
formData={formData}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
||||
{/* Basic Backend Options */}
|
||||
{basicBackendFields.length > 0 && (
|
||||
<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 { 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<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 */}
|
||||
<AutoRestartConfiguration
|
||||
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 type { AppConfig } from '@/types/config'
|
||||
import { useAuth } from './AuthContext'
|
||||
|
||||
interface ConfigContextState {
|
||||
interface ConfigContextType {
|
||||
config: AppConfig | null
|
||||
isLoading: boolean
|
||||
error: string | null
|
||||
}
|
||||
|
||||
interface ConfigContextActions {
|
||||
refetchConfig: () => Promise<void>
|
||||
}
|
||||
|
||||
type ConfigContextType = ConfigContextState & ConfigContextActions
|
||||
|
||||
const ConfigContext = createContext<ConfigContextType | undefined>(undefined)
|
||||
|
||||
interface ConfigProviderProps {
|
||||
@@ -21,44 +16,38 @@ interface ConfigProviderProps {
|
||||
}
|
||||
|
||||
export const ConfigProvider = ({ children }: ConfigProviderProps) => {
|
||||
const { isAuthenticated } = useAuth()
|
||||
const [config, setConfig] = useState<AppConfig | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
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(() => {
|
||||
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 (
|
||||
<ConfigContext.Provider value={value}>
|
||||
<ConfigContext.Provider value={{ config, isLoading, error }}>
|
||||
{children}
|
||||
</ConfigContext.Provider>
|
||||
)
|
||||
@@ -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 || '',
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user