Files
comfy-discord-web/commands/workflow_changes.py
Khoa (Revenovich) Tran Gia 1ed3c9ec4b Initial commit — ComfyUI Discord bot + web UI
Full source for the-third-rev: Discord bot (discord.py), FastAPI web UI
(React/TS/Vite/Tailwind), ComfyUI integration, generation history DB,
preset manager, workflow inspector, and all supporting modules.

Excluded from tracking: .env, invite_tokens.json, *.db (SQLite),
current-workflow-changes.json, user_settings/, presets/, logs/,
web-static/ (build output), frontend/node_modules/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 09:55:48 +07:00

253 lines
8.5 KiB
Python

"""
commands/workflow_changes.py
============================
Workflow override management commands for the Discord ComfyUI bot.
Works with any NodeInput.key discovered by WorkflowInspector — not just
the four original hard-coded keys. Backward-compat aliases are preserved:
``type:prompt``, ``type:negative_prompt``, ``type:input_image``,
``type:seed``.
"""
from __future__ import annotations
import logging
from discord.ext import commands
from config import ARG_TYPE_KEY
from discord_utils import require_comfy_client
logger = logging.getLogger(__name__)
def setup_workflow_changes_commands(bot):
"""Register workflow changes commands with the bot."""
@bot.command(
name="get-current-workflow-changes",
aliases=["getworkflowchanges", "gcwc"],
extras={"category": "Workflow"},
)
@require_comfy_client
async def get_current_workflow_changes_command(
ctx: commands.Context, *, args: str = ""
) -> None:
"""
Show current workflow override values.
Usage::
ttr!get-current-workflow-changes type:all
ttr!get-current-workflow-changes type:prompt
ttr!get-current-workflow-changes type:<any_override_key>
"""
try:
overrides = bot.comfy.state_manager.get_overrides()
if ARG_TYPE_KEY not in args:
await ctx.reply(
f"Use `{ARG_TYPE_KEY}all` to see all overrides, or "
f"`{ARG_TYPE_KEY}<key>` for a specific key.",
mention_author=False,
)
return
param = args.split(ARG_TYPE_KEY, 1)[1].strip().lower()
if param == "all":
if not overrides:
await ctx.reply("No overrides set.", mention_author=False)
return
lines = [f"**{k}**: `{v}`" for k, v in sorted(overrides.items())]
await ctx.reply(
"Current overrides:\n" + "\n".join(lines),
mention_author=False,
)
else:
# Support multi-word value with the key as prefix
key = param.split()[0] if " " in param else param
val = overrides.get(key)
if val is None:
await ctx.reply(
f"Override `{key}` is not set.",
mention_author=False,
)
else:
await ctx.reply(
f"**{key}**: `{val}`",
mention_author=False,
)
except Exception as exc:
logger.exception("Failed to get workflow overrides")
await ctx.reply(f"An error occurred: {type(exc).__name__}: {exc}", mention_author=False)
@bot.command(
name="set-current-workflow-changes",
aliases=["setworkflowchanges", "scwc"],
extras={"category": "Workflow"},
)
@require_comfy_client
async def set_current_workflow_changes_command(
ctx: commands.Context, *, args: str = ""
) -> None:
"""
Set a workflow override value.
Supports any NodeInput.key discovered by WorkflowInspector as well
as the legacy fixed keys.
Usage::
ttr!set-current-workflow-changes type:<key> <value>
Examples::
ttr!scwc type:prompt A beautiful landscape
ttr!scwc type:negative_prompt blurry
ttr!scwc type:input_image my_image.png
ttr!scwc type:steps 30
ttr!scwc type:cfg 7.5
ttr!scwc type:seed 42
"""
try:
if not args or ARG_TYPE_KEY not in args:
await ctx.reply(
f"Usage: `ttr!set-current-workflow-changes {ARG_TYPE_KEY}<key> <value>`",
mention_author=False,
)
return
rest = args.split(ARG_TYPE_KEY, 1)[1]
# Key is the first word; value is everything after the first space
parts = rest.split(None, 1)
if len(parts) < 2:
await ctx.reply(
"Please provide both a key and a value. "
f"Example: `ttr!scwc {ARG_TYPE_KEY}prompt A cat`",
mention_author=False,
)
return
key = parts[0].strip().lower()
raw_value: str = parts[1].strip()
if not key:
await ctx.reply("Key cannot be empty.", mention_author=False)
return
# Type-coerce well-known numeric keys
_int_keys = {"steps", "width", "height"}
_float_keys = {"cfg", "denoise"}
_seed_keys = {"seed", "noise_seed"}
value: object = raw_value
try:
if key in _int_keys:
value = int(raw_value)
elif key in _float_keys:
value = float(raw_value)
elif key in _seed_keys:
value = int(raw_value)
except ValueError:
await ctx.reply(
f"Invalid value for `{key}`: expected a number, got `{raw_value}`.",
mention_author=False,
)
return
bot.comfy.state_manager.set_override(key, value)
await ctx.reply(
f"Override **{key}** set to `{value}`.",
mention_author=False,
)
except Exception as exc:
logger.exception("Failed to set workflow override")
await ctx.reply(f"An error occurred: {type(exc).__name__}: {exc}", mention_author=False)
@bot.command(
name="clear-workflow-change",
aliases=["clearworkflowchange", "cwc"],
extras={"category": "Workflow"},
)
@require_comfy_client
async def clear_workflow_change_command(
ctx: commands.Context, *, args: str = ""
) -> None:
"""
Remove a single override key.
Usage::
ttr!clear-workflow-change type:<key>
"""
try:
if ARG_TYPE_KEY not in args:
await ctx.reply(
f"Usage: `ttr!clear-workflow-change {ARG_TYPE_KEY}<key>`",
mention_author=False,
)
return
key = args.split(ARG_TYPE_KEY, 1)[1].strip().lower()
bot.comfy.state_manager.delete_override(key)
await ctx.reply(f"Override **{key}** cleared.", mention_author=False)
except Exception as exc:
logger.exception("Failed to clear override")
await ctx.reply(f"An error occurred: {type(exc).__name__}: {exc}", mention_author=False)
@bot.command(
name="set-seed",
aliases=["setseed"],
extras={"category": "Workflow"},
)
@require_comfy_client
async def set_seed_command(ctx: commands.Context, *, args: str = "") -> None:
"""
Pin a specific seed for deterministic generation.
Usage::
ttr!set-seed 42
"""
seed_str = args.strip()
if not seed_str:
await ctx.reply("Usage: `ttr!set-seed <number>`", mention_author=False)
return
if not seed_str.isdigit():
await ctx.reply("Seed must be a non-negative integer.", mention_author=False)
return
seed_val = int(seed_str)
max_seed = 2 ** 32 - 1
if seed_val > max_seed:
await ctx.reply(f"Seed must be between 0 and {max_seed}.", mention_author=False)
return
try:
bot.comfy.state_manager.set_seed(seed_val)
await ctx.reply(f"Seed pinned to `{seed_val}`.", mention_author=False)
except Exception as exc:
logger.exception("Failed to set seed")
await ctx.reply(f"An error occurred: {type(exc).__name__}: {exc}", mention_author=False)
@bot.command(
name="clear-seed",
aliases=["clearseed"],
extras={"category": "Workflow"},
)
@require_comfy_client
async def clear_seed_command(ctx: commands.Context) -> None:
"""
Clear the pinned seed and return to random generation.
Usage::
ttr!clear-seed
"""
try:
bot.comfy.state_manager.clear_seed()
await ctx.reply("Seed cleared; generation will now use random seeds.", mention_author=False)
except Exception as exc:
logger.exception("Failed to clear seed")
await ctx.reply(f"An error occurred: {type(exc).__name__}: {exc}", mention_author=False)