š If you like what I do and want to support the development, feel free to buy me a coffee:
Hey!, you're probably wondering: "Are you still alive...?" No. Well... actually, I'm back with something juicy. The truth is, I'm in the red and I simply refuse to buy another external hard drive just to fill it with thousands of LoRAs.
So, instead of more gigabytes, I brought you Intelligence. There are always new LoRAs, LyCORIS, and VAEs, but nobody looks at the "brain" of the checkpoint. We need to make it smarter, not just limit ourselves to the classics.
This is not just a set of scripts; it is an Intelligent Rendering Engine designed for Stable Diffusion. While standard samplers traverse noise linearly, AGGA uses latent masks, dynamic momentum, and temporal anchors to ensure every pixel has a purpose.
Compatibility Note: These samplers and schedulers are fully cross-compatible with the original A1111/Forge samplers. You can mix standard samplers with AGGA schedulers and vice versa.
ā ļø Installation (The "Core Touch" Method)
I had some issues wrapping this as a standard extension (too much overhead), so I went for the most efficient, "Power User" route: Direct Core Integration. It's faster, lighter, and works natively in A1111 and WebUI Forge.
Step 1: Drop the
.pyfiles into yourmodulesfolder.Step 2: Open
modules/sd_schedulers.pyand add this line at the very end:import modules.sd_agga_schedulersStep 3: Open
modules/sd_samplers_kdiffusion.pyand add this line at the very end:import modules.sd_samplers_pseudo_hires_loader
That's it. No complex installation, just pure python logic injection.
100% Laziness MODE!
# @title āļø AGGA Engine: Auto-Injector (Run after uploading files)
# INSTRUCTIONS:
# 1. Upload 'sd_agga_schedulers.py', 'sd_samplers_pseudo_hires.py', and 'sd_samplers_pseudo_hires_loader.py' to the /content folder.
# 2. Run this cell. It will auto-detect your WebUI (A1111/Forge) and install the engine.
import shutil
import os
from pathlib import Path
# 1. Configuración de Rutas / Path Configuration
HOME = Path('/content')
# Lista de posibles rutas de instalación / List of possible install paths
base_paths = [
Path('/content/stable-diffusion-webui'),
Path('/content/webui_forge_cu121_torch231/stable-diffusion-webui'), # Common Forge path
Path('/content/A1111'),
Path('/content/gdrive/MyDrive/sd/stable-diffusion-webui')
]
# Detectar ruta activa / Detect active path
WEBUI_PATH = next((p for p in base_paths if p.exists()), Path('/content/stable-diffusion-webui'))
TARGET_MOD = WEBUI_PATH / "modules"
# 2. Archivos a procesar / Files to process
agga_files = [
'sd_agga_schedulers.py',
'sd_samplers_pseudo_hires.py',
'sd_samplers_pseudo_hires_loader.py'
]
def handle_agga_files():
missing = []
if not TARGET_MOD.exists():
print(f"ā Error: 'modules' folder not found in {WEBUI_PATH}")
return agga_files
print(f"š Moving AGGA Engine to: {TARGET_MOD}")
for f_name in agga_files:
src = HOME / f_name
dst = TARGET_MOD / f_name
if src.exists():
# Si ya existe en el destino, lo borramos para evitar errores / Overwrite safety
if dst.exists():
os.remove(dst)
shutil.move(str(src), str(dst))
print(f" ā
{f_name} installed successfully.")
elif not dst.exists():
missing.append(f_name)
else:
print(f" ā¹ļø {f_name} was already installed.")
return missing
def patch_core_module(file_path, import_line):
if not file_path.exists():
print(f" ā File not found: {file_path.name}")
return
content = file_path.read_text()
if import_line not in content:
with open(file_path, "a") as f:
f.write(f"\n{import_line}\n")
print(f" š Code injection applied to: {file_path.name}")
else:
print(f" ⨠{file_path.name} is already patched.")
# --- EXECUTION ---
print(f"š WebUI detected at: {WEBUI_PATH}")
missing_files = handle_agga_files()
if not missing_files:
print("\nš ļø Patching Core System...")
patch_core_module(TARGET_MOD / "sd_schedulers.py", "import modules.sd_agga_schedulers")
patch_core_module(TARGET_MOD / "sd_samplers_kdiffusion.py", "import modules.sd_samplers_pseudo_hires_loader")
print(f"\nā
AGGA Engine Deployed successfully. Ready to generate.")
else:
print(f"\nā ļø Warning: Source files not found in /content: {', '.join(missing_files)}")
print("Please upload the .py files to the Colab root folder first.")š§ The "Landing Phase" Concept
The main difference between AGGA and standard tools lies in the Landing Phase and Noise Injection. Standard samplers often "wash out" details in the final 20% of steps to eliminate noise. AGGA does the opposite: it detects where the edges and textures are and reinforces detail just before finishing.
š ļø Sampler Guide (Movement Engines)
1. The "Pseudo-Hires" Series (General Purpose & Speed)
Pseudo-Hires Soft: The base engine. It uses a pure Euler trajectory with a custom noise ramp for a balanced composition without artifacts.
Pseudo-Hires Sharp: Introduces an energy reinforcement of 1.12x in the last third of generation (step > 65%), ideal for defining metallic edges and hard textures.
Pseudo-Hires Ultra: Our most aggressive configuration. It applies a progressive boost that scales up to
1.18 + progress * 0.10to maximize detail in hair and skin.DPM++ 2M Pseudo-Hires: A second-order version for those who prefer DPM stability, but with an extra "push" at the end to avoid that typical soft/washed-out look of standard DPM.
š NEW: AGGA Pseudo-HiRes Flash v9 (Universal Fusion)
This major update transforms the Flash sampler into a "Universal 4-in-1 Engine". It is no longer just a fast sampler; it acts as a Smart Engine that adapts its mathematics based on your step count and prompt commands:
Auto-Rescue Mode (<15 Steps): The engine analyzes the image "energy" at step 0.
If the image is too flat/grey, it activates Euler Ancestral to inject life.
If the image is too chaotic, it activates DPM++ 2M to enforce structure.
If the image is balanced, it uses Native for maximum speed.
High-Res Mode (>15 Steps): Automatically activates the classic Flash V2 engine to achieve that sharp, high-contrast texture (latex, armor, mesh details).
Fusion Mode (New): You can now mix two samplers in a single generation using prompt tags (see Prompts section below).
Exact-Match Math: The internal engines for DPM++ 2M and Euler A have been rewritten to be 1:1 mathematical replicas of the official K-Diffusion library. You get official quality with AGGA's smart logic.
2. The "Native" Series (Detail & Structure)
AGGA Detail-Native: An internal "Refiner" mode. In the last 40% of generation, it "tricks" the model with a lower sigma to force it to draw micro-details like pores and fine hairs without changing the composition.
AGGA Structural-Detail (Hybrid) [NEW]: The best of both worlds. The first 45% uses DPM++ 2M logic for perfect anatomical coherence, then seamlessly switches to the AGGA Detail engine to inject texture. Perfect for complex bodies.
AGGA Hyper-Detail Hybrid: Splits generation into two distinct phases: the first 66% is dedicated to clean structure (V2) and the rest to aggressive edge refinement (V4).
3. The "Specialist" Series (Specific Use Cases)
AGGA DMD-Turbo Landing: Specifically for distilled models (Lightning/Turbo/4-8 steps). It replaces the last Euler step with a direct interpolation toward the clean image for a "crystalline" finish.
AGGA Herrscher-Native: Momentum-based engine (0.50). Includes a recovery system that "pushes" tensor energy if it drops below 1.0, keeping colors and contrast vibrant.
AGGA Style-Repair: Designed to rescue the essence of Checkpoints. It extracts a "Style-Delta" by perturbing noise every 3 steps to recover concepts diluted by the model.
AGGA Style-Repair Ultra: The definitive version for a "Studio Look". It includes an atmospheric mask that protects the background while repairing the 3D volume of the main subject.
AGGA Pixel-Master: A latent quantization engine. In the last step, it applies a spatial resize (Downscale
area-> Upscalenearest) to force the model to work on a fixed pixel grid and limited color palette. Use this for authentic Latent Pixel Art (SNES/NES style).
š AGGA Universal Bridge (The Model Translator)
I decided to make Illustrious, NoobAI, SDXL, and PDXL more compatible with each other. This sampler is a mathematical bridge designed so that models that speak different "languages" (different energy and brightness structures) can understand each other without errors.
Unlike other samplers that inject LoRA force linearly or abruptly, Universal Bridge uses a Quartic Stability Curve: f(p) = 1.0 - (2p - 1.0)^4. This formula creates a 'plateau' of compatibility in the intermediate steps, protecting the original anatomy at the beginning and preventing the texture from becoming 'pasty' or gray at the end.
How does it work? When you press "Generate," the sampler performs three critical tasks in milliseconds:
DNA Detection (Step 0): It measures the energy of the base model (checking if it's Velvette, Illustrious, etc.).
Synchronization: If it detects a direction command (e.g.,
Hacia_Velvette), it adjusts the contrast and color so that the LoRA feels "at home."Aesthetic Refinement: Applies a Sharpening Injector and a safety floor to prevent the image from looking blurry or gray.
Practical Example: Prompt: <lora:Pony_Style_V2:1> Hacia_Velvette, Modo_Neutral
The system loads a Pony LoRA (Expected Energy: 0.016593).
Hacia_Velvette: Translates that DNA towards the Velvette destination (Std: 0.019734). Prevents Pony's aggressive aesthetic from breaking the delicate model.Modo_Neutral: Deactivates heavy color translation but keeps the Sharpening Injector active for perfect edges.
š Scheduler Guide (Noise Maps)
The scheduler decides how much "strength" the noise has at each step. AGGA offers maps that standard schedulers cannot replicate.
1. Intelligent & Adaptive (Smart V2)
AGGA Smart (Updated to v2): Intelligent logic with 5 time zones based on your step count:
1-4 Steps: Lightning Curve (Instant convergence).
5-8 Steps: Turbo/DMD Curve (Aggressive, optimized for Flash).
9-19 Steps: AYS (Align Your Steps) Curve - The sweet spot for Fusion modes.
20-49 Steps: Dynamic Rho (High fidelity).
50+ Steps: Double Anchor (Deep repair/upscaling).
Dynamic Rho: The value of Ļ evolves from 5.0 to 9.0. This provides a very stable structure at the beginning and ultra-fine refinement at the end.
2. Anchors (Time Warping)
Style-Anchor: Creates a sinusoidal time "plateau" in the style sigmas. The sampler spends more real-time processing the middle steps where aesthetics are defined.
Ultra-Anchor: Reinforced version (factor 0.32). Creates a pseudo-plateau where time seems to stop to inject the maximum amount of style possible.
Double-Anchor (The Sniper): Our most advanced scheduler. It combines style anchoring with a "sniper" dip at 82% of the process to fix critical details like eyes and faces.
AGGA AYS-Anchor [NEW]: Combines NVIDIA's "Align Your Steps" optimized values with AGGA's anchor warping. Science + Art.
3. Specialized
AGGA DMD: Extreme power curve (Ļ = 7.0). Ideal for Lightning models.
Log-Linear: A perfect logarithmic distribution. The "gold standard" for the Detail-Native sampler.
Pixel-Staircase V1 & V2: A staircase of sigmas. It groups steps (e.g., 3 by 3) so the sampler has time to "settle" the pixel quantization before dropping to the next level. V2 adds "Edge Locking" for cleaner sprites.
š® Prompt Command Guide (Total Control)
AGGA allows you to control the internal engine behavior directly from your prompt.
Fusion Commands (For Flash v9 Sampler)
Mix two engines to get the best of both worlds:
fsn_euler_dpm: Creativity + Precision. Euler creates rich colors, DPM cleans lines. (Ideal: Complex illustrations, backgrounds, abstract art).fsn_native_flash: Stability + Texture. Native ensures perfect anatomy, Flash injects the "crunch". (Ideal: Latex, armor, 8k details).fsn_dpm_euler: Structure + Softness. DPM builds a solid body, Euler softens skin at the end. (Ideal: Soft female portraits, oil painting).fsn_native_dpm: The Tank. Maximum structural stability. Almost impossible to break. (Ideal: Difficult hands, complex poses).Split Control: Decide when the switch happens (Default 50%). E.g.,
split_30(switch at 30%),split_80.
Universal Bridge Commands (LoRA Compatibility)
Use these commands (in Spanish) to direct the DNA translation:
Destination (Hacia_): Indicates towards which "DNA" to translate the influence.
Hacia_Pony,Hacia_Noobai,Hacia_Illustrious_V2,Hacia_Velvette,Hacia_SDXL, etc.
Source (Desde_): To override automatic detection if using a heavily modified merge.
Desde_Pony,Desde_Noobai, etc.
Power Modes:
Modo_Safe: 1:1 Technical compatibility. Avoids errors without changing original colors.Modo_Fuerte: Aggressive injection of color and blacks. Ideal for highlighting character designs.Modo_Ultra: Brute power boost (1.50) for when a LoRA appears invisible.Modo_Neutral: Deactivates color/energy bridge but keeps the Sharpening Injector active.Puente_Inverso/Modo_Reverso: Swaps source and destination.
Advanced Prompt Example (Combo):
(masterpiece), 1girl, latex suit, detailed texture, <lora:StylePony:1>,
fsn_native_flash, split_70, Hacia_SDXL, Modo_Fuerte(This uses Native for 70% for anatomy, Flash at the end for texture, while translating a Pony LoRA to work strongly on an SDXL model).

Thanks so much for your support! ā„
Description
FAQ
Comments (32)
I forgot to mention it, but you don't need to delete your kdiffusion and sd_scheduler files if they're different from the ones I use. Just paste this into each one:
(in sd_scheduler.py add this at the end: import modules.sd_agga_schedulers) and (in sd_samplers_kdiffusion.py add this at the end: import modules.sd_samplers_pseudo_hires_loader).
You only need to do this once if they're different from mine.
Do these work with Comfy?
Edit: Nevermind, I figured out that they don't. Do you have any plans to make ones that do?
Yes, I was thinking of making them for Reforge too, I'll probably upload them later or tomorrow.
:3 ya!
Any chance of a side by side comparison?
Looks like it works with sd-webui-forge-neo. Just make sure you put the import lines AFTER everything else in samplers and schedulers.
Impressive, thanks for the hard work!
For info i tested replacing both "sd_schedulers.py" and "sd_samplers_kdiffusion.py" using your versions from your "core" folder inside of the zip file and i couldn't start Forge anymore (error message). But by just manually adding "import modules.sd_agga_schedulers" to "sd_samplers_kdiffusion.py" and "import modules.sd_samplers_pseudo_hires_loader" to "sd_samplers_kdiffusion.py" as explained in your installation instructions everything works fine. So maybe that you should remove the "core" folder from your zip file so people won't face the same issue.
Yes, that's why I separated them, since we don't all use the same web UI. Take them as an example xd
Hi, I fixed A1111 (I had forgotten to update a sampler and also improved the loader)
ComfyUI nodes don't work. Either Sampler says the object has no attribute Sample, or that tensors are different devices.
I just updated the script (for download), but here it is:
sampler_func = func_map.get(sampler_name, sample_pseudo_hires_soft)
# ================================================================
# (THE FIX)
# ================================================================
def sampler_wrapper(model, x, sigmas, args, *kwargs):
# Forzar que los sigmas estƩn en el mismo dispositivo que los latents (GPU)
if sigmas.device != x.device:
sigmas = sigmas.to(x.device)
return sampler_func(model, x, sigmas, args, *kwargs)
return (sampler_wrapper,)
@Herrscher_AGGAĀ Still no luck.
Maybe I am doing something wrong? Just unzipping and copying it into the custom nodes folder?
@PrometheaĀ Two issues:
1. Syntax error: args, kwargs should be *args, **kwargs (missing asterisk and double asterisk - at least from the comment here it got removed due to formatting.)
2. (edited for more clarity - sorry):
at the top of the file:
import torch
import torch.nn.functional as F
import math
import comfy.samplers <- add this
-
then change line 711 from
return (sampler_wrapper,) to
return (comfy.samplers.KSAMPLER(sampler_wrapper),)
@AmatiramisuĀ SamplerCustomAdvanced
KSAMPLER.sample.<locals>.<lambda>() takes 1 positional argument but 5 were given
@PrometheaĀ ? gimme a few and let me verify..
@PrometheaĀ Yeah, sorry my mistake, should've looked more closely. the ksampler wrapper is the right direction but only half the fix. The callback format inside the sampler functions are still written for a1111/forge, and I thought that the comfy-specific zip was actually already converted. Thats a bit more work than this.
Sorry, I didn't notice that the asterisk had been deleted from the comment, although it does appear in the file I uploaded yesterday. However, I can't use ComfyUI to test it; it's new to me. I'll try to fix it as soon as possible.
@Herrscher_AGGAĀ No pressure. NexorAI showed me some of his generations, and I am just hyped. This concept is much more promising as the 100th new detailer-Lora.
I haven't tried it, but it should be better (I'll try it tomorrow): import torch
import torch.nn.functional as F
import comfy.sample
import comfy.samplers
# =====================================================
def finalize_sigmas(sigmas: torch.Tensor):
if sigmas[-1].item() != 0.0:
sigmas = torch.cat([sigmas, sigmas.new_zeros([1])])
return sigmas
# =====================================================
# SCHEDULERS
# =====================================================
def pseudo_hires_sigmas(n, sigma_min, sigma_max, device):
ramp = torch.linspace(0, 1, n, device=device)
sigmas = sigma_max torch.exp(-ramp 4.5)
p0 = int(n * 0.45)
p1 = int(n * 0.80)
sigmas[p0:p1] = sigmas[p0]
sigmas[p1:] = torch.linspace(sigmas[p1], sigma_min, n - p1, device=device)
# sigmas[-2] = sigmas[-1]
return finalize_sigmas(sigmas)
def get_sigmas_agga_dmd(n, sigma_min, sigma_max, device):
rho = 7.0
ramp = torch.linspace(0, 1, n, device=device)
min_inv = sigma_min ** (1 / rho)
max_inv = sigma_max ** (1 / rho)
sigmas = (max_inv + ramp (min_inv - max_inv)) * rho
return finalize_sigmas(sigmas)
def get_sigmas_log_linear(n, sigma_min, sigma_max, device):
steps = torch.linspace(0, 1, n, device=device)
s_max = torch.as_tensor(sigma_max, device=device)
s_min = torch.as_tensor(sigma_min, device=device)
sigmas = torch.exp(
torch.log(s_max) * (1 - steps) +
torch.log(s_min) * steps
)
return finalize_sigmas(sigmas)
def get_sigmas_dynamic_rho(
n,
sigma_min,
sigma_max,
device,
rho_start=5.0,
rho_end=9.0
):
ramp = torch.linspace(0, 1, n, device=device)
rhos = rho_start + (rho_end - rho_start) * ramp
sigmas = torch.empty(n, device=device)
for i in range(n):
r = rhos[i]
min_inv = sigma_min ** (1 / r)
max_inv = sigma_max ** (1 / r)
sigmas[i] = (max_inv + ramp[i] (min_inv - max_inv)) * r
return finalize_sigmas(sigmas)
def get_sigmas_style_anchor(n, sigma_min, sigma_max, device):
ramp = torch.linspace(0, 1, n, device=device)
ramp_anchored = ramp + 0.15 torch.sin(ramp torch.pi)
sigmas = sigma_max ((sigma_min / sigma_max) * ramp_anchored)
sigmas[-2] *= 0.9
return finalize_sigmas(sigmas)
def get_sigmas_ultra_anchor(n, sigma_min, sigma_max, device):
ramp = torch.linspace(0, 1, n, device=device)
ramp_hybrid = ramp + 0.32 torch.sin(ramp torch.pi)
sigmas = sigma_max ((sigma_min / sigma_max) * ramp_hybrid)
current_last = sigmas[-3]
s_min_t = torch.as_tensor(sigma_min, device=device)
sigmas[-3:] = torch.linspace(
current_last,
s_min_t,
3,
device=device
)
return finalize_sigmas(sigmas)
def get_sigmas_agga_double_anchor(n, sigma_min, sigma_max, device):
t = torch.linspace(0, 1, n, device=device)
style_warp = 0.35 torch.sin(t torch.pi)
eye_sniper = 0.15 torch.exp(-180 (t - 0.82) ** 2)
dmd_tail = 0.10 (t * 4)
t_warped = t + style_warp + eye_sniper + dmd_tail
t_warped = t_warped / t_warped[-1]
sigmas = sigma_max ((sigma_min / sigma_max) * (t_warped ** 1.1))
s_start = torch.log10(sigmas[-4])
s_end = torch.log10(torch.as_tensor(sigma_min, device=device))
sigmas[-4:] = torch.logspace(
s_start,
s_end,
4,
device=device
)
return finalize_sigmas(sigmas)
def get_sigmas_agga_pixel_staircase(n, sigma_min, sigma_max, device):
t = torch.linspace(0, 1, n, device=device)
s_max = torch.as_tensor(sigma_max, device=device)
s_min = torch.as_tensor(sigma_min, device=device)
sigmas = torch.exp(
torch.log(s_max) * (1 - t) +
torch.log(s_min) * t
)
for i in range(1, len(sigmas)):
if i % 3 != 0:
sigmas[i] = sigmas[i - 1] * 0.98
return finalize_sigmas(sigmas)
def get_sigmas_agga_pixel_staircase_v2(
n,
sigma_min,
sigma_max,
device,
hold_steps=3,
decay=0.985
):
t = torch.linspace(0, 1, n, device=device)
s_max = torch.as_tensor(sigma_max, device=device)
s_min = torch.as_tensor(sigma_min, device=device)
sigmas = torch.exp(
torch.log(s_max) * (1 - t) +
torch.log(s_min) * t
)
for i in range(1, len(sigmas)):
if i % hold_steps != 0:
sigmas[i] = sigmas[i - 1] * decay
return finalize_sigmas(sigmas)
def get_sigmas_agga_smart(n, sigma_min, sigma_max, device):
if n <= 8:
return get_sigmas_agga_dmd(n, sigma_min, sigma_max, device)
return get_sigmas_dynamic_rho(n, sigma_min, sigma_max, device)
def get_sigmas_agga_ays_anchor(n, sigma_min, sigma_max, device):
ays_base = torch.tensor(
[14.615, 6.315, 3.771, 2.181, 1.342,
0.862, 0.555, 0.380, 0.234, 0.113, 0.029],
device=device
)
t = torch.linspace(0, 1, n, device=device)
t_warped = t + 0.15 torch.sin(t torch.pi)
log_base = ays_base.log()
indices = t_warped * (len(log_base) - 1)
idx0 = indices.floor().long().clamp(0, len(log_base) - 2)
idx1 = idx0 + 1
alpha = indices - idx0
log_sigmas = (
log_base[idx0] * (1 - alpha) +
log_base[idx1] * alpha
)
sigmas = torch.exp(log_sigmas)
return finalize_sigmas(sigmas)
# =====================================================
# SAMPLERS
# =====================================================
@torch.no_grad()
def sample_pseudo_hires_soft(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = {} if extra_args is None else extra_args
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
x = x + d * dt
if callback:
callback(i, x, denoised, total_steps, sigma)
return x
@torch.no_grad()
def sample_pseudo_hires_sharp(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = {} if extra_args is None else extra_args
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
if i > total_steps * 0.65:
x = x + d dt 1.12
else:
x = x + d * dt
if callback:
callback(i, x, denoised, total_steps, sigma)
return x
@torch.no_grad()
def sample_pseudo_hires_ultra(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = {} if extra_args is None else extra_args
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
if i > total_steps * 0.55:
boost = 1.18 + (i / total_steps) * 0.10
x = x + d dt boost
else:
x = x + d * dt
if callback:
callback(i, x, denoised, total_steps, sigma)
return x
@torch.no_grad()
def sample_dpmpp_2m_pseudo_hires(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = {} if extra_args is None else extra_args
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
old_denoised = None
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
if old_denoised is None:
d = (x - denoised) / sigma
x = x + d * dt
else:
d = (x - denoised) / sigma
d_old = (x - old_denoised) / \
sigmas[max(i-1, 0)] if old_denoised is not None else d
correction = 0.5 (d + d_old) dt
x = x + correction * 1.0
if i > total_steps * 0.60:
boost = 1.12 + (i / total_steps) * 0.08
x = x + d dt boost
old_denoised = denoised
if callback:
callback(i, x, denoised, total_steps, sigma)
return x
@torch.no_grad()
def sample_pseudo_hires_flash_v2(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
old_denoised = None
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
if old_denoised is None:
d = (x - denoised) / sigma
x = x + d dt 1.05
else:
d = (x - denoised) / sigma
d_old = (x - old_denoised) / sigmas[max(i-1, 0)]
x = x + (0.6 d + 0.4 d_old) * dt
if i > total_steps * 0.50:
progress = (i - total_steps 0.50) / (total_steps 0.50)
boost = 1.08 + progress * 0.18
x = x + d dt boost
if i == total_steps - 1:
x = x + (denoised - x) * 0.15
old_denoised = denoised
if callback:
callback(i, x, denoised, total_steps, sigma)
return x
@torch.no_grad()
def sample_pseudo_hires_detail(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
def get_detail_mask(latent):
dy = torch.abs(latent[:, :, 1:, :] - latent[:, :, :-1, :])
dx = torch.abs(latent[:, :, :, 1:] - latent[:, :, :, :-1])
dy = F.pad(dy, (0, 0, 0, 1), mode='replicate')
dx = F.pad(dx, (0, 1, 0, 0), mode='replicate')
grad = torch.sqrt(dx**2 + dy**2).mean(dim=1, keepdim=True)
grad = torch.clamp(grad * 2.0, 0.0, 1.0)
return grad
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
progress = i / total_steps
if progress < 0.4:
multiplier = 1.05
elif progress < 0.7:
multiplier = 1.10 + (progress * 0.10)
else:
multiplier = 1.0
x_next = x + d dt multiplier
if progress >= 0.7:
detail_mask = get_detail_mask(x)
refine_strength = 0.05 * (1.0 - progress)
x_next = x_next + (denoised - x_next) * \
refine_strength * detail_mask
sharpen_strength = 0.03 * detail_mask
x_next = x_next + (x_next - x) * sharpen_strength
x = x_next
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
@torch.no_grad()
def sample_agga_dmd_turbo(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
if i < total_steps - 1:
x = x + d * dt
else:
x = x + (denoised - x) * 0.85
blurred_x = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
sharpen_map = x - blurred_x
x = x + sharpen_map * 0.15
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
@torch.no_grad()
def sample_agga_herrscher_native(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
momentum_beta = 0.50
momentum = None
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i+1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
if momentum is None:
momentum = d
else:
momentum = momentum_beta momentum + (1 - momentum_beta) d
x_next = x + momentum * dt
progress = i / total_steps
if 0.2 < progress < 0.9:
mag = x_next.std()
if mag < 1.0:
scale_factor = (1.0 / (mag + 1e-6)) * 0.05
x_next = x_next * (1.0 + scale_factor)
x_next = x_next - (x_next.mean() * 0.02)
x = x_next
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
@torch.no_grad()
def sample_agga_detail_native(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
progress = i / total_steps
denoised = model(x, sigma s_in, *extra_args)
if i > 0:
dy = torch.abs(denoised[:, :, 1:, :] - denoised[:, :, :-1, :])
dx = torch.abs(denoised[:, :, :, 1:] - denoised[:, :, :, :-1])
dy = F.pad(dy, (0, 0, 0, 1), mode='replicate')
dx = F.pad(dx, (0, 1, 0, 0), mode='replicate')
detail_mask = torch.clamp(
(dx + dy).mean(dim=1, keepdim=True) * 2.5, 0.0, 1.0)
if progress > 0.60:
sigma_refine = sigma * 0.95
refine_denoised = model(x, sigma_refine s_in, *extra_args)
denoised = denoised + \
(refine_denoised - denoised) (0.4 detail_mask)
else:
blurred = F.avg_pool2d(denoised, 3, 1, 1)
denoised = denoised + \
(denoised - blurred) (0.12 detail_mask)
if i < total_steps - 1:
x = denoised + (x - denoised) * (sigma_next / sigma)
else:
x = denoised
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
@torch.no_grad()
def sample_agga_hyper_detail_hybrid(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
split_step = int(total_steps * 0.66)
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
denoised = model(x, sigma s_in, *extra_args)
dy = torch.abs(denoised[:, :, 1:, :] - denoised[:, :, :-1, :])
dx = torch.abs(denoised[:, :, :, 1:] - denoised[:, :, :, :-1])
dy = F.pad(dy, (0, 0, 0, 1), mode='replicate')
dx = F.pad(dx, (0, 1, 0, 0), mode='replicate')
detail_mask = torch.clamp(
(dx + dy).mean(dim=1, keepdim=True) * 2.5, 0.0, 1.0)
if i >= split_step:
sigma_refine = sigma * 0.95
refine_denoised = model(x, sigma_refine s_in, *extra_args)
denoised = denoised + \
(refine_denoised - denoised) (0.45 detail_mask)
else:
blurred = F.avg_pool2d(denoised, 3, 1, 1)
denoised = denoised + (denoised - blurred) (0.12 detail_mask)
if i < total_steps - 1:
x = denoised + (x - denoised) * (sigma_next / sigma)
x_std = x.std()
if x_std < 1.0:
x = x (1.0 + (1.0 - x_std) 0.05)
else:
x = denoised
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
@torch.no_grad()
def sample_agga_style_repair_prompt_aware(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
BOCETADO_STEPS = int(total_steps * 0.40)
prev_x = x.clone()
for i in range(total_steps):
sigma = sigmas[i]
sigma_next = sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
style_force_vector = None
last_noise_uncond = getattr(model, 'last_noise_uncond', None)
if last_noise_uncond is not None and i > BOCETADO_STEPS:
uncond_denoised = x - sigma * last_noise_uncond
style_force_vector = denoised - uncond_denoised
dy = torch.abs(denoised[:, :, 1:, :] - denoised[:, :, :-1, :])
dx = torch.abs(denoised[:, :, :, 1:] - denoised[:, :, :, :-1])
dy = F.pad(dy, (0, 0, 0, 1), mode='replicate')
dx = F.pad(dx, (0, 1, 0, 0), mode='replicate')
style_mask = torch.clamp(torch.sqrt(
dx**2 + dy**2).mean(dim=1, keepdim=True) * 2.8, 0.0, 1.0)
if i < BOCETADO_STEPS:
boost = 1.20 + (i / BOCETADO_STEPS) * 0.20
d = (x - denoised) / sigma
x = x + d dt boost
else:
if style_force_vector is not None:
prompt_guidance = style_force_vector style_mask 0.25
x = x + prompt_guidance * torch.abs(dt)
x = x + (x - prev_x) 0.035 style_mask
d = (x - denoised) / sigma
x = x + d * dt
if i >= BOCETADO_STEPS:
x_std = x.std()
if x_std > 1.15:
x = x * (1.15 / x_std)
prev_x = x.clone()
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
@torch.no_grad()
def sample_agga_style_repair_pro(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
BOCETADO_STEPS = int(total_steps * 0.40)
prev_x = x.clone()
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
progress = i / total_steps
denoised = model(x, sigma s_in, *extra_args)
dy = torch.abs(denoised[:, :, 1:, :] - denoised[:, :, :-1, :])
dx = torch.abs(denoised[:, :, :, 1:] - denoised[:, :, :, :-1])
dy = F.pad(dy, (0, 0, 0, 1), mode='replicate')
dx = F.pad(dx, (0, 1, 0, 0), mode='replicate')
style_mask = torch.clamp(torch.sqrt(
dx**2 + dy**2).mean(dim=1, keepdim=True) * 2.8, 0.0, 1.0)
if i < BOCETADO_STEPS:
boost = 1.20 + (i / BOCETADO_STEPS) * 0.25
d = (x - denoised) / sigma
x = x + d (sigma_next - sigma) boost
else:
strength = 0.05 + progress * 0.15
x = x + (denoised - x) strength style_mask
if i % 3 == 0 and i < total_steps - 1:
sigma_style = sigma (1.12 + 0.05 (progress - 0.4))
denoised_style = model(x, sigma_style s_in, *extra_args)
style_delta = (denoised_style - denoised) 0.04 style_mask
x = x + style_delta
x = x + (x - prev_x) 0.035 style_mask
if i < total_steps - 1 and i >= BOCETADO_STEPS:
x = denoised + (x - denoised) * (sigma_next / sigma)
x_std = x.std()
if x_std < 1.08:
x = x * (1.08 / (x_std + 1e-6))
elif i == total_steps - 1:
x = denoised + (x - denoised) * 0.07
prev_x = x.clone()
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
@torch.no_grad()
def sample_agga_style_repair_ultra(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
BOCETADO_STEPS = int(total_steps * 0.45)
prev_x = x.clone()
for i in range(total_steps):
sigma, sigma_next = sigmas[i], sigmas[i + 1]
dt = sigma_next - sigma
progress = i / total_steps
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
dy = torch.abs(denoised[:, :, 1:, :] - denoised[:, :, :-1, :])
dx = torch.abs(denoised[:, :, :, 1:] - denoised[:, :, :, :-1])
dy = F.pad(dy, (0, 0, 0, 1), mode='replicate')
dx = F.pad(dx, (0, 1, 0, 0), mode='replicate')
style_mask = torch.clamp(torch.sqrt(
dx**2 + dy**2).mean(dim=1, keepdim=True) * 2.8, 0.0, 1.0)
if i < BOCETADO_STEPS:
x = x + d * dt
else:
speed_factor = torch.exp(-torch.abs(dt) * 2.5).item()
u_boost = 1.18 + progress * 0.12
if i % 2 == 0 or speed_factor > 0.6:
sigma_style = sigma (1.15 + 0.05 (progress - 0.4))
denoised_style = model(x, sigma_style s_in, *extra_args)
atm_mask = torch.clamp(style_mask + 0.20, 0.0, 1.0)
style_delta = (denoised_style - denoised) * \
0.05 atm_mask (1.0 + speed_factor)
x = x + style_delta * u_boost
x = x + d dt u_boost
sharpen = 0.038 + (speed_factor * 0.015)
x = x + (x - prev_x) sharpen style_mask
if i == total_steps - 1:
x = denoised + (x - denoised) * 0.08
prev_x = x.clone()
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.5, 5.5)
@torch.no_grad()
def sample_agga_pixel_master(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
block_size = 1.88
for i in range(total_steps):
sigma = sigmas[i]
sigma_next = sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
x = x + d * dt
if i == total_steps - 1:
latents = denoised
h, w = latents.shape[-2:]
small_h, small_w = int(h / block_size), int(w / block_size)
latents_pixelated = F.interpolate(
latents, size=(small_h, small_w), mode='area')
latents_pixelated = F.interpolate(
latents_pixelated, size=(h, w), mode='nearest')
x = latents_pixelated
if callback:
callback(i, x, denoised, total_steps, sigma)
return x
@torch.no_grad()
def sample_agga_structural_detail(model, x, sigmas, extra_args=None, callback=None, **kwargs):
extra_args = extra_args or {}
s_in = x.new_ones([x.shape[0]])
total_steps = len(sigmas) - 1
old_d = None
split_idx = int(total_steps * 0.45)
for i in range(total_steps):
sigma = sigmas[i]
sigma_next = sigmas[i + 1]
dt = sigma_next - sigma
denoised = model(x, sigma s_in, *extra_args)
d = (x - denoised) / sigma
if i < split_idx:
if old_d is None:
x = x + d * dt
else:
x = x + 0.5 (d + old_d) dt
else:
dy = torch.abs(denoised[:, :, 1:, :] - denoised[:, :, :-1, :])
dx = torch.abs(denoised[:, :, :, 1:] - denoised[:, :, :, :-1])
dy = F.pad(dy, (0, 0, 0, 1), mode='replicate')
dx = F.pad(dx, (0, 1, 0, 0), mode='replicate')
detail_mask = torch.clamp(
(dx + dy).mean(dim=1, keepdim=True) * 2.2, 0.0, 1.0)
progress_phase2 = (i - split_idx) / (total_steps - split_idx)
boost_amount = 1.0 + (progress_phase2 * 0.15)
final_dt = dt (1.0 + (boost_amount - 1.0) detail_mask)
x = x + d * final_dt
if progress_phase2 > 0.6:
x = x + (x - x.clone()) 0.04 detail_mask
old_d = d
if callback:
callback(i, x, denoised, total_steps, sigma)
return torch.clamp(x, -5.0, 5.0)
# =====================================================
class AGGA_KSampler_Pro:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"model": ("MODEL",),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
"sampler_name": ([
"AGGA Pseudo-HiRes Soft",
"AGGA Pseudo-HiRes Sharp",
"AGGA Pseudo-HiRes Ultra",
"AGGA DPM++ 2M Pseudo",
"AGGA Flash v2",
"AGGA Detail",
"AGGA DMD Turbo",
"AGGA Herrscher Native",
"AGGA Detail Native",
"AGGA Hyper Detail Hybrid",
"AGGA Style Repair Prompt",
"AGGA Style Repair Pro",
"AGGA Style Repair Ultra",
"AGGA Pixel Master",
"AGGA Structural Detail",
],),
"scheduler": ([
"agga_log_linear",
"agga_dmd",
"agga_pseudo_hires",
"agga_dynamic_rho",
"agga_style_anchor",
"agga_ultra_anchor",
"agga_double_anchor",
"agga_pixel_staircase",
"agga_pixel_staircase_v2",
"agga_smart",
"agga_ays_anchor",
],),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
"latent_image": ("LATENT",),
}
}
RETURN_TYPES = ("LATENT",)
FUNCTION = "sample"
CATEGORY = "sampling/custom_sampling"
def sample(self, model, seed, steps, cfg, sampler_name, scheduler,
positive, negative, latent_image):
latent = latent_image["samples"]
device = latent.device
torch.manual_seed(seed)
noise = torch.randn_like(latent)
model_sampling = model.get_model_object("model_sampling")
sigma_min, sigma_max = model_sampling.sigma_min, model_sampling.sigma_max
scheduler_map = {
"agga_log_linear": get_sigmas_log_linear,
"agga_dmd": get_sigmas_agga_dmd,
"agga_pseudo_hires": pseudo_hires_sigmas,
"agga_dynamic_rho": get_sigmas_dynamic_rho,
"agga_style_anchor": get_sigmas_style_anchor,
"agga_ultra_anchor": get_sigmas_ultra_anchor,
"agga_double_anchor": get_sigmas_agga_double_anchor,
"agga_pixel_staircase": get_sigmas_agga_pixel_staircase,
"agga_pixel_staircase_v2": get_sigmas_agga_pixel_staircase_v2,
"agga_smart": get_sigmas_agga_smart,
"agga_ays_anchor": get_sigmas_agga_ays_anchor,
}
sigmas_fn = scheduler_map.get(scheduler, get_sigmas_log_linear)
sigmas = sigmas_fn(steps, sigma_min, sigma_max, device)
sampler_map = {
"AGGA Pseudo-HiRes Soft": sample_pseudo_hires_soft,
"AGGA Pseudo-HiRes Sharp": sample_pseudo_hires_sharp,
"AGGA Pseudo-HiRes Ultra": sample_pseudo_hires_ultra,
"AGGA DPM++ 2M Pseudo": sample_dpmpp_2m_pseudo_hires,
"AGGA Flash v2": sample_pseudo_hires_flash_v2,
"AGGA Detail": sample_pseudo_hires_detail,
"AGGA DMD Turbo": sample_agga_dmd_turbo,
"AGGA Herrscher Native": sample_agga_herrscher_native,
"AGGA Detail Native": sample_agga_detail_native,
"AGGA Hyper Detail Hybrid": sample_agga_hyper_detail_hybrid,
"AGGA Style Repair Prompt": sample_agga_style_repair_prompt_aware,
"AGGA Style Repair Pro": sample_agga_style_repair_pro,
"AGGA Style Repair Ultra": sample_agga_style_repair_ultra,
"AGGA Pixel Master": sample_agga_pixel_master,
"AGGA Structural Detail": sample_agga_structural_detail,
}
sampler_func = sampler_map[sampler_name]
sampler = comfy.samplers.SamplerCustom(sampler_func)
samples = comfy.sample.sample_custom(
model=model,
noise=noise,
cfg=cfg,
sampler=sampler,
sigmas=sigmas,
positive=positive,
negative=negative,
latent_image=latent_image,
noise_mask=None,
callback=None,
disable_pbar=False,
seed=seed,
)
return ({"samples": samples},)
# =====================================================
NODE_CLASS_MAPPINGS = {
"AGGA KSampler Pro": AGGA_KSampler_Pro
}
NODE_DISPLAY_NAME_MAPPINGS = {
"AGGA KSampler Pro": "AGGA KSampler (All-in-One)"
}
Sadly, with the new code, the nodes don't show up at all. :/
I've managed to get it working and generating images, but they come out blurry. I'll see if I can finish it today; ComfyUI is very sensitive to samplers.
Sorry, but itās not happening. This is all new territory for me, and despite all my research, I just can't get it right. Itās always somethingāeither the nodes fail, or the image looks awful. NexorAI got it running in ComfyUI (apparently using Gemini), so you might want to look into that, but I haven't had any luck making it work myself:
import torch import torch.nn.functional as F import math # --- SCHEDULER LOGIK MIT SICHERHEITS-FALLSCHIRM --- def get_sigmas_agga_double_anchor(n, sigma_min, sigma_max, device): try: # Sicherstellen, dass keine Null-Werte den Logarithmus killen s_min = torch.as_tensor(max(sigma_min, 0.0001), device=device) s_max = torch.as_tensor(max(sigma_max, 0.0001), device=device) t = torch.linspace(0, 1, n, device=device) # AGGA Kurve t_warped = (t + 0.35 torch.sin(t math.pi) + 0.15 torch.exp(-180 (t - 0.82)**2)) t_warped = t_warped / t_warped[-1] sigmas = s_max ((s_min / s_max) * (t_warped**1.1)) if n > 4: sigmas[-4:] = torch.logspace(torch.log10(sigmas[-4]), torch.log10(s_min), 4, device=device) return torch.cat([sigmas, sigmas.new_zeros([1])]) except Exception as e: print(f"AGGA ERROR: Fallback auf Standard-Sigmas wegen: {e}") steps = torch.linspace(0, 1, n, device=device) sig = torch.exp(torch.log(torch.as_tensor(sigma_max, device=device)) (1 - steps) + torch.log(torch.as_tensor(sigma_min, device=device)) steps) return torch.cat([sig, sig.new_zeros([1])])
def get_sigmas_log_linear(n, sigma_min, sigma_max, device): steps = torch.linspace(0, 1, n, device=device) sigmas = torch.exp(torch.log(torch.as_tensor(sigma_max, device=device)) (1 - steps) + torch.log(torch.as_tensor(sigma_min, device=device)) steps) return torch.cat([sigmas, sigmas.new_zeros([1])]) # --- SAMPLER LOGIK --- @torch.no_grad() def sample_agga_structural_detail(model, x, sigmas, **kwargs): extra_args = kwargs.get('extra_args', {}) s_in = x.new_ones([x.shape[0]]) total_steps = len(sigmas) - 1 for i in range(total_steps): sigma, sigma_next = sigmas[i], sigmas[i + 1] denoised = model(x, sigma s_in, *extra_args) boost = (1.0 + (i/total_steps 0.2)) if i > (total_steps 0.45) else 1.0 x = x + ((x - denoised) / sigma) (sigma_next - sigma) boost return x # --- COMFYUI NODES --- class AGGASchedulerNode: @classmethod def INPUT_TYPES(s): return {"required": {"model": ("MODEL",), "steps": ("INT", {"default": 25, "min": 1, "max": 1000}), "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0}), "scheduler": (["agga_double_anchor", "standard"],)}} RETURN_TYPES = ("SIGMAS",) FUNCTION = "get_sigmas" CATEGORY = "sampling/custom_sampling"
def get_sigmas(self, model, steps, denoise, scheduler): device = torch.device("cuda" if torch.cuda.is_available() else "cpu") ms = model.get_model_object("model_sampling") # Sicherheits-Check für das Modell-Objekt s_min = ms.sigma_min if ms else 0.02 s_max = ms.sigma_max if ms else 14.6 s = int(steps / denoise) if denoise < 1.0 else steps if scheduler == "agga_double_anchor": sigmas = get_sigmas_agga_double_anchor(s, s_min, s_max, device) else: sigmas = get_sigmas_log_linear(s, s_min, s_max, device) if denoise < 1.0: sigmas = sigmas[-(steps + 1):] return (sigmas,) class AGGASamplerNode: @classmethod def INPUT_TYPES(s): return {"required": {"sampler_name": (["AGGA Structural-Detail (Hybrid)", "AGGA-Detail-Native", "AGGA Pseudo-HiRes Soft"],)}} RETURN_TYPES = ("SAMPLER",) FUNCTION = "get_sampler" CATEGORY = "sampling/custom_sampling" def get_sampler(self, sampler_name): f = sample_agga_structural_detail def w(*args, kwargs): if len(args) == 8: return f(args[0], args[1], args[6], extra_args=kwargs.get('extra_args'), callback=kwargs.get('callback')) elif len(args) == 4: # Fix für SamplerCustomAdvanced guider = args[0] model = guider.model if hasattr(guider, 'model') else guider return f(model, args[1], args[3], kwargs) elif len(args) >= 3: return f(args[0], args[1], args[2], **kwargs) return f(*args, **kwargs) w.sample = w w.sampler_function = f w.extra_options = {} return (w,) NODE_CLASS_MAPPINGS = {"AGGA Scheduler": AGGASchedulerNode, "AGGA Sampler": AGGASamplerNode} NODE_DISPLAY_NAME_MAPPINGS = {"AGGA Scheduler": "AGGA Custom Scheduler", "AGGA Sampler": "AGGA Custom Sampler"}
Update: I managed to get it working in ComfyUI, but it's just not the same. While it generates sharp definition in A1111, in Comfy itās simply blurryāthe system is just too restrictive. I've spent days sitting here for hours trying to fix it, but Iām burnt out. In A1111, I can build, test, fix, and improve them, but here in Comfy, itās so complicated that Iām exhausted. Iām giving up.
Iām sorry about this, but there won't be any ComfyUI versions. I know there are people who can make it work there, and Iāve tried everythingāwith help from AI and my colleaguesābut nothing worked. Regards!
Too bad, but thumbs up for the attempt and putting all the time and work into it.
@PrometheaĀ Yes, I will continue learning about nodes; I may be able to do it in the future since some people have managed to do it with the help of Gemini, although I will try not to use Python since ComfyUI is different and that was one of the problems.
Thank you for trying!
@Herrscher_AGGAĀ I got it to work with Claude's help! If you're interested, I can send you the files and a transcript of the chat so you can study them. As you said, you're interested in getting more into Nodes. By the way, this is one of the best, if not THE best, sampler/scheduler combos I have tried so far.
@PrometheaĀ Wow, that is amazing news! Yes, please absolutely send me the files and the chat transcript. Seeing how you and Claude managed to solve the node logic is exactly what I need to finally wrap my head around ComfyUI without burning out again.
Also, thank you so much for that massive compliment! Hearing that it's one of the best combos you've tried really makes all those hours of testing and frustration totally worth it. You can send the files to me via direct message here on Civitai, or on Discord (my username is im_agga / ID: 312749266890129408). Thank you again for putting in the effort to make this work!
@PrometheaĀ Would absolutely love that!
@Herrscher_AGGAĀ Any update on this?
@CaptainFurryTrash @Promethea Hi everyone, sorry for the delay! I've been refining PGP so I can fully focus on the samplers next. Once version 4.5 is released (this Monday or Tuesday), I'll start working on ComfyUI. The examples you provided gave me some great ideas on how the nodes work, and I've been testing a ComfyUI version. I apologize for taking so long with this, but it's a bit complicated! You can try this file which includes the missing ones, though I haven't fully tested them yet and I think they can still be improved: https://drive.google.com/file/d/1e6GJPWMznV7bdnRZvLBsj542hnWXNsQ8/view?usp=sharing



