mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-09 09:30:06 +00:00
[Electron] ComfyUI server config (Launch args config) (#1644)
* Remove electron adapter server args * Add server args typing * Add server config constant file * Tooltip to name; name to id * Capitalize category * Server config store * Prevent default value * Add serverconfig test * Guard server config panel with electron flag * Filter nullish values from server args * Use slider for preview size
This commit is contained in:
@@ -71,6 +71,14 @@
|
||||
</template>
|
||||
</Suspense>
|
||||
</TabPanel>
|
||||
<TabPanel key="server-config" value="Server-Config">
|
||||
<Suspense>
|
||||
<ServerConfigPanel />
|
||||
<template #fallback>
|
||||
<div>Loading server config panel...</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</ScrollPanel>
|
||||
@@ -93,6 +101,7 @@ import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import { flattenTree } from '@/utils/treeUtil'
|
||||
import AboutPanel from './setting/AboutPanel.vue'
|
||||
import FirstTimeUIMessage from './setting/FirstTimeUIMessage.vue'
|
||||
import { isElectron } from '@/utils/envUtil'
|
||||
|
||||
const KeybindingPanel = defineAsyncComponent(
|
||||
() => import('./setting/KeybindingPanel.vue')
|
||||
@@ -100,6 +109,9 @@ const KeybindingPanel = defineAsyncComponent(
|
||||
const ExtensionPanel = defineAsyncComponent(
|
||||
() => import('./setting/ExtensionPanel.vue')
|
||||
)
|
||||
const ServerConfigPanel = defineAsyncComponent(
|
||||
() => import('./setting/ServerConfigPanel.vue')
|
||||
)
|
||||
|
||||
interface ISettingGroup {
|
||||
label: string
|
||||
@@ -124,18 +136,33 @@ const extensionPanelNode: SettingTreeNode = {
|
||||
children: []
|
||||
}
|
||||
|
||||
const serverConfigPanelNode: SettingTreeNode = {
|
||||
key: 'server-config',
|
||||
label: 'Server-Config',
|
||||
children: []
|
||||
}
|
||||
|
||||
const extensionPanelNodeList = computed<SettingTreeNode[]>(() => {
|
||||
const settingStore = useSettingStore()
|
||||
const showExtensionPanel = settingStore.get('Comfy.Settings.ExtensionPanel')
|
||||
return showExtensionPanel ? [extensionPanelNode] : []
|
||||
})
|
||||
|
||||
/**
|
||||
* Server config panel is only available in Electron. We might want to support
|
||||
* it in the web version in the future.
|
||||
*/
|
||||
const serverConfigPanelNodeList = computed<SettingTreeNode[]>(() => {
|
||||
return isElectron() ? [serverConfigPanelNode] : []
|
||||
})
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const settingRoot = computed<SettingTreeNode>(() => settingStore.settingTree)
|
||||
const categories = computed<SettingTreeNode[]>(() => [
|
||||
...(settingRoot.value.children || []),
|
||||
keybindingPanelNode,
|
||||
...extensionPanelNodeList.value,
|
||||
...serverConfigPanelNodeList.value,
|
||||
aboutPanelNode
|
||||
])
|
||||
const activeCategory = ref<SettingTreeNode | null>(null)
|
||||
|
||||
43
src/components/dialog/content/setting/ServerConfigPanel.vue
Normal file
43
src/components/dialog/content/setting/ServerConfigPanel.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div
|
||||
v-for="([label, items], i) in Object.entries(serverConfigsByCategory)"
|
||||
:key="label"
|
||||
>
|
||||
<Divider v-if="i > 0" />
|
||||
<h3>{{ formatCamelCase(label) }}</h3>
|
||||
<div v-for="item in items" :key="item.name" class="flex items-center mb-4">
|
||||
<FormItem :item="item" v-model:formValue="item.value" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Divider from 'primevue/divider'
|
||||
import FormItem from '@/components/common/FormItem.vue'
|
||||
import { formatCamelCase } from '@/utils/formatUtil'
|
||||
import { useSettingStore } from '@/stores/settingStore'
|
||||
import { useServerConfigStore } from '@/stores/serverConfigStore'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { onMounted, watch } from 'vue'
|
||||
import { SERVER_CONFIG_ITEMS } from '@/constants/serverConfig'
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const serverConfigStore = useServerConfigStore()
|
||||
const { serverConfigsByCategory, launchArgs, serverConfigValues } =
|
||||
storeToRefs(serverConfigStore)
|
||||
|
||||
onMounted(() => {
|
||||
serverConfigStore.loadServerConfig(
|
||||
SERVER_CONFIG_ITEMS,
|
||||
settingStore.get('Comfy.Server.ServerConfigValues')
|
||||
)
|
||||
})
|
||||
|
||||
watch(launchArgs, (newVal) => {
|
||||
settingStore.set('Comfy.Server.LaunchArgs', newVal)
|
||||
})
|
||||
|
||||
watch(serverConfigValues, (newVal) => {
|
||||
settingStore.set('Comfy.Server.ServerConfigValues', newVal)
|
||||
})
|
||||
</script>
|
||||
428
src/constants/serverConfig.ts
Normal file
428
src/constants/serverConfig.ts
Normal file
@@ -0,0 +1,428 @@
|
||||
import { FormItem } from '@/types/settingTypes'
|
||||
import {
|
||||
LatentPreviewMethod,
|
||||
LogLevel,
|
||||
HashFunction,
|
||||
AutoLaunch,
|
||||
CudaMalloc,
|
||||
FloatingPointPrecision,
|
||||
CrossAttentionMethod,
|
||||
VramManagement
|
||||
} from '@/types/serverArgs'
|
||||
|
||||
export interface ServerConfig<T> extends FormItem {
|
||||
id: string
|
||||
defaultValue: T
|
||||
category?: string[]
|
||||
// Override the default value getter with a custom function.
|
||||
getValue?: (value: T) => Record<string, any>
|
||||
}
|
||||
|
||||
export const SERVER_CONFIG_ITEMS: ServerConfig<any>[] = [
|
||||
// Network settings
|
||||
{
|
||||
id: 'listen',
|
||||
name: 'Host: The IP address to listen on',
|
||||
category: ['Network'],
|
||||
type: 'text',
|
||||
defaultValue: '127.0.0.1'
|
||||
},
|
||||
{
|
||||
id: 'port',
|
||||
name: 'Port: The port to listen on',
|
||||
category: ['Network'],
|
||||
type: 'number',
|
||||
defaultValue: 8188
|
||||
},
|
||||
{
|
||||
id: 'tls-keyfile',
|
||||
name: 'TLS Key File: Path to TLS key file for HTTPS',
|
||||
category: ['Network'],
|
||||
type: 'text',
|
||||
defaultValue: undefined
|
||||
},
|
||||
{
|
||||
id: 'tls-certfile',
|
||||
name: 'TLS Certificate File: Path to TLS certificate file for HTTPS',
|
||||
category: ['Network'],
|
||||
type: 'text',
|
||||
defaultValue: undefined
|
||||
},
|
||||
{
|
||||
id: 'enable-cors-header',
|
||||
name: 'Enable CORS header: Use "*" for all origins or specify domain',
|
||||
category: ['Network'],
|
||||
type: 'text',
|
||||
defaultValue: undefined
|
||||
},
|
||||
{
|
||||
id: 'max-upload-size',
|
||||
name: 'Maximum upload size (MB)',
|
||||
category: ['Network'],
|
||||
type: 'number',
|
||||
defaultValue: 100
|
||||
},
|
||||
|
||||
// Launch behavior
|
||||
{
|
||||
id: 'auto-launch',
|
||||
name: 'Automatically opens in the browser on startup',
|
||||
category: ['Launch'],
|
||||
type: 'combo',
|
||||
options: Object.values(AutoLaunch),
|
||||
defaultValue: AutoLaunch.Auto,
|
||||
getValue: (value: AutoLaunch) => {
|
||||
switch (value) {
|
||||
case AutoLaunch.Auto:
|
||||
return {}
|
||||
case AutoLaunch.Enable:
|
||||
return {
|
||||
['auto-launch']: true
|
||||
}
|
||||
case AutoLaunch.Disable:
|
||||
return {
|
||||
['disable-auto-launch']: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// CUDA settings
|
||||
{
|
||||
id: 'cuda-device',
|
||||
name: 'CUDA device index to use',
|
||||
category: ['CUDA'],
|
||||
type: 'number',
|
||||
defaultValue: undefined
|
||||
},
|
||||
{
|
||||
id: 'cuda-malloc',
|
||||
name: 'Use CUDA malloc for memory allocation',
|
||||
category: ['CUDA'],
|
||||
type: 'combo',
|
||||
options: Object.values(CudaMalloc),
|
||||
defaultValue: CudaMalloc.Auto,
|
||||
getValue: (value: CudaMalloc) => {
|
||||
switch (value) {
|
||||
case CudaMalloc.Auto:
|
||||
return {}
|
||||
case CudaMalloc.Enable:
|
||||
return {
|
||||
['cuda-malloc']: true
|
||||
}
|
||||
case CudaMalloc.Disable:
|
||||
return {
|
||||
['disable-cuda-malloc']: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Precision settings
|
||||
{
|
||||
id: 'global-precision',
|
||||
name: 'Global floating point precision',
|
||||
category: ['Inference'],
|
||||
type: 'combo',
|
||||
options: [
|
||||
FloatingPointPrecision.AUTO,
|
||||
FloatingPointPrecision.FP32,
|
||||
FloatingPointPrecision.FP16
|
||||
],
|
||||
defaultValue: FloatingPointPrecision.AUTO,
|
||||
tooltip: 'Global floating point precision',
|
||||
getValue: (value: FloatingPointPrecision) => {
|
||||
switch (value) {
|
||||
case FloatingPointPrecision.AUTO:
|
||||
return {}
|
||||
case FloatingPointPrecision.FP32:
|
||||
return {
|
||||
['force-fp32']: true
|
||||
}
|
||||
case FloatingPointPrecision.FP16:
|
||||
return {
|
||||
['force-fp16']: true
|
||||
}
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// UNET precision
|
||||
{
|
||||
id: 'unet-precision',
|
||||
name: 'UNET precision',
|
||||
category: ['Inference'],
|
||||
type: 'combo',
|
||||
options: [
|
||||
FloatingPointPrecision.AUTO,
|
||||
FloatingPointPrecision.FP16,
|
||||
FloatingPointPrecision.BF16,
|
||||
FloatingPointPrecision.FP8E4M3FN,
|
||||
FloatingPointPrecision.FP8E5M2
|
||||
],
|
||||
defaultValue: FloatingPointPrecision.AUTO,
|
||||
tooltip: 'UNET precision',
|
||||
getValue: (value: FloatingPointPrecision) => {
|
||||
switch (value) {
|
||||
case FloatingPointPrecision.AUTO:
|
||||
return {}
|
||||
default:
|
||||
return {
|
||||
[`${value.toLowerCase()}-unet`]: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// VAE settings
|
||||
{
|
||||
id: 'vae-precision',
|
||||
name: 'VAE precision',
|
||||
category: ['Inference'],
|
||||
type: 'combo',
|
||||
options: [
|
||||
FloatingPointPrecision.AUTO,
|
||||
FloatingPointPrecision.FP16,
|
||||
FloatingPointPrecision.FP32,
|
||||
FloatingPointPrecision.BF16
|
||||
],
|
||||
defaultValue: FloatingPointPrecision.AUTO,
|
||||
tooltip: 'VAE precision',
|
||||
getValue: (value: FloatingPointPrecision) => {
|
||||
switch (value) {
|
||||
case FloatingPointPrecision.AUTO:
|
||||
return {}
|
||||
default:
|
||||
return {
|
||||
[`${value.toLowerCase()}-vae`]: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'cpu-vae',
|
||||
name: 'Run VAE on CPU',
|
||||
category: ['Inference'],
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
|
||||
// Text Encoder settings
|
||||
{
|
||||
id: 'text-encoder-precision',
|
||||
name: 'Text Encoder precision',
|
||||
category: ['Inference'],
|
||||
type: 'combo',
|
||||
options: [
|
||||
FloatingPointPrecision.AUTO,
|
||||
FloatingPointPrecision.FP8E4M3FN,
|
||||
FloatingPointPrecision.FP8E5M2,
|
||||
FloatingPointPrecision.FP16,
|
||||
FloatingPointPrecision.FP32
|
||||
],
|
||||
defaultValue: FloatingPointPrecision.AUTO,
|
||||
tooltip: 'Text Encoder precision',
|
||||
getValue: (value: FloatingPointPrecision) => {
|
||||
switch (value) {
|
||||
case FloatingPointPrecision.AUTO:
|
||||
return {}
|
||||
default:
|
||||
return {
|
||||
[`${value.toLowerCase()}-text-enc`]: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Memory and performance settings
|
||||
{
|
||||
id: 'force-channels-last',
|
||||
name: 'Force channels-last memory format',
|
||||
category: ['Memory'],
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'directml',
|
||||
name: 'DirectML device index',
|
||||
category: ['Memory'],
|
||||
type: 'number',
|
||||
defaultValue: undefined
|
||||
},
|
||||
{
|
||||
id: 'disable-ipex-optimize',
|
||||
name: 'Disable IPEX optimization',
|
||||
category: ['Memory'],
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
|
||||
// Preview settings
|
||||
{
|
||||
id: 'preview-method',
|
||||
name: 'Method used for latent previews',
|
||||
category: ['Preview'],
|
||||
type: 'combo',
|
||||
options: Object.values(LatentPreviewMethod),
|
||||
defaultValue: LatentPreviewMethod.NoPreviews
|
||||
},
|
||||
{
|
||||
id: 'preview-size',
|
||||
name: 'Size of preview images',
|
||||
category: ['Preview'],
|
||||
type: 'slider',
|
||||
defaultValue: 512,
|
||||
attrs: {
|
||||
min: 128,
|
||||
max: 2048,
|
||||
step: 128
|
||||
}
|
||||
},
|
||||
|
||||
// Cache settings
|
||||
{
|
||||
id: 'cache-classic',
|
||||
name: 'Use classic cache system',
|
||||
category: ['Cache'],
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'cache-lru',
|
||||
name: 'Use LRU caching with a maximum of N node results cached. (0 to disable).',
|
||||
category: ['Cache'],
|
||||
type: 'number',
|
||||
defaultValue: 0,
|
||||
tooltip: 'May use more RAM/VRAM.'
|
||||
},
|
||||
|
||||
// Attention settings
|
||||
{
|
||||
id: 'cross-attention-method',
|
||||
name: 'Cross attention method',
|
||||
category: ['Attention'],
|
||||
type: 'combo',
|
||||
options: Object.values(CrossAttentionMethod),
|
||||
defaultValue: CrossAttentionMethod.Auto,
|
||||
getValue: (value: CrossAttentionMethod) => {
|
||||
switch (value) {
|
||||
case CrossAttentionMethod.Auto:
|
||||
return {}
|
||||
default:
|
||||
return {
|
||||
[`use-${value.toLowerCase()}-cross-attention`]: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'disable-xformers',
|
||||
name: 'Disable xFormers optimization',
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'force-upcast-attention',
|
||||
name: 'Force attention upcast',
|
||||
category: ['Attention'],
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'dont-upcast-attention',
|
||||
name: 'Prevent attention upcast',
|
||||
category: ['Attention'],
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
|
||||
// VRAM management
|
||||
{
|
||||
id: 'vram-management',
|
||||
name: 'VRAM management mode',
|
||||
category: ['Memory'],
|
||||
type: 'combo',
|
||||
options: Object.values(VramManagement),
|
||||
defaultValue: VramManagement.Auto,
|
||||
getValue: (value: VramManagement) => {
|
||||
switch (value) {
|
||||
case VramManagement.Auto:
|
||||
return {}
|
||||
default:
|
||||
return {
|
||||
[value]: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'reserve-vram',
|
||||
name: 'Reserved VRAM (GB)',
|
||||
category: ['Memory'],
|
||||
type: 'number',
|
||||
defaultValue: undefined,
|
||||
tooltip:
|
||||
'Set the amount of vram in GB you want to reserve for use by your OS/other software. By default some amount is reverved depending on your OS.'
|
||||
},
|
||||
|
||||
// Misc settings
|
||||
{
|
||||
id: 'default-hashing-function',
|
||||
name: 'Default hashing function for model files',
|
||||
type: 'combo',
|
||||
options: Object.values(HashFunction),
|
||||
defaultValue: HashFunction.SHA256
|
||||
},
|
||||
{
|
||||
id: 'disable-smart-memory',
|
||||
name: 'Force ComfyUI to agressively offload to regular ram instead of keeping models in vram when it can.',
|
||||
category: ['Memory'],
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'deterministic',
|
||||
name: 'Make pytorch use slower deterministic algorithms when it can.',
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
tooltip: 'Note that this might not make images deterministic in all cases.'
|
||||
},
|
||||
{
|
||||
id: 'fast',
|
||||
name: 'Enable some untested and potentially quality deteriorating optimizations.',
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'dont-print-server',
|
||||
name: "Don't print server output to console.",
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'disable-metadata',
|
||||
name: 'Disable saving prompt metadata in files.',
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'disable-all-custom-nodes',
|
||||
name: 'Disable loading all custom nodes.',
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
id: 'log-level',
|
||||
name: 'Logging verbosity level',
|
||||
type: 'combo',
|
||||
options: Object.values(LogLevel),
|
||||
defaultValue: LogLevel.INFO,
|
||||
getValue: (value: LogLevel) => {
|
||||
return {
|
||||
verbose: value
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -31,14 +31,6 @@ import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil'
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
onChange: onChangeRestartApp
|
||||
},
|
||||
{
|
||||
id: 'Comfy-Desktop.ComfyServer.ExtraLaunchArgs',
|
||||
category: ['Comfy-Desktop', 'ComfyUI Server'],
|
||||
name: 'Extra launch arguments passed to the ComfyUI main.py script',
|
||||
type: 'text',
|
||||
defaultValue: '',
|
||||
onChange: onChangeRestartApp
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
@@ -624,5 +624,23 @@ export const CORE_SETTINGS: SettingParams[] = [
|
||||
type: 'boolean',
|
||||
defaultValue: false,
|
||||
versionAdded: '1.3.13'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Server.ServerConfigValues',
|
||||
name: 'Server config values for frontend display',
|
||||
tooltip: 'Server config values used for frontend display only',
|
||||
type: 'hidden',
|
||||
// Mapping from server config id to value.
|
||||
defaultValue: {} as Record<string, any>,
|
||||
versionAdded: '1.4.8'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.Server.LaunchArgs',
|
||||
name: 'Server launch arguments',
|
||||
tooltip:
|
||||
'These are the actual arguments that are passed to the server when it is launched.',
|
||||
type: 'hidden',
|
||||
defaultValue: {} as Record<string, string>,
|
||||
versionAdded: '1.4.8'
|
||||
}
|
||||
]
|
||||
|
||||
73
src/stores/serverConfigStore.ts
Normal file
73
src/stores/serverConfigStore.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { ServerConfig } from '@/constants/serverConfig'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
export type ServerConfigWithValue<T> = ServerConfig<T> & {
|
||||
value: T
|
||||
}
|
||||
|
||||
export const useServerConfigStore = defineStore('serverConfig', () => {
|
||||
const serverConfigById = ref<Record<string, ServerConfigWithValue<any>>>({})
|
||||
const serverConfigs = computed(() => {
|
||||
return Object.values(serverConfigById.value)
|
||||
})
|
||||
const serverConfigsByCategory = computed<
|
||||
Record<string, ServerConfigWithValue<any>[]>
|
||||
>(() => {
|
||||
return serverConfigs.value.reduce(
|
||||
(acc, config) => {
|
||||
const category = config.category?.[0] ?? 'General'
|
||||
acc[category] = acc[category] || []
|
||||
acc[category].push(config)
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, ServerConfigWithValue<any>[]>
|
||||
)
|
||||
})
|
||||
const serverConfigValues = computed<Record<string, any>>(() => {
|
||||
return Object.fromEntries(
|
||||
serverConfigs.value.map((config) => {
|
||||
return [
|
||||
config.id,
|
||||
config.value === config.defaultValue || !config.value
|
||||
? undefined
|
||||
: config.value
|
||||
]
|
||||
})
|
||||
)
|
||||
})
|
||||
const launchArgs = computed<Record<string, string>>(() => {
|
||||
return Object.assign(
|
||||
{},
|
||||
...serverConfigs.value.map((config) => {
|
||||
if (config.value === config.defaultValue || !config.value) {
|
||||
return {}
|
||||
}
|
||||
return config.getValue
|
||||
? config.getValue(config.value)
|
||||
: { [config.id]: config.value }
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
function loadServerConfig(
|
||||
configs: ServerConfig<any>[],
|
||||
values: Record<string, any>
|
||||
) {
|
||||
for (const config of configs) {
|
||||
serverConfigById.value[config.id] = {
|
||||
...config,
|
||||
value: values[config.id] ?? config.defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
serverConfigById,
|
||||
serverConfigs,
|
||||
serverConfigsByCategory,
|
||||
serverConfigValues,
|
||||
launchArgs,
|
||||
loadServerConfig
|
||||
}
|
||||
})
|
||||
@@ -526,7 +526,9 @@ const zSettings = z.record(z.any()).and(
|
||||
'Comfy.Settings.ExtensionPanel': z.boolean(),
|
||||
'Comfy.LinkRenderMode': z.number(),
|
||||
'Comfy.Node.AutoSnapLinkToSlot': z.boolean(),
|
||||
'Comfy.Node.SnapHighlightsNode': z.boolean()
|
||||
'Comfy.Node.SnapHighlightsNode': z.boolean(),
|
||||
'Comfy.Server.ServerConfigValues': z.record(z.string(), z.any()),
|
||||
'Comfy.Server.LaunchArgs': z.record(z.string(), z.string())
|
||||
})
|
||||
.optional()
|
||||
)
|
||||
|
||||
65
src/types/serverArgs.ts
Normal file
65
src/types/serverArgs.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
export enum LatentPreviewMethod {
|
||||
NoPreviews = 'none',
|
||||
Auto = 'auto',
|
||||
Latent2RGB = 'latent2rgb',
|
||||
TAESD = 'taesd'
|
||||
}
|
||||
|
||||
export enum LogLevel {
|
||||
DEBUG = 'DEBUG',
|
||||
INFO = 'INFO',
|
||||
WARNING = 'WARNING',
|
||||
ERROR = 'ERROR',
|
||||
CRITICAL = 'CRITICAL'
|
||||
}
|
||||
|
||||
export enum HashFunction {
|
||||
MD5 = 'md5',
|
||||
SHA1 = 'sha1',
|
||||
SHA256 = 'sha256',
|
||||
SHA512 = 'sha512'
|
||||
}
|
||||
|
||||
export enum AutoLaunch {
|
||||
// Let server decide whether to auto launch based on the current environment
|
||||
Auto = 'auto',
|
||||
// Disable auto launch
|
||||
Disable = 'disable',
|
||||
// Enable auto launch
|
||||
Enable = 'enable'
|
||||
}
|
||||
|
||||
export enum CudaMalloc {
|
||||
// Let server decide whether to use CUDA malloc based on the current environment
|
||||
Auto = 'auto',
|
||||
// Disable CUDA malloc
|
||||
Disable = 'disable',
|
||||
// Enable CUDA malloc
|
||||
Enable = 'enable'
|
||||
}
|
||||
|
||||
export enum FloatingPointPrecision {
|
||||
AUTO = 'auto',
|
||||
FP32 = 'fp32',
|
||||
FP16 = 'fp16',
|
||||
BF16 = 'bf16',
|
||||
FP8E4M3FN = 'fp8_e4m3fn',
|
||||
FP8E5M2 = 'fp8_e5m2'
|
||||
}
|
||||
|
||||
export enum CrossAttentionMethod {
|
||||
Auto = 'auto',
|
||||
Split = 'split',
|
||||
Quad = 'quad',
|
||||
Pytorch = 'pytorch'
|
||||
}
|
||||
|
||||
export enum VramManagement {
|
||||
Auto = 'auto',
|
||||
GPUOnly = 'gpu-only',
|
||||
HighVram = 'highvram',
|
||||
NormalVram = 'normalvram',
|
||||
LowVram = 'lowvram',
|
||||
NoVram = 'novram',
|
||||
CPU = 'cpu'
|
||||
}
|
||||
189
tests-ui/tests/fast/store/serverConfigStore.test.ts
Normal file
189
tests-ui/tests/fast/store/serverConfigStore.test.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { setActivePinia, createPinia } from 'pinia'
|
||||
import { useServerConfigStore } from '@/stores/serverConfigStore'
|
||||
import { ServerConfig } from '@/constants/serverConfig'
|
||||
import type { FormItem } from '@/types/settingTypes'
|
||||
|
||||
const dummyFormItem: FormItem = {
|
||||
name: '',
|
||||
type: 'text'
|
||||
}
|
||||
|
||||
describe('useServerConfigStore', () => {
|
||||
let store: ReturnType<typeof useServerConfigStore>
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
store = useServerConfigStore()
|
||||
})
|
||||
|
||||
it('should initialize with empty configs', () => {
|
||||
expect(store.serverConfigs).toHaveLength(0)
|
||||
expect(Object.keys(store.serverConfigById)).toHaveLength(0)
|
||||
expect(Object.keys(store.serverConfigsByCategory)).toHaveLength(0)
|
||||
expect(Object.keys(store.serverConfigValues)).toHaveLength(0)
|
||||
expect(Object.keys(store.launchArgs)).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('should load server configs with default values', () => {
|
||||
const configs: ServerConfig<any>[] = [
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config1',
|
||||
defaultValue: 'default1',
|
||||
category: ['Test']
|
||||
},
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config2',
|
||||
defaultValue: 'default2'
|
||||
}
|
||||
]
|
||||
|
||||
store.loadServerConfig(configs, {})
|
||||
|
||||
expect(store.serverConfigs).toHaveLength(2)
|
||||
expect(store.serverConfigById['test.config1'].value).toBe('default1')
|
||||
expect(store.serverConfigById['test.config2'].value).toBe('default2')
|
||||
})
|
||||
|
||||
it('should load server configs with provided values', () => {
|
||||
const configs: ServerConfig<any>[] = [
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config1',
|
||||
defaultValue: 'default1',
|
||||
category: ['Test']
|
||||
}
|
||||
]
|
||||
|
||||
store.loadServerConfig(configs, {
|
||||
'test.config1': 'custom1'
|
||||
})
|
||||
|
||||
expect(store.serverConfigs).toHaveLength(1)
|
||||
expect(store.serverConfigById['test.config1'].value).toBe('custom1')
|
||||
})
|
||||
|
||||
it('should organize configs by category', () => {
|
||||
const configs: ServerConfig<any>[] = [
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config1',
|
||||
defaultValue: 'default1',
|
||||
category: ['Test']
|
||||
},
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config2',
|
||||
defaultValue: 'default2',
|
||||
category: ['Other']
|
||||
},
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config3',
|
||||
defaultValue: 'default3'
|
||||
}
|
||||
]
|
||||
|
||||
store.loadServerConfig(configs, {})
|
||||
|
||||
expect(Object.keys(store.serverConfigsByCategory)).toHaveLength(3)
|
||||
expect(store.serverConfigsByCategory['Test']).toHaveLength(1)
|
||||
expect(store.serverConfigsByCategory['Other']).toHaveLength(1)
|
||||
expect(store.serverConfigsByCategory['General']).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should generate server config values excluding defaults', () => {
|
||||
const configs: ServerConfig<any>[] = [
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config1',
|
||||
defaultValue: 'default1'
|
||||
},
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config2',
|
||||
defaultValue: 'default2'
|
||||
}
|
||||
]
|
||||
|
||||
store.loadServerConfig(configs, {
|
||||
'test.config1': 'custom1',
|
||||
'test.config2': 'default2'
|
||||
})
|
||||
|
||||
expect(Object.keys(store.serverConfigValues)).toHaveLength(2)
|
||||
expect(store.serverConfigValues['test.config1']).toBe('custom1')
|
||||
expect(store.serverConfigValues['test.config2']).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should generate launch arguments with custom getValue function', () => {
|
||||
const configs: ServerConfig<any>[] = [
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config1',
|
||||
defaultValue: 'default1',
|
||||
getValue: (value: string) => ({ customArg: value })
|
||||
},
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config2',
|
||||
defaultValue: 'default2'
|
||||
}
|
||||
]
|
||||
|
||||
store.loadServerConfig(configs, {
|
||||
'test.config1': 'custom1',
|
||||
'test.config2': 'custom2'
|
||||
})
|
||||
|
||||
expect(Object.keys(store.launchArgs)).toHaveLength(2)
|
||||
expect(store.launchArgs['customArg']).toBe('custom1')
|
||||
expect(store.launchArgs['test.config2']).toBe('custom2')
|
||||
})
|
||||
|
||||
it('should not include default values in launch arguments', () => {
|
||||
const configs: ServerConfig<any>[] = [
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config1',
|
||||
defaultValue: 'default1'
|
||||
},
|
||||
{
|
||||
...dummyFormItem,
|
||||
id: 'test.config2',
|
||||
defaultValue: 'default2'
|
||||
}
|
||||
]
|
||||
|
||||
store.loadServerConfig(configs, {
|
||||
'test.config1': 'custom1',
|
||||
'test.config2': 'default2'
|
||||
})
|
||||
|
||||
expect(Object.keys(store.launchArgs)).toHaveLength(1)
|
||||
expect(store.launchArgs['test.config1']).toBe('custom1')
|
||||
expect(store.launchArgs['test.config2']).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should not include nullish values in launch arguments', () => {
|
||||
const configs: ServerConfig<any>[] = [
|
||||
{ ...dummyFormItem, id: 'test.config1', defaultValue: 'default1' },
|
||||
{ ...dummyFormItem, id: 'test.config2', defaultValue: 'default2' },
|
||||
{ ...dummyFormItem, id: 'test.config3', defaultValue: 'default3' }
|
||||
]
|
||||
|
||||
store.loadServerConfig(configs, {
|
||||
'test.config1': undefined,
|
||||
'test.config2': null,
|
||||
'test.config3': ''
|
||||
})
|
||||
|
||||
expect(Object.keys(store.launchArgs)).toHaveLength(0)
|
||||
expect(Object.keys(store.serverConfigValues)).toEqual([
|
||||
'test.config1',
|
||||
'test.config2',
|
||||
'test.config3'
|
||||
])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user