mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-12-22 17:14:22 +00:00
Implement ConfigContext for instance defaults
This commit is contained in:
@@ -14,6 +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";
|
||||||
|
|
||||||
interface InstanceDialogProps {
|
interface InstanceDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -29,6 +30,7 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
|
|||||||
instance,
|
instance,
|
||||||
}) => {
|
}) => {
|
||||||
const isEditing = !!instance;
|
const isEditing = !!instance;
|
||||||
|
const instanceDefaults = useInstanceDefaults();
|
||||||
|
|
||||||
const [instanceName, setInstanceName] = useState("");
|
const [instanceName, setInstanceName] = useState("");
|
||||||
const [formData, setFormData] = useState<CreateInstanceOptions>({});
|
const [formData, setFormData] = useState<CreateInstanceOptions>({});
|
||||||
@@ -45,17 +47,20 @@ const InstanceDialog: React.FC<InstanceDialogProps> = ({
|
|||||||
setInstanceName(instance.name);
|
setInstanceName(instance.name);
|
||||||
setFormData(instance.options || {});
|
setFormData(instance.options || {});
|
||||||
} else {
|
} else {
|
||||||
// Reset form for new instance
|
// Reset form for new instance with defaults from config
|
||||||
setInstanceName("");
|
setInstanceName("");
|
||||||
setFormData({
|
setFormData({
|
||||||
auto_restart: true, // Default value
|
auto_restart: instanceDefaults?.autoRestart ?? true,
|
||||||
|
max_restarts: instanceDefaults?.maxRestarts,
|
||||||
|
restart_delay: instanceDefaults?.restartDelay,
|
||||||
|
on_demand_start: instanceDefaults?.onDemandStart,
|
||||||
backend_type: BackendType.LLAMA_CPP, // Default backend type
|
backend_type: BackendType.LLAMA_CPP, // Default backend type
|
||||||
backend_options: {},
|
backend_options: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setNameError(""); // Reset any name errors
|
setNameError(""); // Reset any name errors
|
||||||
}
|
}
|
||||||
}, [open, instance]);
|
}, [open, instance, instanceDefaults]);
|
||||||
|
|
||||||
const handleFieldChange = (key: keyof CreateInstanceOptions, value: unknown) => {
|
const handleFieldChange = (key: keyof CreateInstanceOptions, value: unknown) => {
|
||||||
setFormData((prev) => {
|
setFormData((prev) => {
|
||||||
|
|||||||
100
webui/src/contexts/ConfigContext.tsx
Normal file
100
webui/src/contexts/ConfigContext.tsx
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { type ReactNode, createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
import { serverApi } from '@/lib/api'
|
||||||
|
import type { AppConfig } from '@/types/config'
|
||||||
|
|
||||||
|
interface ConfigContextState {
|
||||||
|
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 {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConfigProvider = ({ children }: ConfigProviderProps) => {
|
||||||
|
const [config, setConfig] = useState<AppConfig | null>(null)
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
const refetchConfig = useCallback(async () => {
|
||||||
|
await fetchConfig()
|
||||||
|
}, [fetchConfig])
|
||||||
|
|
||||||
|
const value: ConfigContextType = {
|
||||||
|
config,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refetchConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfigContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</ConfigContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useConfig = (): ConfigContextType => {
|
||||||
|
const context = useContext(ConfigContext)
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useConfig must be used within a ConfigProvider')
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper hook to get instance default values from config
|
||||||
|
export const useInstanceDefaults = () => {
|
||||||
|
const { config } = useConfig()
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
autoRestart: config.instances.default_auto_restart,
|
||||||
|
maxRestarts: config.instances.default_max_restarts,
|
||||||
|
restartDelay: config.instances.default_restart_delay,
|
||||||
|
onDemandStart: config.instances.default_on_demand_start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper hook to get backend settings from config
|
||||||
|
export const useBackendConfig = () => {
|
||||||
|
const { config } = useConfig()
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.backends
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { CreateInstanceOptions, Instance } from "@/types/instance";
|
import type { CreateInstanceOptions, Instance } from "@/types/instance";
|
||||||
|
import type { AppConfig } from "@/types/config";
|
||||||
import { handleApiError } from "./errorUtils";
|
import { handleApiError } from "./errorUtils";
|
||||||
|
|
||||||
// Adding baseURI as a prefix to support being served behind a subpath
|
// Adding baseURI as a prefix to support being served behind a subpath
|
||||||
@@ -73,6 +74,9 @@ export const serverApi = {
|
|||||||
|
|
||||||
// GET /backends/llama-cpp/devices
|
// GET /backends/llama-cpp/devices
|
||||||
getDevices: () => apiCall<string>("/backends/llama-cpp/devices", {}, "text"),
|
getDevices: () => apiCall<string>("/backends/llama-cpp/devices", {}, "text"),
|
||||||
|
|
||||||
|
// GET /config
|
||||||
|
getConfig: () => apiCall<AppConfig>("/config"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Backend API functions
|
// Backend API functions
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ import App from './App'
|
|||||||
import { InstancesProvider } from './contexts/InstancesContext'
|
import { InstancesProvider } from './contexts/InstancesContext'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
import { AuthProvider } from './contexts/AuthContext'
|
import { AuthProvider } from './contexts/AuthContext'
|
||||||
|
import { ConfigProvider } from './contexts/ConfigContext'
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<InstancesProvider>
|
<ConfigProvider>
|
||||||
<App />
|
<InstancesProvider>
|
||||||
</InstancesProvider>
|
<App />
|
||||||
|
</InstancesProvider>
|
||||||
|
</ConfigProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
)
|
)
|
||||||
70
webui/src/types/config.ts
Normal file
70
webui/src/types/config.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
export interface BackendSettings {
|
||||||
|
command: string
|
||||||
|
args: string[]
|
||||||
|
environment?: Record<string, string>
|
||||||
|
docker?: DockerSettings
|
||||||
|
response_headers?: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DockerSettings {
|
||||||
|
enabled: boolean
|
||||||
|
image: string
|
||||||
|
args: string[]
|
||||||
|
environment?: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BackendConfig {
|
||||||
|
'llama-cpp': BackendSettings
|
||||||
|
vllm: BackendSettings
|
||||||
|
mlx: BackendSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServerConfig {
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
allowed_origins: string[]
|
||||||
|
allowed_headers: string[]
|
||||||
|
enable_swagger: boolean
|
||||||
|
response_headers?: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InstancesConfig {
|
||||||
|
port_range: [number, number]
|
||||||
|
data_dir: string
|
||||||
|
configs_dir: string
|
||||||
|
logs_dir: string
|
||||||
|
auto_create_dirs: boolean
|
||||||
|
max_instances: number
|
||||||
|
max_running_instances: number
|
||||||
|
enable_lru_eviction: boolean
|
||||||
|
default_auto_restart: boolean
|
||||||
|
default_max_restarts: number
|
||||||
|
default_restart_delay: number
|
||||||
|
default_on_demand_start: boolean
|
||||||
|
on_demand_start_timeout: number
|
||||||
|
timeout_check_interval: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthConfig {
|
||||||
|
require_inference_auth: boolean
|
||||||
|
inference_keys: string[] // Will be empty in sanitized response
|
||||||
|
require_management_auth: boolean
|
||||||
|
management_keys: string[] // Will be empty in sanitized response
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeConfig {
|
||||||
|
address: string
|
||||||
|
api_key: string // Will be empty in sanitized response
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppConfig {
|
||||||
|
server: ServerConfig
|
||||||
|
backends: BackendConfig
|
||||||
|
instances: InstancesConfig
|
||||||
|
auth: AuthConfig
|
||||||
|
local_node: string
|
||||||
|
nodes: Record<string, NodeConfig>
|
||||||
|
version?: string
|
||||||
|
commit_hash?: string
|
||||||
|
build_time?: string
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user