o
    i4                     @   s|  d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlZd dl	m
Z
 d dlmZmZmZ d dlmZ d dlZd dlZd dlZd dlZd dlZd dlmZmZ d dlZd dlZd dlZd dlmZm Z  d dl!m"Z" d dl#m$Z$m%Z%m&Z& d d	lm'Z' G d
d dej(j)Z*dLddZ+dMddZ,G dd dej-j.Z/G dd dejj0Z1dd Z2	dNdejj0fddZ3	dOdej0de4de5ej0 fddZ6G dd  d ej7j8Z9dPd"d#Z:d$d% Z;d&d' Z<d(d) Z=d*d+ Z>d,d- Z?d.d/ Z@d0d1 ZAd2d3 ZBd4d5 ZCd6d7 ZDd8d9 ZEd:d; ZFd<d= ZGd>d? ZHG d@dA dAe%jIZJG dBdC dCe%jIZKG dDdE dEe%jIZLG dFdG dGe%jIZMG dHdI dIe$ZNdeNfdJdKZOdS )Q    N)trange)Image	ImageDraw	ImageFont)override)argsPerformanceFeature)adaptersadapter_maps)BypassInjectionManager)ComfyExtensionioui)ProgressBarc                       s:   e Zd ZdZdd fdd
Z					d	ddZ  ZS )
TrainGuiderzB
    CFGGuider with modifications for training specific logic
    F
offloadingc                   s   t  j|i | || _d S N)super__init__r   )selfr   r   kwargs	__class__ 6/mnt/c/Users/fbmor/ComfyUI/comfy_extras/nodes_train.pyr   !   s   
zTrainGuider.__init__Nc
                 C   s   t jj| j|j| j| j| j | jd\| _| _| _	t
j  | jj}
|d ur/t j||j|
}||
}||
}||
}t jj| j|
| j d z| j  | j|||
|||||||	d
}W | j  n| j  w t j| j| j	 | `| `	|S )N)force_full_loadforce_offload)devicedtype)latent_shapes)comfysampler_helpersprepare_samplingmodel_patchershapecondsmodel_optionsr   inner_modelloaded_modelstorchcudaempty_cacheload_deviceprepare_masktosamplerscast_to_load_optionsmodel_dtypepre_runinner_samplecleanupcleanup_models)r   noiselatent_imagesamplersigmasdenoise_maskcallbackdisable_pbarseedr    r   outputr   r   r   outer_sample%   sP   





zTrainGuider.outer_sample)NNFNN)__name__
__module____qualname____doc__r   r@   __classcell__r   r   r   r   r      s    
r   c                    s   i }|   D ]C\}  }t trt ||d}n,t tjr/|d u s* d|kr. | }nt ttfrEt	 |krE fdd|D }|||< q|S )N	full_sizer   c                       g | ]} | qS r   r   .0ivr   r   
<listcomp>k       z0make_batch_extra_option_dict.<locals>.<listcomp>)
items
isinstancedictmake_batch_extra_option_dictr*   Tensorsizelisttuplelen)dindiciesrG   new_dictknewvr   rL   r   rS   a   s   

rS    c              	   C   s   t | drt | dst| D ]\}}t|| d|  q| S t | drit|  D ]>\}}t|tr>t|| d|  q*t|tjrK|	 | |< q*t|tt
frht|D ]\}}t|| d| d|  qVq*| S )N__iter__rP   .)hasattr	enumerateprocess_cond_listrV   rP   rQ   rR   r*   rT   clonerW   )rY   prefixindexitemr\   rM   r   r   r   rc   p   s   

rc   c                	   @   sn   e Zd Zdddddejdddf	ddZdd Z		dd
dZdd Zdd Z	dd Z
dd Z			dddZdS )TrainSamplerN   r   Fc                 C   s|   || _ || _|| _|| _|| _|| _|| _|| _|	| _|
| _	|r%t
j nd | _|
d ur3| |
 d S d | _d | _d | _d S r   )loss_fn	optimizerloss_callback
batch_sizetotal_stepsgrad_accr>   training_dtypereal_datasetbucket_latentsr*   amp
GradScalergrad_scaler_init_bucket_databucket_offsetsbucket_weights
num_images)r   rj   rk   rl   rm   ro   rn   r>   rp   rq   rr   use_grad_scalerr   r   r   r      s"   
zTrainSampler.__init__c                 C   sd   dg| _ g }|D ]}||jd  | j | j d |jd   q| j d | _tj|tjd| _dS )z3Initialize bucket offsets and weights for sampling.r   r   N)rw   appendr%   ry   r*   tensorfloat32rx   )r   rr   bucket_sizeslatr   r   r   rv      s   zTrainSampler._init_bucket_dataTc
                    s   |j j|||d}
