Workflow1111 / app.py
ysharma's picture
ysharma HF Staff
Workflow1111 β€” Automatic1111-style diffusion studio on gr.Workflow
65865a4 verified
Raw History Blame Contribute Delete
5.54 kB
"""
Workflow1111 Β· a Diffusion Studio built entirely from `gr.Workflow`
==================================================================
An Automatic1111-shaped image studio expressed as **one canvas graph** instead
of a tabbed UI β€” 73 nodes across 11 pipelines, every one of them runnable on
its own.
python apps/05_workflow1111/app.py
What's on the canvas
--------------------
1 txt2img prompt/style builders β†’ FLUX.1-schnell β†’ post-processing
with the real control surface: negative prompt, steps,
CFG, seed, and size presets
2 Hires fix the txt2img result re-rendered through FLUX.1-Kontext
3 img2img upload an image, edit it by instruction
4 Prompt magic an LLM writes a prompt from a rough idea
5 Interrogate recover a prompt from an image (VLM) + classify it (ViT)
6 Detect & mask DETR boxes β†’ annotated preview β†’ inpainting mask
7 Prompt matrix four variants rendered in parallel into an X/Y grid
8 Extras local upscale, AuraSR Γ—4, background removal
9 Annotators Canny / line art / sketch / luma-depth previews
10 PNG Info read generation parameters back out of a file
11 img2video that same PNG animated into a ~3s clip by Wan 2.2 I2V
A14B on Inference Providers, with a motion/camera prompt
builder and real length/steps/guidance/seed/resolution
Why it is shaped like this
--------------------------
`gr.Workflow` raises if constructed inside a `gr.Blocks` context, so an app
like this genuinely cannot be tabs β€” the graph *is* the UI. Each output
("subject") is independently runnable and is also published as an API endpoint,
which is the closest analogue to A1111's tabs.
Five design rules came out of probing gradio 6.22 / huggingface_hub 1.26
directly, and the app depends on all five (details in `nodes.py` and README):
β€’ A `model` node's ports get rewritten to the endpoint's canonical schema the
moment a browser loads the graph β€” and the file is saved back that way. So
anything needing a richer control surface than the schema (`txt2img`,
`chat_llm`, `interrogate`, `img2video`) is an `fn` node calling
`InferenceClient` itself. `build_workflow.py` refuses to build if a `model`
node's ports ever diverge from its schema again.
β€’ `fn` nodes emit images as ``{"path": <file>, "url": <data: URI>}``. The
REST endpoint's `gr.Image` component needs a real file (a bare ``data:``
URI is read as a *filename*); the canvas and a chained `model` node need
the URI. Both keys, one value.
β€’ Video is the exception to that: `_emit_video` returns
``{"path", "url": "/gradio_api/file=…", "is_file": True}`` β€” gradio's own
`_save_tmp` shape β€” because an mp4 is megabytes and base64-inlining one
into the graph value would bloat every canvas update carrying it.
β€’ A `model` image output is never wired straight into another `model`'s image
port; `prep_image` sits between them (gradio would otherwise hand the
provider an unresolvable ``/gradio_api/file=`` path).
β€’ `space` nodes only ever take an *uploaded* image.
22 of the 32 `fn` nodes are pure local Pillow/numpy β€” no token, no quota, no
network β€” so most of the app keeps working even when a provider is having a bad
day. 14 nodes in total leave the machine.
Setup
-----
hf auth login # or: set HF_TOKEN=hf_xxx
pip install -r apps/05_workflow1111/requirements.txt
Tests (no network, ~2s): python apps/05_workflow1111/test_nodes.py
Live pipelines (hits HF): python apps/05_workflow1111/test_pipelines.py
REST endpoints: python apps/05_workflow1111/test_api.py
Regenerate the graph: python apps/05_workflow1111/build_workflow.py
Deploy to a Space: python apps/05_workflow1111/deploy_space.py --push
"""
import os
import sys
import gradio as gr
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import nodes # noqa: E402
# API/MCP callers authenticate per request with an `X-HF-Token` header (see
# `nodes._hf_token`). The four `model`/`space` nodes are run by gradio itself,
# whose `gradio.workflow._resolve_token` only knows OAuth sessions and the local
# write-token β€” so extend it here to fall back to the caller's header token.
import gradio.workflow as _gw # noqa: E402
_orig_resolve_token = _gw._resolve_token
def _resolve_token_with_header(data, idx, token, request=None):
return _orig_resolve_token(data, idx, token, request) or nodes._caller_hf_token(request)
_gw._resolve_token = _resolve_token_with_header
HERE = os.path.dirname(os.path.abspath(__file__))
WORKFLOW = os.path.join(HERE, "workflow.json")
if not os.path.exists(WORKFLOW):
raise SystemExit(
f"{WORKFLOW} is missing.\n"
"Generate it with: python apps/05_workflow1111/build_workflow.py"
)
demo = gr.Workflow(WORKFLOW, bind=nodes.BIND)
if __name__ == "__main__":
from huggingface_hub import get_token
if not (get_token() or os.environ.get("HF_TOKEN")):
print(
"\n ⚠ No Hugging Face token found.\n"
" The 19 local `fn` nodes (post-processing, annotators, masks,\n"
" contact sheet, PNG info) work regardless, but every `model`\n"
" and `space` node will fail until you run `hf auth login`,\n"
" set HF_TOKEN, or sign in from inside the app.\n"
)
demo.launch(mcp_server=True)