Compare commits

...
Author SHA1 Message Date
hanzo-dev e46945427d ci: bootstrap uv on runners that lack it 2026-07-03 22:19:36 -07:00
57bb837d35 fix(auth): route studio login through the server OIDC /callback flow (#5)
The SPA "Sign in with Hanzo" button hand-rolled an authorize URL
(IAM /login?redirect_uri=<studio root>), which the IAM default login app
(hanzo-console) rejected because the studio root is not in its allowed
redirect-URI list -- blocking all customer login.

Give the flow exactly one entry point: the button now links to the studio
server's own /login route, which runs the middleware's OIDC Authorization
Code flow (client_id=hanzo-studio, redirect_uri=<origin>/callback, PKCE +
signed state). /callback is already an allowed redirect URI, so login
completes and lands on /. Removes the client-side URL builder and the dead
{{IAM_URL}} templating.

Tests: /login is public; middleware exposes handle_login; unauth /login
builds the hanzo-studio authorize URL to /callback; post-login return path is /.

Co-authored-by: hanzo-dev <dev@hanzo.ai>
2026-07-03 21:38:48 -07:00
hanzo-dev 99c3d53d8b studio: seed 25-tab fashion workspace idempotently (guard v3)
Move the workspace-tab seeder out of a hand-edited index.html and into a
dedicated branding patcher wired into apply-branding.sh, so it survives a
frontend-package reinstall and re-applies on every branding pass.

Root cause the seeder addresses: the frontend restores open tabs from
localStorage via getStorageValue/setStorageValue, whose clientId-suffixed
sessionStorage layer is read BEFORE plain localStorage. A previously-saved
1-tab shadow (Comfy.OpenWorkflowsPaths:<clientId>) masked the seed, and the
old guard was burned at v2 so existing profiles never re-seeded.

Fix: bump guard v2->v3 (existing profiles re-seed once) and purge the stale
sessionStorage shadows on re-seed so the fresh localStorage seed wins even on
a same-tab reload. Clean-profile boot now opens all 25 fashion workflows
(alongside ComfyUI's own default tab).
2026-07-03 20:49:21 -07:00
hanzo-dev 4ab38a1f06 studio: crash-durable SQLite render queue + engine selector
SqlitePromptQueue (middleware/tasks_queue.py): drop-in for PromptQueue backed
by one SQLite file (stdlib sqlite3, WAL, zero external processes). Opt in with
STUDIO_QUEUE_DB; precedence STUDIO_QUEUE_DB > STUDIO_PERSIST_QUEUE > memory so
local default is unchanged. The put/get/task_done seam maps to a durable job
state machine: idempotent submit (INSERT OR IGNORE on prompt_id), exactly-once
claim (BEGIN IMMEDIATE), retry-on-error, and crash-recovery via a lease + reap
that re-runs an abandoned claim (idempotent — SaveImage suffixes increment).
Multiple Studio processes on the same DB compete safely.

Engine selector (middleware/engine_selector.py): GET /v1/engines lists execution
targets for the org (local + registered compute_config workers), PUT
/v1/engines/default sets a per-org default stored on ComputeProfile; route_prompt
honors it (local = unchanged). Leased cloud machines are a future engine class
(interface stubbed). Frontend picker is a TODO.

docs/federation.md rewritten to the durable-queue reality with one future
paragraph (remote Tasks backend + Go/unified-binary HIP-0106 migration + leased
machines). Tests: middleware_test/{tasks_queue,engine_selector}_test.py, incl.
crash-recovery across queue instances.
2026-07-03 15:43:42 -07:00
hanzo-dev cea6d8029e ci: target arc scale set hanzo-build-linux-amd64 by name
ci@v1 defaults runs-on to [self-hosted,linux,amd64]; ARC scale sets are
matched by installation name, so the job sat queued with no runner. Pin the
runner to the hanzo build pool (same as cloud/release.yml).
2026-07-03 15:27:03 -07:00
hanzo-dev e632401bc5 ci: drop legacy deploy.yml (superseded by cicd.yml); make ruff green
- Remove .github/workflows/deploy.yml — the canonical build+test+deploy path
  is cicd.yml (imports hanzoai/ci build.yml@v1, reads hanzo.yml). One way to
  build. The old Docker workflow called hanzoai/.github docker-build.yml and
  failed at 0s.
- prompt_queue_persist_test.py: drop the 4 diagnostic prints (T201); pytest
  reports pass/fail and the standalone runner raises on failure.
- zen_video_adapter.py: remove unused json/asyncio imports (F401).
2026-07-03 15:13:51 -07:00
hanzo-dev b521933ca3 fix(server): graceful SIGTERM/SIGINT shutdown + crash-durable queue
Root cause of the "Event loop stopped before Future completed" crash that
wiped the in-memory queue/history (seen 3x today): the fork's _graceful_shutdown
handler (added in a45a441) called event_loop.stop(). The server coroutine passed
to run_until_complete() never completes on its own, so stopping the loop makes
run_until_complete() raise RuntimeError; the surrounding try only caught
KeyboardInterrupt, so the process died non-zero with a traceback and discarded
the queue. The dirty exit also failed to release :8188 promptly, and a duplicate
transient/user `hanzo-studio.service` racing the canonical studio.service turned
that into an EADDRINUSE restart storm.

main.py:
- handler is now idempotent, flags the queue closed, snapshots it, then stops
  the loop; the outer try treats RuntimeError('Event loop stopped before Future
  completed.') as the expected graceful-exit path (logs "Server stopped by
  shutdown signal", exits 0). Verified live in journald.

execution.py:
- opt-in crash-durable PromptQueue behind STUDIO_PERSIST_QUEUE=1. Pending +
  in-flight prompts are snapshotted atomically to user/queue_snapshot.json on
  every change and re-queued on boot. Best-effort: all I/O is guarded so it
  never affects the render path; inert when the flag is unset.

ops/systemd/studio.service:
- enable STUDIO_PERSIST_QUEUE=1 on the single canonical unit.

scripts/studio-service-swap.sh:
- batch-safe reconcile+apply: waits for an empty queue, removes any stray
  duplicate unit, reinstalls the canonical unit, restarts, health-checks.

docs/ops-crash-and-service.md:
- topology (one canonical unit), how to read crash history, the fix, persistence.

tests-unit/execution_test/prompt_queue_persist_test.py:
- round-trip, inert-when-disabled, and corrupt-row-skipping tests.
2026-07-03 14:42:03 -07:00
hanzo-dev 748e335add studio: native IAM auth + multi-tenant isolation + GPU federation
Auth (middleware/iam_auth_middleware.py): local JWKS JWT validation against
Hanzo IAM (signature + exp + iss + aud=hanzo-studio; org from the `owner`
claim). Standard OIDC Authorization Code flow with PKCE and a /callback
session cookie for the standalone studio.hanzo.ai app; anonymous localhost
bypass unchanged. verify_jwt() extracted as a pure, unit-tested function.

Tenancy (folder_paths.py, app/user_manager.py): make get_public_user_directory
org-rooted so multi-tenant userdata resolves under user/orgs/{org}/... (it
previously always returned None). Scope execution outputs to output/orgs/{org}/
via set_execution_org(), bound by the prompt worker from the queue item org_id.
Also block a token subject from escaping into a System User namespace.

Federation (middleware/{worker_client,prompt_router}.py, server.py): move the
worker registry + execute surface to /v1/workers/register, /v1/workers,
/v1/worker/execute; add X-Worker-Token coordinator trust. Outbound pull channel
for NAT'd local boxes (GB10) specced in docs/federation.md.

Deploy: hanzo.yml + .github/workflows/cicd.yml (hanzoai/ci reusable) build
ghcr.io/hanzoai/studio and roll the studio operator Service CR at
studio.hanzo.ai. PyJWT[crypto] pinned.

Tests: tests-unit/middleware_test/iam_auth_test.py (RS256 sign/verify, state
CSRF, path exemptions) + folder_paths_test/org_scoping_test.py; 140 passing
across the auth/tenancy/user-manager/prompt-server suites.
2026-07-03 14:38:19 -07:00
hanzo-dev 8d74ce9fca model-shot workflows: anoeses-style calm luxury (pure white, front/back poses, pose menu in notes) 2026-07-03 13:20:59 -07:00
hanzo-dev 7fc06c38c9 website catalog architecture: no-body product base + dynamic-model hover variants (8 designs) + workspace seeder
Anoeses-style PDP pattern: product_<design> = garment-only ghost presentation on pure white (base image); hover_<design> = model in dynamic pose on subtle white studio (rollover image). Workspace: index.html seeds Comfy.OpenWorkflowsPaths with all 25 fashion tabs on first load (set-once, localStorage); Comfy.Workflow.Persist on. Seeder added to apply-branding.sh replay path.
2026-07-03 13:12:34 -07:00
hanzo-dev c2f99d072d fashion_product_shot: fix stale model note, point designers at per-design tabs 2026-07-03 12:49:03 -07:00
hanzo-dev e5132a2d7f fashion workflows: per-design shoot_/product_ sets with proven Qwen-Image-Edit-2511 recipe
17 workflows (8 lifestyle shoots + 8 white-seamless product shots + base CAD->lifestyle), each: FluxKontextImageScale 1MP ref, ModelSamplingAuraFlow 3.1, ref-method(index_timestep_zero) on pos+neg conditioning, CFGNorm, EmptySD3Latent 1024x1536, cfg 4.0, randomized seeds, per-design output dirs. UI-lane proof: shot_00004 matches scripted-lane quality (design-exact festival_fuchsia on beach). Rate limiter: default unlimited via STUDIO_RPM env (single-user box).
2026-07-03 12:27:05 -07:00
hanzo-dev ff0dee00a4 studio: add ready-to-run Antje CAD -> lifestyle workflow (Qwen-Image-Edit-2511)
Native TextEncodeQwenImageEditPlus path wired against the live /object_info
schema: LoadImage -> Plus encoder (image1+vae) + VAEEncode -> KSampler
(30 steps, cfg 2.5, euler/simple) -> VAEDecode -> SaveImage swimwear/antje.
Models installed under models/{diffusion_models,text_encoders,vae} (merged
from the Qwen-Image-Edit-2511 diffusers shards, gitignored). 8 of Antje's
CAD drawings preloaded in input/ and enumerated by LoadImage.
2026-07-03 09:10:22 -07:00
hanzo-dev ca9a72f2a7 studio: finish Hanzo rebrand — sidebar mark, black+purple theme, progress favicon
- Sidebar logo: replace the inline ComfyLogo Vue component (top-left mark,
  compiled into the JS bundle, missed by the file-asset pass) with the Hanzo
  mark via branding/patch_sidebar_logo.py (keyed off the stable __name:`ComfyLogo`).
- Theme: branding/hanzo-theme.css forces a pure-black shell + Hanzo-purple
  accents (AA contrast), injected as the last stylesheet; patch_theme.py also
  rewrites the dark palette's canvas clear color to #000000 (CSS can't reach the
  litegraph <canvas>).
- Favicon: make_progress_favicons.py renders the 10 progress frames with the
  Hanzo mark + purple ring, fixing the tab reverting to the Comfy mark during
  generations. Committed PNGs like favicon.ico (Docker needs no rasterizer).
- Tab title: rebrand the runtime ' - ComfyUI' title suffix the string pass missed.
- apply-branding.sh replays all of the above on frontend-package upgrades.
- Rename fork-owned update_comfyui{,_stable}.bat -> update{,_stable}.bat.
2026-07-03 02:20:33 -07:00
hanzo-dev 1ff6280992 Rebrand fork to Hanzo Studio: licensing, branding, Engine nodes, fashion workflows
Licensing (GPL-3.0 preserved):
- NOTICE attributes the ComfyUI upstream and the fork's modifications, GPL-3.0.
- README credits upstream ComfyUI prominently and states GPL-3.0.

Branding (frontend served from the comfyui-frontend-package static dir):
- apply-branding.sh installs the Hanzo favicon (svg + multi-res ico), injects
  icon links, sets <title>Hanzo Studio</title>, and patches display strings.
- make_favicon.py renders the committed favicon.ico from favicon.svg.

Hanzo Engine nodes (custom_nodes/hanzo_engine/, OpenAI-compatible HTTP):
- HanzoChat, HanzoImageGen, HanzoVisionCaption, HanzoSaveText. Graceful node
  errors on connection failure.

Fashion/swimwear starter workflows (user/default/workflows/fashion/):
- fashion_product_shot (FLUX.2 klein t2i), fashion_edit_garment
  (Qwen-Image-Edit i2i), fashion_caption_dataset (Hanzo vision caption loop).
  Each carries a Note node with usage and required files.
2026-07-02 23:17:57 -07:00
z a45a4413dd docs(brand): add hero banner 2026-06-28 20:06:25 -07:00
z 050911912f chore(brand): dynamic hero banner 2026-06-28 20:06:24 -07:00
a025d1236c fix(iam): migrate get-account to OIDC /v1/iam/oauth/userinfo (HIP-0111) (#4)
Co-authored-by: Zach Kelling <z@zeekay.io>
2026-06-24 19:16:12 -07:00
Antje WorringandClaude Opus 4.8 2ecd73d1f0 docs: tidy LLM.md indexes; CLAUDE.md -> LLM.md symlink convention
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 09:23:06 -07:00
Hanzo DevandGitHub 7b6b0115e8 ci: migrate to canonical hanzoai/.github/docker-build.yml reusable (#1) 2026-04-23 19:06:49 -07:00
Hanzo AI 3a30e79cfa chore: symlink AGENTS.md and CLAUDE.md to LLM.md
Canonical project context lives in LLM.md. Symlinks ensure
agentic coding tools (Claude Code, Cursor, etc.) find context
automatically regardless of which filename they look for.
2026-04-01 14:14:38 -07:00
Hanzo AI 5354c63777 ci: update GitHub Actions workflows 2026-03-03 14:00:00 -08:00
Hanzo Dev 110139fb28 fix: inline deploy with KMS auth (replace broken reusable workflow ref) 2026-03-12 00:13:02 -07:00
Hanzo Dev c24dee9a5e fix: use secrets inherit for reusable workflow 2026-03-12 00:10:50 -07:00
Hanzo Dev fcc30e1894 fix: use KMS Universal Auth for deploy (replace legacy HANZO_API_KEY) 2026-03-12 00:03:28 -07:00
Hanzo Dev 528f997fc4 ci: migrate deploy to HANZO_API_KEY + KMS via reusable workflow
Replace direct DO_API_TOKEN with reusable-deploy-service.yml from
hanzoai/universe that fetches credentials from KMS using HANZO_API_KEY.
2026-03-11 14:34:53 -07:00
Hanzo Dev 83a9fc6536 docs: add LLM.md project guide 2026-03-11 11:04:48 -07:00
Hanzo Dev bfee9e4b07 Redesign login page to monochrome style matching hanzo.ai
Black background, white text, opacity-based visual hierarchy,
pill-shaped CTA button, Inter font, feature cards with subtle
white/5-10% backgrounds. Removes red accent entirely.
2026-02-23 15:31:30 -08:00
Hanzo Dev dafd40026c Fix lint: remove unused imports in main.py and billing_middleware.py 2026-02-23 15:21:20 -08:00
Hanzo Dev ece384a3d0 Add GPU worker system, compute config API, and login gate
- Compute profiles: per-org CPU/GPU config stored in user dir with
  thread-safe CRUD and atomic file writes
- Prompt routing: forwards GPU prompts to registered workers, falls
  back to local execution when no worker available
- Worker mode: --worker-mode flag runs Studio as headless executor
  that registers with coordinator via heartbeats
- Visor integration: provision/terminate GPU VMs on AWS (T4/V100/A100/H100)
  with shell-safe startup scripts
- Login page: unauthenticated browsers see branded login gate with
  "Sign in with Hanzo" button instead of broken ComfyUI interface
- 6 new API endpoints under /api/compute/*
2026-02-23 15:17:59 -08:00
Hanzo Dev c43cab93ba Fix metrics endpoint content_type for aiohttp compatibility 2026-02-23 14:31:07 -08:00
Hanzo Dev 91654b973a Add metrics, billing, and rate limiting middleware
- /metrics endpoint (Prometheus text format) for VictoriaMetrics scraping
- Commerce billing integration: usage recording after prompt execution,
  balance check before accepting prompts (402 Payment Required)
- Per-org rate limiting on prompt submissions (default 60 rpm)
- All features toggled via STUDIO_* env vars for K8s deployment
- Metrics tracks: prompts total/active, queue depth, websocket connections,
  execution duration histogram, HTTP request counters
2026-02-23 14:17:24 -08:00
Hanzo Dev 8c9652bb78 Fix P0 production readiness issues
- Add /health and /ready endpoints for K8s probes
- Add SIGTERM graceful shutdown handler in main.py
- Fix IAM middleware: shared aiohttp session (not per-request),
  60s token validation cache, stale entry eviction
- Remove /api/system_stats from unauthenticated public paths
- Update .dockerignore to exclude tests, scripts, temp data
2026-02-23 13:17:32 -08:00
Hanzo Dev eb5935be5f Exclude build scripts from ruff linting
scripts/ and branding/ are build-time utilities, not runtime code.
2026-02-23 13:10:59 -08:00
Hanzo Dev 79f0bf36a1 Add IAM auth, multi-tenant storage, and fix frontend branding
- Fix branding patcher to target actual upstream logo filenames
  (comfy-logo-*.svg, not studio-logo-*.svg)
- Add more display string patterns to patch_frontend.py
- Add IAM authentication middleware (hanzo.id/api/get-account)
  with localhost bypass and browser login redirect
- Add multi-tenant org-scoped storage (folder_paths.get_org_*)
  with per-org output/input/temp/user/models directories
- Add CLI flags: --enable-iam-auth, --iam-url, --multi-tenant,
  --org-id, --no-localhost-bypass
- Support env var toggles for Docker: STUDIO_ENABLE_IAM_AUTH,
  STUDIO_MULTI_TENANT, STUDIO_ORG_ID, STUDIO_IAM_URL
- Integrate IAM user context into UserManager and server routes
2026-02-23 13:00:26 -08:00
Hanzo Dev 5cf6ce2508 Rename all internal modules: comfy* → studio*
Complete module rename across 552 files:
- comfy/ → studio/
- comfy_extras/ → studio_extras/
- comfy_api/ → studio_api/
- comfy_api_nodes/ → studio_api_nodes/
- comfy_config/ → studio_config/
- comfy_execution/ → studio_execution/
- comfy/comfy_types/ → studio/node_types/
- comfyui_version.py → studio_version.py
- All test directories renamed
- All import statements updated
- All internal identifiers renamed (ComfyNodeABC → StudioNodeABC, etc.)
- All wire protocol types renamed (COMFY_* → STUDIO_*)
- No backward compatibility shims — clean break
- External pip packages preserved (comfyui-frontend-package, comfy-kitchen, etc.)
- 518 Python files syntax-validated, 0 errors
2026-02-23 12:49:38 -08:00
Hanzo Dev fdb4b5aab8 Complete Hanzo Studio rebrand: purge all remaining ComfyUI references
99 files changed across the entire codebase:
- CLI help strings, error messages, and display text
- README.md fully rewritten for Hanzo Studio
- CONTRIBUTING.md, CODEOWNERS, issue templates updated
- All GitHub workflow references updated
- Default output filename prefixes (ComfyUI -> HanzoStudio)
- .ci/ batch files and READMEs for Windows portable builds
- Core file headers ("This file is part of Hanzo Studio")
- API docs URLs (docs.comfy.org -> docs.hanzo.ai)
- API base URL default (api.comfy.org -> api.hanzo.ai)
- comfy_extras node descriptions and default paths
- Test file comments and docstrings
- extra_model_paths.yaml.example config comments

Preserved as-is (would break imports/packages):
- Python package names (comfyui_frontend_package, comfyui_manager, etc.)
- Internal module paths (comfy/, comfy_extras/, comfy_api/, etc.)
- API contract values (AUTH_TOKEN_COMFY_ORG, etc.)
- Third-party source attribution URLs
- custom_nodes/ directory contents
2026-02-23 10:13:55 -08:00
Hanzo Dev 4f0ddab1b8 Refine branding patcher: remove catch-all Comfy-Org replacement
- Remove bare 'Comfy-Org' and 'ComfyOrg' from plain text replacements
  (was corrupting i18n keys like OpenComfy-OrgDiscord)
- Only replace Comfy-Org/ComfyOrg in multi-word display phrases
- Add regex patterns for quoted string value contexts
- Add standalone quoted value patterns ("ComfyUI" → "Hanzo Studio")
- Add Comfy Cloud → Hanzo Cloud display text replacement
2026-02-23 09:51:59 -08:00
Hanzo Dev ee7da07a4e Fix branding: use smart Python patcher instead of blind sed
The previous sed-based approach broke JavaScript by replacing code
identifiers (class names, import paths, i18n keys) alongside display
strings. The new Python patcher:

- Only replaces display strings within quoted contexts
- Safely replaces all URLs (comfy.org → hanzo.ai, Discord, GitHub)
- Preserves JS class names (ComfyUI, ComfyApp, etc.)
- Preserves CSS class names (comfyui-button, etc.)
- Preserves dynamic import paths (./ComfyOrgHeader-*.js)
- Preserves i18n keys while updating i18n values
- Recursively patches all files including extensions/
2026-02-23 09:46:46 -08:00
Hanzo Dev 4526acbfa6 Comprehensive rebrand: ComfyUI → Hanzo Studio
- Replace all user-facing "ComfyUI" strings with "Hanzo Studio"
- Update Help menu links: Discord → discord.gg/hanzoai, GitHub → hanzoai/studio
- Replace comfy.org URLs with hanzo.ai equivalents
- Update support email to support@hanzo.ai
- Rewrite branding script to patch ALL JS bundles comprehensively
- Update default filename prefixes from ComfyUI to HanzoStudio
- Fix CI to trigger on main branch (not hanzo/main)
- Update pyproject.toml metadata (name, URLs)
2026-02-23 09:32:01 -08:00
Hanzo Dev ee087ae50e Rebrand ComfyUI to Hanzo Studio
- Replace ComfyUI logo with Hanzo geometric H mark
- Replace favicon with Hanzo H favicon
- Patch index.html title, PWA manifest, and JS bundles
- Update AboutPanel and OrgHeader references
- Branding applied as Docker build step after pip install
2026-02-23 09:21:18 -08:00
Hanzo Dev e6873292a6 Add --cpu flag to ComfyUI startup for non-CUDA environments
ComfyUI's model_management crashes on startup without CUDA unless
--cpu flag is passed. GPU deployments will override CMD.
2026-02-23 01:54:06 -08:00
Hanzo Dev 54b02fac6c Fix Dockerfile: libgl1-mesa-glx obsoleted in Debian Trixie, use libgl1 2026-02-23 01:38:56 -08:00
Hanzo Dev cd55589499 Fix CI trigger branch to hanzo/main 2026-02-23 01:37:48 -08:00
Hanzo Dev d816711e01 Add Dockerfile, CI/CD, and .dockerignore for Hanzo Studio
ComfyUI deployment via GHCR + K8s on hanzo-k8s cluster.
CPU-only base image, GPU support via nvidia runtime at deploy.
2026-02-23 01:36:42 -08:00
comfyanonymousandGitHub f7a591ffa2 Fix issue loading fp8 ltxav checkpoints. (#12582) 2026-02-22 16:00:02 -05:00
comfyanonymousandGitHub 4e31960261 Fix dtype issue in embeddings connector. (#12570) 2026-02-22 03:18:20 -05:00
comfyanonymousandGitHub dd19d1b91a Move LTXAV av embedding connectors to diffusion model. (#12569) 2026-02-21 22:29:58 -05:00
Christian ByrneandGitHub 2c8d4e628f chore: tune CodeRabbit config to limit review scope and disable for drafts (#12567)
* chore: tune CodeRabbit config to limit review scope and disable for drafts

- Add tone_instructions to focus only on newly introduced issues
- Add global path_instructions entry to ignore pre-existing issues in moved/reformatted code
- Disable draft PR reviews (drafts: false) and add WIP title keywords
- Disable ruff tool to prevent linter-based outside-diff-range comments

Addresses feedback from maintainers about CodeRabbit flagging pre-existing
issues in code that was merely moved or de-indented (e.g., PR #12557),
which can discourage community contributions and cause scope creep.

Amp-Thread-ID: https://ampcode.com/threads/T-019c82de-0481-7253-ad42-20cb595bb1ba

* chore: add 'DO NOT MERGE' to ignore_title_keywords

Amp-Thread-ID: https://ampcode.com/threads/T-019c82de-0481-7253-ad42-20cb595bb1ba
2026-02-21 18:32:15 -08:00
Christian ByrneandGitHub 9668d4d48b Add category to Normalized Attention Guidance node (#12565) 2026-02-21 19:51:21 -05:00
664f312d50 fix: specify UTF-8 encoding when reading subgraph files (#12563)
On Windows, Python defaults to cp1252 encoding when no encoding is
specified. JSON files containing UTF-8 characters (e.g., non-ASCII
characters) cause UnicodeDecodeError when read with cp1252.

This fixes the error that occurs when loading blueprint subgraphs
on Windows systems.

https://claude.ai/code/session_014WHi3SL9Gzsi3U6kbSjbSb

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-21 15:05:00 -08:00
rattusandGitHub a8eac8219c comfy-aimdo 0.2 - Improved pytorch allocator integration (#12557)
Integrate comfy-aimdo 0.2 which takes a different approach to
installing the memory allocator hook. Instead of using the complicated
and buggy pytorch MemPool+CudaPluggableAlloctor, cuda is directly hooked
making the process much more transparent to both comfy and pytorch. As
far as pytorch knows, aimdo doesnt exist anymore, and just operates
behind the scenes.

Remove all the mempool setup stuff for dynamic_vram and bump the
comfy-aimdo version. Remove the allocator object from memory_management
and demote its use as an enablment check to a boolean flag.

Comfy-aimdo 0.2 also support the pytorch cuda async allocator, so
remove the dynamic_vram based force disablement of cuda_malloc and
just go back to the old settings of allocators based on command line
input.
2026-02-21 10:52:57 -08:00
pythongosssssandGitHub b777ae17bc add support for pyopengl < 3.1.4 where the size parameter does not exist (#12555) 2026-02-21 06:14:57 -08:00
9e4b3e8cd6 fix: swap essentials_category from CLIPTextEncode to PrimitiveStringMultiline (#12553)
Remove CLIPTextEncode from Basics essentials category and add
PrimitiveStringMultiline (String Multiline) in its place.

Amp-Thread-ID: https://ampcode.com/threads/T-019c7efb-d916-7244-8c43-77b615ba0622

Co-authored-by: Jedrzej Kosinski <kosinkadink1@gmail.com>
2026-02-20 23:46:46 -08:00
671 changed files with 12097 additions and 5367 deletions
+8
View File
@@ -0,0 +1,8 @@
@echo off
..\python_embeded\python.exe .\update.py ..\HanzoStudio\
if exist update_new.py (
move /y update_new.py update.py
echo Running updater again since it got updated.
..\python_embeded\python.exe .\update.py ..\HanzoStudio\ --skip_self_update
)
if "%~1"=="" pause
+3 -3
View File
@@ -47,7 +47,7 @@ def pull(repo, remote_name='origin', branch='master'):
pygit2.option(pygit2.GIT_OPT_SET_OWNER_VALIDATION, 0)
repo_path = str(sys.argv[1])
repo = pygit2.Repository(repo_path)
ident = pygit2.Signature('comfyui', 'comfy@ui')
ident = pygit2.Signature('hanzo-studio', 'studio@hanzo.ai')
try:
print("stashing current changes") # noqa: T201
repo.stash(ident)
@@ -153,8 +153,8 @@ if not os.path.exists(req_path) or not files_equal(repo_req_path, req_path):
pass
stable_update_script = os.path.join(repo_path, ".ci/update_windows/update_comfyui_stable.bat")
stable_update_script_to = os.path.join(cur_path, "update_comfyui_stable.bat")
stable_update_script = os.path.join(repo_path, ".ci/update_windows/update_studio_stable.bat")
stable_update_script_to = os.path.join(cur_path, "update_studio_stable.bat")
try:
if not file_size(stable_update_script_to) > 10:
-8
View File
@@ -1,8 +0,0 @@
@echo off
..\python_embeded\python.exe .\update.py ..\ComfyUI\
if exist update_new.py (
move /y update_new.py update.py
echo Running updater again since it got updated.
..\python_embeded\python.exe .\update.py ..\ComfyUI\ --skip_self_update
)
if "%~1"=="" pause
@@ -1,8 +0,0 @@
@echo off
..\python_embeded\python.exe .\update.py ..\ComfyUI\ --stable
if exist update_new.py (
move /y update_new.py update.py
echo Running updater again since it got updated.
..\python_embeded\python.exe .\update.py ..\ComfyUI\ --skip_self_update --stable
)
if "%~1"=="" pause
+8
View File
@@ -0,0 +1,8 @@
@echo off
..\python_embeded\python.exe .\update.py ..\HanzoStudio\ --stable
if exist update_new.py (
move /y update_new.py update.py
echo Running updater again since it got updated.
..\python_embeded\python.exe .\update.py ..\HanzoStudio\ --skip_self_update --stable
)
if "%~1"=="" pause
@@ -7,22 +7,19 @@ If you have a AMD gpu:
run_amd_gpu.bat
If you have memory issues you can try disabling the smart memory management by running comfyui with:
If you have memory issues you can try disabling the smart memory management by running Hanzo Studio with:
run_amd_gpu_disable_smart_memory.bat
IF YOU GET A RED ERROR IN THE UI MAKE SURE YOU HAVE A MODEL/CHECKPOINT IN: ComfyUI\models\checkpoints
IF YOU GET A RED ERROR IN THE UI MAKE SURE YOU HAVE A MODEL/CHECKPOINT IN: HanzoStudio\models\checkpoints
You can download the stable diffusion XL one from: https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/blob/main/sd_xl_base_1.0_0.9vae.safetensors
RECOMMENDED WAY TO UPDATE:
To update the ComfyUI code: update\update_comfyui.bat
To update the Hanzo Studio code: update\update_studio.bat
TO SHARE MODELS BETWEEN COMFYUI AND ANOTHER UI:
In the ComfyUI directory you will find a file: extra_model_paths.yaml.example
TO SHARE MODELS BETWEEN HANZO STUDIO AND ANOTHER UI:
In the HanzoStudio directory you will find a file: extra_model_paths.yaml.example
Rename this file to: extra_model_paths.yaml and edit it with your favorite text editor.
+1 -1
View File
@@ -1,2 +1,2 @@
.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build
.\python_embeded\python.exe -s HanzoStudio\main.py --windows-standalone-build
pause
@@ -1,2 +1,2 @@
.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build --disable-smart-memory
.\python_embeded\python.exe -s HanzoStudio\main.py --windows-standalone-build --disable-smart-memory
pause
@@ -1,2 +1,2 @@
.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build --fast
.\python_embeded\python.exe -s HanzoStudio\main.py --windows-standalone-build --fast
pause
@@ -15,20 +15,20 @@ run_cpu.bat
IF YOU GET A RED ERROR IN THE UI MAKE SURE YOU HAVE A MODEL/CHECKPOINT IN: ComfyUI\models\checkpoints
IF YOU GET A RED ERROR IN THE UI MAKE SURE YOU HAVE A MODEL/CHECKPOINT IN: HanzoStudio\models\checkpoints
You can download the stable diffusion 1.5 one from: https://huggingface.co/Comfy-Org/stable-diffusion-v1-5-archive/blob/main/v1-5-pruned-emaonly-fp16.safetensors
You can download the stable diffusion 1.5 one from: https://huggingface.co/hanzoai/stable-diffusion-v1-5-archive/blob/main/v1-5-pruned-emaonly-fp16.safetensors
RECOMMENDED WAY TO UPDATE:
To update the ComfyUI code: update\update_comfyui.bat
To update the Hanzo Studio code: update\update_studio.bat
To update ComfyUI with the python dependencies, note that you should ONLY run this if you have issues with python dependencies.
update\update_comfyui_and_python_dependencies.bat
To update Hanzo Studio with the python dependencies, note that you should ONLY run this if you have issues with python dependencies.
update\update_studio_and_python_dependencies.bat
TO SHARE MODELS BETWEEN COMFYUI AND ANOTHER UI:
In the ComfyUI directory you will find a file: extra_model_paths.yaml.example
TO SHARE MODELS BETWEEN HANZO STUDIO AND ANOTHER UI:
In the HanzoStudio directory you will find a file: extra_model_paths.yaml.example
Rename this file to: extra_model_paths.yaml and edit it with your favorite text editor.
@@ -1,3 +1,3 @@
..\python_embeded\python.exe -s ..\ComfyUI\main.py --windows-standalone-build --disable-api-nodes
echo If you see this and ComfyUI did not start try updating your Nvidia Drivers to the latest. If you get a c10.dll error you need to install vc redist that you can find: https://aka.ms/vc14/vc_redist.x64.exe
..\python_embeded\python.exe -s ..\HanzoStudio\main.py --windows-standalone-build --disable-api-nodes
echo If you see this and Hanzo Studio did not start try updating your Nvidia Drivers to the latest. If you get a c10.dll error you need to install vc redist that you can find: https://aka.ms/vc14/vc_redist.x64.exe
pause
+1 -1
View File
@@ -1,2 +1,2 @@
.\python_embeded\python.exe -s ComfyUI\main.py --cpu --windows-standalone-build
.\python_embeded\python.exe -s HanzoStudio\main.py --cpu --windows-standalone-build
pause
@@ -1,3 +1,3 @@
.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build
echo If you see this and ComfyUI did not start try updating your Nvidia Drivers to the latest. If you get a c10.dll error you need to install vc redist that you can find: https://aka.ms/vc14/vc_redist.x64.exe
.\python_embeded\python.exe -s HanzoStudio\main.py --windows-standalone-build
echo If you see this and Hanzo Studio did not start try updating your Nvidia Drivers to the latest. If you get a c10.dll error you need to install vc redist that you can find: https://aka.ms/vc14/vc_redist.x64.exe
pause
@@ -1,3 +1,3 @@
.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build --fast fp16_accumulation
echo If you see this and ComfyUI did not start try updating your Nvidia Drivers to the latest. If you get a c10.dll error you need to install vc redist that you can find: https://aka.ms/vc14/vc_redist.x64.exe
.\python_embeded\python.exe -s HanzoStudio\main.py --windows-standalone-build --fast fp16_accumulation
echo If you see this and Hanzo Studio did not start try updating your Nvidia Drivers to the latest. If you get a c10.dll error you need to install vc redist that you can find: https://aka.ms/vc14/vc_redist.x64.exe
pause
+20 -7
View File
@@ -1,6 +1,7 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: "en-US"
early_access: false
tone_instructions: "Only comment on issues introduced by this PR's changes. Do not flag pre-existing problems in moved, re-indented, or reformatted code."
reviews:
profile: "chill"
@@ -25,7 +26,7 @@ reviews:
enable_prompt_for_ai_agents: true
path_filters:
- "!comfy_api_nodes/apis/**"
- "!studio_api_nodes/apis/**"
- "!**/generated/*.pyi"
- "!.ci/**"
- "!script_examples/**"
@@ -35,26 +36,34 @@ reviews:
- "!**/*.bat"
path_instructions:
- path: "comfy/**"
- path: "**"
instructions: |
IMPORTANT: Only comment on issues directly introduced by this PR's code changes.
Do NOT flag pre-existing issues in code that was merely moved, re-indented,
de-indented, or reformatted without logic changes. If code appears in the diff
only due to whitespace or structural reformatting (e.g., removing a `with:` block),
treat it as unchanged. Contributors should not feel obligated to address
pre-existing issues outside the scope of their contribution.
- path: "studio/**"
instructions: |
Core ML/diffusion engine. Focus on:
- Backward compatibility (breaking changes affect all custom nodes)
- Memory management and GPU resource handling
- Performance implications in hot paths
- Thread safety for concurrent execution
- path: "comfy_api_nodes/**"
- path: "studio_api_nodes/**"
instructions: |
Third-party API integration nodes. Focus on:
- No hardcoded API keys or secrets
- Proper error handling for API failures (timeouts, rate limits, auth errors)
- Correct Pydantic model usage
- Security of user data passed to external APIs
- path: "comfy_extras/**"
- path: "studio_extras/**"
instructions: |
Community-contributed extra nodes. Focus on:
- Consistency with node patterns (INPUT_TYPES, RETURN_TYPES, FUNCTION, CATEGORY)
- No breaking changes to existing node interfaces
- path: "comfy_execution/**"
- path: "studio_execution/**"
instructions: |
Execution engine (graph execution, caching, jobs). Focus on:
- Caching correctness
@@ -74,7 +83,11 @@ reviews:
auto_review:
enabled: true
auto_incremental_review: true
drafts: true
drafts: false
ignore_title_keywords:
- "WIP"
- "DO NOT REVIEW"
- "DO NOT MERGE"
finishing_touches:
docstrings:
@@ -84,7 +97,7 @@ reviews:
tools:
ruff:
enabled: true
enabled: false
pylint:
enabled: false
flake8:
+26
View File
@@ -0,0 +1,26 @@
models/
output/
input/
custom_nodes/
user/
temp/
orgs/
.git/
.github/
__pycache__/
*.pyc
*.pyo
*.log
*.egg-info/
.venv/
venv/
.mypy_cache/
.pytest_cache/
.ruff_cache/
tests/
tests-unit/
scripts/
notebooks/
*.swp
.env
.env.*
+7 -7
View File
@@ -1,5 +1,5 @@
name: Bug Report
description: "Something is broken inside of ComfyUI. (Do not use this if you're just having issues and need help, or if the issue relates to a custom node)"
description: "Something is broken inside of Hanzo Studio. (Do not use this if you're just having issues and need help, or if the issue relates to a custom node)"
labels: ["Potential Bug"]
body:
- type: markdown
@@ -7,23 +7,23 @@ body:
value: |
Before submitting a **Bug Report**, please ensure the following:
- **1:** You are running the latest version of ComfyUI.
- **2:** You have your ComfyUI logs and relevant workflow on hand and will post them in this bug report.
- **1:** You are running the latest version of Hanzo Studio.
- **2:** You have your Hanzo Studio logs and relevant workflow on hand and will post them in this bug report.
- **3:** You confirmed that the bug is not caused by a custom node. You can disable all custom nodes by passing
`--disable-all-custom-nodes` command line argument. If you have custom node try updating them to the latest version.
- **4:** This is an actual bug in ComfyUI, not just a support question. A bug is when you can specify exact
- **4:** This is an actual bug in Hanzo Studio, not just a support question. A bug is when you can specify exact
steps to replicate what went wrong and others will be able to repeat your steps and see the same issue happen.
## Very Important
Please make sure that you post ALL your ComfyUI logs in the bug report. A bug report without logs will likely be ignored.
Please make sure that you post ALL your Hanzo Studio logs in the bug report. A bug report without logs will likely be ignored.
- type: checkboxes
id: custom-nodes-test
attributes:
label: Custom Node Testing
description: Please confirm you have tried to reproduce the issue with all custom nodes disabled.
options:
- label: I have tried disabling custom nodes and the issue persists (see [how to disable custom nodes](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled) if you need help)
- label: I have tried disabling custom nodes and the issue persists (see [how to disable custom nodes](https://docs.hanzo.ai/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled) if you need help)
required: false
- type: textarea
attributes:
@@ -40,7 +40,7 @@ body:
- type: textarea
attributes:
label: Steps to Reproduce
description: "Describe how to reproduce the issue. Please be sure to attach a workflow JSON or PNG, ideally one that doesn't require custom nodes to test. If the bug open happens when certain custom nodes are used, most likely that custom node is what has the bug rather than ComfyUI, in which case it should be reported to the node's author."
description: "Describe how to reproduce the issue. Please be sure to attach a workflow JSON or PNG, ideally one that doesn't require custom nodes to test. If the bug open happens when certain custom nodes are used, most likely that custom node is what has the bug rather than Hanzo Studio, in which case it should be reported to the node's author."
validations:
required: true
- type: textarea
+6 -9
View File
@@ -1,11 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: ComfyUI Frontend Issues
url: https://github.com/Comfy-Org/ComfyUI_frontend/issues
about: Issues related to the ComfyUI frontend (display issues, user interaction bugs), please go to the frontend repo to file the issue
- name: ComfyUI Matrix Space
url: https://app.element.io/#/room/%23comfyui_space%3Amatrix.org
about: The ComfyUI Matrix Space is available for support and general discussion related to ComfyUI (Matrix is like Discord but open source).
- name: Comfy Org Discord
url: https://discord.gg/comfyorg
about: The Comfy Org Discord is available for support and general discussion related to ComfyUI.
- name: Hanzo Studio Frontend Issues
url: https://github.com/hanzoai/studio/issues
about: Issues related to the Hanzo Studio frontend (display issues, user interaction bugs).
- name: Hanzo AI Discord
url: https://discord.gg/hanzoai
about: The Hanzo AI Discord is available for support and general discussion related to Hanzo Studio.
+4 -4
View File
@@ -1,5 +1,5 @@
name: Feature Request
description: "You have an idea for something new you would like to see added to ComfyUI's core."
description: "You have an idea for something new you would like to see added to Hanzo Studio's core."
labels: [ "Feature" ]
body:
- type: markdown
@@ -7,11 +7,11 @@ body:
value: |
Before submitting a **Feature Request**, please ensure the following:
**1:** You are running the latest version of ComfyUI.
**1:** You are running the latest version of Hanzo Studio.
**2:** You have looked to make sure there is not already a feature that does what you need, and there is not already a Feature Request listed for the same idea.
**3:** This is something that makes sense to add to ComfyUI Core, and wouldn't make more sense as a custom node.
**3:** This is something that makes sense to add to Hanzo Studio Core, and wouldn't make more sense as a custom node.
If unsure, ask on the [ComfyUI Matrix Space](https://app.element.io/#/room/%23comfyui_space%3Amatrix.org) or the [Comfy Org Discord](https://discord.gg/comfyorg) first.
If unsure, ask on the [Hanzo AI Discord](https://discord.gg/hanzoai) first.
- type: textarea
attributes:
label: Feature Idea
+3 -3
View File
@@ -7,17 +7,17 @@ body:
value: |
Before submitting a **User Report** issue, please ensure the following:
**1:** You are running the latest version of ComfyUI.
**1:** You are running the latest version of Hanzo Studio.
**2:** You have made an effort to find public answers to your question before asking here. In other words, you googled it first, and scrolled through recent help topics.
If unsure, ask on the [ComfyUI Matrix Space](https://app.element.io/#/room/%23comfyui_space%3Amatrix.org) or the [Comfy Org Discord](https://discord.gg/comfyorg) first.
If unsure, ask on the [Hanzo AI Discord](https://discord.gg/hanzoai) first.
- type: checkboxes
id: custom-nodes-test
attributes:
label: Custom Node Testing
description: Please confirm you have tried to reproduce the issue with all custom nodes disabled.
options:
- label: I have tried disabling custom nodes and the issue persists (see [how to disable custom nodes](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled) if you need help)
- label: I have tried disabling custom nodes and the issue persists (see [how to disable custom nodes](https://docs.hanzo.ai/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled) if you need help)
required: false
- type: textarea
attributes:
+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" role="img" aria-label="studio">
<rect width="1280" height="640" fill="#0A0A0A"/>
<svg x="96" y="215" width="210" height="210" viewBox="0 0 67 67"><path d="M22.21 67V44.6369H0V67H22.21Z" fill="#fff"/><path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#fff"/><path d="M22.21 0H0V22.3184H22.21V0Z" fill="#fff"/><path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#fff"/><path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#fff"/></svg>
<text x="378" y="276" font-family="Inter,system-ui,-apple-system,sans-serif" font-size="78" font-weight="800" letter-spacing="-2" fill="#ffffff">studio</text>
<text x="378" y="322" font-family="Inter,system-ui,sans-serif" font-size="30" fill="#ffffff" opacity=".66">Hanzo Studio — Visual AI Engine</text>
<rect x="378" y="338" width="806" height="3" rx="1.5" fill="#ffffff" opacity=".9"/>
<text x="378" y="390" font-family="Inter,system-ui,sans-serif" font-size="24" font-weight="600" fill="#ffffff" opacity=".5">github.com/hanzoai</text>
<text x="1184" y="390" text-anchor="end" font-family="Inter,system-ui,sans-serif" font-size="24" font-weight="600" fill="#ffffff" opacity=".5">hanzo.ai</text>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+1 -1
View File
@@ -4,7 +4,7 @@ on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]
paths:
- 'comfy_api_nodes/**' # only run if these files changed
- 'studio_api_nodes/**' # only run if these files changed
permissions:
contents: read
+22
View File
@@ -0,0 +1,22 @@
# Canonical CI/CD — imports the shared hanzoai/ci reusable workflow, which
# reads hanzo.yml (images + test + deploy + kms). No per-repo build logic,
# no GitHub-hosted runners (defaults to the Hanzo cloud arc pool).
name: CI/CD
on:
push:
branches: [main]
tags: ['v*']
pull_request:
workflow_dispatch:
jobs:
cicd:
uses: hanzoai/ci/.github/workflows/build.yml@v1
# Target the Hanzo arc scale set by its installation name. ARC scale sets
# are matched by name, not by the [self-hosted,linux,amd64] label triple
# that ci@v1 defaults to — without this override the job never gets a
# runner and sits queued.
with:
runner: '["hanzo-build-linux-amd64"]'
secrets: inherit
+4 -4
View File
@@ -1,5 +1,5 @@
# This is the GitHub Workflow that drives full-GPU-enabled tests of pull requests to ComfyUI, when the 'Run-CI-Test' label is added
# Results are reported as checkmarks on the commits, as well as onto https://ci.comfy.org/
# This is the GitHub Workflow that drives full-GPU-enabled tests of pull requests to Hanzo Studio, when the 'Run-CI-Test' label is added
# Results are reported as checkmarks on the commits, as well as onto https://ci.hanzo.ai/
name: Pull Request CI Workflow Runs
on:
pull_request_target:
@@ -34,7 +34,7 @@ jobs:
python_version: ${{ matrix.python_version }}
torch_version: ${{ matrix.torch_version }}
google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
comfyui_flags: ${{ matrix.flags }}
studio_flags: ${{ matrix.flags }}
use_prior_commit: 'true'
comment:
if: ${{ github.event.label.name == 'Run-CI-Test' }}
@@ -49,5 +49,5 @@ jobs:
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '(Automated Bot Message) CI Tests are running, you can view the results at https://ci.comfy.org/?branch=${{ github.event.pull_request.number }}%2Fmerge'
body: '(Automated Bot Message) CI Tests are running, you can view the results at https://ci.hanzo.ai/?branch=${{ github.event.pull_request.number }}%2Fmerge'
})
+3 -3
View File
@@ -126,7 +126,7 @@ jobs:
--arg release_tag "$RELEASE_TAG" \
--arg release_url "$RELEASE_URL" \
'{
event_type: "comfyui_release_published",
event_type: "studio_release_published",
client_payload: {
release_tag: $release_tag,
release_url: $release_url
@@ -138,7 +138,7 @@ jobs:
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${DISPATCH_TOKEN}" \
https://api.github.com/repos/Comfy-Org/desktop/dispatches \
https://api.github.com/repos/hanzoai/studio-desktop/dispatches \
-d "$PAYLOAD"
echo "✅ Dispatched ComfyUI release ${RELEASE_TAG} to Comfy-Org/desktop"
echo "✅ Dispatched Hanzo Studio release ${RELEASE_TAG} to hanzoai/studio-desktop"
+11 -6
View File
@@ -16,8 +16,11 @@ jobs:
with:
python-version: 3.x
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install Ruff
run: pip install ruff
run: uv pip install --system ruff
- name: Run Ruff
run: ruff check .
@@ -35,14 +38,16 @@ jobs:
with:
python-version: '3.12'
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
uv pip install --system torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
uv pip install --system -r requirements.txt
- name: Install Pylint
run: pip install pylint
run: uv pip install --system pylint
- name: Run Pylint
run: pylint comfy_api_nodes
run: pylint studio_api_nodes
+23 -23
View File
@@ -76,7 +76,7 @@ on:
default: true
jobs:
package_comfy_windows:
package_studio_windows:
permissions:
contents: "write"
packages: "write"
@@ -93,12 +93,12 @@ jobs:
with:
path: |
${{ inputs.cache_tag }}_python_deps.tar
update_comfyui_and_python_dependencies.bat
update_studio_and_python_dependencies.bat
key: ${{ runner.os }}-build-${{ inputs.cache_tag }}-${{ inputs.python_minor }}
- shell: bash
run: |
mv ${{ inputs.cache_tag }}_python_deps.tar ../
mv update_comfyui_and_python_dependencies.bat ../
mv update_studio_and_python_dependencies.bat ../
cd ..
tar xf ${{ inputs.cache_tag }}_python_deps.tar
pwd
@@ -107,7 +107,7 @@ jobs:
- shell: bash
run: |
cd ..
cp -r ComfyUI ComfyUI_copy
cp -r HanzoStudio HanzoStudio_copy
curl https://www.python.org/ftp/python/3.${{ inputs.python_minor }}.${{ inputs.python_patch }}/python-3.${{ inputs.python_minor }}.${{ inputs.python_patch }}-embed-amd64.zip -o python_embeded.zip
unzip python_embeded.zip -d python_embeded
cd python_embeded
@@ -117,11 +117,11 @@ jobs:
./python.exe get-pip.py
./python.exe -s -m pip install ../${{ inputs.cache_tag }}_python_deps/*
grep comfy ../ComfyUI/requirements.txt > ./requirements_comfyui.txt
./python.exe -s -m pip install -r requirements_comfyui.txt
rm requirements_comfyui.txt
grep -E "comfyui-frontend-package|comfyui-workflow-templates|comfyui-embedded-docs|comfy-kitchen|comfy-aimdo" ../HanzoStudio/requirements.txt > ./requirements_studio.txt
./python.exe -s -m pip install -r requirements_studio.txt
rm requirements_studio.txt
sed -i '1i../ComfyUI' ./python3${{ inputs.python_minor }}._pth
sed -i '1i../HanzoStudio' ./python3${{ inputs.python_minor }}._pth
if test -f ./Lib/site-packages/torch/lib/dnnl.lib; then
rm ./Lib/site-packages/torch/lib/dnnl.lib #I don't think this is actually used and I need the space
@@ -131,40 +131,40 @@ jobs:
cd ..
git clone --depth 1 https://github.com/comfyanonymous/taesd
cp taesd/*.safetensors ./ComfyUI_copy/models/vae_approx/
git clone --depth 1 https://github.com/hanzoai/taesd
cp taesd/*.safetensors ./HanzoStudio_copy/models/vae_approx/
mkdir ComfyUI_windows_portable
mv python_embeded ComfyUI_windows_portable
mv ComfyUI_copy ComfyUI_windows_portable/ComfyUI
mkdir HanzoStudio_windows_portable
mv python_embeded HanzoStudio_windows_portable
mv HanzoStudio_copy HanzoStudio_windows_portable/HanzoStudio
cd ComfyUI_windows_portable
cd HanzoStudio_windows_portable
mkdir update
cp -r ComfyUI/.ci/update_windows/* ./update/
cp -r ComfyUI/.ci/windows_${{ inputs.rel_name }}_base_files/* ./
cp ../update_comfyui_and_python_dependencies.bat ./update/
cp -r HanzoStudio/.ci/update_windows/* ./update/
cp -r HanzoStudio/.ci/windows_${{ inputs.rel_name }}_base_files/* ./
cp ../update_studio_and_python_dependencies.bat ./update/
cd ..
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=9 -mfb=128 -md=768m -ms=on -mf=BCJ2 ComfyUI_windows_portable.7z ComfyUI_windows_portable
mv ComfyUI_windows_portable.7z ComfyUI/ComfyUI_windows_portable_${{ inputs.rel_name }}${{ inputs.rel_extra_name }}.7z
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=9 -mfb=128 -md=768m -ms=on -mf=BCJ2 HanzoStudio_windows_portable.7z HanzoStudio_windows_portable
mv HanzoStudio_windows_portable.7z HanzoStudio/HanzoStudio_windows_portable_${{ inputs.rel_name }}${{ inputs.rel_extra_name }}.7z
- shell: bash
if: ${{ inputs.test_release }}
run: |
cd ..
cd ComfyUI_windows_portable
python_embeded/python.exe -s ComfyUI/main.py --quick-test-for-ci --cpu
cd HanzoStudio_windows_portable
python_embeded/python.exe -s HanzoStudio/main.py --quick-test-for-ci --cpu
python_embeded/python.exe -s ./update/update.py ComfyUI/
python_embeded/python.exe -s ./update/update.py HanzoStudio/
ls
- name: Upload binaries to release
uses: softprops/action-gh-release@v2
with:
files: ComfyUI_windows_portable_${{ inputs.rel_name }}${{ inputs.rel_extra_name }}.7z
files: HanzoStudio_windows_portable_${{ inputs.rel_name }}${{ inputs.rel_extra_name }}.7z
tag_name: ${{ inputs.git_tag }}
draft: true
overwrite_files: true
+3 -2
View File
@@ -25,7 +25,8 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
uv pip install --system -r requirements.txt
+6 -6
View File
@@ -1,6 +1,6 @@
# This is the GitHub Workflow that drives automatic full-GPU-enabled tests of all new commits to the master branch of ComfyUI
# Results are reported as checkmarks on the commits, as well as onto https://ci.comfy.org/
name: Full Comfy CI Workflow Runs
# This is the GitHub Workflow that drives automatic full-GPU-enabled tests of all new commits to the main branch of Hanzo Studio
# Results are reported as checkmarks on the commits, as well as onto https://ci.hanzo.ai/
name: Full Hanzo Studio CI Workflow Runs
on:
push:
branches:
@@ -46,7 +46,7 @@ jobs:
python_version: ${{ matrix.python_version }}
torch_version: ${{ matrix.torch_version }}
google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
comfyui_flags: ${{ matrix.flags }}
studio_flags: ${{ matrix.flags }}
# test-win-nightly:
# strategy:
@@ -69,7 +69,7 @@ jobs:
# python_version: ${{ matrix.python_version }}
# torch_version: ${{ matrix.torch_version }}
# google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
# comfyui_flags: ${{ matrix.flags }}
# studio_flags: ${{ matrix.flags }}
test-unix-nightly:
strategy:
@@ -96,4 +96,4 @@ jobs:
python_version: ${{ matrix.python_version }}
torch_version: ${{ matrix.torch_version }}
google_credentials: ${{ secrets.GCS_SERVICE_ACCOUNT_JSON }}
comfyui_flags: ${{ matrix.flags }}
studio_flags: ${{ matrix.flags }}
+5 -4
View File
@@ -19,12 +19,13 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install -r tests-unit/requirements.txt
uv pip install --system torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
uv pip install --system -r requirements.txt
uv pip install --system -r tests-unit/requirements.txt
- name: Run Execution Tests
run: |
python -m pytest tests/execution -v --skip-timing-checks
+13 -12
View File
@@ -10,26 +10,27 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout ComfyUI
- name: Checkout Hanzo Studio
uses: actions/checkout@v4
with:
repository: "Comfy-Org/ComfyUI"
path: "ComfyUI"
repository: "hanzoai/studio"
path: "studio"
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Start ComfyUI server
uv pip install --system torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
uv pip install --system -r requirements.txt
uv pip install --system wait-for-it
working-directory: studio
- name: Start Hanzo Studio server
run: |
python main.py --cpu 2>&1 | tee console_output.log &
wait-for-it --service 127.0.0.1:8188 -t 30
working-directory: ComfyUI
working-directory: studio
- name: Check for unhandled exceptions in server log
run: |
grep -v "Found comfy_kitchen backend triton: {'available': False, 'disabled': True, 'unavailable_reason': \"ImportError: No module named 'triton'\", 'capabilities': \[\]}" console_output.log | grep -v "Found comfy_kitchen backend triton: {'available': False, 'disabled': False, 'unavailable_reason': \"ImportError: No module named 'triton'\", 'capabilities': \[\]}" > console_output_filtered.log
@@ -38,10 +39,10 @@ jobs:
echo "Unhandled exception/error found in server log."
exit 1
fi
working-directory: ComfyUI
working-directory: studio
- uses: actions/upload-artifact@v4
if: always()
with:
name: console-output
path: ComfyUI/console_output.log
path: studio/console_output.log
retention-days: 30
+18 -18
View File
@@ -1,4 +1,4 @@
name: Generate Pydantic Stubs from api.comfy.org
name: Generate Pydantic Stubs from api.hanzo.ai
on:
schedule:
@@ -8,49 +8,49 @@ on:
jobs:
generate-models:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install 'datamodel-code-generator[http]'
npm install @redocly/cli
- name: Download OpenAPI spec
run: |
curl -o openapi.yaml https://api.comfy.org/openapi
curl -o openapi.yaml https://api.hanzo.ai/openapi
- name: Filter OpenAPI spec with Redocly
run: |
npx @redocly/cli bundle openapi.yaml --output filtered-openapi.yaml --config comfy_api_nodes/redocly.yaml --remove-unused-components
npx @redocly/cli bundle openapi.yaml --output filtered-openapi.yaml --config studio_api_nodes/redocly.yaml --remove-unused-components
- name: Generate API models
run: |
datamodel-codegen --use-subclass-enum --input filtered-openapi.yaml --output comfy_api_nodes/apis --output-model-type pydantic_v2.BaseModel
datamodel-codegen --use-subclass-enum --input filtered-openapi.yaml --output studio_api_nodes/apis --output-model-type pydantic_v2.BaseModel
- name: Check for changes
id: git-check
run: |
git diff --exit-code comfy_api_nodes/apis || echo "changes=true" >> $GITHUB_OUTPUT
git diff --exit-code studio_api_nodes/apis || echo "changes=true" >> $GITHUB_OUTPUT
- name: Create Pull Request
if: steps.git-check.outputs.changes == 'true'
uses: peter-evans/create-pull-request@v5
with:
commit-message: 'chore: update API models from OpenAPI spec'
title: 'Update API models from api.comfy.org'
title: 'Update API models from api.hanzo.ai'
body: |
This PR updates the API models based on the latest api.comfy.org OpenAPI specification.
Generated automatically by the a Github workflow.
This PR updates the API models based on the latest api.hanzo.ai OpenAPI specification.
Generated automatically by a Github workflow.
branch: update-api-stubs
delete-branch: true
base: master
base: main
+9 -9
View File
@@ -6,7 +6,7 @@ on:
workflow_dispatch:
inputs:
version:
description: 'ComfyUI version (e.g., v0.7.0)'
description: 'Hanzo Studio version (e.g., v0.7.0)'
required: true
type: string
@@ -26,34 +26,34 @@ jobs:
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Checkout comfyui-ci-container
- name: Checkout studio-ci-container
uses: actions/checkout@v4
with:
repository: comfy-org/comfyui-ci-container
repository: hanzoai/studio-ci-container
token: ${{ secrets.CI_CONTAINER_PAT }}
- name: Check current version
id: current
run: |
CURRENT=$(grep -oP 'ARG COMFYUI_VERSION=\K.*' Dockerfile || echo "unknown")
CURRENT=$(grep -oP 'ARG STUDIO_VERSION=\K.*' Dockerfile || echo "unknown")
echo "current_version=$CURRENT" >> $GITHUB_OUTPUT
- name: Update Dockerfile
run: |
VERSION="${{ steps.version.outputs.version }}"
sed -i "s/^ARG COMFYUI_VERSION=.*/ARG COMFYUI_VERSION=${VERSION}/" Dockerfile
sed -i "s/^ARG STUDIO_VERSION=.*/ARG STUDIO_VERSION=${VERSION}/" Dockerfile
- name: Create Pull Request
id: create-pr
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.CI_CONTAINER_PAT }}
branch: automation/comfyui-${{ steps.version.outputs.version }}
title: "chore: bump ComfyUI to ${{ steps.version.outputs.version }}"
branch: automation/studio-${{ steps.version.outputs.version }}
title: "chore: bump Hanzo Studio to ${{ steps.version.outputs.version }}"
body: |
Updates ComfyUI version from `${{ steps.current.outputs.current_version }}` to `${{ steps.version.outputs.version }}`
Updates Hanzo Studio version from `${{ steps.current.outputs.current_version }}` to `${{ steps.version.outputs.version }}`
**Triggered by:** ${{ github.event_name == 'release' && format('[Release {0}]({1})', github.event.release.tag_name, github.event.release.html_url) || 'Manual workflow dispatch' }}
labels: automation
commit-message: "chore: bump ComfyUI to ${{ steps.version.outputs.version }}"
commit-message: "chore: bump Hanzo Studio to ${{ steps.version.outputs.version }}"
+6 -6
View File
@@ -30,9 +30,9 @@ jobs:
run: |
python -m pip install --upgrade pip
- name: Update comfyui_version.py
- name: Update studio_version.py
run: |
# Read version from pyproject.toml and update comfyui_version.py
# Read version from pyproject.toml and update studio_version.py
python -c '
import tomllib
@@ -41,8 +41,8 @@ jobs:
config = tomllib.load(f)
version = config["project"]["version"]
# Write version to comfyui_version.py
with open("comfyui_version.py", "w") as f:
# Write version to studio_version.py
with open("studio_version.py", "w") as f:
f.write("# This file is automatically generated by the build process when version is\n")
f.write("# updated in pyproject.toml.\n")
f.write(f"__version__ = \"{version}\"\n")
@@ -54,6 +54,6 @@ jobs:
git config --local user.email "github-actions@github.com"
git fetch origin ${{ github.head_ref }}
git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }}
git add comfyui_version.py
git diff --quiet && git diff --staged --quiet || git commit -m "chore: Update comfyui_version.py to match pyproject.toml"
git add studio_version.py
git diff --quiet && git diff --staged --quiet || git commit -m "chore: Update studio_version.py to match pyproject.toml"
git push origin HEAD:${{ github.head_ref }}
@@ -46,18 +46,18 @@ jobs:
- shell: bash
run: |
echo "@echo off
call update_comfyui.bat nopause
call update_studio.bat nopause
echo -
echo This will try to update pytorch and all python dependencies.
echo -
echo If you just want to update normally, close this and run update_comfyui.bat instead.
echo If you just want to update normally, close this and run update_studio.bat instead.
echo -
pause
..\python_embeded\python.exe -s -m pip install --upgrade torch torchvision torchaudio ${{ inputs.xformers }} --extra-index-url https://download.pytorch.org/whl/cu${{ inputs.cu }} -r ../ComfyUI/requirements.txt pygit2
pause" > update_comfyui_and_python_dependencies.bat
..\python_embeded\python.exe -s -m pip install --upgrade torch torchvision torchaudio ${{ inputs.xformers }} --extra-index-url https://download.pytorch.org/whl/cu${{ inputs.cu }} -r ../HanzoStudio/requirements.txt pygit2
pause" > update_studio_and_python_dependencies.bat
grep -v comfyui requirements.txt > requirements_nocomfyui.txt
python -m pip wheel --no-cache-dir torch torchvision torchaudio ${{ inputs.xformers }} ${{ inputs.extra_dependencies }} --extra-index-url https://download.pytorch.org/whl/cu${{ inputs.cu }} -r requirements_nocomfyui.txt pygit2 -w ./temp_wheel_dir
grep -v studio requirements.txt > requirements_nostudio.txt
python -m pip wheel --no-cache-dir torch torchvision torchaudio ${{ inputs.xformers }} ${{ inputs.extra_dependencies }} --extra-index-url https://download.pytorch.org/whl/cu${{ inputs.cu }} -r requirements_nostudio.txt pygit2 -w ./temp_wheel_dir
python -m pip install --no-cache-dir ./temp_wheel_dir/*
echo installed basic
ls -lah temp_wheel_dir
@@ -68,5 +68,5 @@ jobs:
with:
path: |
cu${{ inputs.cu }}_python_deps.tar
update_comfyui_and_python_dependencies.bat
update_studio_and_python_dependencies.bat
key: ${{ runner.os }}-build-cu${{ inputs.cu }}-${{ inputs.python_minor }}
@@ -38,18 +38,18 @@ jobs:
- shell: bash
run: |
echo "@echo off
call update_comfyui.bat nopause
call update_studio.bat nopause
echo -
echo This will try to update pytorch and all python dependencies.
echo -
echo If you just want to update normally, close this and run update_comfyui.bat instead.
echo If you just want to update normally, close this and run update_studio.bat instead.
echo -
pause
..\python_embeded\python.exe -s -m pip install --upgrade ${{ inputs.torch_dependencies }} -r ../ComfyUI/requirements.txt pygit2
pause" > update_comfyui_and_python_dependencies.bat
..\python_embeded\python.exe -s -m pip install --upgrade ${{ inputs.torch_dependencies }} -r ../HanzoStudio/requirements.txt pygit2
pause" > update_studio_and_python_dependencies.bat
grep -v comfyui requirements.txt > requirements_nocomfyui.txt
python -m pip wheel --no-cache-dir ${{ inputs.torch_dependencies }} -r requirements_nocomfyui.txt pygit2 -w ./temp_wheel_dir
grep -v studio requirements.txt > requirements_nostudio.txt
python -m pip wheel --no-cache-dir ${{ inputs.torch_dependencies }} -r requirements_nostudio.txt pygit2 -w ./temp_wheel_dir
python -m pip install --no-cache-dir ./temp_wheel_dir/*
echo installed basic
ls -lah temp_wheel_dir
@@ -60,5 +60,5 @@ jobs:
with:
path: |
${{ inputs.cache_tag }}_python_deps.tar
update_comfyui_and_python_dependencies.bat
update_studio_and_python_dependencies.bat
key: ${{ runner.os }}-build-${{ inputs.cache_tag }}-${{ inputs.python_minor }}
@@ -42,45 +42,45 @@ jobs:
- shell: bash
run: |
cd ..
cp -r ComfyUI ComfyUI_copy
cp -r HanzoStudio HanzoStudio_copy
curl https://www.python.org/ftp/python/3.${{ inputs.python_minor }}.${{ inputs.python_patch }}/python-3.${{ inputs.python_minor }}.${{ inputs.python_patch }}-embed-amd64.zip -o python_embeded.zip
unzip python_embeded.zip -d python_embeded
cd python_embeded
echo 'import site' >> ./python3${{ inputs.python_minor }}._pth
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
./python.exe get-pip.py
python -m pip wheel torch torchvision torchaudio --pre --extra-index-url https://download.pytorch.org/whl/nightly/cu${{ inputs.cu }} -r ../ComfyUI/requirements.txt pygit2 -w ../temp_wheel_dir
python -m pip wheel torch torchvision torchaudio --pre --extra-index-url https://download.pytorch.org/whl/nightly/cu${{ inputs.cu }} -r ../HanzoStudio/requirements.txt pygit2 -w ../temp_wheel_dir
ls ../temp_wheel_dir
./python.exe -s -m pip install --pre ../temp_wheel_dir/*
sed -i '1i../ComfyUI' ./python3${{ inputs.python_minor }}._pth
sed -i '1i../HanzoStudio' ./python3${{ inputs.python_minor }}._pth
rm ./Lib/site-packages/torch/lib/dnnl.lib #I don't think this is actually used and I need the space
cd ..
git clone --depth 1 https://github.com/comfyanonymous/taesd
cp taesd/*.safetensors ./ComfyUI_copy/models/vae_approx/
git clone --depth 1 https://github.com/hanzoai/taesd
cp taesd/*.safetensors ./HanzoStudio_copy/models/vae_approx/
mkdir ComfyUI_windows_portable_nightly_pytorch
mv python_embeded ComfyUI_windows_portable_nightly_pytorch
mv ComfyUI_copy ComfyUI_windows_portable_nightly_pytorch/ComfyUI
mkdir HanzoStudio_windows_portable_nightly_pytorch
mv python_embeded HanzoStudio_windows_portable_nightly_pytorch
mv HanzoStudio_copy HanzoStudio_windows_portable_nightly_pytorch/HanzoStudio
cd ComfyUI_windows_portable_nightly_pytorch
cd HanzoStudio_windows_portable_nightly_pytorch
mkdir update
cp -r ComfyUI/.ci/update_windows/* ./update/
cp -r ComfyUI/.ci/windows_nvidia_base_files/* ./
cp -r ComfyUI/.ci/windows_nightly_base_files/* ./
cp -r HanzoStudio/.ci/update_windows/* ./update/
cp -r HanzoStudio/.ci/windows_nvidia_base_files/* ./
cp -r HanzoStudio/.ci/windows_nightly_base_files/* ./
echo "call update_comfyui.bat nopause
..\python_embeded\python.exe -s -m pip install --upgrade --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cu${{ inputs.cu }} -r ../ComfyUI/requirements.txt pygit2
pause" > ./update/update_comfyui_and_python_dependencies.bat
echo "call update_studio.bat nopause
..\python_embeded\python.exe -s -m pip install --upgrade --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cu${{ inputs.cu }} -r ../HanzoStudio/requirements.txt pygit2
pause" > ./update/update_studio_and_python_dependencies.bat
cd ..
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=9 -mfb=128 -md=512m -ms=on -mf=BCJ2 ComfyUI_windows_portable_nightly_pytorch.7z ComfyUI_windows_portable_nightly_pytorch
mv ComfyUI_windows_portable_nightly_pytorch.7z ComfyUI/ComfyUI_windows_portable_nvidia_or_cpu_nightly_pytorch.7z
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=9 -mfb=128 -md=512m -ms=on -mf=BCJ2 HanzoStudio_windows_portable_nightly_pytorch.7z HanzoStudio_windows_portable_nightly_pytorch
mv HanzoStudio_windows_portable_nightly_pytorch.7z HanzoStudio/HanzoStudio_windows_portable_nvidia_or_cpu_nightly_pytorch.7z
cd ComfyUI_windows_portable_nightly_pytorch
python_embeded/python.exe -s ComfyUI/main.py --quick-test-for-ci --cpu
cd HanzoStudio_windows_portable_nightly_pytorch
python_embeded/python.exe -s HanzoStudio/main.py --quick-test-for-ci --cpu
ls
@@ -88,6 +88,6 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ComfyUI_windows_portable_nvidia_or_cpu_nightly_pytorch.7z
file: HanzoStudio_windows_portable_nvidia_or_cpu_nightly_pytorch.7z
tag: "latest"
overwrite: true
+20 -20
View File
@@ -25,7 +25,7 @@ on:
# - master
jobs:
package_comfyui:
package_studio:
permissions:
contents: "write"
packages: "write"
@@ -37,12 +37,12 @@ jobs:
with:
path: |
cu${{ inputs.cu }}_python_deps.tar
update_comfyui_and_python_dependencies.bat
update_studio_and_python_dependencies.bat
key: ${{ runner.os }}-build-cu${{ inputs.cu }}-${{ inputs.python_minor }}
- shell: bash
run: |
mv cu${{ inputs.cu }}_python_deps.tar ../
mv update_comfyui_and_python_dependencies.bat ../
mv update_studio_and_python_dependencies.bat ../
cd ..
tar xf cu${{ inputs.cu }}_python_deps.tar
pwd
@@ -55,7 +55,7 @@ jobs:
- shell: bash
run: |
cd ..
cp -r ComfyUI ComfyUI_copy
cp -r HanzoStudio HanzoStudio_copy
curl https://www.python.org/ftp/python/3.${{ inputs.python_minor }}.${{ inputs.python_patch }}/python-3.${{ inputs.python_minor }}.${{ inputs.python_patch }}-embed-amd64.zip -o python_embeded.zip
unzip python_embeded.zip -d python_embeded
cd python_embeded
@@ -63,36 +63,36 @@ jobs:
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
./python.exe get-pip.py
./python.exe -s -m pip install ../cu${{ inputs.cu }}_python_deps/*
sed -i '1i../ComfyUI' ./python3${{ inputs.python_minor }}._pth
sed -i '1i../HanzoStudio' ./python3${{ inputs.python_minor }}._pth
rm ./Lib/site-packages/torch/lib/dnnl.lib #I don't think this is actually used and I need the space
rm ./Lib/site-packages/torch/lib/libprotoc.lib
rm ./Lib/site-packages/torch/lib/libprotobuf.lib
cd ..
git clone --depth 1 https://github.com/comfyanonymous/taesd
cp taesd/*.safetensors ./ComfyUI_copy/models/vae_approx/
git clone --depth 1 https://github.com/hanzoai/taesd
cp taesd/*.safetensors ./HanzoStudio_copy/models/vae_approx/
mkdir ComfyUI_windows_portable
mv python_embeded ComfyUI_windows_portable
mv ComfyUI_copy ComfyUI_windows_portable/ComfyUI
mkdir HanzoStudio_windows_portable
mv python_embeded HanzoStudio_windows_portable
mv HanzoStudio_copy HanzoStudio_windows_portable/HanzoStudio
cd ComfyUI_windows_portable
cd HanzoStudio_windows_portable
mkdir update
cp -r ComfyUI/.ci/update_windows/* ./update/
cp -r ComfyUI/.ci/windows_nvidia_base_files/* ./
cp ../update_comfyui_and_python_dependencies.bat ./update/
cp -r HanzoStudio/.ci/update_windows/* ./update/
cp -r HanzoStudio/.ci/windows_nvidia_base_files/* ./
cp ../update_studio_and_python_dependencies.bat ./update/
cd ..
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=9 -mfb=128 -md=768m -ms=on -mf=BCJ2 ComfyUI_windows_portable.7z ComfyUI_windows_portable
mv ComfyUI_windows_portable.7z ComfyUI/new_ComfyUI_windows_portable_nvidia_cu${{ inputs.cu }}_or_cpu.7z
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=9 -mfb=128 -md=768m -ms=on -mf=BCJ2 HanzoStudio_windows_portable.7z HanzoStudio_windows_portable
mv HanzoStudio_windows_portable.7z HanzoStudio/new_HanzoStudio_windows_portable_nvidia_cu${{ inputs.cu }}_or_cpu.7z
cd ComfyUI_windows_portable
python_embeded/python.exe -s ComfyUI/main.py --quick-test-for-ci --cpu
cd HanzoStudio_windows_portable
python_embeded/python.exe -s HanzoStudio/main.py --quick-test-for-ci --cpu
python_embeded/python.exe -s ./update/update.py ComfyUI/
python_embeded/python.exe -s ./update/update.py HanzoStudio/
ls
@@ -100,7 +100,7 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: new_ComfyUI_windows_portable_nvidia_cu${{ inputs.cu }}_or_cpu.7z
file: new_HanzoStudio_windows_portable_nvidia_cu${{ inputs.cu }}_or_cpu.7z
tag: "latest"
overwrite: true
+7
View File
@@ -0,0 +1,7 @@
name: Workflow Sanity
on:
pull_request:
paths: ['.github/workflows/**']
jobs:
sanity:
uses: hanzoai/.github/.github/workflows/workflow-sanity.yml@main
Symlink
+1
View File
@@ -0,0 +1 @@
LLM.md
Symlink
+1
View File
@@ -0,0 +1 @@
LLM.md
+1 -1
View File
@@ -1,2 +1,2 @@
# Admins
* @comfyanonymous @kosinkadink @guill
* @hanzoai/studio
+8 -8
View File
@@ -1,12 +1,12 @@
# Contributing to ComfyUI
# Contributing to Hanzo Studio
Welcome, and thank you for your interest in contributing to ComfyUI!
Welcome, and thank you for your interest in contributing to Hanzo Studio!
There are several ways in which you can contribute, beyond writing code. The goal of this document is to provide a high-level overview of how you can get involved.
## Asking Questions
Have a question? Instead of opening an issue, please ask on [Discord](https://comfy.org/discord) or [Matrix](https://app.element.io/#/room/%23comfyui_space%3Amatrix.org) channels. Our team and the community will help you.
Have a question? Instead of opening an issue, please ask on [Discord](https://discord.gg/hanzoai) first. Our team and the community will help you.
## Providing Feedback
@@ -16,24 +16,24 @@ See the `#bug-report`, `#feature-request` and `#feedback` channels on Discord.
## Reporting Issues
Have you identified a reproducible problem in ComfyUI? Do you have a feature request? We want to hear about it! Here's how you can report your issue as effectively as possible.
Have you identified a reproducible problem in Hanzo Studio? Do you have a feature request? We want to hear about it! Here's how you can report your issue as effectively as possible.
### Look For an Existing Issue
Before you create a new issue, please do a search in [open issues](https://github.com/comfyanonymous/ComfyUI/issues) to see if the issue or feature request has already been filed.
Before you create a new issue, please do a search in [open issues](https://github.com/hanzoai/studio/issues) to see if the issue or feature request has already been filed.
If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment:
* 👍 - upvote
* 👎 - downvote
* upvote
* downvote
If you cannot find an existing issue that describes your bug or feature, create a new issue. We have an issue template in place to organize new issues.
### Creating Pull Requests
* Please refer to the article on [creating pull requests](https://github.com/comfyanonymous/ComfyUI/wiki/How-to-Contribute-Code) and contributing to this project.
* Please refer to the article on [creating pull requests](https://github.com/hanzoai/studio/wiki/How-to-Contribute-Code) and contributing to this project.
## Thank You
+46
View File
@@ -0,0 +1,46 @@
FROM python:3.11-slim AS base
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
build-essential \
libgl1 \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install PyTorch CPU first (large layer, cached separately)
RUN pip install --no-cache-dir \
torch torchvision torchaudio \
--index-url https://download.pytorch.org/whl/cpu
# Install remaining dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Apply Hanzo Studio branding to frontend package
COPY branding/ /tmp/branding/
RUN chmod +x /tmp/branding/apply-branding.sh && /tmp/branding/apply-branding.sh && rm -rf /tmp/branding
# Copy application code
COPY . .
# Create directories for runtime volumes
RUN mkdir -p models output input custom_nodes user
EXPOSE 8188
# Default: listen on all interfaces, CPU mode
# Environment variables:
# STUDIO_ENABLE_IAM_AUTH=1 - Enable IAM auth via hanzo.id
# STUDIO_IAM_URL=https://hanzo.id
# STUDIO_NO_LOCALHOST_BYPASS=1 - Require auth even for localhost
# STUDIO_MULTI_TENANT=1 - Per-org storage isolation
# STUDIO_ORG_ID=default-org - Default org ID
# STUDIO_ENABLE_BILLING=1 - Commerce billing integration
# STUDIO_BILLING_CHECK_BALANCE=1 - Check balance before prompts
# STUDIO_COMMERCE_URL=http://commerce.hanzo.svc:8001
# STUDIO_COMMERCE_TOKEN=... - Commerce API token
# STUDIO_ENABLE_METRICS=1 - /metrics endpoint for VictoriaMetrics
# STUDIO_RATE_LIMIT_RPM=60 - Prompts per minute per org
CMD ["python", "main.py", "--listen", "0.0.0.0", "--port", "8188", "--cpu"]
+92
View File
@@ -0,0 +1,92 @@
# Hanzo Studio
## Overview
**The most powerful and modular visual AI engine and application.**
## Tech Stack
- **Language**: Python
## Build & Run
```bash
uv sync
uv run pytest
```
## Structure
```
studio/
CODEOWNERS
CONTRIBUTING.md
Dockerfile
LICENSE
QUANTIZATION.md
README.md
alembic.ini
alembic_db/
api_server/
app/
blueprints/
branding/
comfyui.log
comfyui.prev.log
comfyui.prev2.log
```
## Key Files
- `README.md` -- Project documentation
- `pyproject.toml` -- Python project config
- `Dockerfile` -- Container build (applies `branding/` then runs `main.py`)
- `hanzo.yml` + `.github/workflows/cicd.yml` -- canonical CI/CD (hanzoai/ci); builds `ghcr.io/hanzoai/studio`, rolls the `studio` operator Service CR at `studio.hanzo.ai`.
## Multi-tenant + IAM (cloud: studio.hanzo.ai)
Standalone app; `console.hanzo.ai` link-outs land here already authenticated via
shared Hanzo IAM (standard OIDC code flow). No embedding.
- **Auth** (`middleware/iam_auth_middleware.py`): local **JWKS** JWT validation
against `${STUDIO_IAM_URL}/v1/iam/.well-known/jwks` — signature + `exp` +
`iss` + `aud` (client_id `hanzo-studio`). Org from the `owner` claim
(`organization` fallback) → `request["iam_user"]["org_id"]`. Anonymous mode:
off by default locally (loopback bypass); enable in cloud with
`STUDIO_ENABLE_IAM_AUTH=true` (the one auth switch — this is the
"STUDIO_AUTH=off default" behavior). Browser flow: unauth HTML → IAM
`authorize` (code+PKCE) → `/callback` exchanges the code, sets an httpOnly
session cookie (no frontend changes). `verify_jwt(...)` is a pure, unit-tested
function.
- **Tenancy** (`folder_paths.py`, `app/user_manager.py`): with
`STUDIO_MULTI_TENANT=true`, userdata is `user/orgs/{org}/user/{user}/…` and
outputs are `output/orgs/{org}/…`. `get_public_user_directory(user, org)` is
org-rooted (fixed the bug where org userdata always resolved to `None`).
Execution outputs are scoped via `folder_paths.set_execution_org()`, bound by
the prompt worker from the queue item's `org_id`.
- **GPU federation** (`middleware/{worker_client,prompt_router,compute_config,
visor_client}.py`): `/v1/workers/register` (heartbeat), `/v1/workers` (list),
`/v1/worker/execute` (in-cluster push fast path). Worker↔coordinator trust via
`X-Worker-Token` (`STUDIO_WORKER_TOKEN`, KMS-sourced).
- **Durable render queue** (`middleware/tasks_queue.py`, `docs/federation.md`):
`SqlitePromptQueue` — crash-durable render queue in a single SQLite file
(stdlib `sqlite3`, WAL, **zero external processes**). `STUDIO_QUEUE_DB=<path>`
→ `server.py` swaps `PromptQueue` for it (precedence **STUDIO_QUEUE_DB >
STUDIO_PERSIST_QUEUE > memory**; default local behavior unchanged). The
`PromptQueue` seam maps to a durable job state machine: `put→INSERT (pending,
idempotent on prompt_id)`, `get→claim next pending under BEGIN IMMEDIATE
(exactly-once, leased)`, `task_done→done | retry/failed`. Crash-recovery: a
lease + reap returns an abandoned claim to `pending` (on every get(), a
heartbeat thread, and on boot) so another process re-runs it — idempotent
(SaveImage suffixes increment). Multiple Studio processes on the same DB file
compete safely. Tested: `middleware_test/tasks_queue_test.py` (incl.
crash-recovery across instances). Future (federation.md §6): a remote queue
backend served by Hanzo Tasks + a Go/unified-binary (HIP-0106) migration.
- **Engine selector** (`middleware/engine_selector.py`): `/v1/engines` lists
execution targets for the org (`local` + registered compute_config workers) and
`PUT /v1/engines/default` sets a per-org default (stored on
`ComputeProfile.default_engine`). `prompt_router.route_prompt` routes to the
chosen worker; `local` = unchanged. Leased cloud machines (Visor
`GET /v1/machines`) are a future engine class (interface stubbed, not built).
Frontend picker is a TODO. Tested: `middleware_test/engine_selector_test.py`.
## Tests
`tests-unit/{folder_paths_test,middleware_test,app_test,prompt_server_test}` —
run `uv run pytest tests-unit/folder_paths_test tests-unit/middleware_test
tests-unit/app_test tests-unit/prompt_server_test -q`. The auth/tenancy work is
covered by `middleware_test/iam_auth_test.py` and
`folder_paths_test/org_scoping_test.py`.
+39
View File
@@ -0,0 +1,39 @@
Hanzo Studio
============
Hanzo Studio is a fork of ComfyUI (https://github.com/comfyanonymous/ComfyUI),
Copyright (c) ComfyUI contributors, licensed under the GNU General Public
License v3.0 (GPL-3.0).
Modifications Copyright (c) 2026 Hanzo Industries Inc, also released under the
GNU General Public License v3.0 (GPL-3.0). See the LICENSE file for the full
license text.
As required by the GPL-3.0, this fork remains under the same license as the
upstream project. The original ComfyUI copyright notices are retained.
Modifications made in this fork
-------------------------------
- Branding: user-visible name changed from "ComfyUI" to "Hanzo Studio",
including the served page <title>, favicon, in-app logos, and display
strings. The prebuilt frontend package (comfyui-frontend-package) is patched
at install time by branding/apply-branding.sh; source assets live in
branding/. Backend Python packages are namespaced under studio_* and the
server reports itself as "Hanzo Studio".
- Hanzo Engine integration nodes (custom_nodes/hanzo_engine/): custom nodes
that call a local Hanzo Engine OpenAI-compatible server — HanzoChat
(chat/completions), HanzoImageGen (images/generations), and
HanzoVisionCaption (vision chat/completions). Hanzo Engine is the native-Rust,
all-modality inference and training backend.
- Fashion/swimwear starter workflows (user/default/workflows/fashion/):
product-shot, garment-edit, and caption-dataset workflow templates for a
designer's day-to-day use.
- Deployment/integration middleware for Hanzo infrastructure (IAM auth via
hanzo.id, commerce/billing, metrics), wired through environment variables and
the Dockerfile.
Third-party components retain their own upstream copyrights and licenses.
+7 -7
View File
@@ -1,4 +1,4 @@
# The Comfy guide to Quantization
# The Hanzo Studio guide to Quantization
## How does quantization work?
@@ -27,7 +27,7 @@ tensor_dq ~ tensor
Given that additional information (scaling factor) is needed to "interpret" the quantized values, we describe those as derived datatypes.
## Quantization in Comfy
## Quantization in Hanzo Studio
```
QuantizedTensor (torch.Tensor subclass)
@@ -39,7 +39,7 @@ MixedPrecisionOps + Metadata Detection
### Representation
To represent these derived datatypes, ComfyUI uses a subclass of torch.Tensor to implements these using the `QuantizedTensor` class found in `comfy/quant_ops.py`
To represent these derived datatypes, Hanzo Studio uses a subclass of torch.Tensor to implements these using the `QuantizedTensor` class found in `studio/quant_ops.py`
A `Layout` class defines how a specific quantization format behaves:
- Required parameters
@@ -47,7 +47,7 @@ A `Layout` class defines how a specific quantization format behaves:
- De-Quantize method
```python
from comfy.quant_ops import QuantizedLayout
from studio.quant_ops import QuantizedLayout
class MyLayout(QuantizedLayout):
@classmethod
@@ -67,7 +67,7 @@ The first is a **generic registry** that handles operations common to all quanti
The second registry is layout-specific and allows to implement fast-paths like nn.Linear.
```python
from comfy.quant_ops import register_layout_op
from studio.quant_ops import register_layout_op
@register_layout_op(torch.ops.aten.linear.default, MyLayout)
def my_linear(func, args, kwargs):
@@ -80,7 +80,7 @@ For any unsupported operation, QuantizedTensor will fallback to call `dequantize
### Mixed Precision
The `MixedPrecisionOps` class (lines 542-648 in `comfy/ops.py`) enables per-layer quantization decisions, allowing different layers in a model to use different precisions. This is activated when a model config contains a `layer_quant_config` dictionary that specifies which layers should be quantized and how.
The `MixedPrecisionOps` class (lines 542-648 in `studio/ops.py`) enables per-layer quantization decisions, allowing different layers in a model to use different precisions. This is activated when a model config contains a `layer_quant_config` dictionary that specifies which layers should be quantized and how.
**Architecture:**
@@ -125,7 +125,7 @@ We define 4 possible scaling parameters that should cover most recipes in the ne
|--------|---------------|--------------|----------------|-----------------|-------------|
| float8_e4m3fn | float32 | float32 (scalar) | - | - | float32 (scalar) |
You can find the defined formats in `comfy/quant_ops.py` (QUANT_ALGOS).
You can find the defined formats in `studio/quant_ops.py` (QUANT_ALGOS).
### Quantization Metadata
+70 -192
View File
@@ -1,92 +1,74 @@
<p align="center"><img src=".github/hero.svg" alt="studio" width="880"></p>
<div align="center">
# ComfyUI
# Hanzo Studio
**The most powerful and modular visual AI engine and application.**
[![Website][website-shield]][website-url]
[![Dynamic JSON Badge][discord-shield]][discord-url]
[![Twitter][twitter-shield]][twitter-url]
[![Matrix][matrix-shield]][matrix-url]
<br>
[![][github-release-shield]][github-release-link]
[![][github-release-date-shield]][github-release-link]
[![][github-downloads-shield]][github-downloads-link]
[![][github-downloads-latest-shield]][github-downloads-link]
[matrix-shield]: https://img.shields.io/badge/Matrix-000000?style=flat&logo=matrix&logoColor=white
[matrix-url]: https://app.element.io/#/room/%23comfyui_space%3Amatrix.org
[website-shield]: https://img.shields.io/badge/ComfyOrg-4285F4?style=flat
[website-url]: https://www.comfy.org/
[website-shield]: https://img.shields.io/badge/HanzoAI-4285F4?style=flat
[website-url]: https://www.hanzo.ai/
<!-- Workaround to display total user from https://github.com/badges/shields/issues/4500#issuecomment-2060079995 -->
[discord-shield]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2Fcomfyorg%3Fwith_counts%3Dtrue&query=%24.approximate_member_count&logo=discord&logoColor=white&label=Discord&color=green&suffix=%20total
[discord-url]: https://www.comfy.org/discord
[twitter-shield]: https://img.shields.io/twitter/follow/ComfyUI
[twitter-url]: https://x.com/ComfyUI
[discord-shield]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2Fhanzoai%3Fwith_counts%3Dtrue&query=%24.approximate_member_count&logo=discord&logoColor=white&label=Discord&color=green&suffix=%20total
[discord-url]: https://discord.gg/hanzoai
[twitter-shield]: https://img.shields.io/twitter/follow/hanaboroai
[twitter-url]: https://x.com/hanzoai
[github-release-shield]: https://img.shields.io/github/v/release/comfyanonymous/ComfyUI?style=flat&sort=semver
[github-release-link]: https://github.com/comfyanonymous/ComfyUI/releases
[github-release-date-shield]: https://img.shields.io/github/release-date/comfyanonymous/ComfyUI?style=flat
[github-downloads-shield]: https://img.shields.io/github/downloads/comfyanonymous/ComfyUI/total?style=flat
[github-downloads-latest-shield]: https://img.shields.io/github/downloads/comfyanonymous/ComfyUI/latest/total?style=flat&label=downloads%40latest
[github-downloads-link]: https://github.com/comfyanonymous/ComfyUI/releases
[github-release-shield]: https://img.shields.io/github/v/release/hanzoai/studio?style=flat&sort=semver
[github-release-link]: https://github.com/hanzoai/studio/releases
[github-release-date-shield]: https://img.shields.io/github/release-date/hanzoai/studio?style=flat
[github-downloads-shield]: https://img.shields.io/github/downloads/hanzoai/studio/total?style=flat
[github-downloads-latest-shield]: https://img.shields.io/github/downloads/hanzoai/studio/latest/total?style=flat&label=downloads%40latest
[github-downloads-link]: https://github.com/hanzoai/studio/releases
![ComfyUI Screenshot](https://github.com/user-attachments/assets/7ccaf2c1-9b72-41ae-9a89-5688c94b7abe)
![Hanzo Studio Screenshot](https://github.com/user-attachments/assets/7ccaf2c1-9b72-41ae-9a89-5688c94b7abe)
</div>
ComfyUI lets you design and execute advanced stable diffusion pipelines using a graph/nodes/flowchart based interface. Available on Windows, Linux, and macOS.
Hanzo Studio lets you design and execute advanced stable diffusion pipelines using a graph/nodes/flowchart based interface. Available on Windows, Linux, and macOS.
## Upstream & License
Hanzo Studio is a fork of [**ComfyUI**](https://github.com/comfyanonymous/ComfyUI) by comfyanonymous and the ComfyUI contributors — full credit to the upstream project for the engine this builds on. ComfyUI is licensed under the **GNU General Public License v3.0 (GPL-3.0)**, and this fork stays under the same license. All modifications in this fork are likewise GPL-3.0.
See [`LICENSE`](LICENSE) for the full license text and [`NOTICE`](NOTICE) for the fork's attribution and a summary of what it changes (branding, Hanzo Engine integration nodes, and fashion workflow templates).
## Get Started
#### [Desktop Application](https://www.comfy.org/download)
- The easiest way to get started.
- Available on Windows & macOS.
#### [Windows Portable Package](#installing)
- Get the latest commits and completely portable.
- Available on Windows.
#### [Manual Install](#manual-install-windows-linux)
Supports all operating systems and GPU types (NVIDIA, AMD, Intel, Apple Silicon, Ascend).
## [Examples](https://comfyanonymous.github.io/ComfyUI_examples/)
See what ComfyUI can do with the [example workflows](https://comfyanonymous.github.io/ComfyUI_examples/).
#### Docker (Recommended for servers)
```bash
docker pull ghcr.io/hanzoai/studio:latest
docker run -p 8188:8188 ghcr.io/hanzoai/studio:latest --listen --cpu
```
## Features
- Nodes/graph/flowchart interface to experiment and create complex Stable Diffusion workflows without needing to code anything.
- Image Models
- SD1.x, SD2.x ([unCLIP](https://comfyanonymous.github.io/ComfyUI_examples/unclip/))
- [SDXL](https://comfyanonymous.github.io/ComfyUI_examples/sdxl/), [SDXL Turbo](https://comfyanonymous.github.io/ComfyUI_examples/sdturbo/)
- [Stable Cascade](https://comfyanonymous.github.io/ComfyUI_examples/stable_cascade/)
- [SD3 and SD3.5](https://comfyanonymous.github.io/ComfyUI_examples/sd3/)
- Pixart Alpha and Sigma
- [AuraFlow](https://comfyanonymous.github.io/ComfyUI_examples/aura_flow/)
- [HunyuanDiT](https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_dit/)
- [Flux](https://comfyanonymous.github.io/ComfyUI_examples/flux/)
- [Lumina Image 2.0](https://comfyanonymous.github.io/ComfyUI_examples/lumina2/)
- [HiDream](https://comfyanonymous.github.io/ComfyUI_examples/hidream/)
- [Qwen Image](https://comfyanonymous.github.io/ComfyUI_examples/qwen_image/)
- [Hunyuan Image 2.1](https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_image/)
- [Flux 2](https://comfyanonymous.github.io/ComfyUI_examples/flux2/)
- [Z Image](https://comfyanonymous.github.io/ComfyUI_examples/z_image/)
- SD1.x, SD2.x, SDXL, SDXL Turbo
- Stable Cascade, SD3 and SD3.5
- Pixart Alpha and Sigma, AuraFlow, HunyuanDiT
- Flux, Flux 2, Lumina Image 2.0, HiDream
- Qwen Image, Hunyuan Image 2.1, Z Image
- Image Editing Models
- [Omnigen 2](https://comfyanonymous.github.io/ComfyUI_examples/omnigen/)
- [Flux Kontext](https://comfyanonymous.github.io/ComfyUI_examples/flux/#flux-kontext-image-editing-model)
- [HiDream E1.1](https://comfyanonymous.github.io/ComfyUI_examples/hidream/#hidream-e11)
- [Qwen Image Edit](https://comfyanonymous.github.io/ComfyUI_examples/qwen_image/#edit-model)
- Omnigen 2, Flux Kontext, HiDream E1.1, Qwen Image Edit
- Video Models
- [Stable Video Diffusion](https://comfyanonymous.github.io/ComfyUI_examples/video/)
- [Mochi](https://comfyanonymous.github.io/ComfyUI_examples/mochi/)
- [LTX-Video](https://comfyanonymous.github.io/ComfyUI_examples/ltxv/)
- [Hunyuan Video](https://comfyanonymous.github.io/ComfyUI_examples/hunyuan_video/)
- [Wan 2.1](https://comfyanonymous.github.io/ComfyUI_examples/wan/)
- [Wan 2.2](https://comfyanonymous.github.io/ComfyUI_examples/wan22/)
- [Hunyuan Video 1.5](https://docs.comfy.org/tutorials/video/hunyuan/hunyuan-video-1-5)
- Stable Video Diffusion, Mochi, LTX-Video
- Hunyuan Video, Wan 2.1, Wan 2.2, Hunyuan Video 1.5
- Audio Models
- [Stable Audio](https://comfyanonymous.github.io/ComfyUI_examples/audio/)
- [ACE Step](https://comfyanonymous.github.io/ComfyUI_examples/audio/)
- Stable Audio, ACE Step
- 3D Models
- [Hunyuan3D 2.0](https://docs.comfy.org/tutorials/3d/hunyuan3D-2)
- Hunyuan3D 2.0
- Asynchronous Queue system
- Many optimizations: Only re-executes the parts of the workflow that changes between executions.
- Smart memory management: can automatically run large models on GPUs with as low as 1GB vram with smart offloading.
@@ -94,45 +76,23 @@ See what ComfyUI can do with the [example workflows](https://comfyanonymous.gith
- Can load ckpt and safetensors: All in one checkpoints or standalone diffusion models, VAEs and CLIP models.
- Safe loading of ckpt, pt, pth, etc.. files.
- Embeddings/Textual inversion
- [Loras (regular, locon and loha)](https://comfyanonymous.github.io/ComfyUI_examples/lora/)
- [Hypernetworks](https://comfyanonymous.github.io/ComfyUI_examples/hypernetworks/)
- Loras (regular, locon and loha)
- Hypernetworks
- Loading full workflows (with seeds) from generated PNG, WebP and FLAC files.
- Saving/Loading workflows as Json files.
- Nodes interface can be used to create complex workflows like one for [Hires fix](https://comfyanonymous.github.io/ComfyUI_examples/2_pass_txt2img/) or much more advanced ones.
- [Area Composition](https://comfyanonymous.github.io/ComfyUI_examples/area_composition/)
- [Inpainting](https://comfyanonymous.github.io/ComfyUI_examples/inpaint/) with both regular and inpainting models.
- [ControlNet and T2I-Adapter](https://comfyanonymous.github.io/ComfyUI_examples/controlnet/)
- [Upscale Models (ESRGAN, ESRGAN variants, SwinIR, Swin2SR, etc...)](https://comfyanonymous.github.io/ComfyUI_examples/upscale_models/)
- [GLIGEN](https://comfyanonymous.github.io/ComfyUI_examples/gligen/)
- [Model Merging](https://comfyanonymous.github.io/ComfyUI_examples/model_merging/)
- [LCM models and Loras](https://comfyanonymous.github.io/ComfyUI_examples/lcm/)
- Latent previews with [TAESD](#how-to-show-high-quality-previews)
- Nodes interface can be used to create complex workflows like one for Hires fix or much more advanced ones.
- Area Composition
- Inpainting with both regular and inpainting models.
- ControlNet and T2I-Adapter
- Upscale Models (ESRGAN, ESRGAN variants, SwinIR, Swin2SR, etc...)
- GLIGEN
- Model Merging
- LCM models and Loras
- Latent previews with TAESD
- Works fully offline: core will never download anything unless you want to.
- Optional API nodes to use paid models from external providers through the online [Comfy API](https://docs.comfy.org/tutorials/api-nodes/overview) disable with: `--disable-api-nodes`
- Optional API nodes to use paid models from external providers through the online API. Disable with: `--disable-api-nodes`
- [Config file](extra_model_paths.yaml.example) to set the search paths for models.
Workflow examples can be found on the [Examples page](https://comfyanonymous.github.io/ComfyUI_examples/)
## Release Process
ComfyUI follows a weekly release cycle targeting Monday but this regularly changes because of model releases or large changes to the codebase. There are three interconnected repositories:
1. **[ComfyUI Core](https://github.com/comfyanonymous/ComfyUI)**
- Releases a new stable version (e.g., v0.7.0) roughly every week.
- Starting from v0.4.0 patch versions will be used for fixes backported onto the current stable release.
- Minor versions will be used for releases off the master branch.
- Patch versions may still be used for releases on the master branch in cases where a backport would not make sense.
- Commits outside of the stable release tags may be very unstable and break many custom nodes.
- Serves as the foundation for the desktop release
2. **[ComfyUI Desktop](https://github.com/Comfy-Org/desktop)**
- Builds a new release using the latest stable core version
3. **[ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend)**
- Weekly frontend updates are merged into the core repository
- Features are frozen for the upcoming core release
- Development continues for the next release cycle
## Shortcuts
| Keybind | Explanation |
@@ -173,39 +133,6 @@ ComfyUI follows a weekly release cycle targeting Monday but this regularly chang
# Installing
## Windows Portable
There is a portable standalone build for Windows that should work for running on Nvidia GPUs or for running on your CPU only on the [releases page](https://github.com/comfyanonymous/ComfyUI/releases).
### [Direct link to download](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_nvidia.7z)
Simply download, extract with [7-Zip](https://7-zip.org) or with the windows explorer on recent windows versions and run. For smaller models you normally only need to put the checkpoints (the huge ckpt/safetensors files) in: ComfyUI\models\checkpoints but many of the larger models have multiple files. Make sure to follow the instructions to know which subfolder to put them in ComfyUI\models\
If you have trouble extracting it, right click the file -> properties -> unblock
The portable above currently comes with python 3.13 and pytorch cuda 13.0. Update your Nvidia drivers if it doesn't start.
#### Alternative Downloads:
[Experimental portable for AMD GPUs](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_amd.7z)
[Portable with pytorch cuda 12.8 and python 3.12](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_nvidia_cu128.7z).
[Portable with pytorch cuda 12.6 and python 3.12](https://github.com/comfyanonymous/ComfyUI/releases/latest/download/ComfyUI_windows_portable_nvidia_cu126.7z) (Supports Nvidia 10 series and older GPUs).
#### How do I share models between another UI and ComfyUI?
See the [Config file](extra_model_paths.yaml.example) to set the search paths for models. In the standalone windows build you can find this file in the ComfyUI directory. Rename this file to extra_model_paths.yaml and edit it with your favorite text editor.
## [comfy-cli](https://docs.comfy.org/comfy-cli/getting-started)
You can install and start ComfyUI using comfy-cli:
```bash
pip install comfy-cli
comfy install
```
## Manual Install (Windows, Linux)
Python 3.14 works but some custom nodes may have issues. The free threaded variant works but some dependencies will enable the GIL so it's not fully supported.
@@ -282,24 +209,24 @@ And install it again with the command above.
### Dependencies
Install the dependencies by opening your terminal inside the ComfyUI folder and:
Install the dependencies by opening your terminal inside the Hanzo Studio folder and:
```pip install -r requirements.txt```
After this you should have everything installed and can proceed to running ComfyUI.
After this you should have everything installed and can proceed to running Hanzo Studio.
### Others:
#### Apple Mac silicon
You can install ComfyUI in Apple Mac silicon (M1 or M2) with any recent macOS version.
You can install Hanzo Studio in Apple Mac silicon (M1 or M2) with any recent macOS version.
1. Install pytorch nightly. For instructions, read the [Accelerated PyTorch training on Mac](https://developer.apple.com/metal/pytorch/) Apple Developer guide (make sure to install the latest pytorch nightly).
1. Follow the [ComfyUI manual installation](#manual-install-windows-linux) instructions for Windows and Linux.
1. Install the ComfyUI [dependencies](#dependencies). If you have another Stable Diffusion UI [you might be able to reuse the dependencies](#i-already-have-another-ui-for-stable-diffusion-installed-do-i-really-have-to-install-all-of-these-dependencies).
1. Launch ComfyUI by running `python main.py`
1. Follow the [Hanzo Studio manual installation](#manual-install-windows-linux) instructions for Windows and Linux.
1. Install the Hanzo Studio [dependencies](#dependencies). If you have another Stable Diffusion UI you might be able to reuse the dependencies.
1. Launch Hanzo Studio by running `python main.py`
> **Note**: Remember to add your models, VAE, LoRAs etc. to the corresponding Comfy folders, as discussed in [ComfyUI manual installation](#manual-install-windows-linux).
> **Note**: Remember to add your models, VAE, LoRAs etc. to the corresponding folders, as discussed in [Hanzo Studio manual installation](#manual-install-windows-linux).
#### Ascend NPUs
@@ -308,7 +235,7 @@ For models compatible with Ascend Extension for PyTorch (torch_npu). To get star
1. Begin by installing the recommended or newer kernel version for Linux as specified in the Installation page of torch-npu, if necessary.
2. Proceed with the installation of Ascend Basekit, which includes the driver, firmware, and CANN, following the instructions provided for your specific platform.
3. Next, install the necessary packages for torch-npu by adhering to the platform-specific instructions on the [Installation](https://ascend.github.io/docs/sources/pytorch/install.html#pytorch) page.
4. Finally, adhere to the [ComfyUI manual installation](#manual-install-windows-linux) guide for Linux. Once all components are installed, you can run ComfyUI as described earlier.
4. Finally, adhere to the [Hanzo Studio manual installation](#manual-install-windows-linux) guide for Linux. Once all components are installed, you can run Hanzo Studio as described earlier.
#### Cambricon MLUs
@@ -316,19 +243,19 @@ For models compatible with Cambricon Extension for PyTorch (torch_mlu). Here's a
1. Install the Cambricon CNToolkit by adhering to the platform-specific instructions on the [Installation](https://www.cambricon.com/docs/sdk_1.15.0/cntoolkit_3.7.2/cntoolkit_install_3.7.2/index.html)
2. Next, install the PyTorch(torch_mlu) following the instructions on the [Installation](https://www.cambricon.com/docs/sdk_1.15.0/cambricon_pytorch_1.17.0/user_guide_1.9/index.html)
3. Launch ComfyUI by running `python main.py`
3. Launch Hanzo Studio by running `python main.py`
#### Iluvatar Corex
For models compatible with Iluvatar Extension for PyTorch. Here's a step-by-step guide tailored to your platform and installation method:
1. Install the Iluvatar Corex Toolkit by adhering to the platform-specific instructions on the [Installation](https://support.iluvatar.com/#/DocumentCentre?id=1&nameCenter=2&productId=520117912052801536)
2. Launch ComfyUI by running `python main.py`
2. Launch Hanzo Studio by running `python main.py`
## [ComfyUI-Manager](https://github.com/Comfy-Org/ComfyUI-Manager/tree/manager-v4)
## Manager
**ComfyUI-Manager** is an extension that allows you to easily install, update, and manage custom nodes for ComfyUI.
The **Manager** extension allows you to easily install, update, and manage custom nodes for Hanzo Studio.
### Setup
@@ -337,7 +264,7 @@ For models compatible with Iluvatar Extension for PyTorch. Here's a step-by-step
pip install -r manager_requirements.txt
```
2. Enable the manager with the `--enable-manager` flag when running ComfyUI:
2. Enable the manager with the `--enable-manager` flag when running Hanzo Studio:
```bash
python main.py --enable-manager
```
@@ -346,7 +273,7 @@ For models compatible with Iluvatar Extension for PyTorch. Here's a step-by-step
| Flag | Description |
|------|-------------|
| `--enable-manager` | Enable ComfyUI-Manager |
| `--enable-manager` | Enable the Manager |
| `--enable-manager-legacy-ui` | Use the legacy manager UI instead of the new UI (requires `--enable-manager`) |
| `--disable-manager-ui` | Disable the manager UI and endpoints while keeping background features like security checks and scheduled installation completion (requires `--enable-manager`) |
@@ -365,7 +292,7 @@ For AMD 7600 and maybe other RDNA3 cards: ```HSA_OVERRIDE_GFX_VERSION=11.0.0 pyt
### AMD ROCm Tips
You can enable experimental memory efficient attention on recent pytorch in ComfyUI on some AMD GPUs using this command, it should already be enabled by default on RDNA3. If this improves speed for you on latest pytorch on your GPU please report it so that I can enable it by default.
You can enable experimental memory efficient attention on recent pytorch on some AMD GPUs using this command, it should already be enabled by default on RDNA3. If this improves speed for you on latest pytorch on your GPU please report it so that we can enable it by default.
```TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL=1 python main.py --use-pytorch-cross-attention```
@@ -379,9 +306,9 @@ Only parts of the graph that change from each execution to the next will be exec
Dragging a generated png on the webpage or loading one will give you the full workflow including seeds that were used to create it.
You can use () to change emphasis of a word or phrase like: (good code:1.2) or (bad code:0.8). The default emphasis for () is 1.1. To use () characters in your actual prompt escape them like \\( or \\).
You can use () to change emphasis of a word or phrase like: (good code:1.2) or (bad code:0.8). The default emphasis for () is 1.1. To use () characters in your actual prompt escape them like \( or \).
You can use {day|night}, for wildcard/dynamic prompts. With this syntax "{wild|card|test}" will be randomly replaced by either "wild", "card" or "test" by the frontend every time you queue the prompt. To use {} characters in your actual prompt escape them like: \\{ or \\}.
You can use {day|night}, for wildcard/dynamic prompts. With this syntax "{wild|card|test}" will be randomly replaced by either "wild", "card" or "test" by the frontend every time you queue the prompt. To use {} characters in your actual prompt escape them like: \{ or \}.
Dynamic prompts also support C-style comments, like `// comment` or `/* comment */`.
@@ -394,7 +321,7 @@ To use a textual inversion concepts/embeddings in a text prompt put them in the
Use ```--preview-method auto``` to enable previews.
The default installation includes a fast latent preview method that's low-resolution. To enable higher-quality previews with [TAESD](https://github.com/madebyollin/taesd), download the [taesd_decoder.pth, taesdxl_decoder.pth, taesd3_decoder.pth and taef1_decoder.pth](https://github.com/madebyollin/taesd/) and place them in the `models/vae_approx` folder. Once they're installed, restart ComfyUI and launch it with `--preview-method taesd` to enable high-quality previews.
The default installation includes a fast latent preview method that's low-resolution. To enable higher-quality previews with [TAESD](https://github.com/madebyollin/taesd), download the [taesd_decoder.pth, taesdxl_decoder.pth, taesd3_decoder.pth and taef1_decoder.pth](https://github.com/madebyollin/taesd/) and place them in the `models/vae_approx` folder. Once they're installed, restart Hanzo Studio and launch it with `--preview-method taesd` to enable high-quality previews.
## How to use TLS/SSL?
Generate a self-signed certificate (not appropriate for shared/production use) and key by running the command: `openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"`
@@ -406,55 +333,6 @@ Use `--tls-keyfile key.pem --tls-certfile cert.pem` to enable TLS/SSL, the app w
## Support and dev channel
[Discord](https://comfy.org/discord): Try the #help or #feedback channels.
[Discord](https://discord.gg/hanzoai): Try the #help or #feedback channels.
[Matrix space: #comfyui_space:matrix.org](https://app.element.io/#/room/%23comfyui_space%3Amatrix.org) (it's like discord but open source).
See also: [https://www.comfy.org/](https://www.comfy.org/)
## Frontend Development
As of August 15, 2024, we have transitioned to a new frontend, which is now hosted in a separate repository: [ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend). This repository now hosts the compiled JS (from TS/Vue) under the `web/` directory.
### Reporting Issues and Requesting Features
For any bugs, issues, or feature requests related to the frontend, please use the [ComfyUI Frontend repository](https://github.com/Comfy-Org/ComfyUI_frontend). This will help us manage and address frontend-specific concerns more efficiently.
### Using the Latest Frontend
The new frontend is now the default for ComfyUI. However, please note:
1. The frontend in the main ComfyUI repository is updated fortnightly.
2. Daily releases are available in the separate frontend repository.
To use the most up-to-date frontend version:
1. For the latest daily release, launch ComfyUI with this command line argument:
```
--front-end-version Comfy-Org/ComfyUI_frontend@latest
```
2. For a specific version, replace `latest` with the desired version number:
```
--front-end-version Comfy-Org/ComfyUI_frontend@1.2.2
```
This approach allows you to easily switch between the stable fortnightly release and the cutting-edge daily updates, or even specific versions for testing purposes.
### Accessing the Legacy Frontend
If you need to use the legacy frontend for any reason, you can access it using the following command line argument:
```
--front-end-version Comfy-Org/ComfyUI_legacy_frontend@latest
```
This will use a snapshot of the legacy frontend preserved in the [ComfyUI Legacy Frontend repository](https://github.com/Comfy-Org/ComfyUI_legacy_frontend).
# QA
### Which GPU should I buy for this?
[See this page for some recommendations](https://github.com/comfyanonymous/ComfyUI/wiki/Which-GPU-should-I-buy-for-ComfyUI)
See also: [https://www.hanzo.ai/](https://www.hanzo.ai/)
+1 -1
View File
@@ -63,7 +63,7 @@ version_path_separator = os
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = sqlite:///user/comfyui.db
sqlalchemy.url = sqlite:///user/hanzo_studio.db
[post_write_hooks]
+2 -2
View File
@@ -1,3 +1,3 @@
# ComfyUI Internal Routes
# Hanzo Studio Internal Routes
All routes under the `/internal` path are designated for **internal use by ComfyUI only**. These routes are not intended for use by external applications may change at any time without notice.
All routes under the `/internal` path are designated for **internal use by Hanzo Studio only**. These routes are not intended for use by external applications may change at any time without notice.
@@ -8,7 +8,7 @@ import os
class InternalRoutes:
'''
The top level web router for internal routes: /internal/*
The endpoints here should NOT be depended upon. It is for ComfyUI frontend use only.
The endpoints here should NOT be depended upon. It is for Hanzo Studio frontend use only.
Check README.md for more information.
'''
+2 -2
View File
@@ -12,7 +12,7 @@ class AppSettings():
try:
file = self.user_manager.get_request_user_filepath(
request,
"comfy.settings.json"
"studio.settings.json"
)
except KeyError as e:
logging.error("User settings not found.")
@@ -29,7 +29,7 @@ class AppSettings():
def save_settings(self, request, settings):
file = self.user_manager.get_request_user_filepath(
request, "comfy.settings.json")
request, "studio.settings.json")
with open(file, "w") as f:
f.write(json.dumps(settings, indent=4))
+7 -7
View File
@@ -37,7 +37,7 @@ def list_tree(base_dir: str) -> list[str]:
def prefixes_for_root(root: RootType) -> list[str]:
if root == "models":
bases: list[str] = []
for _bucket, paths in get_comfy_models_folders():
for _bucket, paths in get_studio_models_folders():
bases.extend(paths)
return [os.path.abspath(p) for p in bases]
if root == "input":
@@ -74,16 +74,16 @@ def utcnow() -> datetime:
"""Naive UTC timestamp (no tzinfo). We always treat DB datetimes as UTC."""
return datetime.now(timezone.utc).replace(tzinfo=None)
def get_comfy_models_folders() -> list[tuple[str, list[str]]]:
def get_studio_models_folders() -> list[tuple[str, list[str]]]:
"""Build a list of (folder_name, base_paths[]) categories that are configured for model locations.
We trust `folder_paths.folder_names_and_paths` and include a category if
*any* of its base paths lies under the Comfy `models_dir`.
*any* of its base paths lies under the Studio `models_dir`.
"""
targets: list[tuple[str, list[str]]] = []
models_root = os.path.abspath(folder_paths.models_dir)
for name, values in folder_paths.folder_names_and_paths.items():
paths, _exts = values[0], values[1] # NOTE: this prevents nodepacks that hackily edit folder_... from breaking ComfyUI
paths, _exts = values[0], values[1] # NOTE: this prevents nodepacks that hackily edit folder_... from breaking Hanzo Studio
if any(os.path.abspath(p).startswith(models_root + os.sep) for p in paths):
targets.append((name, paths))
return targets
@@ -152,7 +152,7 @@ def get_relative_to_root_category_path_of_asset(file_path: str) -> tuple[Literal
"""Given an absolute or relative file path, determine which root category the path belongs to:
- 'input' if the file resides under `folder_paths.get_input_directory()`
- 'output' if the file resides under `folder_paths.get_output_directory()`
- 'models' if the file resides under any base path of categories returned by `get_comfy_models_folders()`
- 'models' if the file resides under any base path of categories returned by `get_studio_models_folders()`
Returns:
(root_category, relative_path_inside_that_root)
@@ -185,7 +185,7 @@ def get_relative_to_root_category_path_of_asset(file_path: str) -> tuple[Literal
# 3) models (check deepest matching base to avoid ambiguity)
best: tuple[int, str, str] | None = None # (base_len, bucket, rel_inside_bucket)
for bucket, bases in get_comfy_models_folders():
for bucket, bases in get_studio_models_folders():
for b in bases:
base_abs = os.path.abspath(b)
if not _is_within(fp_abs, base_abs):
@@ -232,7 +232,7 @@ def normalize_tags(tags: list[str] | None) -> list[str]:
def collect_models_files() -> list[str]:
out: list[str] = []
for folder_name, bases in get_comfy_models_folders():
for folder_name, bases in get_studio_models_folders():
rel_files = folder_paths.get_filename_list(folder_name) or []
for rel_path in rel_files:
abs_path = folder_paths.get_full_path(folder_name, rel_path)
+2 -2
View File
@@ -3,7 +3,7 @@ import os
import shutil
from app.logger import log_startup_warning
from utils.install_util import get_missing_requirements_message
from comfy.cli_args import args
from studio.cli_args import args
_DB_AVAILABLE = False
Session = None
@@ -24,7 +24,7 @@ except ImportError as e:
------------------------------------------------------------------------
Error importing dependencies: {e}
{get_missing_requirements_message()}
This error is happening because ComfyUI now uses a local sqlite database.
This error is happening because Hanzo Studio now uses a local sqlite database.
------------------------------------------------------------------------
""".strip()
)
+3 -3
View File
@@ -19,7 +19,7 @@ from typing_extensions import NotRequired
from utils.install_util import get_missing_requirements_message, requirements_path
from comfy.cli_args import DEFAULT_VERSION_STRING
from studio.cli_args import DEFAULT_VERSION_STRING
import app.logger
@@ -27,7 +27,7 @@ def frontend_install_warning_message():
return f"""
{get_missing_requirements_message()}
This error is happening because the ComfyUI frontend is no longer shipped as part of the main repo but as a pip package instead.
This error is happening because the Hanzo Studio frontend is no longer shipped as part of the main repo but as a pip package instead.
""".strip()
def parse_version(version: str) -> tuple[int, int, int]:
@@ -87,7 +87,7 @@ ________________________________________________________________________
""".strip()
)
else:
logging.info("ComfyUI frontend version: {}".format(frontend_version_str))
logging.info("Hanzo Studio frontend version: {}".format(frontend_version_str))
except Exception as e:
logging.error(f"Failed to check frontend version: {e}")
+2 -2
View File
@@ -7,7 +7,7 @@ import time
import logging
import folder_paths
import glob
import comfy.utils
import studio.utils
from aiohttp import web
from PIL import Image
from io import BytesIO
@@ -180,7 +180,7 @@ class ModelFileManager:
if safetensors_file:
safetensors_filepath = os.path.join(dirname, safetensors_file)
header = comfy.utils.safetensors_header(safetensors_filepath, max_size=8*1024*1024)
header = studio.utils.safetensors_header(safetensors_filepath, max_size=8*1024*1024)
if header:
safetensors_metadata = json.loads(header)
safetensors_images = safetensors_metadata.get("__metadata__", {}).get("ssmd_cover_images", None)
+2 -2
View File
@@ -4,9 +4,9 @@ from aiohttp import web
from typing import TYPE_CHECKING, TypedDict
if TYPE_CHECKING:
from comfy_api.latest._io_public import NodeReplace
from studio_api.latest._io_public import NodeReplace
from comfy_execution.graph_utils import is_link
from studio_execution.graph_utils import is_link
import nodes
class NodeStruct(TypedDict):
+2 -2
View File
@@ -53,7 +53,7 @@ class SubgraphManager:
return entry_id, entry
async def load_entry_data(self, entry: SubgraphEntry):
with open(entry['path'], 'r') as f:
with open(entry['path'], 'r', encoding='utf-8') as f:
entry['data'] = f.read()
return entry
@@ -100,7 +100,7 @@ class SubgraphManager:
if os.path.exists(blueprints_dir):
for file in glob.glob(os.path.join(blueprints_dir, "*.json")):
file = file.replace('\\', '/')
entry_id, entry = self._create_entry(file, Source.templates, "comfyui")
entry_id, entry = self._create_entry(file, Source.templates, "studio")
subgraphs_dict[entry_id] = entry
self.cached_blueprint_subgraphs = subgraphs_dict
+28 -5
View File
@@ -8,7 +8,7 @@ import shutil
import logging
from aiohttp import web
from urllib import parse
from comfy.cli_args import args
from studio.cli_args import args
import folder_paths
from .app_settings import AppSettings
from typing import TypedDict
@@ -57,8 +57,22 @@ class UserManager():
def get_request_user_id(self, request):
user = "default"
if args.multi_user and "comfy-user" in request.headers:
user = request.headers["comfy-user"]
# If IAM auth is active, extract user from IAM context
iam_user = request.get("iam_user")
if iam_user and iam_user.get("sub") and iam_user["sub"] != "local":
user = iam_user["sub"]
# A token subject must never escape into a System User namespace.
if user.startswith(folder_paths.SYSTEM_USER_PREFIX):
raise KeyError("Unknown user: " + user)
# Auto-register IAM users
if user not in self.users:
display = iam_user.get("name") or iam_user.get("email") or user
self.users[user] = display
return user
if args.multi_user and "studio-user" in request.headers:
user = request.headers["studio-user"]
# Block System Users (use same error message to prevent probing)
if user.startswith(folder_paths.SYSTEM_USER_PREFIX):
raise KeyError("Unknown user: " + user)
@@ -68,14 +82,23 @@ class UserManager():
return user
def get_request_org_id(self, request) -> str | None:
"""Extract org_id from IAM context or fallback to CLI arg."""
iam_user = request.get("iam_user")
if iam_user and iam_user.get("org_id"):
return iam_user["org_id"]
return folder_paths.get_org_id()
def get_request_user_filepath(self, request, file, type="userdata", create_dir=True):
if type == "userdata":
root_dir = folder_paths.get_user_directory()
# Use org-scoped user directory if multi-tenant
org_id = self.get_request_org_id(request)
root_dir = folder_paths.get_org_user_directory(org_id)
else:
raise KeyError("Unknown filepath type:" + type)
user = self.get_request_user_id(request)
user_root = folder_paths.get_public_user_directory(user)
user_root = folder_paths.get_public_user_directory(user, org_id)
if user_root is None:
return None
path = user_root
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"revision": 0, "last_node_id": 15, "last_link_id": 0, "nodes": [{"id": 15, "type": "24d8bbfd-39d4-4774-bff0-3de40cc7a471", "pos": [-1490, 2040], "size": [400, 260], "flags": {}, "order": 0, "mode": 0, "inputs": [{"name": "prompt", "type": "STRING", "widget": {"name": "prompt"}, "link": null}, {"label": "reference images", "name": "images", "type": "IMAGE", "link": null}], "outputs": [{"name": "STRING", "type": "STRING", "links": null}], "title": "Prompt Enhance", "properties": {"proxyWidgets": [["-1", "prompt"]], "cnr_id": "comfy-core", "ver": "0.14.1"}, "widgets_values": [""]}], "links": [], "version": 0.4, "definitions": {"subgraphs": [{"id": "24d8bbfd-39d4-4774-bff0-3de40cc7a471", "version": 1, "state": {"lastGroupId": 0, "lastNodeId": 15, "lastLinkId": 14, "lastRerouteId": 0}, "revision": 0, "config": {}, "name": "Prompt Enhance", "inputNode": {"id": -10, "bounding": [-2170, 2110, 138.876953125, 80]}, "outputNode": {"id": -20, "bounding": [-640, 2110, 120, 60]}, "inputs": [{"id": "aeab7216-00e0-4528-a09b-bba50845c5a6", "name": "prompt", "type": "STRING", "linkIds": [11], "pos": [-2051.123046875, 2130]}, {"id": "7b73fd36-aa31-4771-9066-f6c83879994b", "name": "images", "type": "IMAGE", "linkIds": [14], "label": "reference images", "pos": [-2051.123046875, 2150]}], "outputs": [{"id": "c7b0d930-68a1-48d1-b496-0519e5837064", "name": "STRING", "type": "STRING", "linkIds": [13], "pos": [-620, 2130]}], "widgets": [], "nodes": [{"id": 11, "type": "GeminiNode", "pos": [-1560, 1990], "size": [470, 470], "flags": {}, "order": 0, "mode": 0, "inputs": [{"localized_name": "images", "name": "images", "shape": 7, "type": "IMAGE", "link": 14}, {"localized_name": "audio", "name": "audio", "shape": 7, "type": "AUDIO", "link": null}, {"localized_name": "video", "name": "video", "shape": 7, "type": "VIDEO", "link": null}, {"localized_name": "files", "name": "files", "shape": 7, "type": "GEMINI_INPUT_FILES", "link": null}, {"localized_name": "prompt", "name": "prompt", "type": "STRING", "widget": {"name": "prompt"}, "link": 11}, {"localized_name": "model", "name": "model", "type": "COMBO", "widget": {"name": "model"}, "link": null}, {"localized_name": "seed", "name": "seed", "type": "INT", "widget": {"name": "seed"}, "link": null}, {"localized_name": "system_prompt", "name": "system_prompt", "shape": 7, "type": "STRING", "widget": {"name": "system_prompt"}, "link": null}], "outputs": [{"localized_name": "STRING", "name": "STRING", "type": "STRING", "links": [13]}], "properties": {"cnr_id": "comfy-core", "ver": "0.14.1", "Node name for S&R": "GeminiNode"}, "widgets_values": ["", "gemini-3-pro-preview", 42, "randomize", "You are an expert in prompt writing.\nBased on the input, rewrite the user's input into a detailed prompt.\nincluding camera settings, lighting, composition, and style.\nReturn the prompt only"], "color": "#432", "bgcolor": "#653"}], "groups": [], "links": [{"id": 11, "origin_id": -10, "origin_slot": 0, "target_id": 11, "target_slot": 4, "type": "STRING"}, {"id": 13, "origin_id": 11, "origin_slot": 0, "target_id": -20, "target_slot": 0, "type": "STRING"}, {"id": 14, "origin_id": -10, "origin_slot": 1, "target_id": 11, "target_slot": 0, "type": "IMAGE"}], "extra": {"workflowRendererVersion": "LG"}, "category": "Text generation/Prompt enhance"}]}, "extra": {}}
{"revision": 0, "last_node_id": 15, "last_link_id": 0, "nodes": [{"id": 15, "type": "24d8bbfd-39d4-4774-bff0-3de40cc7a471", "pos": [-1490, 2040], "size": [400, 260], "flags": {}, "order": 0, "mode": 0, "inputs": [{"name": "prompt", "type": "STRING", "widget": {"name": "prompt"}, "link": null}, {"label": "reference images", "name": "images", "type": "IMAGE", "link": null}], "outputs": [{"name": "STRING", "type": "STRING", "links": null}], "title": "Prompt Enhance", "properties": {"proxyWidgets": [["-1", "prompt"]], "cnr_id": "studio-core", "ver": "0.14.1"}, "widgets_values": [""]}], "links": [], "version": 0.4, "definitions": {"subgraphs": [{"id": "24d8bbfd-39d4-4774-bff0-3de40cc7a471", "version": 1, "state": {"lastGroupId": 0, "lastNodeId": 15, "lastLinkId": 14, "lastRerouteId": 0}, "revision": 0, "config": {}, "name": "Prompt Enhance", "inputNode": {"id": -10, "bounding": [-2170, 2110, 138.876953125, 80]}, "outputNode": {"id": -20, "bounding": [-640, 2110, 120, 60]}, "inputs": [{"id": "aeab7216-00e0-4528-a09b-bba50845c5a6", "name": "prompt", "type": "STRING", "linkIds": [11], "pos": [-2051.123046875, 2130]}, {"id": "7b73fd36-aa31-4771-9066-f6c83879994b", "name": "images", "type": "IMAGE", "linkIds": [14], "label": "reference images", "pos": [-2051.123046875, 2150]}], "outputs": [{"id": "c7b0d930-68a1-48d1-b496-0519e5837064", "name": "STRING", "type": "STRING", "linkIds": [13], "pos": [-620, 2130]}], "widgets": [], "nodes": [{"id": 11, "type": "GeminiNode", "pos": [-1560, 1990], "size": [470, 470], "flags": {}, "order": 0, "mode": 0, "inputs": [{"localized_name": "images", "name": "images", "shape": 7, "type": "IMAGE", "link": 14}, {"localized_name": "audio", "name": "audio", "shape": 7, "type": "AUDIO", "link": null}, {"localized_name": "video", "name": "video", "shape": 7, "type": "VIDEO", "link": null}, {"localized_name": "files", "name": "files", "shape": 7, "type": "GEMINI_INPUT_FILES", "link": null}, {"localized_name": "prompt", "name": "prompt", "type": "STRING", "widget": {"name": "prompt"}, "link": 11}, {"localized_name": "model", "name": "model", "type": "COMBO", "widget": {"name": "model"}, "link": null}, {"localized_name": "seed", "name": "seed", "type": "INT", "widget": {"name": "seed"}, "link": null}, {"localized_name": "system_prompt", "name": "system_prompt", "shape": 7, "type": "STRING", "widget": {"name": "system_prompt"}, "link": null}], "outputs": [{"localized_name": "STRING", "name": "STRING", "type": "STRING", "links": [13]}], "properties": {"cnr_id": "studio-core", "ver": "0.14.1", "Node name for S&R": "GeminiNode"}, "widgets_values": ["", "gemini-3-pro-preview", 42, "randomize", "You are an expert in prompt writing.\nBased on the input, rewrite the user's input into a detailed prompt.\nincluding camera settings, lighting, composition, and style.\nReturn the prompt only"], "color": "#432", "bgcolor": "#653"}], "groups": [], "links": [{"id": 11, "origin_id": -10, "origin_slot": 0, "target_id": 11, "target_slot": 4, "type": "STRING"}, {"id": 13, "origin_id": 11, "origin_slot": 0, "target_id": -20, "target_slot": 0, "type": "STRING"}, {"id": 14, "origin_id": -10, "origin_slot": 1, "target_id": 11, "target_slot": 0, "type": "IMAGE"}], "extra": {"workflowRendererVersion": "LG"}, "category": "Text generation/Prompt enhance"}]}, "extra": {}}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"revision": 0, "last_node_id": 13, "last_link_id": 0, "nodes": [{"id": 13, "type": "cf95b747-3e17-46cb-8097-cac60ff9b2e1", "pos": [1120, 330], "size": [240, 58], "flags": {}, "order": 3, "mode": 0, "inputs": [{"localized_name": "video", "name": "video", "type": "VIDEO", "link": null}, {"name": "model_name", "type": "COMBO", "widget": {"name": "model_name"}, "link": null}], "outputs": [{"localized_name": "VIDEO", "name": "VIDEO", "type": "VIDEO", "links": []}], "title": "Video Upscale(GAN x4)", "properties": {"proxyWidgets": [["-1", "model_name"]], "cnr_id": "comfy-core", "ver": "0.14.1"}, "widgets_values": ["RealESRGAN_x4plus.safetensors"]}], "links": [], "version": 0.4, "definitions": {"subgraphs": [{"id": "cf95b747-3e17-46cb-8097-cac60ff9b2e1", "version": 1, "state": {"lastGroupId": 0, "lastNodeId": 13, "lastLinkId": 19, "lastRerouteId": 0}, "revision": 0, "config": {}, "name": "Video Upscale(GAN x4)", "inputNode": {"id": -10, "bounding": [550, 460, 120, 80]}, "outputNode": {"id": -20, "bounding": [1490, 460, 120, 60]}, "inputs": [{"id": "666d633e-93e7-42dc-8d11-2b7b99b0f2a6", "name": "video", "type": "VIDEO", "linkIds": [10], "localized_name": "video", "pos": [650, 480]}, {"id": "2e23a087-caa8-4d65-99e6-662761aa905a", "name": "model_name", "type": "COMBO", "linkIds": [19], "pos": [650, 500]}], "outputs": [{"id": "0c1768ea-3ec2-412f-9af6-8e0fa36dae70", "name": "VIDEO", "type": "VIDEO", "linkIds": [15], "localized_name": "VIDEO", "pos": [1510, 480]}], "widgets": [], "nodes": [{"id": 2, "type": "ImageUpscaleWithModel", "pos": [1110, 450], "size": [320, 46], "flags": {}, "order": 1, "mode": 0, "inputs": [{"localized_name": "upscale_model", "name": "upscale_model", "type": "UPSCALE_MODEL", "link": 1}, {"localized_name": "image", "name": "image", "type": "IMAGE", "link": 14}], "outputs": [{"localized_name": "IMAGE", "name": "IMAGE", "type": "IMAGE", "links": [13]}], "properties": {"cnr_id": "comfy-core", "ver": "0.10.0", "Node name for S&R": "ImageUpscaleWithModel"}}, {"id": 11, "type": "CreateVideo", "pos": [1110, 550], "size": [320, 78], "flags": {}, "order": 3, "mode": 0, "inputs": [{"localized_name": "images", "name": "images", "type": "IMAGE", "link": 13}, {"localized_name": "audio", "name": "audio", "shape": 7, "type": "AUDIO", "link": 16}, {"localized_name": "fps", "name": "fps", "type": "FLOAT", "widget": {"name": "fps"}, "link": 12}], "outputs": [{"localized_name": "VIDEO", "name": "VIDEO", "type": "VIDEO", "links": [15]}], "properties": {"cnr_id": "comfy-core", "ver": "0.10.0", "Node name for S&R": "CreateVideo"}, "widgets_values": [30]}, {"id": 10, "type": "GetVideoComponents", "pos": [1110, 330], "size": [320, 70], "flags": {}, "order": 2, "mode": 0, "inputs": [{"localized_name": "video", "name": "video", "type": "VIDEO", "link": 10}], "outputs": [{"localized_name": "images", "name": "images", "type": "IMAGE", "links": [14]}, {"localized_name": "audio", "name": "audio", "type": "AUDIO", "links": [16]}, {"localized_name": "fps", "name": "fps", "type": "FLOAT", "links": [12]}], "properties": {"cnr_id": "comfy-core", "ver": "0.10.0", "Node name for S&R": "GetVideoComponents"}}, {"id": 1, "type": "UpscaleModelLoader", "pos": [750, 450], "size": [280, 60], "flags": {}, "order": 0, "mode": 0, "inputs": [{"localized_name": "model_name", "name": "model_name", "type": "COMBO", "widget": {"name": "model_name"}, "link": 19}], "outputs": [{"localized_name": "UPSCALE_MODEL", "name": "UPSCALE_MODEL", "type": "UPSCALE_MODEL", "links": [1]}], "properties": {"cnr_id": "comfy-core", "ver": "0.10.0", "Node name for S&R": "UpscaleModelLoader", "models": [{"name": "RealESRGAN_x4plus.safetensors", "url": "https://huggingface.co/Comfy-Org/Real-ESRGAN_repackaged/resolve/main/RealESRGAN_x4plus.safetensors", "directory": "upscale_models"}]}, "widgets_values": ["RealESRGAN_x4plus.safetensors"]}], "groups": [], "links": [{"id": 1, "origin_id": 1, "origin_slot": 0, "target_id": 2, "target_slot": 0, "type": "UPSCALE_MODEL"}, {"id": 14, "origin_id": 10, "origin_slot": 0, "target_id": 2, "target_slot": 1, "type": "IMAGE"}, {"id": 13, "origin_id": 2, "origin_slot": 0, "target_id": 11, "target_slot": 0, "type": "IMAGE"}, {"id": 16, "origin_id": 10, "origin_slot": 1, "target_id": 11, "target_slot": 1, "type": "AUDIO"}, {"id": 12, "origin_id": 10, "origin_slot": 2, "target_id": 11, "target_slot": 2, "type": "FLOAT"}, {"id": 10, "origin_id": -10, "origin_slot": 0, "target_id": 10, "target_slot": 0, "type": "VIDEO"}, {"id": 15, "origin_id": 11, "origin_slot": 0, "target_id": -20, "target_slot": 0, "type": "VIDEO"}, {"id": 19, "origin_id": -10, "origin_slot": 1, "target_id": 1, "target_slot": 0, "type": "COMBO"}], "extra": {"workflowRendererVersion": "LG"}, "category": "Video generation and editing/Enhance video"}]}, "extra": {}}
{"revision": 0, "last_node_id": 13, "last_link_id": 0, "nodes": [{"id": 13, "type": "cf95b747-3e17-46cb-8097-cac60ff9b2e1", "pos": [1120, 330], "size": [240, 58], "flags": {}, "order": 3, "mode": 0, "inputs": [{"localized_name": "video", "name": "video", "type": "VIDEO", "link": null}, {"name": "model_name", "type": "COMBO", "widget": {"name": "model_name"}, "link": null}], "outputs": [{"localized_name": "VIDEO", "name": "VIDEO", "type": "VIDEO", "links": []}], "title": "Video Upscale(GAN x4)", "properties": {"proxyWidgets": [["-1", "model_name"]], "cnr_id": "studio-core", "ver": "0.14.1"}, "widgets_values": ["RealESRGAN_x4plus.safetensors"]}], "links": [], "version": 0.4, "definitions": {"subgraphs": [{"id": "cf95b747-3e17-46cb-8097-cac60ff9b2e1", "version": 1, "state": {"lastGroupId": 0, "lastNodeId": 13, "lastLinkId": 19, "lastRerouteId": 0}, "revision": 0, "config": {}, "name": "Video Upscale(GAN x4)", "inputNode": {"id": -10, "bounding": [550, 460, 120, 80]}, "outputNode": {"id": -20, "bounding": [1490, 460, 120, 60]}, "inputs": [{"id": "666d633e-93e7-42dc-8d11-2b7b99b0f2a6", "name": "video", "type": "VIDEO", "linkIds": [10], "localized_name": "video", "pos": [650, 480]}, {"id": "2e23a087-caa8-4d65-99e6-662761aa905a", "name": "model_name", "type": "COMBO", "linkIds": [19], "pos": [650, 500]}], "outputs": [{"id": "0c1768ea-3ec2-412f-9af6-8e0fa36dae70", "name": "VIDEO", "type": "VIDEO", "linkIds": [15], "localized_name": "VIDEO", "pos": [1510, 480]}], "widgets": [], "nodes": [{"id": 2, "type": "ImageUpscaleWithModel", "pos": [1110, 450], "size": [320, 46], "flags": {}, "order": 1, "mode": 0, "inputs": [{"localized_name": "upscale_model", "name": "upscale_model", "type": "UPSCALE_MODEL", "link": 1}, {"localized_name": "image", "name": "image", "type": "IMAGE", "link": 14}], "outputs": [{"localized_name": "IMAGE", "name": "IMAGE", "type": "IMAGE", "links": [13]}], "properties": {"cnr_id": "studio-core", "ver": "0.10.0", "Node name for S&R": "ImageUpscaleWithModel"}}, {"id": 11, "type": "CreateVideo", "pos": [1110, 550], "size": [320, 78], "flags": {}, "order": 3, "mode": 0, "inputs": [{"localized_name": "images", "name": "images", "type": "IMAGE", "link": 13}, {"localized_name": "audio", "name": "audio", "shape": 7, "type": "AUDIO", "link": 16}, {"localized_name": "fps", "name": "fps", "type": "FLOAT", "widget": {"name": "fps"}, "link": 12}], "outputs": [{"localized_name": "VIDEO", "name": "VIDEO", "type": "VIDEO", "links": [15]}], "properties": {"cnr_id": "studio-core", "ver": "0.10.0", "Node name for S&R": "CreateVideo"}, "widgets_values": [30]}, {"id": 10, "type": "GetVideoComponents", "pos": [1110, 330], "size": [320, 70], "flags": {}, "order": 2, "mode": 0, "inputs": [{"localized_name": "video", "name": "video", "type": "VIDEO", "link": 10}], "outputs": [{"localized_name": "images", "name": "images", "type": "IMAGE", "links": [14]}, {"localized_name": "audio", "name": "audio", "type": "AUDIO", "links": [16]}, {"localized_name": "fps", "name": "fps", "type": "FLOAT", "links": [12]}], "properties": {"cnr_id": "studio-core", "ver": "0.10.0", "Node name for S&R": "GetVideoComponents"}}, {"id": 1, "type": "UpscaleModelLoader", "pos": [750, 450], "size": [280, 60], "flags": {}, "order": 0, "mode": 0, "inputs": [{"localized_name": "model_name", "name": "model_name", "type": "COMBO", "widget": {"name": "model_name"}, "link": 19}], "outputs": [{"localized_name": "UPSCALE_MODEL", "name": "UPSCALE_MODEL", "type": "UPSCALE_MODEL", "links": [1]}], "properties": {"cnr_id": "studio-core", "ver": "0.10.0", "Node name for S&R": "UpscaleModelLoader", "models": [{"name": "RealESRGAN_x4plus.safetensors", "url": "https://huggingface.co/Comfy-Org/Real-ESRGAN_repackaged/resolve/main/RealESRGAN_x4plus.safetensors", "directory": "upscale_models"}]}, "widgets_values": ["RealESRGAN_x4plus.safetensors"]}], "groups": [], "links": [{"id": 1, "origin_id": 1, "origin_slot": 0, "target_id": 2, "target_slot": 0, "type": "UPSCALE_MODEL"}, {"id": 14, "origin_id": 10, "origin_slot": 0, "target_id": 2, "target_slot": 1, "type": "IMAGE"}, {"id": 13, "origin_id": 2, "origin_slot": 0, "target_id": 11, "target_slot": 0, "type": "IMAGE"}, {"id": 16, "origin_id": 10, "origin_slot": 1, "target_id": 11, "target_slot": 1, "type": "AUDIO"}, {"id": 12, "origin_id": 10, "origin_slot": 2, "target_id": 11, "target_slot": 2, "type": "FLOAT"}, {"id": 10, "origin_id": -10, "origin_slot": 0, "target_id": 10, "target_slot": 0, "type": "VIDEO"}, {"id": 15, "origin_id": 11, "origin_slot": 0, "target_id": -20, "target_slot": 0, "type": "VIDEO"}, {"id": 19, "origin_id": -10, "origin_slot": 1, "target_id": 1, "target_slot": 0, "type": "COMBO"}], "extra": {"workflowRendererVersion": "LG"}, "category": "Video generation and editing/Enhance video"}]}, "extra": {}}
+84
View File
@@ -0,0 +1,84 @@
#!/bin/bash
# Apply Hanzo Studio branding to the prebuilt frontend package (comfyui-frontend-package).
# Runs during the Docker build after pip install, and can be run against a venv
# for local dev. Idempotent: safe to run more than once.
set -e
STATIC_DIR=$(python3 -c "import comfyui_frontend_package, importlib.resources; print(str(importlib.resources.files(comfyui_frontend_package) / 'static'))")
BRANDING_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=== Hanzo Studio Branding ==="
echo "Target: $STATIC_DIR"
# --- 1. Replace in-app logos ---
echo "[1/7] Replacing logos..."
for logo_file in comfy-logo-single.svg comfy-logo-mono.svg comfy-cloud-logo.svg; do
target="$STATIC_DIR/assets/images/$logo_file"
if [ -f "$target" ]; then
cp "$BRANDING_DIR/hanzo-logo.svg" "$target"
echo " Replaced $logo_file"
else
echo " Not found: $logo_file (skipping)"
fi
done
# --- 2. Install favicon (Hanzo mark) ---
# The frontend sets the runtime favicon to /assets/favicon.ico via useFavicon();
# the web root copies cover the pre-boot page load and the default /favicon.ico
# request. During a generation it instead cycles the 10 progress frames under
# assets/images/favicon_progress_16x16/, so those must be Hanzo-branded too or the
# tab reverts to the Comfy mark while a job runs. All PNG/ICO are committed
# artifacts (rendered by make_favicon.py / make_progress_favicons.py) so the Docker
# image needs no rasterizer.
echo "[2/7] Installing favicon..."
cp "$BRANDING_DIR/favicon.ico" "$STATIC_DIR/assets/favicon.ico"
cp "$BRANDING_DIR/favicon.ico" "$STATIC_DIR/favicon.ico"
cp "$BRANDING_DIR/favicon.svg" "$STATIC_DIR/favicon.svg"
progress_dst="$STATIC_DIR/assets/images/favicon_progress_16x16"
if [ -d "$progress_dst" ]; then
cp "$BRANDING_DIR"/favicon_progress_16x16/frame_*.png "$progress_dst/"
echo " Installed favicon.ico + favicon.svg + progress frames"
else
echo " Installed favicon.ico + favicon.svg (progress dir absent, skipped)"
fi
# --- 3. Patch index.html (title + icon link + loading text) ---
echo "[3/7] Patching index.html..."
if [ -f "$STATIC_DIR/index.html" ]; then
# Rename the title and inject icon links (the upstream head has none).
sed -i 's#<title>ComfyUI</title>#<title>Hanzo Studio</title><link rel="icon" type="image/svg+xml" href="favicon.svg"/><link rel="alternate icon" href="favicon.ico"/>#g' "$STATIC_DIR/index.html"
sed -i 's/Loading ComfyUI/Loading Hanzo Studio/g' "$STATIC_DIR/index.html"
sed -i 's/content="ComfyUI/content="Hanzo Studio/g' "$STATIC_DIR/index.html"
echo " Patched index.html"
fi
# --- 4. Patch manifest JSON files ---
echo "[4/7] Patching manifests..."
find "$STATIC_DIR" -name "manifest*.json" -exec sed -i 's/"ComfyUI"/"Hanzo Studio"/g' {} \;
find "$STATIC_DIR" -name "manifest*.json" -exec sed -i 's/studio\.org/hanzo.ai/g' {} \;
echo " Done"
# --- 5. Smart JS/CSS/JSON display-string patching ---
echo "[5/7] Smart JS patching..."
python3 "$BRANDING_DIR/patch_frontend.py" "$STATIC_DIR"
# --- 6. Replace the inline sidebar logo (ComfyLogo Vue component) ---
# This is the top-left in-app mark, an inline SVG compiled into a JS chunk — a
# different asset from the file-based logos handled in step 1.
echo "[6/7] Replacing in-app sidebar logo..."
python3 "$BRANDING_DIR/patch_sidebar_logo.py" "$STATIC_DIR"
# --- 7. Apply the black + Hanzo-purple theme ---
# Installs hanzo-theme.css, injects it as the last stylesheet in index.html, and
# rewrites the "dark" palette's canvas clear color to pure black.
echo "[7/8] Applying black + purple theme..."
python3 "$BRANDING_DIR/patch_theme.py" "$STATIC_DIR" "$BRANDING_DIR"
# --- 8. Seed the default 25-tab fashion workspace ---
# Injects a <head> bootstrap that pre-populates the localStorage keys the
# frontend reads to restore open workflow tabs. Idempotent; bump SEED_VERSION
# in patch_workspace_seed.py to force existing profiles to re-seed once.
echo "[8/8] Seeding default workspace tabs..."
python3 "$BRANDING_DIR/patch_workspace_seed.py" "$STATIC_DIR"
echo "=== Hanzo Studio branding complete ==="
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<rect width="64" height="64" rx="8" fill="#000000"/>
<g transform="translate(8, 8) scale(0.716)">
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

+10
View File
@@ -0,0 +1,10 @@
<svg width="520" height="520" viewBox="0 0 520 520" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="520" height="520" rx="120" fill="#111111"/>
<g transform="translate(100, 100) scale(4.78)">
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 582 B

+64
View File
@@ -0,0 +1,64 @@
/* Hanzo Studio black surface + Hanzo-purple accent theme.
* Minimal override: only background/surface/accent tokens are touched so the
* rest of the frontend's dark palette (text, node colors, borders) is intact.
* Loaded LAST in index.html so it beats PrimeVue's runtime-injected tokens;
* every declaration is !important because those tokens are injected after this
* sheet at boot. Colors chosen for WCAG AA contrast on black:
* accent #8B5CF6 on #000 -> 6.2:1 (AA normal text)
* button #ffffff on #7C3AED -> 5.25:1 (AA normal text)
*/
:root {
/* --- App shell (ComfyUI-native vars from the "dark" palette) --- */
--bg-color: #000000 !important;
--fg-color: #ffffff !important;
--comfy-menu-bg: #0a0a0a !important;
--comfy-menu-secondary-bg: #141414 !important;
--comfy-input-bg: #0a0a0a !important;
--content-bg: #0a0a0a !important;
--content-hover-bg: #171717 !important;
--border-color: #1f1f1f !important;
--tr-even-bg-color: #0a0a0a !important;
--tr-odd-bg-color: #141414 !important;
/* --- PrimeVue surface ramp: darken the panel/sidebar/topbar shades only.
* 500..900 are the backgrounds; 100/300 stay light (used for text/borders). */
--p-surface-900: #000000 !important;
--p-surface-800: #0a0a0a !important;
--p-surface-700: #141414 !important;
--p-surface-600: #1c1c1c !important;
--p-surface-500: #242424 !important;
/* --- PrimeVue semantic surfaces --- */
--p-content-background: #000000 !important;
--p-content-hover-background: #171717 !important;
--p-overlay-modal-background: #0a0a0a !important;
--p-overlay-popover-background: #0a0a0a !important;
--p-mask-background: rgba(0, 0, 0, 0.72) !important;
/* --- Hanzo purple accent --- */
--p-primary-color: #8b5cf6 !important; /* icons/links/active — AA on black */
--p-primary-contrast-color: #ffffff !important;
--p-primary-400: #a78bfa !important;
--p-primary-500: #8b5cf6 !important;
--p-primary-600: #7c3aed !important;
--p-focus-ring-color: #8b5cf6 !important;
--p-highlight-background: rgba(124, 58, 237, 0.24) !important;
--p-highlight-focus-background: rgba(124, 58, 237, 0.32) !important;
--p-highlight-color: #ede9fe !important;
/* --- Primary buttons: white on deeper violet keeps AA (5.25:1) --- */
--p-button-primary-background: #7c3aed !important;
--p-button-primary-hover-background: #6d28d9 !important;
--p-button-primary-active-background: #5b21b6 !important;
--p-button-primary-border-color: #7c3aed !important;
--p-button-primary-color: #ffffff !important;
}
/* Pure-black canvas gutter behind the litegraph <canvas>. */
html,
body,
body.litegraph,
#vue-app {
background-color: #000000 !important;
}
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""Render branding/favicon.svg to a multi-resolution favicon.ico.
Source of truth is favicon.svg (the Hanzo mark). The .ico is a committed build
artifact so that apply-branding.sh can install it without needing a rasterizer
in the Docker image. Re-run this whenever favicon.svg changes:
rsvg-convert must be on PATH (librsvg2-bin); Pillow provides ICO packing.
python3 branding/make_favicon.py
"""
import os
import subprocess
import tempfile
from PIL import Image
HERE = os.path.dirname(os.path.abspath(__file__))
SVG = os.path.join(HERE, "favicon.svg")
ICO = os.path.join(HERE, "favicon.ico")
SIZES = [16, 32, 48, 64, 128, 256]
def render_png(size: int, path: str) -> None:
subprocess.run(
["rsvg-convert", "-w", str(size), "-h", str(size), SVG, "-o", path],
check=True,
)
def main() -> None:
with tempfile.TemporaryDirectory() as tmp:
frames = []
for size in SIZES:
png = os.path.join(tmp, f"favicon-{size}.png")
render_png(size, png)
frames.append(Image.open(png).convert("RGBA"))
base = frames[-1]
base.save(ICO, format="ICO", sizes=[(s, s) for s in SIZES])
print(f"Wrote {ICO} ({os.path.getsize(ICO)} bytes) sizes={SIZES}")
if __name__ == "__main__":
main()
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Render the 10 animated progress-favicon frames with the Hanzo mark.
During a generation the frontend cycles
`/assets/images/favicon_progress_16x16/frame_{0..9}.png` as the browser-tab
favicon. Upstream ships ComfyUI-branded frames, so the tab reverts to the Comfy
mark whenever a job runs. These frames are the Hanzo mark (white on a black tile,
matching favicon.svg) with a Hanzo-purple progress ring that fills 0 -> 100%.
Like favicon.ico, the PNGs are committed build artifacts so apply-branding.sh can
install them without a rasterizer in the Docker image. Re-run only if the mark or
palette changes: python3 branding/make_progress_favicons.py
"""
import os
from PIL import Image, ImageDraw
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "favicon_progress_16x16")
SS = 128 # supersampled canvas, downsampled to 16x16
FRAMES = 10
MARK_SCALE = 0.92
RING_BBOX = (12, 12, SS - 12, SS - 12)
RING_WIDTH = 10
BG = (0, 0, 0, 255) # black tile, like favicon.svg
MARK = (255, 255, 255, 255) # white Hanzo mark
RING = (139, 92, 246, 255) # Hanzo purple #8B5CF6
# Hanzo mark: five subpaths from branding/hanzo-logo.svg, as polygons (0..67 space).
POLYS = [
[(22.21, 67), (22.21, 44.6369), (0, 44.6369), (0, 67)],
[(66.7038, 22.3184), (22.2534, 22.3184), (0.0878906, 44.6367), (44.4634, 44.6367)],
[(22.21, 0), (0, 0), (0, 22.3184), (22.21, 22.3184)],
[(66.7198, 0), (44.5098, 0), (44.5098, 22.3184), (66.7198, 22.3184)],
[(66.7198, 67), (66.7198, 44.6369), (44.5098, 44.6369), (44.5098, 67)],
]
MARK_W = 66.7198
MARK_H = 67.0
OFF_X = (SS - MARK_W * MARK_SCALE) / 2
OFF_Y = (SS - MARK_H * MARK_SCALE) / 2
def draw_frame(frac: float) -> Image.Image:
img = Image.new("RGBA", (SS, SS), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
d.rounded_rectangle((0, 0, SS - 1, SS - 1), radius=20, fill=BG)
for poly in POLYS:
pts = [(x * MARK_SCALE + OFF_X, y * MARK_SCALE + OFF_Y) for x, y in poly]
d.polygon(pts, fill=MARK)
if frac > 0:
d.arc(RING_BBOX, start=-90, end=-90 + frac * 360, fill=RING, width=RING_WIDTH)
return img.resize((16, 16), Image.LANCZOS)
def main() -> None:
os.makedirs(OUT, exist_ok=True)
for i in range(FRAMES):
frac = (i + 1) / FRAMES
draw_frame(frac).save(os.path.join(OUT, f"frame_{i}.png"))
print(f"Wrote {FRAMES} progress frames to {OUT}")
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More