|j jt|t||d} fdd|D |jd< t|||d}tj|
jj	| j
d! ||
d|dfi |}| | | }W d    n1 s[w   Y  |	rz|| j }| jd urv| j|  |S |  |S )NFc                    rH   r   r   rI   condr   r   rN      rO   z(TrainSampler.fwd_bwd.<locals>.<listcomp>positiverF   r|   T)r(   model_samplingnoise_scalingr*   
zeros_liker&   rS   autocastr   typerp   requires_grad_rj   floatro   ru   scalebackward)r   
model_wrapbatch_sigmasbatch_noisebatch_latentr   rZ   
extra_argsdataset_sizebwdxtx0batch_extra_argsx0_predlossbwd_lossr   r   r   fwd_bwd   s:   

zTrainSampler.fwd_bwdc                    s&    fddt |D }t||S )z)Generate random sigma values for a batch.c                    s$   g | ]} j jtd  qS )ri   )r(   r   percent_to_sigmar*   randrg   )rJ   _r   r   r   rN      s    z7TrainSampler._generate_batch_sigmas.<locals>.<listcomp>)ranger*   r~   r/   )r   r   rm   r   r   r   r   r   _generate_batch_sigmas   s   
z#TrainSampler._generate_batch_sigmasc                    s   t | jd }| j| }|jd }	| j|  t| j|	}
t 	|	d|
 
 } fdd|D }|| |}|d|i|j}| ||
|j}| j|||||||| jdd	}| jre| |  || d	|d
 dS )z)Execute one training step in bucket mode.ri   r   Nc                    s   g | ]} | qS r   r   )rJ   idxbucket_offsetr   r   rN      rO   z8TrainSampler._train_step_bucket_mode.<locals>.<listcomp>samplesTr   .4f)r   bucket)r*   multinomialrx   rg   rr   r%   rw   minrm   randpermtolistr/   generate_noiser   r   r   ry   rl   set_postfix)r   r   r   r   noisegenr8   pbar
bucket_idxbucket_latentbucket_sizeactual_batch_sizerelative_indicesabsolute_indicesr   r   r   r   r   r   r   _train_step_bucket_mode   s4   


z$TrainSampler._train_step_bucket_modec                    s   t |d| j  }t  fdd|D }	|d|	i|	j}
| |t	| j||	j}| j
|||
|	||||dd	}| jrG| |  |d| di dS )	zGExecute one training step in standard (non-bucket, non-multi-res) mode.Nc                    rH   r   r   rI   r8   r   r   rN     rO   z:TrainSampler._train_step_standard_mode.<locals>.<listcomp>r   Tr   r   r   )r*   r   rm   r   stackr   r/   r   r   r   r   rl   rg   r   )r   r   r   r   r   r8   r   r   rZ   r   r   r   r   r   r   r   _train_step_standard_mode	  s(   z&TrainSampler._train_step_standard_modec                 C   s  t |d| j  }d}	|D ]>}
| j|
 |}|d|i|j}|jj	
t d }t |g|j}| j||||||
g||dd	}|	|7 }	q|	| j t| }	| jdurf| j|	  n|	  | jrt| |	  |d|	 di dS )	zIExecute one training step in multi-resolution mode (real_dataset is set).Nr   r   r   Fr   r   r   )r*   r   rm   r   rq   r/   r   r   r(   r   r   r   rg   r~   r   ro   rX   ru   r   r   rl   r   )r   r   r   r   r   r8   r   r   rZ   
total_lossrf   single_latentr   r   r   r   r   r   _train_step_multires_mode!  sB   

z&TrainSampler._train_step_multires_modec	              
   C   s  t |j|_|jd }	|d}
