初始化项目,由ModelHub XC社区提供模型
Model: Kezmark/Mordant-7B-Think Source: Original Platform
This commit is contained in:
350
mordant-prompt-enhancer/Mordant-Prompt-Enhancer.py
Normal file
350
mordant-prompt-enhancer/Mordant-Prompt-Enhancer.py
Normal file
@@ -0,0 +1,350 @@
|
||||
import os
|
||||
import gc
|
||||
import threading
|
||||
from typing import Optional
|
||||
|
||||
import torch
|
||||
from llama_cpp import Llama
|
||||
from comfy import model_management
|
||||
|
||||
import folder_paths
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. Helpers for GGUF file listing & resolution (unchanged)
|
||||
# -------------------------------------------------------------------
|
||||
_CACHE = {}
|
||||
_CACHE_LOCK = threading.Lock()
|
||||
|
||||
def _list_gguf_files():
|
||||
candidates = []
|
||||
base_dirs = []
|
||||
try:
|
||||
base_dirs.extend(folder_paths.get_folder_paths("text_encoders"))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for base in base_dirs:
|
||||
if not base or not os.path.isdir(base):
|
||||
continue
|
||||
for root, _, files in os.walk(base):
|
||||
for name in files:
|
||||
if name.lower().endswith(".gguf"):
|
||||
full = os.path.join(root, name)
|
||||
try:
|
||||
rel = os.path.relpath(full, base)
|
||||
if rel not in candidates:
|
||||
candidates.append(rel)
|
||||
except Exception:
|
||||
if full not in candidates:
|
||||
candidates.append(full)
|
||||
|
||||
if "<manual_path>" not in candidates:
|
||||
candidates.append("<manual_path>")
|
||||
return candidates
|
||||
|
||||
def _resolve_model_path(model_name: str, manual_model_path: str = "") -> str:
|
||||
if os.path.isabs(model_name) and os.path.isfile(model_name):
|
||||
return model_name
|
||||
if model_name == "<manual_path>":
|
||||
p = (manual_model_path or "").strip()
|
||||
if not p:
|
||||
raise ValueError("model is <manual_path> but manual_model_path is empty")
|
||||
if not os.path.isfile(p):
|
||||
raise FileNotFoundError(f"GGUF model not found: {p}")
|
||||
return p
|
||||
full = folder_paths.get_full_path("text_encoders", model_name)
|
||||
if full and os.path.isfile(full):
|
||||
return full
|
||||
try:
|
||||
for base in folder_paths.get_folder_paths("text_encoders"):
|
||||
probe = os.path.join(base, model_name)
|
||||
if os.path.isfile(probe):
|
||||
return probe
|
||||
except Exception:
|
||||
pass
|
||||
raise FileNotFoundError(f"Could not resolve GGUF model path for: {model_name}")
|
||||
|
||||
def _build_messages(system_prompt: str, user_prompt: str):
|
||||
messages = []
|
||||
if system_prompt.strip():
|
||||
messages.append({"role": "system", "content": system_prompt.strip()})
|
||||
messages.append({"role": "user", "content": user_prompt.strip()})
|
||||
return messages
|
||||
|
||||
def _assemble_prompt(messages: list, model_family: str) -> str:
|
||||
prompt = ""
|
||||
if model_family == "granite":
|
||||
for msg in messages:
|
||||
prompt += f"<|start_of_role|>{msg['role']}<|end_of_role|>{msg['content']}<|end_of_text|>\n"
|
||||
prompt += "<|start_of_role|>assistant<|end_of_role|><think>\n"
|
||||
else:
|
||||
for msg in messages:
|
||||
prompt += f"<|im_start|>{msg['role']}\n{msg['content']}<|im_end|>\n"
|
||||
prompt += "<|im_start|>assistant\n<think>"
|
||||
return prompt
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. Lookup table for Mordant models (unchanged)
|
||||
# -------------------------------------------------------------------
|
||||
MORDANT_INFO = {
|
||||
"mordant-1.2b": {
|
||||
"layers": 16,
|
||||
"bf16_total_gb": 2.35,
|
||||
"per_layer": {
|
||||
"bf16": 2.35 / 16,
|
||||
"q8_0": 1.27 / 16,
|
||||
"q6_k_m": 1.01682 / 16,
|
||||
"q5_k_m": None,
|
||||
"q4_k_m": None,
|
||||
}
|
||||
},
|
||||
"mordant-3b": {
|
||||
"layers": 40,
|
||||
"bf16_total_gb": 6.99,
|
||||
"per_layer": {
|
||||
"bf16": 6.99 / 40,
|
||||
"q8_0": 3.86 / 40,
|
||||
"q6_k_m": 3.05 / 40,
|
||||
"q5_k_m": 2.70 / 40,
|
||||
"q4_k_m": None,
|
||||
}
|
||||
},
|
||||
"mordant-7b": {
|
||||
"layers": 32,
|
||||
"bf16_total_gb": 16.29,
|
||||
"per_layer": {
|
||||
"bf16": 16.29 / 32,
|
||||
"q8_0": 9.58 / 32,
|
||||
"q6_k_m": 7.85 / 32,
|
||||
"q5_k_m": 7.08 / 32,
|
||||
"q4_k_m": None,
|
||||
}
|
||||
},
|
||||
"mordant-12b": {
|
||||
"layers": 40,
|
||||
"bf16_total_gb": 24.81,
|
||||
"per_layer": {
|
||||
"bf16": 24.81 / 40,
|
||||
"q8_0": 13.55 / 40,
|
||||
"q6_k_m": 10.64 / 40,
|
||||
"q5_k_m": 9.33 / 40,
|
||||
"q4_k_m": 8.11 / 40,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
def _detect_model_info(model_path: str):
|
||||
try:
|
||||
llm_meta = Llama(model_path=model_path, vocab_only=True, n_gpu_layers=0, verbose=False)
|
||||
meta = llm_meta.metadata
|
||||
arch = meta.get("general.architecture", "unknown")
|
||||
total_layers = int(meta.get(f"{arch}.block_count", 0))
|
||||
quant_version = meta.get("general.quantization_version", "unknown").lower()
|
||||
if quant_version == "unknown":
|
||||
quant_version = meta.get("tokenizer.ggml.quantization_version", "unknown").lower()
|
||||
del llm_meta
|
||||
except Exception:
|
||||
fname = os.path.basename(model_path).lower()
|
||||
for name in MORDANT_INFO:
|
||||
if name in fname:
|
||||
info = MORDANT_INFO[name]
|
||||
total_layers = info["layers"]
|
||||
quant_version = "bf16"
|
||||
for q in ["q8_0", "q6_k_m", "q5_k_m", "q4_k_m", "q3_k_m", "q2_k_m"]:
|
||||
if q in fname:
|
||||
quant_version = q
|
||||
break
|
||||
break
|
||||
else:
|
||||
total_layers = 32
|
||||
quant_version = "bf16"
|
||||
|
||||
model_name = None
|
||||
fname_lower = os.path.basename(model_path).lower()
|
||||
for name in MORDANT_INFO:
|
||||
if name in fname_lower:
|
||||
model_name = name
|
||||
break
|
||||
|
||||
if model_name is None:
|
||||
file_size_gb = os.path.getsize(model_path) / (1024**3)
|
||||
per_layer_vram = file_size_gb / total_layers if total_layers > 0 else 0.5
|
||||
return (model_name or "unknown", total_layers, per_layer_vram, quant_version)
|
||||
|
||||
info = MORDANT_INFO[model_name]
|
||||
layers = info["layers"]
|
||||
per_layer_dict = info.get("per_layer", {})
|
||||
per_layer_vram = None
|
||||
quant_clean = quant_version.replace("_0", "").replace("_1", "").lower()
|
||||
if quant_version in per_layer_dict and per_layer_dict[quant_version] is not None:
|
||||
per_layer_vram = per_layer_dict[quant_version]
|
||||
elif quant_clean in per_layer_dict and per_layer_dict[quant_clean] is not None:
|
||||
per_layer_vram = per_layer_dict[quant_clean]
|
||||
else:
|
||||
bf16_per_layer = per_layer_dict.get("bf16")
|
||||
if bf16_per_layer is None:
|
||||
bf16_total = info["bf16_total_gb"]
|
||||
bf16_per_layer = bf16_total / layers
|
||||
actual_file_size_gb = os.path.getsize(model_path) / (1024**3)
|
||||
per_layer_vram = actual_file_size_gb / layers
|
||||
|
||||
return model_name, layers, per_layer_vram, quant_version
|
||||
|
||||
|
||||
def _auto_n_gpu_layers(model_path: str, verbose: bool = False) -> int:
|
||||
if not torch.cuda.is_available():
|
||||
return 0
|
||||
|
||||
total_vram_gb = torch.cuda.get_device_properties(0).total_memory / (1024**3)
|
||||
usable_vram_gb = max(0, total_vram_gb - 2.0)
|
||||
|
||||
model_name, max_layers, per_layer_vram, quant = _detect_model_info(model_path)
|
||||
if per_layer_vram <= 0:
|
||||
return 0
|
||||
|
||||
n_layers = int(usable_vram_gb / per_layer_vram)
|
||||
n_layers = max(0, min(max_layers, n_layers))
|
||||
|
||||
if verbose:
|
||||
print(f"[Mordant Enhancer] GPU VRAM: {total_vram_gb:.2f} GB, "
|
||||
f"Reserved: 2.00 GB, Usable: {usable_vram_gb:.2f} GB")
|
||||
print(f"[Mordant Enhancer] Model: {model_name}, Layers: {max_layers}, "
|
||||
f"Quant: {quant}, Per‑layer VRAM: {per_layer_vram:.4f} GB")
|
||||
print(f"[Mordant Enhancer] Auto‑selected GPU layers: {n_layers}")
|
||||
|
||||
return n_layers
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 3. Main node class – Mordant Prompt Enhancer
|
||||
# -------------------------------------------------------------------
|
||||
class MordantPromptEnhancer:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model": (_list_gguf_files(),),
|
||||
"text": ("STRING", {"multiline": True, "default": "Rewrite this image prompt conservatively."}),
|
||||
"mode": (["enhancer", "general"], {"default": "enhancer"}),
|
||||
"enable_enhancement": ("BOOLEAN", {"default": True}),
|
||||
"sampling_mode": ("BOOLEAN", {"default": True}),
|
||||
"max_new_tokens": ("INT", {"default": 8192, "min": 1, "max": 8192}),
|
||||
"seed": ("INT", {"default": 0, "min": -1, "max": 2147483647}),
|
||||
"temperature": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 5.0, "step": 0.05}),
|
||||
"top_p": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"top_k": ("INT", {"default": 50, "min": 0, "max": 1000}),
|
||||
"repeat_penalty": ("FLOAT", {"default": 1.0, "min": 1.0, "max": 2.0, "step": 0.01}),
|
||||
"n_batch": ("INT", {"default": 2048, "min": 32, "max": 4096, "step": 32}),
|
||||
"keep_loaded": ("BOOLEAN", {"default": False}),
|
||||
"offload_kqv": ("BOOLEAN", {"default": True}),
|
||||
"flash_attn": ("BOOLEAN", {"default": True}),
|
||||
"verbose": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
"optional": {
|
||||
"manual_model_path": ("STRING", {"default": "", "multiline": False}),
|
||||
"custom_system_prompt": ("STRING", {"default": "", "multiline": True}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING")
|
||||
RETURN_NAMES = ("composition", "thinking")
|
||||
FUNCTION = "generate"
|
||||
CATEGORY = "text/llm"
|
||||
|
||||
def generate(self, model, text, mode, enable_enhancement,
|
||||
sampling_mode, max_new_tokens, seed, temperature, top_p, top_k,
|
||||
repeat_penalty, n_batch, keep_loaded, offload_kqv, flash_attn,
|
||||
verbose, manual_model_path="", custom_system_prompt=""):
|
||||
|
||||
if not enable_enhancement:
|
||||
return (text, "")
|
||||
|
||||
model_path = _resolve_model_path(model, manual_model_path)
|
||||
system_prompt = (custom_system_prompt or "").strip()
|
||||
|
||||
n_ctx = 8192
|
||||
n_threads = os.cpu_count() or 4
|
||||
n_gpu_layers = _auto_n_gpu_layers(model_path, verbose=verbose)
|
||||
|
||||
if not sampling_mode:
|
||||
eff_temp, eff_top_p, eff_top_k, eff_repeat_penalty = 0.2, 0.9, 20, max(repeat_penalty, 1.05)
|
||||
else:
|
||||
eff_temp, eff_top_p, eff_top_k, eff_repeat_penalty = temperature, top_p, top_k, repeat_penalty
|
||||
|
||||
try:
|
||||
from llama_cpp import Llama
|
||||
except ImportError:
|
||||
raise RuntimeError("llama-cpp-python not installed.")
|
||||
|
||||
def _create_model():
|
||||
kwargs = dict(
|
||||
model_path=model_path,
|
||||
n_ctx=n_ctx,
|
||||
n_gpu_layers=n_gpu_layers,
|
||||
n_batch=n_batch,
|
||||
verbose=verbose,
|
||||
cuda_graphs=False,
|
||||
use_mmap=False,
|
||||
use_mlock=False,
|
||||
)
|
||||
if n_threads is not None:
|
||||
kwargs["n_threads"] = n_threads
|
||||
try:
|
||||
kwargs["offload_kqv"] = offload_kqv
|
||||
kwargs["flash_attn"] = flash_attn
|
||||
return Llama(**kwargs)
|
||||
except TypeError:
|
||||
kwargs.pop("offload_kqv", None)
|
||||
kwargs.pop("flash_attn", None)
|
||||
return Llama(**kwargs)
|
||||
|
||||
if keep_loaded:
|
||||
key = (model_path, n_ctx, n_gpu_layers, n_threads, n_batch, offload_kqv, flash_attn)
|
||||
with _CACHE_LOCK:
|
||||
llm = _CACHE.get(key)
|
||||
if llm is None:
|
||||
llm = _create_model()
|
||||
_CACHE[key] = llm
|
||||
else:
|
||||
llm = _create_model()
|
||||
|
||||
actual_family = "chatml"
|
||||
if hasattr(llm, 'metadata'):
|
||||
chat_template = llm.metadata.get('tokenizer.chat_template', '').lower()
|
||||
if 'granite' in chat_template or 'start_of_role' in chat_template:
|
||||
actual_family = "granite"
|
||||
|
||||
messages = _build_messages(system_prompt, text)
|
||||
prompt = _assemble_prompt(messages, actual_family)
|
||||
|
||||
out = llm.create_completion(
|
||||
prompt=prompt,
|
||||
max_tokens=max_new_tokens,
|
||||
temperature=eff_temp,
|
||||
top_p=eff_top_p,
|
||||
top_k=eff_top_k,
|
||||
repeat_penalty=eff_repeat_penalty,
|
||||
seed=None if seed < 0 else seed,
|
||||
)
|
||||
|
||||
result_text = out["choices"][0]["text"]
|
||||
if "</think>" in (result_text or ""):
|
||||
parts = (result_text or "").rsplit("</think>", 1)
|
||||
analysis_text, final_text = parts[0].strip(), parts[1].strip()
|
||||
else:
|
||||
analysis_text, final_text = "", (result_text or "").strip()
|
||||
|
||||
return (final_text, analysis_text)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 4. Node registration
|
||||
# -------------------------------------------------------------------
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"MordantPromptEnhancer": MordantPromptEnhancer,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"MordantPromptEnhancer": "Mordant Prompt Enhancer",
|
||||
}
|
||||
54
mordant-prompt-enhancer/Readme.txt
Normal file
54
mordant-prompt-enhancer/Readme.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
Just place the Mordant-Prompt-Enhancer file in your ComfyUI custom_nodes folder.
|
||||
You can see in the example workflow how to connect everything. And the default settings should be good, and you can experiment after.
|
||||
The node looks for the models in the /models/text_encoders forlders, so place the mordant gguf there.
|
||||
|
||||
What does the mode do?
|
||||
|
||||
Enhancer is your daily prompt enhancer mode, general is more of a llm.
|
||||
|
||||
enable_enhancement toggle, basically turns the node on and off, for when you want to just use your own un-altered prompt.
|
||||
|
||||
Everything else is fairly obvious, if not, you should just leave it as is. Keep_loaded is if you have enough vram to keep the model loaded after generation, otherwise, by default the node unloads it.
|
||||
|
||||
|
||||
**Installation**
|
||||
Place the node file in ComfyUI's `custom_nodes/` folder. The only additional dependency is `llama-cpp-python`.
|
||||
|
||||
**CPU‑only** (no GPU offloading):
|
||||
```bat
|
||||
# Windows portable
|
||||
python_embeded\python.exe -m pip install llama-cpp-python
|
||||
```
|
||||
```bash
|
||||
# Linux / venv / Mac
|
||||
pip install llama-cpp-python
|
||||
```
|
||||
|
||||
**GPU / CUDA — recommended** (pre‑built wheels, no compiler required):
|
||||
```bat
|
||||
# Windows portable — replace cu121 with your CUDA version
|
||||
python_embeded\python.exe -m pip install llama-cpp-python --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu121
|
||||
```
|
||||
```bash
|
||||
# Linux / venv — replace cu121 with your CUDA version
|
||||
pip install llama-cpp-python --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu121
|
||||
```
|
||||
> Supported versions: `cu121` · `cu122` · `cu124` · `cu125` — run `nvidia-smi` to check yours (top‑right of output).
|
||||
|
||||
<details>
|
||||
<summary>Build from source (advanced — only if wheels fail)</summary>
|
||||
|
||||
Requires CUDA Toolkit, CMake, and C++ build tools (Visual Studio on Windows).
|
||||
|
||||
```bat
|
||||
# Windows
|
||||
set CMAKE_ARGS=-DGGML_CUDA=on
|
||||
python_embeded\python.exe -m pip install llama-cpp-python --force-reinstall --no-cache-dir
|
||||
```
|
||||
```bash
|
||||
# Linux
|
||||
CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python --force-reinstall --no-cache-dir
|
||||
```
|
||||
</details>
|
||||
|
||||
After restarting ComfyUI, the node appears under `text/llm` as **Mordant Prompt Enhancer**. Just pick your model and start composing.
|
||||
757
mordant-prompt-enhancer/Simple Example Workflow.json
Normal file
757
mordant-prompt-enhancer/Simple Example Workflow.json
Normal file
@@ -0,0 +1,757 @@
|
||||
{
|
||||
"id": "8e195d44-7179-4dfd-b08d-635cff8984d8",
|
||||
"revision": 0,
|
||||
"last_node_id": 37,
|
||||
"last_link_id": 51,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 2,
|
||||
"type": "LoaderGGUFAdvanced",
|
||||
"pos": [
|
||||
22242.338811590744,
|
||||
11999.450597519388
|
||||
],
|
||||
"size": [
|
||||
270.8125,
|
||||
203.96875
|
||||
],
|
||||
"flags": {
|
||||
"pinned": true
|
||||
},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
37
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoaderGGUFAdvanced"
|
||||
},
|
||||
"widgets_values": [
|
||||
"QwenImg\\flux2-dev-Q5_K_M.gguf",
|
||||
"float32",
|
||||
"float32",
|
||||
false
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "CLIPLoader",
|
||||
"pos": [
|
||||
22242.338811590744,
|
||||
12239.450597519388
|
||||
],
|
||||
"size": [
|
||||
270.4375,
|
||||
187.03125
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false,
|
||||
"pinned": true
|
||||
},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"showAdvanced": true,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
34,
|
||||
42
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"mistral_3_small_flux2_fp8.safetensors",
|
||||
"flux2",
|
||||
"default"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "VAELoaderKJ",
|
||||
"pos": [
|
||||
22242.338811590744,
|
||||
12459.450597519388
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
175.96875
|
||||
],
|
||||
"flags": {
|
||||
"pinned": true
|
||||
},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
22,
|
||||
24,
|
||||
47
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAELoaderKJ"
|
||||
},
|
||||
"widgets_values": [
|
||||
"flux2-vae.safetensors",
|
||||
"main_device",
|
||||
"fp32"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "TextEncodeQwenImageEdit",
|
||||
"pos": [
|
||||
22560,
|
||||
13050
|
||||
],
|
||||
"size": [
|
||||
287.875,
|
||||
184.40625
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false,
|
||||
"pinned": true
|
||||
},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 42
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"shape": 7,
|
||||
"type": "VAE",
|
||||
"link": 47
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"shape": 7,
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "prompt"
|
||||
},
|
||||
"link": 51
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
41
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Positive",
|
||||
"properties": {
|
||||
"Node name for S&R": "TextEncodeQwenImageEdit"
|
||||
},
|
||||
"widgets_values": [
|
||||
"A painterly strong graphic novel illustration of a dynamic combat scene set within the catacombs of an abandoned cathedral during the Warhammer 40k universe. The composition is rendered with expressive brushwork that emphasizes the gritty texture of the environment against the warm glow of the setting sun.\n\n**Foreground Action:**\nThe lower section of the frame is dominated by the massive, red sash of a leviathan-clad warrior, its fabric rippling in the wind. He leans forward with aggressive momentum, his body angled slightly toward the viewer but his gaze fixed on the enemy ahead. His heavy, gold-trimmed leather armor is detailed with heavy pauldrons featuring intricate black and red sigils that catch the light. On his back, a large, segmented leviathan head—black bone and skin with glowing red eyes—recedes into the frame, its mouth open in a roar or claw strike. The texture of his leather is rough-hewn, with visible brushstrokes that emphasize the weight of his armor.\n\n**Mid-Ground Action:**\nTo the left, a towering figure in heavy, black leather armor with glowing red sigils on his chest stands in a dynamic pose. His massive, horned headpiece with jagged edges and a glowing red sigil on the forehead is a focal point of the scene. He holds a massive, glowing green weapon (the Leviathan's staff) diagonally across his body, the energy of the weapon rendered as distinct, pulsating lines that contrast sharply with the cool tones of his armor. The light from the weapon casts sharp, defined shadows on his face and chest, while the dust particles in the air catch the light, adding depth to the scene.\n\n**Background and Atmosphere:**\nThe upper portion of the image is defined by the cathedral's towering, gothic architecture with massive, arched windows that reveal a bright, hazy sky. The sun's rays, captured as distinct, glowing beams, slice through the dust and smoke, illuminating the dust motes floating in the air. The light creates a strong contrast between the warm, golden tones of the sunlit areas and the cool, purple shadows of the catacombs. The architecture is detailed with stone arches and windows that frame the action, their edges catching the light in sharp, defined points.\n\n**Lighting and Atmosphere:**\nThe lighting is high-contrast, with a golden sunbeam (god rays) cutting through the dust, creating a dramatic, almost theatrical illumination. The color palette is dominated by warm reds, golds, and deep purples, with the red sash and weapon providing a sharp visual anchor. The atmosphere is thick with dust and smoke, giving the scene a gritty, cinematic quality. The texture of the air is palpable, with the light beams creating a sense of volume and movement, while the stone walls of the cathedral are rendered with rough, detailed brushwork that emphasizes their ancient, crumbling nature."
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "TextEncodeQwenImageEdit",
|
||||
"pos": [
|
||||
22871.35154241452,
|
||||
13052.572534054745
|
||||
],
|
||||
"size": [
|
||||
280.9375,
|
||||
178.25
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false,
|
||||
"pinned": true
|
||||
},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 34
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"shape": 7,
|
||||
"type": "VAE",
|
||||
"link": 22
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"shape": 7,
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
8
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Negative",
|
||||
"properties": {
|
||||
"Node name for S&R": "TextEncodeQwenImageEdit"
|
||||
},
|
||||
"widgets_values": [
|
||||
"低分辨率,低画质,肢体畸形,手指畸形,画面过饱和,蜡像感,人脸无细节,过度光滑,画面具有AI感。构图混乱。文字模糊,扭曲\n"
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
23187.038623038603,
|
||||
13019.460469625074
|
||||
],
|
||||
"size": [
|
||||
338.5625,
|
||||
316.65625
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false,
|
||||
"pinned": true
|
||||
},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 37
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 41
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 8
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 12
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
9
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
906386284359144,
|
||||
"fixed",
|
||||
16,
|
||||
6,
|
||||
"euler",
|
||||
"simple",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
23570.694240662328,
|
||||
13334.885792891377
|
||||
],
|
||||
"size": [
|
||||
225,
|
||||
7.328125
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": true,
|
||||
"pinned": true
|
||||
},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 9
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 24
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
11
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "SaveImage",
|
||||
"pos": [
|
||||
23545.778409193183,
|
||||
13017.942215410685
|
||||
],
|
||||
"size": [
|
||||
264.78125,
|
||||
315.328125
|
||||
],
|
||||
"flags": {
|
||||
"pinned": true
|
||||
},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 11
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "SaveImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"ComfyUI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "EmptySD3LatentImage",
|
||||
"pos": [
|
||||
22242.338811590744,
|
||||
12669.450597519388
|
||||
],
|
||||
"size": [
|
||||
264.34375,
|
||||
143.328125
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false,
|
||||
"pinned": true
|
||||
},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
12
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptySD3LatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
1680,
|
||||
720,
|
||||
1
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"type": "PreviewAny",
|
||||
"pos": [
|
||||
22980,
|
||||
11990
|
||||
],
|
||||
"size": [
|
||||
746.09375,
|
||||
310.625
|
||||
],
|
||||
"flags": {
|
||||
"pinned": true
|
||||
},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "source",
|
||||
"type": "*",
|
||||
"link": 49
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"title": "Thinking",
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewAny",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.19.0",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.7",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"color": "#223",
|
||||
"bgcolor": "#335"
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
"type": "PreviewAny",
|
||||
"pos": [
|
||||
22980,
|
||||
12360
|
||||
],
|
||||
"size": [
|
||||
759.1875,
|
||||
600.34375
|
||||
],
|
||||
"flags": {
|
||||
"pinned": true
|
||||
},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "source",
|
||||
"type": "*",
|
||||
"link": 48
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"title": "Composition",
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewAny",
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.19.0",
|
||||
"enableTabs": false,
|
||||
"tabWidth": 65,
|
||||
"tabXOffset": 10,
|
||||
"hasSecondTab": false,
|
||||
"secondTabText": "Send Back",
|
||||
"secondTabOffset": 80,
|
||||
"secondTabWidth": 65,
|
||||
"ue_properties": {
|
||||
"widget_ue_connectable": {},
|
||||
"version": "7.7",
|
||||
"input_ue_unconnectable": {}
|
||||
}
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"color": "#223",
|
||||
"bgcolor": "#335"
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"type": "MordantPromptEnhancer",
|
||||
"pos": [
|
||||
22587.859813643525,
|
||||
12162.405451552098
|
||||
],
|
||||
"size": [
|
||||
353.96875,
|
||||
728.65625
|
||||
],
|
||||
"flags": {
|
||||
"pinned": true
|
||||
},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "composition",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
48,
|
||||
51
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "thinking",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
49
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MordantPromptEnhancer"
|
||||
},
|
||||
"widgets_values": [
|
||||
"models\\Kezmark\\Mordant-1.2B-BF16\\Mordant-1.2B-BF16.gguf",
|
||||
"",
|
||||
"enhancer",
|
||||
true,
|
||||
true,
|
||||
8192,
|
||||
325853571,
|
||||
"randomize",
|
||||
0.7,
|
||||
0.95,
|
||||
50,
|
||||
1,
|
||||
2048,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
"",
|
||||
""
|
||||
],
|
||||
"color": "#223",
|
||||
"bgcolor": "#335"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
8,
|
||||
9,
|
||||
0,
|
||||
10,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
9,
|
||||
10,
|
||||
0,
|
||||
13,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
11,
|
||||
13,
|
||||
0,
|
||||
14,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
12,
|
||||
15,
|
||||
0,
|
||||
10,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
22,
|
||||
5,
|
||||
0,
|
||||
9,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
24,
|
||||
5,
|
||||
0,
|
||||
13,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
34,
|
||||
4,
|
||||
0,
|
||||
9,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
37,
|
||||
2,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
41,
|
||||
7,
|
||||
0,
|
||||
10,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
42,
|
||||
4,
|
||||
0,
|
||||
7,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
47,
|
||||
5,
|
||||
0,
|
||||
7,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
48,
|
||||
37,
|
||||
0,
|
||||
36,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
49,
|
||||
37,
|
||||
1,
|
||||
35,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
51,
|
||||
37,
|
||||
0,
|
||||
7,
|
||||
3,
|
||||
"STRING"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Loaders",
|
||||
"bounding": [
|
||||
22232.338811590744,
|
||||
11929.450597519388,
|
||||
290.8125,
|
||||
893.328125
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Prompt",
|
||||
"bounding": [
|
||||
22550,
|
||||
12980,
|
||||
612.2890424145189,
|
||||
264.421875
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Prompt Enhancer",
|
||||
"bounding": [
|
||||
22570,
|
||||
11920,
|
||||
1187.4399139007546,
|
||||
1055.7277089007566
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.31863081771035934,
|
||||
"offset": [
|
||||
-21670.282977170747,
|
||||
-11392.588403980808
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.44.19",
|
||||
"VHS_latentpreview": false,
|
||||
"VHS_latentpreviewrate": 0,
|
||||
"VHS_MetadataImage": true,
|
||||
"VHS_KeepIntermediate": true
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user