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>
This commit is contained in:
252
commands/workflow_changes.py
Normal file
252
commands/workflow_changes.py
Normal file
@@ -0,0 +1,252 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user