tj  t| j}t| jddt	j
j d }D ]}tj| j|d  }| jd urE| ||	|||| n| jd u rV| ||	||||
| n| ||	||||
| |d | j dkr| jd urv| j| j | jjD ]}|d D ]}|jd u rq|jj|jj|j_qqz| jd ur| j| j | j  n| j  | j   |d q(tj  t!|S )	Nr   r   zTraining LoRAg{Gz?)desc	smoothingdisablei  ri   params)"rc   r&   rU   r*   r+   r,   r   rn   r   r!   utilsPROGRESS_BAR_ENABLEDcomfy_extrasnodes_custom_samplerNoise_RandomNoiser>   rr   r   rq   r   r   ro   ru   unscale_rk   param_groupsgraddatar/   r   stepupdate	zero_gradr   )r   r   r:   r   r<   r7   r8   r;   r=   r   r   ui_pbarr   rK   r   r   paramr   r   r   sampleE  sJ   












zTrainSampler.sample)T)NNF)rA   rB   rC   r*   bfloat16r   rv   r   r   r   r   r   r   r   r   r   r   rh      s,    
%
*
#+rh   c                       s4   e Zd Z fddZdd Zdd Zdd Z  ZS )	BiasDiffc                    s   t    || _d S r   )r   r   bias)r   r   r   r   r   r   |  s   

zBiasDiff.__init__c                 C   s   |j }|| j| j |S r   )r   r/   r   )r   b	org_dtyper   r   r   __call__  s   zBiasDiff.__call__c                 C   s   | j  | j   S r   )r   nelementelement_sizer   r   r   r   passive_memory_usage  s   zBiasDiff.passive_memory_usagec                 C   s   | j |d |  S )N)r   )r/   r   )r   r   r   r   r   move_to  s   zBiasDiff.move_to)rA   rB   rC   r   r   r   r   rE   r   r   r   r   r   {  s
    r   c                    s   d\}}t d||fd}t|}t|  t|    fdd|  D }d|t|d |  f}t|dd  ddD ]&\}}	t||d  | }
|t|	|  }|j	||
|fgd	d
d |
|f}qA|S )N)i  i,  RGBwhitec                       g | ]
}|    qS r   r   rJ   lmax_lossmin_lossr   r   rN         z#draw_loss_graph.<locals>.<listcomp>r   ri   startblue   fillwidth)
r   newr   Drawr   valuesmaxintrb   line)loss_mapstepsr   heightimgdrawscaled_loss
prev_pointrK   r   xyr   r   r   draw_loss_graph  s   

r   modelc                 C   s   |d u rg }n(t | dr/t| tjjtjjtjjfs/||  t	d| d| j
j d |S |p2d}|  D ]\}}t||| d|  q7|S )NforwardzFound module with forward:  ()rootr`   )ra   rQ   r*   nn
ModuleList
Sequential
ModuleDictr}   loggingdebugr   rA   named_children*find_all_highest_child_module_with_forward)r   resultname	next_namechildr   r   r   r	    s   
r	  ri   depthreturnc           	   	   C   s   |du rg }|p	d}t | tjtjtjf}t| do| }|r?|d7 }||kr?||  td| d| d| j	j
 d |S |  D ]\}}t||||| d	|  qC|S )
a  
    Find modules at a specific depth level for gradient checkpointing.

    Args:
        model: The model to search
        depth: Target depth level (1 = top-level blocks, 2 = their children, etc.)
        result: Accumulator for results
        current_depth: Current recursion depth
        name: Current module name for logging

    Returns:
        List of modules at the target depth
    Nr  r   ri   zFound module at depth z: r   r   r`   )rQ   r  r  r  r  ra   r}   r  r  r   rA   r  find_modules_at_depth)	r   r  r
  current_depthr  is_containerhas_forwardr  r  r   r   r   r    s   
"r  c                   @   s8   e Zd ZdZedejfddZedejfddZdS )	OffloadCheckpointFunctiona  
    Gradient checkpointing that works with weight offloading.

    Forward: no_grad -> compute -> weights can be freed
    Backward: enable_grad -> recompute -> backward -> weights can be freed

    For single input, single output modules (Linear, Conv*).
    r   c                 C   sD   |  | || _t  ||W  d    S 1 sw   Y  d S r   )save_for_backward
forward_fnr*   no_grad)ctxr   r  r   r   r   r     s
   

$z!OffloadCheckpointFunction.forwardgrad_outc                 C   sp   | j \}| j}d | _t  | d}||}|| |j}W d    n1 s,w   Y  ~~~|d fS )NT)saved_tensorsr  r*   enable_graddetachr   r   r   )r  r  r   r  
x_detachedr   grad_xr   r   r   r     s   

