import React, { useState } from 'react'; import { Plus, X } from 'lucide-react'; import LoraBrowserModal, { LoraPick } from '@/components/generate/LoraBrowserModal'; import { Checkbox, CreatableSelectInput, FormGroup, SelectInput, SliderInput, TextAreaInput, TextInput, } from '@/components/formInputs'; import { CaptionJobConfig, CaptionLora } from '@/types'; import { handleCaptionerTypeChange } from '@/helpers/captionJobConfig'; import { batchSizeOptions, captionFormatOptions, captionerTypes, defaultQtype, groupedCaptionerTypes, maxNewTokensOptions, maxResOptions, quantizationOptions, } from '@/helpers/captionOptions'; type Props = { jobConfig: CaptionJobConfig; setJobConfig: (value: any, key?: string) => void; gpuIDs: string | null; setGpuIDs: (value: string | null) => void; gpuList: any; showGPUSelect: boolean; }; const CaptionSimpleJob: React.FC = ({ jobConfig, setJobConfig, gpuIDs, setGpuIDs, gpuList, showGPUSelect }) => { const selectedCaptionOption = captionerTypes.find(option => option.name === jobConfig.config.process[0].type); const additionalSections = selectedCaptionOption?.additionalSections || []; const captionPrompts = selectedCaptionOption?.captionPrompts || {}; const promptPresetNames = Object.keys(captionPrompts); const minNewTokens = selectedCaptionOption?.minNewTokens ?? 0; const newTokensOptions = maxNewTokensOptions.filter(option => parseInt(option.value) >= minNewTokens); const [loraModalOpen, setLoraModalOpen] = useState(false); const loras: CaptionLora[] = jobConfig.config.process[0].caption.loras || []; const setLoras = (next: CaptionLora[]) => setJobConfig(next, 'config.process[0].caption.loras'); const addLora = (pick: LoraPick) => { if (loras.some(l => l.path === pick.path)) return; setLoras([...loras, { path: pick.path, name: pick.name, strength: 1.0 }]); }; return (
{ handleCaptionerTypeChange(jobConfig.config.process[0].type, value, jobConfig, setJobConfig); }} options={groupedCaptionerTypes} />
{showGPUSelect && (
setGpuIDs(value)} options={gpuList.map((gpu: any) => ({ value: `${gpu.index}`, label: `GPU #${gpu.index}` }))} />
)}
{ if (value?.trim() === '') { value = null; } setJobConfig(value, 'config.process[0].caption.model_name_or_path'); }} placeholder="" options={selectedCaptionOption?.name_or_path_options || []} required />
{additionalSections.includes('caption.model_name_or_path2') && (
{ if (value?.trim() === '') { value = null; } setJobConfig(value, 'config.process[0].caption.model_name_or_path2'); }} placeholder="" options={selectedCaptionOption?.name_or_path2_options || []} />
)} {additionalSections.includes('caption.caption_format') && (
setJobConfig(value, 'config.process[0].caption.caption_format')} options={captionFormatOptions} />
)} {additionalSections.includes('caption.fixed_caption') && (
{ if (value?.trim() === '') { //@ts-ignore value = undefined; } setJobConfig(value, 'config.process[0].caption.fixed_caption'); }} placeholder="Enter fixed caption (if you want the same caption for all audio files)" />
)} {selectedCaptionOption?.supportsLoras && (
LoRAs
{loras.map((l, i) => (
{l.name} { const v = parseFloat(e.target.value); const next = [...loras]; next[i] = { ...l, strength: isNaN(v) ? 0 : v }; setLoras(next); }} className="w-16 text-xs px-1.5 py-0.5 bg-gray-950 border border-gray-700 rounded text-gray-100 text-right" />
))}
Applied as sidechains at caption time; the model weights are never merged.
setLoraModalOpen(false)} onPick={addLora} cloudLoras={selectedCaptionOption?.cloudLoras} />
)}
{ if (value === '') { setJobConfig(false, 'config.process[0].caption.quantize'); value = defaultQtype; } else { setJobConfig(true, 'config.process[0].caption.quantize'); } setJobConfig(value, 'config.process[0].caption.qtype'); }} options={quantizationOptions} />
{ setJobConfig(value, 'config.process[0].caption.caption_extension'); }} options={[ { value: 'txt', label: 'txt' }, { value: 'json', label: 'json' }, { value: 'caption', label: 'caption' }, ]} />
{additionalSections.includes('caption.max_res') && (
{ const intVal = parseInt(value); if (!isNaN(intVal)) { setJobConfig(intVal, 'config.process[0].caption.max_res'); } }} options={maxResOptions} />
)} {additionalSections.includes('caption.max_new_tokens') && (
{ const intVal = parseInt(value); if (!isNaN(intVal)) { setJobConfig(intVal, 'config.process[0].caption.max_new_tokens'); } }} options={newTokensOptions} />
)} {additionalSections.includes('caption.batch_size') && (
{ const intVal = parseInt(value); if (!isNaN(intVal)) { setJobConfig(intVal, 'config.process[0].caption.batch_size'); } }} options={batchSizeOptions} />
)}
setJobConfig(value, 'config.process[0].caption.low_vram')} /> setJobConfig(value, 'config.process[0].caption.recaption')} /> setJobConfig(value, 'config.process[0].caption.compile')} /> {additionalSections.includes('caption.thinking') && ( setJobConfig(value, 'config.process[0].caption.thinking')} /> )} {additionalSections.includes('caption.layer_offloading') && ( <> setJobConfig(value, 'config.process[0].caption.layer_offloading')} /> {jobConfig.config.process[0].caption.layer_offloading && (
setJobConfig(value * 0.01, 'config.process[0].caption.layer_offloading_percent') } min={0} max={100} step={1} />
)} )}
{additionalSections.includes('caption.caption_prompt') && (
{promptPresetNames.length > 1 && (
captionPrompts[name] === jobConfig.config.process[0].caption.caption_prompt, ) || '' } onChange={value => { if (captionPrompts[value] !== undefined) { setJobConfig(captionPrompts[value], 'config.process[0].caption.caption_prompt'); } }} options={[ { value: '', label: '- Custom -' }, ...promptPresetNames.map(name => ({ value: name, label: name })), ]} />
)} { setJobConfig(value, 'config.process[0].caption.caption_prompt'); }} placeholder="Enter caption prompt" />
)}
); }; export default CaptionSimpleJob;