mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 03:01:54 +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>
|
</template>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
<TabPanel key="server-config" value="Server-Config">
|
||||||
|
<Suspense>
|
||||||
|
<ServerConfigPanel />
|
||||||
|
<template #fallback>
|
||||||
|
<div>Loading server config panel...</div>
|
||||||
|
</template>
|
||||||
|
</Suspense>
|
||||||
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</ScrollPanel>
|
</ScrollPanel>
|
||||||
@@ -93,6 +101,7 @@ import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
|||||||
import { flattenTree } from '@/utils/treeUtil'
|
import { flattenTree } from '@/utils/treeUtil'
|
||||||
import AboutPanel from './setting/AboutPanel.vue'
|
import AboutPanel from './setting/AboutPanel.vue'
|
||||||
import FirstTimeUIMessage from './setting/FirstTimeUIMessage.vue'
|
import FirstTimeUIMessage from './setting/FirstTimeUIMessage.vue'
|
||||||
|
import { isElectron } from '@/utils/envUtil'
|
||||||
|
|
||||||
const KeybindingPanel = defineAsyncComponent(
|
const KeybindingPanel = defineAsyncComponent(
|
||||||
() => import('./setting/KeybindingPanel.vue')
|
() => import('./setting/KeybindingPanel.vue')
|
||||||
@@ -100,6 +109,9 @@ const KeybindingPanel = defineAsyncComponent(
|
|||||||
const ExtensionPanel = defineAsyncComponent(
|
const ExtensionPanel = defineAsyncComponent(
|
||||||
() => import('./setting/ExtensionPanel.vue')
|
() => import('./setting/ExtensionPanel.vue')
|
||||||
)
|
)
|
||||||
|
const ServerConfigPanel = defineAsyncComponent(
|
||||||
|
() => import('./setting/ServerConfigPanel.vue')
|
||||||
|
)
|
||||||
|
|
||||||
interface ISettingGroup {
|
interface ISettingGroup {
|
||||||
label: string
|
label: string
|
||||||
@@ -124,18 +136,33 @@ const extensionPanelNode: SettingTreeNode = {
|
|||||||
children: []
|
children: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serverConfigPanelNode: SettingTreeNode = {
|
||||||
|
key: 'server-config',
|
||||||
|
label: 'Server-Config',
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
|
||||||
const extensionPanelNodeList = computed<SettingTreeNode[]>(() => {
|
const extensionPanelNodeList = computed<SettingTreeNode[]>(() => {
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
const showExtensionPanel = settingStore.get('Comfy.Settings.ExtensionPanel')
|
const showExtensionPanel = settingStore.get('Comfy.Settings.ExtensionPanel')
|
||||||
return showExtensionPanel ? [extensionPanelNode] : []
|
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 settingStore = useSettingStore()
|
||||||
const settingRoot = computed<SettingTreeNode>(() => settingStore.settingTree)
|
const settingRoot = computed<SettingTreeNode>(() => settingStore.settingTree)
|
||||||
const categories = computed<SettingTreeNode[]>(() => [
|
const categories = computed<SettingTreeNode[]>(() => [
|
||||||
...(settingRoot.value.children || []),
|
...(settingRoot.value.children || []),
|
||||||
keybindingPanelNode,
|
keybindingPanelNode,
|
||||||
...extensionPanelNodeList.value,
|
...extensionPanelNodeList.value,
|
||||||
|
...serverConfigPanelNodeList.value,
|
||||||
aboutPanelNode
|
aboutPanelNode
|
||||||
])
|
])
|
||||||
const activeCategory = ref<SettingTreeNode | null>(null)
|
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',
|
type: 'boolean',
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
onChange: onChangeRestartApp
|
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',
|
type: 'boolean',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
versionAdded: '1.3.13'
|
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.Settings.ExtensionPanel': z.boolean(),
|
||||||
'Comfy.LinkRenderMode': z.number(),
|
'Comfy.LinkRenderMode': z.number(),
|
||||||
'Comfy.Node.AutoSnapLinkToSlot': z.boolean(),
|
'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()
|
.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