z"OffloadCheckpointFunction.backwardN)	rA   rB   rC   rD   staticmethodr*   rT   r   r   r   r   r   r   r    s    	r  Fc                    sh   t | dsd S | j|r t| tjtjtjtjfr fdd}nfdd  fdd}| _|| _d S )Nr   c                    s   t |  S r   )r  apply)r   org_forwardr   r   checkpointing_fwd  s   z patch.<locals>.checkpointing_fwdc                    s    | i |S r   r   r   r   r!  r   r   fwd  s   zpatch.<locals>.fwdc                     s   t jjj | |ddS )NF)use_reentrant)r*   r   
checkpointr$  )r%  r   r   r#    s   )	ra   r   rQ   r  LinearConv1dConv2dConv3dr"  )mr   r#  r   )r%  r"  r   patch  s   

r-  c                 C   s   t | dr| j| _| `d S d S )Nr"  )ra   r"  r   )r,  r   r   r   unpatch  s   
r.  c                 C   s    g }| D ]	}| |d  q|S )zProcess latents for bucket mode training.

    Args:
        latents: list[{"samples": tensor}] where each tensor is (Bi, C, Hi, Wi)

    Returns:
        list of latent tensors
    r   r}   )latentsrr   latent_dictr   r   r   _process_latents_bucket_mode  s   	r2  c                 C   sh   t | dkr| d d S g }| D ]!}|d }|jd }|dkr,|D ]	}||d  q!q|| q|S )zProcess latents for standard (non-bucket) mode training.

    Args:
        latents: list of latent dicts or single latent dict

    Returns:
        Processed latents (tensor or list of tensors)
    ri   r   r   N)rX   r%   r}   )r0  latent_listlatentbs
sub_latentr   r   r   _process_latents_standard_mode$  s   	
r7  c                 C   sF   t | dkr
| d S g }| D ]}t|tr|| q|| q|S )zProcess conditioning - either single list or list of lists.

    Args:
        positive: list of conditioning

    Returns:
        Flattened conditioning list
    ri   r   )rX   rQ   rV   extendr}   )r   flat_positiver   r   r   r   _process_conditioning<  s   	
r:  c           
         sB  |r@ fdd| D } t | }tdd | D }d}td| d| d t| D ]\}}td	| d
|j  q*| ||fS t| tr{t } fdd| D } | D ]}	|	|	j qStd|  t |dkrmd}n	d}t
j| dd} t | }n!t| t
jr|  } | jd }d}ntdt|   d}d}| ||fS )a  Convert latents to dtype and compute image counts.

    Args:
        latents: Latents (tensor, list of tensors, or bucket list)
        dtype: Target dtype
        bucket_mode: Whether bucket mode is enabled

    Returns:
        tuple: (processed_latents, num_images, multi_res)
    c                       g | ]}|  qS r   r/   rJ   tr|   r   r   rN   _      z._prepare_latents_and_count.<locals>.<listcomp>c                 s   s    | ]}|j d  V  qdS )r   N)r%   r=  r   r   r   	<genexpr>a  s    z-_prepare_latents_and_count.<locals>.<genexpr>FzBucket mode: z
 buckets, z total samplesz	  Bucket z: shape c                    r;  r   r<  r=  r|   r   r   rN   l  r?  zLatent shapes: ri   Tr   )dimzInvalid latents type: )rX   sumr  r  rb   r%   rQ   rV   setaddr*   catrT   r/   errorr   )
r0  r   bucket_modenum_bucketsry   	multi_resrK   r   
all_shapesr4  r   r|   r   _prepare_latents_and_countR  s8   





rK  c                 C   sh   |r| S t d| dt|   t| dkr|dkr| | S t| |kr2tdt|  d| d| S )a[  Validate conditioning count matches image count, expand if needed.

    Args:
        positive: Conditioning list
        num_images: Number of images
        bucket_mode: Whether bucket mode is enabled

    Returns:
        Validated/expanded conditioning list

    Raises:
        ValueError: If conditioning count doesn't match image count
    zTotal Images: z, Total Captions: ri   zNumber of positive conditions (z#) does not match number of images (z).)r  r  rX   
ValueError)r   ry   rG  r   r   r   !_validate_and_expand_conditioning  s   rM  c                 C   sT   | dkri dfS t d| }t| dd dd }i }|r&tj|}||fS )zLoad existing LoRA weights if provided.

    Args:
        existing_lora: LoRA filename or "[None]"

    Returns:
        tuple: (existing_weights dict, existing_steps int)
    [None]r   loras_steps_r   r{   )folder_pathsget_full_path_or_raiser   splitr!   r   load_torch_file)existing_lora	lora_pathexisting_stepsexisting_weightsr   r   r   _load_existing_lora  s   	rY  c                 C   sD  | d}| j j}i }td| d|  t|dkrt|| dd}	|| dd}
d}tD ]}||||	|
}|durD nq4|du rMt	| }|durY|
 |}n|j| j |dd	|}| D ]\}}||| d
| < qi| d|fS tjtj| j j|dd}t| d}||| d< ||fS )a  Create a weight adapter for a module with weight.

    Args:
        module: The module to create adapter for
        module_name: Name of the module
        existing_weights: Dict of existing LoRA weights
        algorithm: Algorithm name for new adapters
        lora_dtype: dtype for LoRA weights
        rank: Rank for new LoRA adapters

    Returns:
        tuple: (train_adapter, lora_params dict)
    .weightzCreating weight adapter for z with shape r   z.alpha      ?z.dora_scaleN)rankalphar`   Tr   requires_gradz.diff)weightr%   r  r  rX   r   getr	   loadr
   to_trainr/   create_trainnamed_parameterstrainr   r*   r  	Parameterzerosr   )modulemodule_namerX  	algorithm
