Skip to content

Commit 7964b4b

Browse files
chore: update default sampler (#49)
* feat: auto config some settings based on chosen model * chore: handle clip skip when selecting pony model * chore: fix layout issue * chore: UI improvements on orientation select modal * chore: update changelog
1 parent 687b3d7 commit 7964b4b

File tree

10 files changed

+176
-41
lines changed

10 files changed

+176
-41
lines changed

.prettierrc

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"bracketSpacing": true,
33
"printWidth": 80,
4-
"semi": false,
4+
"semi": true,
55
"singleQuote": true,
66
"tabWidth": 2,
77
"trailingComma": "none",
88
"useTabs": false
9-
}
9+
}

app/_components/AdvancedOptions/ImageOrientation_Custom.tsx

+17-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { IconArrowsShuffle, IconLock, IconLockOpen } from '@tabler/icons-react'
55
import Button from '../Button'
66
import PromptInput from '@/app/_data-models/PromptInput'
77
import useImageSize from '@/app/_hooks/useImageSize'
8+
import NiceModal from '@ebay/nice-modal-react'
89

910
export default function CustomImageOrientation({
1011
input,
@@ -22,7 +23,7 @@ export default function CustomImageOrientation({
2223
input.width,
2324
input.height
2425
)
25-
const [lockRatio, setLockRatio] = useState(true)
26+
const [lockRatio, setLockRatio] = useState(false)
2627

2728
const handleSetDimensions = (w: number, h: number) => {
2829
setHeight(h)
@@ -118,7 +119,12 @@ export default function CustomImageOrientation({
118119
</div>
119120
<div className="row w-full gap-2 justify-end">
120121
<div
122+
onClick={() => {
123+
handleSetDimensions(width, height)
124+
setLockRatio(!lockRatio)
125+
}}
121126
style={{
127+
cursor: 'pointer',
122128
position: 'relative',
123129
width: '20px',
124130
height: '22px',
@@ -236,6 +242,16 @@ export default function CustomImageOrientation({
236242
<IconArrowsShuffle /> Swap
237243
</span>
238244
</Button>
245+
<Button
246+
onClick={() => {
247+
NiceModal.remove('modal')
248+
}}
249+
style={{
250+
minWidth: '60px'
251+
}}
252+
>
253+
OK
254+
</Button>
239255
</div>
240256
</div>
241257
)

app/_components/AdvancedOptions/ModelSelect/index.tsx

-3
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,8 @@ export async function getData() {
5555
}
5656

5757
export default async function ModelSelect() {
58-
// const data = await getData()
5958
return (
6059
<ModelSelectComponent
61-
// hasError={data.success === false}
62-
// models={data.models}
6360
/>
6461
)
6562
}

app/_components/AdvancedOptions/ModelSelect/modelSelectComponent.tsx

+29-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import { IconListDetails, IconWand } from '@tabler/icons-react'
88
import ModelModalWrapper from './modalWrapper'
99
import { useStore } from 'statery'
1010
import { ModelStore } from '@/app/_stores/ModelStore'
11-
import SelectCombo, { SelectOption } from '../../ComboBox'
11+
import SelectCombo from '../../ComboBox'
12+
import PromptInput from '@/app/_data-models/PromptInput'
1213

1314
export default function ModelSelect() {
1415
const { availableModels, modelDetails } = useStore(ModelStore)
@@ -30,18 +31,39 @@ export default function ModelSelect() {
3031
>
3132
<div className="w-full row">
3233
<SelectCombo
33-
onChange={(option: SelectOption) => {
34+
// @ts-expect-error - value will always be string here
35+
onChange={(option: { value: string }) => {
3436
if (!option || !option.value) return
3537

3638
const modelInfo = {
3739
baseline: modelDetails[option.value as string]?.baseline ?? '',
3840
version: modelDetails[option.value as string]?.version ?? ''
3941
}
4042

41-
setInput({
42-
models: [option.value as string],
43-
modelDetails: modelInfo
44-
})
43+
// PonyXL models require a CLIP setting of at least 2
44+
const isPonyModel = option.value.toLowerCase().indexOf('pony') >= 0
45+
46+
if (PromptInput.isDefaultPromptInput(input) && option.value !== 'AlbedoBase XL (SDXL)') {
47+
setInput(PromptInput.setNonTurboDefaultPromptInput({
48+
...input, models: [option.value as string],
49+
clipskip: isPonyModel && input.clipskip < 2 ? 2 : input.clipskip
50+
}))
51+
} else if (input.models[0] !== 'AlbedoBase XL (SDXL)' && option.value === 'AlbedoBase XL (SDXL)') {
52+
setInput(PromptInput.setTurboDefaultPromptInput({ ...input, models: [option.value as string] }))
53+
} else {
54+
setInput({
55+
models: [option.value as string],
56+
modelDetails: modelInfo
57+
})
58+
}
59+
60+
if (option.value.toLowerCase().indexOf('pony') >= 0 && input.clipskip < 2) {
61+
setInput({
62+
models: [option.value as string],
63+
modelDetails: modelInfo,
64+
clipskip: 2
65+
})
66+
}
4567
}}
4668
options={availableModels.map((model) => ({
4769
value: model.name,
@@ -60,7 +82,7 @@ export default function ModelSelect() {
6082

6183
const randomModel =
6284
availableModels[
63-
Math.floor(Math.random() * availableModels.length)
85+
Math.floor(Math.random() * availableModels.length)
6486
]
6587

6688
setInput({ models: [randomModel.name] })

app/_components/AdvancedOptions/StylePresetSelect/stylePresetSelectComponent.tsx

+16-12
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
StylePreviewConfigurations
1515
} from '@/app/_types/HordeTypes'
1616
import { useCallback } from 'react'
17-
import PromptInput from '@/app/_data-models/PromptInput'
17+
import PromptInput, { DEFAULT_TURBO_LORA } from '@/app/_data-models/PromptInput'
1818
import { SavedLora } from '@/app/_data-models/Civitai'
1919
import { useStore } from 'statery'
2020
import { ModelStore } from '@/app/_stores/ModelStore'
@@ -55,17 +55,21 @@ export default function StylePresetSelectComponent({
5555

5656
if (key === 'loras' && typeof presetSettings.loras !== 'undefined') {
5757
updateInput.loras = []
58-
5958
presetSettings.loras.forEach((lora) => {
60-
const updateLora = new SavedLora({
61-
id: lora.name,
62-
versionId: lora.is_version ? Number(lora.name) : false,
63-
versionName: lora.name,
64-
isArtbotManualEntry: true,
65-
name: lora.name,
66-
strength: lora.model || 1,
67-
clip: lora.clip_skip || 1
68-
})
59+
let updateLora: SavedLora
60+
if (lora.name == '247778') {
61+
updateLora = DEFAULT_TURBO_LORA
62+
} else {
63+
updateLora = new SavedLora({
64+
id: lora.name,
65+
versionId: lora.is_version ? Number(lora.name) : false,
66+
versionName: lora.name,
67+
isArtbotManualEntry: true,
68+
name: lora.name,
69+
strength: lora.model || 1,
70+
clip: lora.clip_skip || 1
71+
})
72+
}
6973

7074
// @ts-expect-error updateInput.loras is defined right above this.
7175
updateInput.loras.push({ ...updateLora })
@@ -138,7 +142,7 @@ export default function StylePresetSelectComponent({
138142
modalClassName: 'w-full md:min-w-[640px] max-w-[648px]'
139143
})
140144
}}
141-
onChange={() => {}}
145+
onChange={() => { }}
142146
options={options}
143147
value={options[0]}
144148
/>

app/_components/AdvancedOptions/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ import ModelSelect from './ModelSelect'
1313
import SamplerSelect from './SamplerSelect'
1414
import Seed from './Seed'
1515
import Steps from './Steps'
16-
import UploadImage from './UploadImage'
1716
import Upscalers from './Upscalers'
1817
import StylePresetSelect from './StylePresetSelect'
1918
import AddWorkflow from './AddWorkflow'
2019
import HiresFix from './HiresFix'
20+
import UploadImage from './UploadImage'
2121

2222
export default function AdvancedOptions() {
2323
return (

app/_data-models/PromptInput.test.ts

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import PromptInput from './PromptInput'
1+
import PromptInput, { DEFAULT_TURBO_LORA } from './PromptInput'
22
import { JobType } from '@/app/_types/ArtbotTypes'
33
import { SourceProcessing } from '@/app/_types/HordeTypes'
4+
import { SavedLora } from './Civitai'
45

56
describe('PromptInput', () => {
67
it('should initialize with default values', () => {
@@ -30,7 +31,7 @@ describe('PromptInput', () => {
3031
expect(defaultPromptInput.preset).toEqual([])
3132
expect(defaultPromptInput.prompt).toBe('')
3233
expect(defaultPromptInput.return_control_map).toBe(false)
33-
expect(defaultPromptInput.sampler).toBe('k_dpmpp_sde')
34+
expect(defaultPromptInput.sampler).toBe('euler_a')
3435
expect(defaultPromptInput.seed).toBe('')
3536
expect(defaultPromptInput.source_processing).toBe(SourceProcessing.Prompt)
3637
expect(defaultPromptInput.steps).toBe(8)
@@ -77,4 +78,40 @@ describe('PromptInput', () => {
7778
{ text: 'example', reference: 'ref' }
7879
])
7980
})
81+
82+
it('should correctly identify a default prompt', () => {
83+
const defaultPromptInput = new PromptInput({})
84+
expect(PromptInput.isDefaultPromptInput(defaultPromptInput)).toBe(true)
85+
86+
const nonDefaultPromptInput = new PromptInput({
87+
models: ['Different Model'],
88+
steps: 10,
89+
cfg_scale: 3
90+
})
91+
expect(PromptInput.isDefaultPromptInput(nonDefaultPromptInput)).toBe(false)
92+
})
93+
94+
it('should correctly set non-turbo default prompt input', () => {
95+
const input = new PromptInput({
96+
loras: [DEFAULT_TURBO_LORA, { id: 'other', versionId: 'other' } as SavedLora]
97+
})
98+
const result = PromptInput.setNonTurboDefaultPromptInput(input)
99+
100+
expect(result.steps).toBe(24)
101+
expect(result.cfg_scale).toBe(6)
102+
expect(result.loras).toHaveLength(1)
103+
expect(result.loras[0].versionId).not.toBe('247778')
104+
})
105+
106+
it('should correctly set turbo default prompt input', () => {
107+
const input = new PromptInput({
108+
loras: [{ id: 'other', versionId: 'other' } as SavedLora]
109+
})
110+
const result = PromptInput.setTurboDefaultPromptInput(input)
111+
112+
expect(result.steps).toBe(8)
113+
expect(result.cfg_scale).toBe(2)
114+
expect(result.loras).toHaveLength(2)
115+
expect(result.loras[0]).toEqual(DEFAULT_TURBO_LORA)
116+
})
80117
})

app/_data-models/PromptInput.ts

+67-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ import {
66
} from '@/app/_types/HordeTypes'
77
import { SavedEmbedding, SavedLora } from './Civitai'
88

9+
export const DEFAULT_TURBO_LORA = new SavedLora({
10+
id: '247778',
11+
civitAiType: 'LORA',
12+
versionId: '247778',
13+
versionName: '',
14+
isArtbotManualEntry: true,
15+
name: 'SDXL | LCM TurboMix LoRA (SDE sampler)',
16+
strength: 1,
17+
clip: 1
18+
})
19+
920
class PromptInput {
1021
// ArtBot ID for mainting relationships in IndexedDb
1122
artbot_id: string = ''
@@ -44,16 +55,7 @@ class PromptInput {
4455
jobType: JobType = JobType.Text2Img
4556
karras: boolean = true
4657
loras: SavedLora[] = [
47-
new SavedLora({
48-
id: '247778',
49-
civitAiType: 'LORA',
50-
versionId: '247778',
51-
versionName: '',
52-
isArtbotManualEntry: true,
53-
name: 'SDXL | LCM TurboMix LoRA (SDE sampler)',
54-
strength: 1,
55-
clip: 1
56-
})
58+
DEFAULT_TURBO_LORA
5759
]
5860
models: Array<string> = ['AlbedoBase XL (SDXL)']
5961
negative: string = ''
@@ -62,7 +64,7 @@ class PromptInput {
6264
post_processing: Array<string> = []
6365
prompt: string = ''
6466
return_control_map: boolean = false
65-
sampler: string = 'k_dpmpp_sde'
67+
sampler: string = 'euler_a'
6668
seed: string = ''
6769
source_processing?: SourceProcessing = SourceProcessing.Prompt
6870
steps: number = 8
@@ -77,6 +79,60 @@ class PromptInput {
7779
constructor(params: Partial<PromptInput> = {}) {
7880
Object.assign(this, params)
7981
}
82+
83+
/**
84+
* Checks if the prompt input is a default prompt. I've hardcoded some default values here
85+
* but this should be okay as any changes above will cause the below test to fail at built time.
86+
* @param input - The prompt input to check.
87+
* @returns True if the prompt input is a default prompt, false otherwise.
88+
*/
89+
static isDefaultPromptInput(input: PromptInput): boolean {
90+
const hasTurboLora = input.loras.filter(lora => lora.versionId === '247778').length > 0
91+
const hasDefaultStepsAndCfgScale = input.models[0] === 'AlbedoBase XL (SDXL)' && Number(input.steps) === 8 && Number(input.cfg_scale) === 2
92+
const hasDefaultModelAndLora = input.models[0] === 'AlbedoBase XL (SDXL)' && hasTurboLora
93+
94+
return hasDefaultStepsAndCfgScale || hasDefaultModelAndLora
95+
}
96+
97+
/**
98+
* Sets the prompt input to a non-turbo default prompt. This occurs when a user
99+
* selects a non-SDXL turbo model, so we need to adjust steps and cfg_scale to
100+
* the non-turbo defaults.
101+
* @param input - The prompt input to set to a non-turbo default prompt.
102+
* @returns The prompt input set to a non-turbo default prompt.
103+
*/
104+
static setNonTurboDefaultPromptInput(input: PromptInput): PromptInput {
105+
// Remove the LCM TurboMix LoRA if present
106+
input.loras = input.loras.filter(lora => lora.versionId != '247778');
107+
108+
return new PromptInput({
109+
...input,
110+
steps: 24,
111+
cfg_scale: 6
112+
})
113+
}
114+
115+
/**
116+
* Sets the prompt input to a turbo default prompt. This occurs when a user
117+
* selects a SDXL turbo model, so we need to adjust steps, cfg_scale, and
118+
* add the LCM TurboMix LoRA to the loras array.
119+
* @param input - The prompt input to set to a turbo default prompt.
120+
* @returns The prompt input set to a turbo default prompt.
121+
*/
122+
static setTurboDefaultPromptInput(input: PromptInput): PromptInput {
123+
// Check if the LCM TurboMix LoRA is not present in the input.loras array
124+
if (!input.loras.some(lora => lora.versionId == '247778')) {
125+
// If not present, add it to the beginning of the array
126+
input.loras.unshift(DEFAULT_TURBO_LORA);
127+
}
128+
129+
130+
return new PromptInput({
131+
...input,
132+
steps: 8,
133+
cfg_scale: 2
134+
})
135+
}
80136
}
81137

82138
export default PromptInput

app/changelog/_updates/2024.08.29.md

+3
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@
3131
- (fix) more work handling stuck jobs when "is_possible" is false. It really works, this time!
3232
- (feat) add ability to cancel job in progress without losing existing downloaded images (useful for jobs where GPU worker goes offline).
3333
- (feat) likewise, if a job returns a 404 error after taking too long to complete, you will no longer lose existing images related to the job if they had already been downloaded.
34+
- (chore) update default sampler
35+
- (feat) update some common settings if selecting different model from default AlbedoXL (update steps, cfg, LoRA) to reduce confusion
36+
- (feat) update CLIP if selecting a PonyXL model and CLIP skip is less than 2.

app/layout.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ export default function RootLayout({
7373
<div
7474
className="flex flex-col flex-1"
7575
style={{
76-
padding: '42px 8px 8px 8px',
76+
padding: '42px 0 0 0',
7777
paddingBottom: 'var(--footer-padding)'
7878
}}
7979
>
8080
<HeaderNav />
81-
<main className="flex flex-col gap-2 w-full flex-1 sm:p-1">
81+
<main className="flex flex-col gap-2 w-full flex-1" style={{ padding: '8px' }}>
8282
<ContentWrapper>
8383
<BetaBanner />
8484
{children}

0 commit comments

Comments
 (0)