lora_dtyper\  keyr%   lora_paramsr]  
dora_scaleexisting_adapteradapter_clstrain_adapterr  	parameterdiffdiff_moduler   r   r   _create_weight_adapter  sB   
rv  c                 C   sD   t jt j| jj|dd}t| d}| d|i}||fS )zCreate a bias adapter for a module with bias.

    Args:
        module: The module with bias
        module_name: Name of the module
        lora_dtype: dtype for LoRA weights

    Returns:
        tuple: (bias_module, lora_params dict)
    Tr^  z.diff_b)	r*   r  rg  rh  r   r%   r   rf  r   )ri  rj  rl  r   bias_modulern  r   r   r   _create_bias_adapter  s   rx  c                 C   s   i }g }| j  D ]U\}}t|dr^|jdur7t||||||\}	}
||
 | d}| ||	 ||	 t|dr^|jdur^t	|||\}}|| | d}| || || q	||fS )aT  Setup all LoRA adapters on the model.

    Args:
        mp: Model patcher
        existing_weights: Dict of existing LoRA weights
        algorithm: Algorithm name for new adapters
        lora_dtype: dtype for LoRA weights
        rank: Rank for new LoRA adapters

    Returns:
        tuple: (lora_sd dict, all_weight_adapters list)
    weight_functionNrZ  r   .bias)
r   named_modulesra   r`  rv  r   add_weight_wrapperr}   r   rx  )mprX  rk  rl  r\  lora_sdall_weight_adaptersnr,  adapterr   rm  bias_adapterbias_paramsr   r   r   _setup_lora_adapters  s(   







r  c                 C   s  i }g }t  }| j D ]{\}}	t|	dr|	jdurXt|	|||||\}
}|| ||
 | d}t|
t	rH| 
||
 td|  n|j||
dd td|  t|	dr|	jdurt|	||\}}|| | d	}| 
|| || td
|  q|||fS )a  Setup LoRA adapters in bypass mode.

    In bypass mode:
        - Weight adapters (lora/lokr/oft) use bypass injection (forward hook)
        - Bias/norm adapters (BiasDiff) still use weight wrapper (direct modification)

    This is useful when the base model weights are quantized and cannot be
    directly modified.

    Args:
        mp: Model patcher
        existing_weights: Dict of existing LoRA weights
        algorithm: Algorithm name for new adapters
        lora_dtype: dtype for LoRA weights
        rank: Rank for new LoRA adapters

    Returns:
        tuple: (lora_sd dict, all_weight_adapters list, bypass_manager)
    ry  NrZ  z:[BypassMode] Added 1D weight adapter (weight wrapper) for r[  )strengthz/[BypassMode] Added weight adapter (bypass) for r   rz  z5[BypassMode] Added bias adapter (weight wrapper) for )r   r   r{  ra   r`  rv  r   r}   rQ   r   r|  r  r  add_adapterr   rx  )r}  rX  rk  rl  r\  r~  r  bypass_managerr  r,  r  r   rm  r  r  r   r   r   _setup_lora_adapters_bypass&  s4   









r  c                 C   sd   | dkrt jj||dS | dkrt jj||dS | dkr$t jj||dS | dkr0t jj||dS dS )zCreate optimizer based on name.

    Args:
        optimizer_name: Name of optimizer ("Adam", "AdamW", "SGD", "RMSprop")
        parameters: Parameters to optimize
        learning_rate: Learning rate

    Returns:
        Optimizer instance
    Adam)lrAdamWSGDRMSpropN)r*   optimr  r  r  r  )optimizer_name
parameterslearning_rater   r   r   _create_optimizer]  s   r  c                 C   sL   | dkr	t j S | dkrt j S | dkrt j S | dkr$t j S dS )zCreate loss function based on name.

    Args:
        loss_function_name: Name of loss function ("MSE", "L1", "Huber", "SmoothL1")

    Returns:
        Loss function instance
    MSEL1HuberSmoothL1N)r*   r  MSELossL1Loss	HuberLossSmoothL1Loss)loss_function_namer   r   r   _create_loss_functionr  s   	



r  c           
      C   s   t t|}tj|}|r/|d dd |ddd}	| j|d|	i|	|||j	d dS |rM|d |ddd}| j|d|i||||j	d dS | j|d|i||||j	d dS )aG  Execute the training loop.

    Args:
        guider: The guider object
        train_sampler: The training sampler
        latents: Latent tensors
        num_images: Number of images
        seed: Random seed
        bucket_mode: Whether bucket mode is enabled
        multi_res: Whether multi-resolution mode is enabled
    r   Nri   r   )r>   )
r*   r~   r   r   r   r   repeatr   r   r>   )
guidertrain_samplerr0  ry   r>   rG  rI  r:   r7   dummy_latentr   r   r   _run_training_loop  s6   


r  c                   @   s$   e Zd Zedd Zedd ZdS )TrainLoraNodec                 C   s  t jdddddt jjdddt jjdd	dt jjd
ddt jjddddddt jjddddddt jjddddddt jjdddddddt jjdddd d!dt jjd"g d#d$d%d&t jjd'g d(d)d*d&t jjd+d,d,d-d.dt jjd/g d0d1d2d&t jjd3d1d4gd1d5d&t j	jd6d7d8d9t jjd:t
t t
t d, d;d&t j	jd<dd=d9t jjd>ddd?d@dt j	jdAd7dBd9t jjdCtdDdEg dEdFd&t j	jdGd7dHd9t j	jdId7dJd9gt dKjdLdMdNt dOjdPdQdNt jjddRdNgdSS )TNr  z
Train LoRAtrainingTr   zThe model to train the LoRA on.tooltipr0  zEThe Latents to use for training, serve as dataset/input of the model.r   z.The positive conditioning to use for training.rm   ri   i'  z#The batch size to use for training.defaultr   r   r  grad_accumulation_stepsi   z>The number of gradient accumulation steps to use for training.r      i z*The number of steps to train the LoRA for.r  gMb@?gHz>r[  z&The learning rate to use for training.)r  r   r   r   r  r\        zThe rank of the LoRA layers.rk   )r  r  r  r  r  z"The optimizer to use for training.)optionsr  r  loss_function)r  r  r  r  r  z&The loss function to use for training.r>   r   l    zbThe seed to use for training (used in generator for LoRA weight initialization and noise sampling)rp   )bf16fp32noner  zThe dtype to use for training. 'none' preserves the model's native compute dtype instead of overriding it. For fp16 models, GradScaler is automatically enabled.rl  r  zThe dtype to use for lora.quantized_backwardFztWhen using training_dtype 'none' and training on quantized model, doing backward with quantized matmul when enabled.r  r  rk  z"The algorithm to use for training.gradient_checkpointingz(Use gradient checkpointing for training.checkpoint_depth   z'Depth level for gradient checkpointing.r   z@Offload model weights to CPU during training to save GPU memory.rU  rO  rN  z9The existing LoRA to append to. Set to None for new LoRA.rG  zeEnable resolution bucket mode. When enabled, expects pre-bucketed latents from ResolutionBucket node.bypass_modezEnable bypass mode for training. When enabled, adapters are applied via forward hooks instead of weight modification. Useful for quantized models where weights cannot be directly modified.
LORA_MODELlorazLoRA weightsdisplay_namer  LOSS_MAPr   zLoss historyzTotal training steps)node_idr  categoryis_experimentalis_input_listinputsoutputs)r   SchemaModelInputLatentConditioningIntFloatComboBooleanrV   r
   keysrQ  get_filename_listCustomOutputclsr   r   r   define_schema  s  

z
zTrainLoraNode.define_schemac           .         sJ  |d }|d }|d }|d }|d }|d }|	d }|
d }|d }|d }|d }|d }|d }|d }|d }|d }|d }|d }|d }|t j_|rWt|}nt|}t|}| }d}t|}|dkryt|}|	| n#|j
 }|tjkrtj}|tjkrd}tjtjv rtd ntj}|dvr|ntj}t|||\}}}t|||}td` |j
d t|\}} d }!|rtd t|||||\}"}#}!n
t|||||\}"}#t||" |}	t|}$|rt |j
j!|d}%t"d	t#|% d
|  |%D ]	}&t$|&|d qtj%&  t jj'|gd| d tj%&  dg i  fdd}'|rIt(|$|	|'|||| ||||d
}(nt(|$|	|'|||| |||rY|nd |d
}(t)||d})|)*| d }*|!d ur|!+|j
}*|*D ]}+|+,| qxtd|!-  d z6dt j_.t/|)|(||||| W dt j_.|*d ur|*D ]}+|+0| qtd |j
1 D ]}&t2|& qn'dt j_.|*d ur|*D ]}+|+0| qtd |j
1 D ]}&t2|& qw ~(~	|"D ]},|"|, 3|4 |"|,< q|#D ]	}-|-d ~-q~#t56|" ||  W  d    S 1 sw   Y  d S )Nr   Fr  Ta  WARNING: FP16 model detected with fp16_accumulation enabled. This combination can be numerically unstable during training and may cause NaN values. Suggested fixes: 1) Set training_dtype to 'bf16', or 2) Disable fp16_accumulation (remove from --fast flags).r   zUsing bypass mode for training)r  z!Gradient checkpointing: patching z modules at depth r   g@xD)memory_requiredr   r   c                    s    d  |  d S )Nr   r/  )r   r   r   r   rl     s   z,TrainLoraNode.execute.<locals>.loss_callback)rl   rm   ro   rn   r>   rp   rr   rz   )rl   rm   ro   rn   r>   rp   rq   rz   z[BypassMode] Injected z bypass hooksz![BypassMode] Ejected bypass hooks)7r!   model_managementtraining_fp8_bwdr2  r7  r:  rd   node_helpersstring_to_torch_dtypeset_model_compute_dtyper   	get_dtyper*   float16r   r   Fp16Accumulationr   fastr  warningrK  rM  inference_moder   rY  r  r  r  r  r   r  r  diffusion_modelinforX   r-  r+   r,   load_models_gpurh   r   	set_condscreate_injectionsinjectget_hook_countin_trainingr  ejectmodulesr.  r/   r  r   
NodeOutput).r  r   r0  r   rm   r   r  r  r\  rk   r  r>   rp   rl  r  rk  r  r  r   rU  rG  r  r  r  r}  rz   r   r2   latents_dtypery   rI  rX  rW  r  r~  r  	criterionmodules_to_patchr,  rl   r  r  bypass_injections	injectionr   r  r   r  r   executeA  s"  





















&zTrainLoraNode.executeNrA   rB   rC   classmethodr  r  r   r   r   r   r    s    
 r  c                   @   s&   e Zd Zedd ZedddZdS )LoraModelLoaderc                 C   sh   t jddddt jjdddt djd	d
dt jjddddddt jjddddgt jjdddgdS )Nr  zLoad LoRA ModelloadersTr   z0The diffusion model the LoRA will be applied to.r  r  r  z/The LoRA model to apply to the diffusion model.strength_modelr[  g      Yg      Y@zGHow strongly to modify the diffusion model. This value can be negative.r  bypassFzWhen enabled, applies LoRA in bypass mode without modifying base model weights. Useful for training and when model weights are offloaded.r  zThe modified diffusion model.r  )r  r  r  r  r  r  )r   r  r  r  r  r  r  r  r  r   r   r   r    s:   
zLoraModelLoader.define_schemaFc                 C   sR   |dkr	t |S |rtj|d ||d\}}ntj|d ||d\}}t |S )Nr   )r   r  r!   sdload_bypass_lora_for_modelsload_lora_for_models)r  r   r  r  r  
model_lorar   r   r   r   r  =  s   




zLoraModelLoader.executeNFr  r   r   r   r   r    s
    
!r  c                   @   &   e Zd Zedd ZedddZdS )SaveLoRAc                 C   sN   t jddgddddt djddd	t jjd
dddt jjddddgg dS )Nr  zexport lorazSave LoRA Weightsr  Tr  r  z>The LoRA model to save. Do not use the model with LoRA layers.r  re   zloras/ComfyUI_trained_loraz*The prefix to use for the saved LoRA file.r  r   zYOptional: The number of steps the LoRA has been trained for, used to name the saved file.)optionalr  )r  search_aliasesr  r  r  is_output_noder  r  )r   r  r  r  Stringr  r  r   r   r   r  N  s0   
zSaveLoRA.define_schemaNc                 C   sv   t  }t ||\}}}}}	|d u r| d|dd}
n| d| d|dd}
tj||
}
tj||
 t	
 S )Nr   05z_.safetensorsrP  )rQ  get_output_directoryget_save_image_pathospathjoinsafetensorsr*   	save_filer   r  )r  r  re   r   
output_dirfull_output_folderfilenamecounter	subfolderfilename_prefixoutput_checkpointr   r   r   r  j  s   
zSaveLoRA.executer   r  r   r   r   r   r  M  s
    
r  c                   @   r  )LossGraphNodec                 C   sN   t jdg dddddt djddd	t jjd
dddgg t jjt jjgd	S )Nr  )ztraining chartztraining visualizationz	plot losszPlot Loss Graphr  Tr  r   zLoss map from training node.r  r  
loss_graphz&Prefix for the saved loss graph image.r  )	r  r  r  r  r  r  r  r  hidden)r   r  r  r  r   Hiddenpromptextra_pnginfor  r   r   r   r  z  s&   

zLossGraphNode.define_schemaNc                    s  |d }d\}}d}t d|| || fd}	t|	}
t|t|  fdd|D }t|}||t|d |  f}t|d	d  d	d
D ]&\}}|t|| |  }|t||  }|
j	|||fgddd ||f}qI|
j	|df||fgddd |
j	||f|| |fgddd d }zt
dd}W n ty   t
 }Y nw |
jd|d fd|dd |
j|d |d fd|dd |
j|d df d|dd |
j|d |d fd|dd t|	tjd }t|d }tjtj|| ddS )Nr   )i   i  (   r   r   c                    r   r   r   r   r   r   r   rN     r   z)LossGraphNode.execute.<locals>.<listcomp>r   ri   r   r   r   r   blackz	arial.ttf   r  Loss)fontr   
   Steps   z.2fg     o@r   r  )r   )r   r   r   r   r   r   rX   r   rb   r   r   truetypeIOErrorload_defaulttextnparrayastyper   r*   
from_numpyr   r  r   PreviewImage)r  r   r  r  r  loss_valuesr   r   marginr   r   r   r   r   rK   r   r   r   r  	img_array
img_tensorr   r   r   r    sF   

zLossGraphNode.executeNNr  r   r   r   r   r  y  s
    
r  c                   @   s(   e Zd Zedeeej  fddZdS )TrainingExtensionr  c                    s   t tttgS r   )r  r  r  r  r   r   r   r   get_node_list  s   zTrainingExtension.get_node_listN)	rA   rB   rC   r   rV   r   r   	ComfyNoder-  r   r   r   r   r,    s    r,  c                      s   t  S r   )r,  r   r   r   r   comfy_entrypoint  s   r/  r   )r^   r+  )ri   Nr   Nr  )Pr  r  numpyr"  r  r*   torch.nnr  torch.utils.checkpoint	tqdm.autor   PILr   r   r   typing_extensionsr   comfy.samplersr!   comfy.sampler_helperscomfy.sdcomfy.utilscomfy.model_managementcomfy.cli_argsr   r   !comfy_extras.nodes_custom_samplerr   rQ  r  comfy.weight_adapterr	   r
   comfy.weight_adapter.bypassr   comfy_api.latestr   r   r   r   r   Guider_Basicr   rS   rc   r0   Samplerrh   Moduler   r   r	  r   rV   r  autogradFunctionr  r-  r.  r2  r7  r:  rK  rM  rY  rv  rx  r  r  r  r  r  r.  r  r  r  r  r,  r/  r   r   r   r   <module>   s    
E
 {

&
%0<%7/  h3,O