From 46ddd9ab3b280b978700aeaf0224a7c8d69c5c8b Mon Sep 17 00:00:00 2001 From: Quango Date: Sun, 14 Jun 2026 07:25:43 -0600 Subject: [PATCH 1/2] Add cross-platform ComfyUI and FaceFusion environment Co-authored-by: Cursor --- .gitignore | 16 + GenAI/.gitignore | 64 +++ GenAI/CHANGELOG.md | 18 + GenAI/README.md | 70 +++ GenAI/config/facefusion.example.ini | 10 + GenAI/config/local-paths.example.json | 21 + GenAI/config/model-manifest.example.json | 19 + GenAI/config/settings.example.env | 20 + GenAI/docs/00-start-here.md | 52 +++ GenAI/docs/01-system-overview.md | 60 +++ GenAI/docs/02-installation-overview.md | 52 +++ GenAI/docs/architecture/ADR-001-comfyui.md | 52 +++ GenAI/docs/architecture/ADR-002-facefusion.md | 57 +++ .../ADR-003-environment-isolation.md | 39 ++ .../ADR-004-upstream-management.md | 44 ++ GenAI/docs/facefusion/01-overview.md | 17 + .../facefusion/02-first-image-face-swap.md | 22 + GenAI/docs/facefusion/03-video-face-swap.md | 18 + GenAI/docs/facefusion/04-multiple-faces.md | 7 + .../05-face-selection-and-indexing.md | 5 + .../06-enhancement-and-output-quality.md | 7 + .../07-batch-and-headless-operation.md | 18 + GenAI/docs/facefusion/08-performance.md | 10 + GenAI/docs/facefusion/09-troubleshooting.md | 11 + .../10-consent-provenance-and-safety.md | 24 + .../docs/macos/apple-silicon-installation.md | 25 + GenAI/docs/macos/intel-mac-assessment.md | 12 + GenAI/docs/macos/performance.md | 17 + GenAI/docs/macos/troubleshooting.md | 10 + .../docs/stable-diffusion/01-fundamentals.md | 30 ++ GenAI/docs/stable-diffusion/02-first-image.md | 34 ++ .../stable-diffusion/03-prompt-engineering.md | 28 ++ .../04-models-checkpoints-and-vaes.md | 17 + .../05-loras-and-embeddings.md | 12 + .../06-samplers-schedulers-steps-and-cfg.md | 16 + .../stable-diffusion/07-image-to-image.md | 9 + .../08-inpainting-and-outpainting.md | 11 + .../09-controlnet-and-image-guidance.md | 9 + .../10-upscaling-and-restoration.md | 12 + .../11-comfyui-fundamentals.md | 18 + .../12-building-comfyui-workflows.md | 9 + .../13-performance-and-memory.md | 16 + .../stable-diffusion/14-model-management.md | 14 + .../stable-diffusion/15-troubleshooting.md | 12 + GenAI/docs/testing-validation-matrix.md | 28 ++ GenAI/docs/windows/installation.md | 26 ++ GenAI/docs/windows/performance.md | 22 + GenAI/docs/windows/rtx-4090-configuration.md | 30 ++ GenAI/docs/windows/troubleshooting.md | 13 + GenAI/inputs/.gitkeep | 0 GenAI/inputs/README.md | 9 + GenAI/logs/.gitkeep | 0 GenAI/logs/README.md | 9 + GenAI/models/README.md | 77 ++++ GenAI/outputs/.gitkeep | 0 GenAI/outputs/README.md | 6 + GenAI/scripts/lib/GenAI-Common.ps1 | 203 ++++++++ GenAI/scripts/lib/common.sh | 97 ++++ GenAI/scripts/macos/doctor.sh | 42 ++ GenAI/scripts/macos/launch-comfyui.sh | 24 + GenAI/scripts/macos/launch-facefusion.sh | 24 + GenAI/scripts/macos/repair.sh | 6 + GenAI/scripts/macos/reset-runtime.sh | 14 + GenAI/scripts/macos/setup-all.sh | 6 + GenAI/scripts/macos/setup-comfyui.sh | 49 ++ GenAI/scripts/macos/setup-facefusion.sh | 33 ++ GenAI/scripts/macos/smoke-test.sh | 10 + GenAI/scripts/macos/update-all.sh | 8 + GenAI/scripts/windows/doctor.ps1 | 123 +++++ GenAI/scripts/windows/launch-comfyui.ps1 | 65 +++ GenAI/scripts/windows/launch-facefusion.ps1 | 73 +++ GenAI/scripts/windows/repair.ps1 | 17 + GenAI/scripts/windows/reset-runtime.ps1 | 32 ++ GenAI/scripts/windows/setup-all.ps1 | 9 + GenAI/scripts/windows/setup-comfyui.ps1 | 86 ++++ GenAI/scripts/windows/setup-facefusion.ps1 | 82 ++++ GenAI/scripts/windows/smoke-test.ps1 | 13 + GenAI/scripts/windows/update-all.ps1 | 28 ++ GenAI/tests/generate_docs.py | 436 ++++++++++++++++++ GenAI/tests/smoke_test.py | 59 +++ GenAI/tests/validate-config.py | 59 +++ GenAI/tests/validate-upstreams.py | 45 ++ GenAI/tests/validate-workflows.py | 55 +++ GenAI/upstreams.lock.json | 26 ++ .../comfyui/beginner-text-to-image.json | 126 +++++ .../comfyui/controlled-generation.json | 375 +++++++++++++++ GenAI/workflows/comfyui/image-to-image.json | 375 +++++++++++++++ GenAI/workflows/comfyui/inpainting.json | 375 +++++++++++++++ GenAI/workflows/comfyui/upscale.json | 375 +++++++++++++++ GenAI/workflows/facefusion/README.md | 27 ++ .../facefusion/example-image-job.ini | 10 + 91 files changed, 4651 insertions(+) create mode 100644 GenAI/.gitignore create mode 100644 GenAI/CHANGELOG.md create mode 100644 GenAI/README.md create mode 100644 GenAI/config/facefusion.example.ini create mode 100644 GenAI/config/local-paths.example.json create mode 100644 GenAI/config/model-manifest.example.json create mode 100644 GenAI/config/settings.example.env create mode 100644 GenAI/docs/00-start-here.md create mode 100644 GenAI/docs/01-system-overview.md create mode 100644 GenAI/docs/02-installation-overview.md create mode 100644 GenAI/docs/architecture/ADR-001-comfyui.md create mode 100644 GenAI/docs/architecture/ADR-002-facefusion.md create mode 100644 GenAI/docs/architecture/ADR-003-environment-isolation.md create mode 100644 GenAI/docs/architecture/ADR-004-upstream-management.md create mode 100644 GenAI/docs/facefusion/01-overview.md create mode 100644 GenAI/docs/facefusion/02-first-image-face-swap.md create mode 100644 GenAI/docs/facefusion/03-video-face-swap.md create mode 100644 GenAI/docs/facefusion/04-multiple-faces.md create mode 100644 GenAI/docs/facefusion/05-face-selection-and-indexing.md create mode 100644 GenAI/docs/facefusion/06-enhancement-and-output-quality.md create mode 100644 GenAI/docs/facefusion/07-batch-and-headless-operation.md create mode 100644 GenAI/docs/facefusion/08-performance.md create mode 100644 GenAI/docs/facefusion/09-troubleshooting.md create mode 100644 GenAI/docs/facefusion/10-consent-provenance-and-safety.md create mode 100644 GenAI/docs/macos/apple-silicon-installation.md create mode 100644 GenAI/docs/macos/intel-mac-assessment.md create mode 100644 GenAI/docs/macos/performance.md create mode 100644 GenAI/docs/macos/troubleshooting.md create mode 100644 GenAI/docs/stable-diffusion/01-fundamentals.md create mode 100644 GenAI/docs/stable-diffusion/02-first-image.md create mode 100644 GenAI/docs/stable-diffusion/03-prompt-engineering.md create mode 100644 GenAI/docs/stable-diffusion/04-models-checkpoints-and-vaes.md create mode 100644 GenAI/docs/stable-diffusion/05-loras-and-embeddings.md create mode 100644 GenAI/docs/stable-diffusion/06-samplers-schedulers-steps-and-cfg.md create mode 100644 GenAI/docs/stable-diffusion/07-image-to-image.md create mode 100644 GenAI/docs/stable-diffusion/08-inpainting-and-outpainting.md create mode 100644 GenAI/docs/stable-diffusion/09-controlnet-and-image-guidance.md create mode 100644 GenAI/docs/stable-diffusion/10-upscaling-and-restoration.md create mode 100644 GenAI/docs/stable-diffusion/11-comfyui-fundamentals.md create mode 100644 GenAI/docs/stable-diffusion/12-building-comfyui-workflows.md create mode 100644 GenAI/docs/stable-diffusion/13-performance-and-memory.md create mode 100644 GenAI/docs/stable-diffusion/14-model-management.md create mode 100644 GenAI/docs/stable-diffusion/15-troubleshooting.md create mode 100644 GenAI/docs/testing-validation-matrix.md create mode 100644 GenAI/docs/windows/installation.md create mode 100644 GenAI/docs/windows/performance.md create mode 100644 GenAI/docs/windows/rtx-4090-configuration.md create mode 100644 GenAI/docs/windows/troubleshooting.md create mode 100644 GenAI/inputs/.gitkeep create mode 100644 GenAI/inputs/README.md create mode 100644 GenAI/logs/.gitkeep create mode 100644 GenAI/logs/README.md create mode 100644 GenAI/models/README.md create mode 100644 GenAI/outputs/.gitkeep create mode 100644 GenAI/outputs/README.md create mode 100644 GenAI/scripts/lib/GenAI-Common.ps1 create mode 100644 GenAI/scripts/lib/common.sh create mode 100644 GenAI/scripts/macos/doctor.sh create mode 100644 GenAI/scripts/macos/launch-comfyui.sh create mode 100644 GenAI/scripts/macos/launch-facefusion.sh create mode 100644 GenAI/scripts/macos/repair.sh create mode 100644 GenAI/scripts/macos/reset-runtime.sh create mode 100644 GenAI/scripts/macos/setup-all.sh create mode 100644 GenAI/scripts/macos/setup-comfyui.sh create mode 100644 GenAI/scripts/macos/setup-facefusion.sh create mode 100644 GenAI/scripts/macos/smoke-test.sh create mode 100644 GenAI/scripts/macos/update-all.sh create mode 100644 GenAI/scripts/windows/doctor.ps1 create mode 100644 GenAI/scripts/windows/launch-comfyui.ps1 create mode 100644 GenAI/scripts/windows/launch-facefusion.ps1 create mode 100644 GenAI/scripts/windows/repair.ps1 create mode 100644 GenAI/scripts/windows/reset-runtime.ps1 create mode 100644 GenAI/scripts/windows/setup-all.ps1 create mode 100644 GenAI/scripts/windows/setup-comfyui.ps1 create mode 100644 GenAI/scripts/windows/setup-facefusion.ps1 create mode 100644 GenAI/scripts/windows/smoke-test.ps1 create mode 100644 GenAI/scripts/windows/update-all.ps1 create mode 100644 GenAI/tests/generate_docs.py create mode 100644 GenAI/tests/smoke_test.py create mode 100644 GenAI/tests/validate-config.py create mode 100644 GenAI/tests/validate-upstreams.py create mode 100644 GenAI/tests/validate-workflows.py create mode 100644 GenAI/upstreams.lock.json create mode 100644 GenAI/workflows/comfyui/beginner-text-to-image.json create mode 100644 GenAI/workflows/comfyui/controlled-generation.json create mode 100644 GenAI/workflows/comfyui/image-to-image.json create mode 100644 GenAI/workflows/comfyui/inpainting.json create mode 100644 GenAI/workflows/comfyui/upscale.json create mode 100644 GenAI/workflows/facefusion/README.md create mode 100644 GenAI/workflows/facefusion/example-image-job.ini diff --git a/.gitignore b/.gitignore index e81ad31f53a..9f850ec2fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,19 @@ notification.mp3 /cache trace.json /sysinfo-????-??-??-??-??.json + +# GenAI project (local runtime, models, media) +/GenAI/runtime/ +/GenAI/runtime/** +/GenAI/models/**/* +!/GenAI/models/README.md +/GenAI/inputs/**/* +!/GenAI/inputs/README.md +/GenAI/outputs/**/* +!/GenAI/outputs/README.md +/GenAI/logs/**/* +!/GenAI/logs/README.md +/GenAI/config/settings.env +/GenAI/config/local-paths.json +/GenAI/config/model-manifest.json +/GenAI/config/facefusion.ini diff --git a/GenAI/.gitignore b/GenAI/.gitignore new file mode 100644 index 00000000000..f0bf0f26f44 --- /dev/null +++ b/GenAI/.gitignore @@ -0,0 +1,64 @@ +# Local configuration +config/settings.env +config/local-paths.json +config/facefusion.ini +config/model-manifest.json + +# Runtime clones and environments +runtime/comfyui/ +runtime/facefusion/ +runtime/environments/ +runtime/facefusion-temp/ + +# Models and caches +models/**/*.ckpt +models/**/*.safetensors +models/**/*.onnx +models/**/*.pth +models/**/*.pt +models/**/*.bin +models/**/*.gguf +models/**/.cache/ +**/.hf_cache/ +**/.cache/huggingface/ + +# User media (never commit private faces or outputs) +inputs/**/* +!inputs/README.md +!inputs/**/.gitkeep +outputs/**/* +!outputs/README.md +!outputs/**/.gitkeep + +# Logs and secrets +logs/**/* +!logs/README.md +!logs/**/.gitkeep +*.env +!config/settings.example.env +secrets/ +tokens/ +*.pem +*.key + +# Generated and temporary +**/__pycache__/ +**/*.pyc +**/.pytest_cache/ +**/temp/ +**/tmp/ +**/*.tmp +benchmark/ + +# OS and IDE +.DS_Store +Thumbs.db +desktop.ini +.vscode/ +.idea/ + +# Installers +*.exe +*.msi +*.dmg +*.pkg diff --git a/GenAI/CHANGELOG.md b/GenAI/CHANGELOG.md new file mode 100644 index 00000000000..a7fe61c0487 --- /dev/null +++ b/GenAI/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +## [0.1.0] - 2026-06-14 + +### Added +- Initial GenAI project scaffold +- ComfyUI v0.9.2 and FaceFusion 3.6.1 upstream pins +- Windows PowerShell and macOS shell scripts (setup, launch, doctor, repair, reset, update, smoke-test) +- Separate venv environments under `runtime/environments/` +- Starter ComfyUI workflow and tutorial documentation +- Configuration templates and validation tests +- Architecture decision records (ADR-001 through ADR-004) + +### Not tested in CI +- Full ComfyUI image generation (requires user checkpoint) +- FaceFusion face swap (requires authorized local media) +- Apple Silicon MPS / CoreML hardware acceleration +- NVIDIA RTX 4090 CUDA acceleration diff --git a/GenAI/README.md b/GenAI/README.md new file mode 100644 index 00000000000..c5283d517c9 --- /dev/null +++ b/GenAI/README.md @@ -0,0 +1,70 @@ +# GenAI + +Local generative-AI tooling for **ComfyUI** (Stable Diffusion workflows) and **FaceFusion** (image/video face swapping), isolated from the parent AUTOMATIC1111 WebUI in this repository. + +## What this project provides + +| Component | Purpose | Upstream pin | +|-----------|---------|--------------| +| ComfyUI | Stable Diffusion workflow interface | [v0.9.2](https://github.com/comfyanonymous/ComfyUI/releases/tag/v0.9.2) | +| FaceFusion | Authorized face swap (image + video) | [3.6.1](https://github.com/facefusion/facefusion/releases/tag/3.6.1) | + +- **Separate Python environments** for ComfyUI and FaceFusion +- **Windows (RTX 4090)** and **macOS (Apple Silicon)** scripts +- **Pinned upstream commits** in `upstreams.lock.json` +- **Tutorials**, starter workflows, diagnostics, and repair tooling + +FaceFusion is the **only** supported face-swapping application in this project. Roop and Roop-derived extensions are intentionally excluded. + +## Quick start (Windows + NVIDIA) + +```powershell +cd GenAI +.\scripts\windows\setup-all.ps1 +.\scripts\windows\doctor.ps1 +.\scripts\windows\launch-comfyui.ps1 +``` + +Place a checkpoint in `models/comfyui/checkpoints/` (see [models/README.md](models/README.md)), then load `workflows/comfyui/beginner-text-to-image.json` in ComfyUI. + +## Quick start (macOS Apple Silicon) + +```bash +cd GenAI +chmod +x scripts/macos/*.sh scripts/lib/common.sh +./scripts/macos/setup-all.sh +./scripts/macos/doctor.sh +./scripts/macos/launch-comfyui.sh +``` + +## Configuration + +1. Copy `config/settings.example.env` → `config/settings.env` (auto-created on first setup) +2. Copy `config/local-paths.example.json` → `config/local-paths.json` +3. Default bind address: **127.0.0.1** (localhost only) + +## Scripts + +| Action | Windows | macOS | +|--------|---------|-------| +| Setup all | `setup-all.ps1` | `setup-all.sh` | +| Launch ComfyUI | `launch-comfyui.ps1` | `launch-comfyui.sh` | +| Launch FaceFusion | `launch-facefusion.ps1` | `launch-facefusion.sh` | +| Diagnostics | `doctor.ps1` | `doctor.sh` | +| Smoke tests | `smoke-test.ps1` | `smoke-test.sh` | +| Repair envs | `repair.ps1` | `repair.sh` | +| Reset runtime | `reset-runtime.ps1 -Confirm` | `reset-runtime.sh --confirm` | + +## Documentation + +Start at [docs/00-start-here.md](docs/00-start-here.md). + +## Privacy and lawful use + +Use only consenting subjects, licensed media, or synthetic fixtures. See [docs/facefusion/10-consent-provenance-and-safety.md](docs/facefusion/10-consent-provenance-and-safety.md). + +## Relationship to parent repo + +This `GenAI/` tree is **additive** and self-contained. It does not require changes to AUTOMATIC1111 WebUI files (`webui-user.bat`, `modules/`, etc.). If your working tree has unrelated local edits to those files, keep them separate from GenAI commits. + +**Port note:** FaceFusion defaults to **7861** to avoid conflicting with the parent WebUI default (**7860**). diff --git a/GenAI/config/facefusion.example.ini b/GenAI/config/facefusion.example.ini new file mode 100644 index 00000000000..4f911ff994b --- /dev/null +++ b/GenAI/config/facefusion.example.ini @@ -0,0 +1,10 @@ +[paths] +temp_path = +output_path = + +[execution] +execution_providers = +execution_device_ids = 0 + +[uis] +open_browser = true diff --git a/GenAI/config/local-paths.example.json b/GenAI/config/local-paths.example.json new file mode 100644 index 00000000000..a551d70e641 --- /dev/null +++ b/GenAI/config/local-paths.example.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 1, + "runtimeRoot": "runtime", + "comfyui": { + "cloneDir": "runtime/comfyui", + "envDir": "runtime/environments/comfyui", + "modelsDir": "models/comfyui", + "inputDir": "inputs/comfyui", + "outputDir": "outputs/comfyui", + "logDir": "logs/comfyui" + }, + "facefusion": { + "cloneDir": "runtime/facefusion", + "envDir": "runtime/environments/facefusion", + "modelsDir": "models/facefusion", + "inputDir": "inputs/facefusion", + "outputDir": "outputs/facefusion", + "tempDir": "runtime/facefusion-temp", + "logDir": "logs/facefusion" + } +} diff --git a/GenAI/config/model-manifest.example.json b/GenAI/config/model-manifest.example.json new file mode 100644 index 00000000000..9d1bd359a7a --- /dev/null +++ b/GenAI/config/model-manifest.example.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": 1, + "models": [ + { + "displayName": "SD 1.5 Base", + "category": "checkpoint", + "source": "https://huggingface.co/runwayml/stable-diffusion-v1-5", + "repository": "runwayml/stable-diffusion-v1-5", + "version": "v1-5", + "filename": "v1-5-pruned-emaonly.safetensors", + "SHA256": "", + "license": "CreativeML Open RAIL-M", + "baseModel": "SD1.5", + "intendedUse": "General text-to-image starter checkpoint", + "obtainedAt": "", + "notes": "Place in models/comfyui/checkpoints/. Accept Hugging Face license manually before download." + } + ] +} diff --git a/GenAI/config/settings.example.env b/GenAI/config/settings.example.env new file mode 100644 index 00000000000..1171a61363e --- /dev/null +++ b/GenAI/config/settings.example.env @@ -0,0 +1,20 @@ +# Copy to settings.env and adjust. settings.env is git-ignored. +# All paths may contain spaces; quote values in shell scripts when exporting. + +GENAI_HOST=127.0.0.1 +COMFYUI_PORT=8188 +# Default 7861 avoids collision with parent A1111 WebUI (7860) +FACEFUSION_PORT=7861 + +# Performance profile: compatibility | balanced | performance +GENAI_PROFILE=balanced + +# Windows FaceFusion execution provider: cuda | directml | cpu +# macOS FaceFusion execution provider: coreml | cpu +FACEFUSION_EXECUTION_PROVIDER=cuda + +# Optional external model root (leave empty to use GenAI/models only) +EXTERNAL_MODEL_ROOT= + +# Reserved for future explicit update workflows (currently manual via upstreams.lock.json) +UPDATE_CHANNEL=pinned diff --git a/GenAI/docs/00-start-here.md b/GenAI/docs/00-start-here.md new file mode 100644 index 00000000000..36effd0e842 --- /dev/null +++ b/GenAI/docs/00-start-here.md @@ -0,0 +1,52 @@ +# Start here + +Welcome to **GenAI** — a self-contained local stack for: + +1. **ComfyUI** — generate and edit images with Stable Diffusion workflows +2. **FaceFusion** — swap faces in images and videos (authorized use only) + +## Prerequisites + +### Windows (RTX 4090 target) +- Windows 10/11, 64-bit +- [Git for Windows](https://git-scm.com/download/win) +- Python 3.10–3.12 with **Add to PATH** +- Latest [NVIDIA driver](https://www.nvidia.com/drivers) +- [FFmpeg](https://ffmpeg.org/download.html) (for FaceFusion video) +- ~50 GB free disk (apps + models) + +### macOS (Apple Silicon target) +- macOS 13+ recommended +- Xcode Command Line Tools / Homebrew +- Python 3.10–3.12, Git, FFmpeg (`brew install ffmpeg python@3.12 git`) + +## Installation path + +| Step | Windows | macOS | +|------|---------|-------| +| 1. Enter project | `cd GenAI` | `cd GenAI` | +| 2. Setup | `.\scripts\windows\setup-all.ps1` | `./scripts/macos/setup-all.sh` | +| 3. Verify | `.\scripts\windows\doctor.ps1` | `./scripts/macos/doctor.sh` | +| 4. Add a checkpoint | See [models/README.md](../models/README.md) | Same | +| 5. First image | [02-first-image.md](stable-diffusion/02-first-image.md) | Same | + +## Learning paths + +**Stable Diffusion (ComfyUI)** +→ [01-fundamentals.md](stable-diffusion/01-fundamentals.md) → [02-first-image.md](stable-diffusion/02-first-image.md) → [11-comfyui-fundamentals.md](stable-diffusion/11-comfyui-fundamentals.md) + +**FaceFusion** +→ [01-overview.md](facefusion/01-overview.md) → [02-first-image-face-swap.md](facefusion/02-first-image-face-swap.md) → [03-video-face-swap.md](facefusion/03-video-face-swap.md) + +## Important boundaries + +- This project **does not** replace the parent AUTOMATIC1111 WebUI in the repo root. +- **Roop is not supported.** Use FaceFusion only. +- Default network binding is **localhost** — do not expose to LAN without understanding the risk. +- **Never commit** private faces, videos, or model weights to Git. + +## Get help + +- Diagnostics: `doctor.ps1` / `doctor.sh` +- Static tests: `smoke-test.ps1` / `smoke-test.sh` +- Troubleshooting: [stable-diffusion/15-troubleshooting.md](stable-diffusion/15-troubleshooting.md), [facefusion/09-troubleshooting.md](facefusion/09-troubleshooting.md) diff --git a/GenAI/docs/01-system-overview.md b/GenAI/docs/01-system-overview.md new file mode 100644 index 00000000000..e5856148e69 --- /dev/null +++ b/GenAI/docs/01-system-overview.md @@ -0,0 +1,60 @@ +# System overview + +``` +GenAI/ +├── config/ # Example + local settings (local files git-ignored) +├── docs/ # Tutorials and architecture +├── models/ # Checkpoints, LoRAs, FaceFusion weights (git-ignored contents) +├── inputs/ # Your source media (git-ignored) +├── outputs/ # Generated results (git-ignored) +├── logs/ # Setup and runtime logs +├── runtime/ +│ ├── comfyui/ # Cloned ComfyUI (git-ignored) +│ ├── facefusion/ # Cloned FaceFusion (git-ignored) +│ ├── environments/ +│ │ ├── comfyui/ # ComfyUI Python venv +│ │ └── facefusion/ # FaceFusion Python venv +│ └── facefusion-temp/ # Video frame temp (git-ignored) +├── scripts/ +│ ├── windows/ # PowerShell entrypoints +│ ├── macos/ # Bash entrypoints +│ └── lib/ # Shared helpers +├── tests/ # Static validation +├── workflows/ # ComfyUI JSON + FaceFusion job examples +└── upstreams.lock.json +``` + +## Data flow — ComfyUI + +1. Checkpoint in `models/comfyui/checkpoints/` +2. Load workflow JSON in browser UI +3. ComfyUI writes images to its output folder (configure Save Image node prefix) +4. Generation metadata embedded in PNG when using default Save Image + +## Data flow — FaceFusion + +1. Source face + target image/video in `inputs/facefusion/` +2. Launch FaceFusion UI or CLI +3. Processed output in `outputs/facefusion/` +4. Temp frames in `runtime/facefusion-temp/` during video jobs + +## Isolation guarantees + +| Concern | ComfyUI | FaceFusion | +|---------|---------|------------| +| Python venv | `runtime/environments/comfyui` | `runtime/environments/facefusion` | +| Upstream clone | `runtime/comfyui` | `runtime/facefusion` | +| Default port | 8188 | 7861 (Gradio) | +| GPU stack | PyTorch CUDA / MPS | ONNX Runtime providers | + +Applications do not require each other to be running. + +## Configuration files + +| File | In Git | Purpose | +|------|--------|---------| +| `config/settings.example.env` | Yes | Template for host, ports, profile | +| `config/settings.env` | No | Your overrides | +| `config/local-paths.example.json` | Yes | Directory layout | +| `config/local-paths.json` | No | Your path overrides | +| `upstreams.lock.json` | Yes | Pinned upstream commits | diff --git a/GenAI/docs/02-installation-overview.md b/GenAI/docs/02-installation-overview.md new file mode 100644 index 00000000000..99416c04d79 --- /dev/null +++ b/GenAI/docs/02-installation-overview.md @@ -0,0 +1,52 @@ +# Installation overview + +GenAI uses **bootstrap scripts** that clone pinned upstream releases and create **isolated Python virtual environments**. Models and private media are never downloaded automatically without your action. + +## Order of operations + +1. **Install OS prerequisites** (Git, Python, FFmpeg, GPU drivers) +2. **Run `setup-all`** — clones ComfyUI + FaceFusion, creates venvs, installs dependencies +3. **Run `doctor`** — verifies GPU, envs, ports, FFmpeg +4. **Configure paths** (optional) — edit `config/settings.env` and `config/local-paths.json` +5. **Add models** — manual download; see [models/README.md](../models/README.md) +6. **Run smoke tests** — static validation without requiring models + +## Platform guides + +- Windows + RTX 4090: [windows/installation.md](windows/installation.md) +- macOS Apple Silicon: [macos/apple-silicon-installation.md](macos/apple-silicon-installation.md) +- Intel Mac: [macos/intel-mac-assessment.md](macos/intel-mac-assessment.md) + +## What setup does *not* do + +- Download multi-GB Stable Diffusion checkpoints (license + size) +- Download FaceFusion ONNX models until first run or explicit `facefusion.py force-download` +- Modify the parent A1111 `venv/` or `webui-user.bat` +- Install Roop or ComfyUI custom nodes + +## Idempotency + +Re-running `setup-comfyui` or `setup-facefusion` is safe: existing clones are checked out to the pinned commit; venvs are reused and dependencies refreshed. + +## Repair vs reset + +| Command | Deletes models? | Deletes outputs? | Use when | +|---------|-----------------|------------------|----------| +| `repair` | No | No | Broken venv, failed pip install | +| `reset-runtime -Confirm` | No | No | Corrupt clone; wipes runtime + venv only | + +## Verification + +```powershell +# Windows +.\scripts\windows\doctor.ps1 +.\scripts\windows\smoke-test.ps1 +``` + +```bash +# macOS +./scripts/macos/doctor.sh +./scripts/macos/smoke-test.sh +``` + +Expected: all static checks **PASS**; GPU checks **PASS** or **WARN** until setup completes on target hardware. diff --git a/GenAI/docs/architecture/ADR-001-comfyui.md b/GenAI/docs/architecture/ADR-001-comfyui.md new file mode 100644 index 00000000000..9ac575595a8 --- /dev/null +++ b/GenAI/docs/architecture/ADR-001-comfyui.md @@ -0,0 +1,52 @@ +# ADR-001: ComfyUI as the Stable Diffusion Interface + +## Status +Accepted + +## Context +This project needs a local Stable Diffusion workflow engine that supports node-based pipelines, reproducible JSON workflows, and cross-platform GPU acceleration without duplicating the parent repo's AUTOMATIC1111 WebUI. + +## Decision +Use **ComfyUI** ([comfyanonymous/ComfyUI](https://github.com/comfyanonymous/ComfyUI)) with a **pinned manual installation** cloned into `runtime/comfyui/`. + +### Why ComfyUI +- Node graph maps directly to diffusion pipeline stages (model load, encode, sample, decode) +- Workflow JSON is portable and version-controllable +- Active upstream, broad model support (SD1.5, SDXL, Flux, etc.) +- Official support for NVIDIA CUDA and Apple MPS + +### Manual vs desktop installer +**Manual clone + project scripts** was chosen over ComfyUI Desktop because: +- Reproducible pinned commits in `upstreams.lock.json` +- Scriptable setup, doctor, and repair +- Consistent layout with FaceFusion bootstrap +- No dependency on a separate desktop auto-updater + +### Platform support +| Platform | Backend | PyTorch install | +|----------|---------|-----------------| +| Windows + RTX 4090 | CUDA | `cu124` index from download.pytorch.org | +| Apple Silicon | MPS | Default PyTorch wheel from PyPI | +| Intel Mac / CPU | CPU | Documented fallback; not primary target | + +### Python and PyTorch +- Python **3.10–3.12** per ComfyUI v0.9.2 requirements +- Install **torch/torchvision/torchaudio** before `requirements.txt` +- Windows: verify `torch.cuda.is_available()` after setup + +### Update strategy +Explicit edit of `upstreams.lock.json` + re-run setup. No silent tracking of `master`. + +### Model directory strategy +Models live under `models/comfyui/` with subfolders (`checkpoints`, `vae`, `loras`, etc.). `extra_model_paths.yaml` in the ComfyUI clone points to this tree. Optional external storage via junction/symlink documented in `models/README.md`. + +### Workflow portability +Committed workflows use stock ComfyUI nodes only. Missing custom nodes are documented, not auto-installed. + +### Custom-node policy +No custom nodes in the default install. Add only after explicit review (supply chain, license, maintenance). + +## Consequences +- Users learn ComfyUI node graphs (see tutorials) +- A1111-specific extensions do not apply here +- ComfyUI runs independently of FaceFusion diff --git a/GenAI/docs/architecture/ADR-002-facefusion.md b/GenAI/docs/architecture/ADR-002-facefusion.md new file mode 100644 index 00000000000..b8d2d8d46a9 --- /dev/null +++ b/GenAI/docs/architecture/ADR-002-facefusion.md @@ -0,0 +1,57 @@ +# ADR-002: FaceFusion as the Face-Swapping Application + +## Status +Accepted + +## Context +The project requires local image and video face manipulation with browser UI, CLI, and headless modes. + +## Decision +Use official **[FaceFusion](https://github.com/facefusion/facefusion)** pinned at **3.6.1**, cloned to `runtime/facefusion/`. + +### Why FaceFusion +- Active upstream with image + video pipeline, enhancement, and job system +- Documented execution providers and CLI (`facefusion.py`) +- Gradio UI for interactive use + +### Why Roop is excluded +- Roop is unmaintained; community forks vary in quality and licensing +- Roop-derived Stable Diffusion extensions conflate face swap with the diffusion stack +- FaceFusion is the designated supported tool; one supported path reduces support burden + +### Why separate from ComfyUI +FaceFusion pins **numpy 2.2.1**, **onnxruntime 1.24.4**, and **gradio 5.x** — incompatible with ComfyUI's PyTorch-centric stack. Separate venvs prevent dependency conflicts. + +### Execution providers + +| OS | Preferred | Fallback | Notes | +|----|-----------|----------|-------| +| Windows + NVIDIA | `cuda` (onnxruntime-gpu) | `directml`, `cpu` | Verify with `ort.get_available_providers()` | +| macOS Apple Silicon | `coreml` (onnxruntime-silicon when available) | `cpu` | CoreML support varies by model/op | +| Intel Mac | `cpu` | — | Not a primary target | + +**Do not claim CUDA/CoreML is active without runtime verification.** + +### FFmpeg +Required for video encode/decode. Must be on PATH (`ffmpeg`, `ffprobe`). + +### ONNX Runtime +Installed per platform in the FaceFusion venv only — never globally replaced. + +### Environment isolation +Dedicated venv at `runtime/environments/facefusion/`. No shared site-packages with ComfyUI. + +### Update strategy +Same as ComfyUI: pin in lock file, manual upgrade after reading release notes. + +### Model download +FaceFusion downloads models on first use via built-in commands. Document manual steps; do not commit `.onnx` weights. + +### Privacy +- Source faces stay in git-ignored `inputs/facefusion/` +- No upload to external services by default +- Localhost binding only unless user changes `GENAI_HOST` + +## Consequences +- Users run FaceFusion via `launch-facefusion.ps1` / `.sh`, not inside ComfyUI +- ReActor and similar ComfyUI nodes are not installed by default diff --git a/GenAI/docs/architecture/ADR-003-environment-isolation.md b/GenAI/docs/architecture/ADR-003-environment-isolation.md new file mode 100644 index 00000000000..232fb3a7f5b --- /dev/null +++ b/GenAI/docs/architecture/ADR-003-environment-isolation.md @@ -0,0 +1,39 @@ +# ADR-003: Environment Isolation + +## Status +Accepted + +## Decision +Use **Python `venv`** per application, managed by platform setup scripts. + +| Application | Environment path | Python | +|-------------|------------------|--------| +| ComfyUI | `runtime/environments/comfyui/` | 3.10–3.12 | +| FaceFusion | `runtime/environments/facefusion/` | 3.10–3.12 | + +## Why venv +- Matches parent repository convention (`venv/` for A1111) +- No conda license or cross-platform conda path complexity +- Officially supported by Python; works on Windows and macOS +- Clear separation: two directories, zero shared `site-packages` + +## Incompatible packages (must not share one env) +- **PyTorch** (ComfyUI) vs **ONNX Runtime GPU** (FaceFusion) +- **numpy** version pins differ +- **opencv-python** / **gradio** versions differ +- **CUDA** PyTorch wheels vs **onnxruntime-gpu** + +## Rules +1. No `pip install` into system Python +2. No global NumPy/PyTorch/ONNX downgrades +3. Setup scripts install dependencies; launch scripts do **not** +4. Paths are relative to `GenAI/` — no hard-coded user home directories + +## Alternatives considered +- **uv**: faster installs; may adopt later without changing isolation model +- **conda**: heavier; unnecessary for two-app layout +- **single venv**: rejected due to numpy/onnx/torch conflicts + +## Consequences +- Disk: ~2× Python env overhead (~4–8 GB each with torch) +- Repair: re-run `setup-*.ps1` per app or `repair.ps1` diff --git a/GenAI/docs/architecture/ADR-004-upstream-management.md b/GenAI/docs/architecture/ADR-004-upstream-management.md new file mode 100644 index 00000000000..0afc55c7cb9 --- /dev/null +++ b/GenAI/docs/architecture/ADR-004-upstream-management.md @@ -0,0 +1,44 @@ +# ADR-004: Upstream Management + +## Status +Accepted + +## Decision +Record upstream applications in `upstreams.lock.json` with **release tag + full commit SHA**. + +```json +{ + "schemaVersion": 1, + "upstreams": { + "comfyui": { "repository", "release", "commit", "license", "purpose" }, + "facefusion": { ... } + } +} +``` + +## Clone layout +- `runtime/comfyui/` — ComfyUI source at pinned commit +- `runtime/facefusion/` — FaceFusion source at pinned commit + +Both directories are **git-ignored**. + +## Update process +1. Review GitHub release notes +2. Update `release` and `commit` in lock file +3. Run `python tests/validate-upstreams.py` +4. Run platform `setup-all` script +5. Run `doctor` and `smoke-test` + +No automatic major-version upgrades. No `git pull` on default branch during ordinary launch. + +## Rollback +Restore previous `upstreams.lock.json` from Git history and re-run setup. + +## Security +- HTTPS clone URLs only +- No `curl | bash` installers +- Validate lock file in CI/static tests + +## Consequences +- Updates are deliberate and auditable +- Custom nodes and pip deps are not updated unless setup is re-run diff --git a/GenAI/docs/facefusion/01-overview.md b/GenAI/docs/facefusion/01-overview.md new file mode 100644 index 00000000000..d263107bcbc --- /dev/null +++ b/GenAI/docs/facefusion/01-overview.md @@ -0,0 +1,17 @@ +# FaceFusion overview + +FaceFusion detects faces in source and target media, swaps identity, optionally enhances faces/frames, and re-encodes video with audio preservation. + +## vs Stable Diffusion +SD generates images from noise; FaceFusion manipulates existing pixels/video frames. + +## Key concepts +- **Source**: face identity donor (authorized only) +- **Target**: image or video to modify +- **Face swapper model**: ONNX identity transfer +- **Execution provider**: cuda / directml / coreml / cpu +- **Jobs**: batch/headless configurations + +Launch: `launch-facefusion.ps1` or `.sh` + +No Roop in this project. diff --git a/GenAI/docs/facefusion/02-first-image-face-swap.md b/GenAI/docs/facefusion/02-first-image-face-swap.md new file mode 100644 index 00000000000..e459b43cb57 --- /dev/null +++ b/GenAI/docs/facefusion/02-first-image-face-swap.md @@ -0,0 +1,22 @@ +# First image face swap + +## Authorized media only +Use your own face or documented consent + license. + +## Requirements +- Clear frontal or three-quarter source face, well lit +- Target with visible face, similar angle when possible + +## UI steps +1. Launch FaceFusion +2. Select source image (inputs/facefusion/source/) +3. Select target image +4. Choose face swapper model (defaults from FaceFusion) +5. Execution provider: cuda (Windows NVIDIA) or coreml/cpu (Mac) +6. Output to outputs/facefusion/ +7. Run / preview + +## Failed detection +Try higher-resolution source, better lighting, or manual face selector in UI. + +Next: [03-video-face-swap.md](03-video-face-swap.md) diff --git a/GenAI/docs/facefusion/03-video-face-swap.md b/GenAI/docs/facefusion/03-video-face-swap.md new file mode 100644 index 00000000000..cb32f3d5a2f --- /dev/null +++ b/GenAI/docs/facefusion/03-video-face-swap.md @@ -0,0 +1,18 @@ +# Video face swap + +## Before long videos +Process **5–10 second clip** first. + +## Pipeline +1. FFmpeg extracts frames +2. FaceFusion processes each frame +3. Re-encode video; audio copied when configured + +## Disk space +Temp frames in `runtime/facefusion-temp/` — ensure 2× video size free. + +## Flicker +Caused by detection inconsistency — use consistent source angle; enable enhancement cautiously. + +## Audio missing +Verify FFmpeg on PATH; check FaceFusion output encoder settings. diff --git a/GenAI/docs/facefusion/04-multiple-faces.md b/GenAI/docs/facefusion/04-multiple-faces.md new file mode 100644 index 00000000000..c719e7e2288 --- /dev/null +++ b/GenAI/docs/facefusion/04-multiple-faces.md @@ -0,0 +1,7 @@ +# Multiple faces + +FaceFusion indexes faces left-to-right or by detection order. + +Map source faces to target indices explicitly in UI before processing group scenes. + +Preview each mapping on a single frame before full video. diff --git a/GenAI/docs/facefusion/05-face-selection-and-indexing.md b/GenAI/docs/facefusion/05-face-selection-and-indexing.md new file mode 100644 index 00000000000..f308bc17718 --- /dev/null +++ b/GenAI/docs/facefusion/05-face-selection-and-indexing.md @@ -0,0 +1,5 @@ +# Face selection and indexing + +Lower detection confidence threshold only if faces are missed — increases false positives. + +For occluded faces, partial swaps may fail; mask/occlusion handling is limited — set realistic expectations. diff --git a/GenAI/docs/facefusion/06-enhancement-and-output-quality.md b/GenAI/docs/facefusion/06-enhancement-and-output-quality.md new file mode 100644 index 00000000000..20ce1b5f770 --- /dev/null +++ b/GenAI/docs/facefusion/06-enhancement-and-output-quality.md @@ -0,0 +1,7 @@ +# Enhancement and output quality + +Enhancement can fix blur but causes **plastic skin** if pushed too high. + +Match lighting between source and target when possible. + +Check edge blending around hair and jaw — common artifact regions. diff --git a/GenAI/docs/facefusion/07-batch-and-headless-operation.md b/GenAI/docs/facefusion/07-batch-and-headless-operation.md new file mode 100644 index 00000000000..bbed27523db --- /dev/null +++ b/GenAI/docs/facefusion/07-batch-and-headless-operation.md @@ -0,0 +1,18 @@ +# Batch and headless operation + +Verify flags on pinned 3.6.1: +```bash +python facefusion.py --help +python facefusion.py run --help +python facefusion.py headless-run --help +``` + +Typical patterns: +```bash +python facefusion.py force-download +python facefusion.py headless-run -s SOURCE -t TARGET -o OUTPUT +``` + +Use placeholder paths from `workflows/facefusion/example-image-job.ini`. + +Job system commands (if available in 3.6.1): check `job-list`, `job-create` via --help output. diff --git a/GenAI/docs/facefusion/08-performance.md b/GenAI/docs/facefusion/08-performance.md new file mode 100644 index 00000000000..69bc734c20e --- /dev/null +++ b/GenAI/docs/facefusion/08-performance.md @@ -0,0 +1,10 @@ +# FaceFusion performance + +| Platform | Provider | Relative speed | +|----------|----------|----------------| +| RTX 4090 | cuda | Fast | +| Windows | directml | Medium | +| Apple Silicon | coreml | Medium-variable | +| Any | cpu | Slow | + +Video scales linearly with frame count × resolution. diff --git a/GenAI/docs/facefusion/09-troubleshooting.md b/GenAI/docs/facefusion/09-troubleshooting.md new file mode 100644 index 00000000000..c2004fc484b --- /dev/null +++ b/GenAI/docs/facefusion/09-troubleshooting.md @@ -0,0 +1,11 @@ +# FaceFusion troubleshooting + +| Symptom | Cause | Diagnostic | Fix | +|---------|-------|------------|-----| +| No face detected | Poor source | doctor + preview | Better source photo | +| CUDA provider missing | Wrong onnxruntime | doctor ONNX line | Re-run setup-facefusion | +| FFmpeg error | Not installed | `ffmpeg -version` | Install FFmpeg | +| Port occupied | Other Gradio app | doctor port check | Change port / stop app | +| Temp disk full | Long video | Check facefusion-temp | Clear temp, shorten clip | + +Logs: `logs/facefusion/setup.log`, terminal output. diff --git a/GenAI/docs/facefusion/10-consent-provenance-and-safety.md b/GenAI/docs/facefusion/10-consent-provenance-and-safety.md new file mode 100644 index 00000000000..9bf2b11f354 --- /dev/null +++ b/GenAI/docs/facefusion/10-consent-provenance-and-safety.md @@ -0,0 +1,24 @@ +# Consent, provenance, and safety + +## Prohibited uses +- Non-consensual intimate imagery +- Sexualized depictions of minors +- Fraudulent impersonation or identity theft +- Fabricated legal/financial/political evidence +- Evading consent or forensic detection +- Presenting manipulated media as authentic evidence + +## Permitted sources +- Your own likeness +- Consenting adults with documented permission +- Licensed stock/media +- Synthetic/public-domain test fixtures + +## Provenance workflow +1. Keep original unedited media +2. Save FaceFusion settings / job config +3. Record app version from upstreams.lock.json +4. Date outputs; label manipulated media where sharing +5. Retain authorization records for commercial work + +Private media stays in git-ignored `inputs/` and `outputs/` only. diff --git a/GenAI/docs/macos/apple-silicon-installation.md b/GenAI/docs/macos/apple-silicon-installation.md new file mode 100644 index 00000000000..2188e7ab3a9 --- /dev/null +++ b/GenAI/docs/macos/apple-silicon-installation.md @@ -0,0 +1,25 @@ +# Apple Silicon installation + +## Prerequisites +```bash +xcode-select --install # if needed +brew install python@3.12 git ffmpeg +``` + +## Install +```bash +cd GenAI +chmod +x scripts/macos/*.sh scripts/lib/common.sh +./scripts/macos/setup-all.sh +./scripts/macos/doctor.sh +``` + +ComfyUI uses **PyTorch MPS** when available. FaceFusion uses **coreml** or **cpu** per `FACEFUSION_EXECUTION_PROVIDER` in `config/settings.env`. + +## Verify MPS +```bash +./runtime/environments/comfyui/bin/python -c "import torch; print('mps', torch.backends.mps.is_available())" +``` + +## Unified memory +Close heavy apps before SDXL or long FaceFusion video jobs — memory is shared between CPU and GPU. diff --git a/GenAI/docs/macos/intel-mac-assessment.md b/GenAI/docs/macos/intel-mac-assessment.md new file mode 100644 index 00000000000..4734bb52de8 --- /dev/null +++ b/GenAI/docs/macos/intel-mac-assessment.md @@ -0,0 +1,12 @@ +# Intel Mac assessment + +Intel Macs are **not a primary target** for this project. + +| Capability | Intel Mac | +|------------|-----------| +| ComfyUI + MPS | Not available | +| ComfyUI CPU | Works, very slow | +| FaceFusion CoreML | Unreliable / unavailable | +| FaceFusion CPU | Works; video impractically slow | + +**Recommendation:** Use Apple Silicon or Windows + NVIDIA for interactive work. Intel Mac may run static validation and documentation only. diff --git a/GenAI/docs/macos/performance.md b/GenAI/docs/macos/performance.md new file mode 100644 index 00000000000..b85c449f931 --- /dev/null +++ b/GenAI/docs/macos/performance.md @@ -0,0 +1,17 @@ +# macOS performance (Apple Silicon) + +## MPS behavior +- First generation compiles shaders — expect slower first run +- Some operators fall back to CPU without warning +- Monitor Memory pressure in Activity Monitor + +## Starting settings +| Model | Resolution | Batch | +|-------|------------|-------| +| SD1.5 | 512 | 1 | +| SDXL | 1024 | 1 | + +## FaceFusion video +Process short test clips first. Temp frames use significant disk and memory. + +See [stable-diffusion/13-performance-and-memory.md](../stable-diffusion/13-performance-and-memory.md). diff --git a/GenAI/docs/macos/troubleshooting.md b/GenAI/docs/macos/troubleshooting.md new file mode 100644 index 00000000000..b7ec73cea23 --- /dev/null +++ b/GenAI/docs/macos/troubleshooting.md @@ -0,0 +1,10 @@ +# macOS troubleshooting + +| Symptom | Action | +|---------|--------| +| `Permission denied` on scripts | `chmod +x scripts/macos/*.sh scripts/lib/common.sh` | +| MPS False on M-series | Reinstall torch in comfyui venv via setup-comfyui.sh | +| CoreML unavailable | Set `FACEFUSION_EXECUTION_PROVIDER=cpu` | +| FFmpeg missing | `brew install ffmpeg` | + +Run `./scripts/macos/doctor.sh` first. diff --git a/GenAI/docs/stable-diffusion/01-fundamentals.md b/GenAI/docs/stable-diffusion/01-fundamentals.md new file mode 100644 index 00000000000..4ce9cda6618 --- /dev/null +++ b/GenAI/docs/stable-diffusion/01-fundamentals.md @@ -0,0 +1,30 @@ +# Stable Diffusion fundamentals + +## What diffusion does +Stable Diffusion starts from random noise in **latent space** (a compressed representation of an image) and iteratively denoises it guided by your text prompt until a recognizable image emerges. + +## Core terms + +| Term | Meaning | +|------|---------| +| **Checkpoint** | Full trained model weights (UNet + text encoder + VAE bundled or partial) | +| **Latent space** | Lower-dimensional grid the UNet denoises (not pixel space) | +| **Text encoder** | Converts prompt tokens to conditioning vectors (CLIP for SD1.5) | +| **VAE** | Encodes/decodes between pixels and latents | +| **Positive prompt** | What you want | +| **Negative prompt** | What to suppress | +| **Seed** | RNG seed — same seed + settings → reproducible image | +| **Sampler** | Algorithm for each denoise step (euler, dpmpp_2m, etc.) | +| **Scheduler** | Noise schedule paired with sampler | +| **Steps** | Number of denoise iterations (more ≠ always better) | +| **CFG / guidance** | How strongly prompt constrains image (typical 5–8) | +| **Width / height** | Output resolution (affects VRAM quadratically) | +| **Denoising strength** | img2img: how much to change input (0=none, 1=full) | +| **LoRA** | Small add-on weights for style/subject | +| **ControlNet** | Structural guidance from edges, depth, pose, etc. | + +## VRAM vs unified memory +- **NVIDIA VRAM**: dedicated GPU memory; OOM stops generation +- **Apple unified memory**: CPU and GPU share pool; pressure causes swapping + +Continue to [02-first-image.md](02-first-image.md). diff --git a/GenAI/docs/stable-diffusion/02-first-image.md b/GenAI/docs/stable-diffusion/02-first-image.md new file mode 100644 index 00000000000..30c546a86aa --- /dev/null +++ b/GenAI/docs/stable-diffusion/02-first-image.md @@ -0,0 +1,34 @@ +# Your first image + +## Model requirement +Download **SD 1.5** `v1-5-pruned-emaonly.safetensors` (accept Hugging Face license manually) to: +`models/comfyui/checkpoints/` + +## Steps +1. `.\scripts\windows\launch-comfyui.ps1` (or macOS equivalent) +2. Open http://127.0.0.1:8188 +3. **Load** → `workflows/comfyui/beginner-text-to-image.json` +4. Confirm checkpoint name in **CheckpointLoaderSimple** node +5. Click **Queue Prompt** + +## Exact settings in starter workflow +| Setting | Value | +|---------|-------| +| Positive | cozy reading nook, warm afternoon sunlight... | +| Negative | blurry, low quality, watermark... | +| Seed | 42 (fixed) | +| Resolution | 512 × 512 | +| Sampler | euler | +| Steps | 25 | +| CFG | 7.0 | + +## Output +Images save with prefix `genai_beginner` in ComfyUI output folder. + +## Reproduce +Keep seed **42** and same checkpoint — output should match. + +## Variations +Change seed to `randomize` or increment seed for controlled diversity. + +Next: [03-prompt-engineering.md](03-prompt-engineering.md) diff --git a/GenAI/docs/stable-diffusion/03-prompt-engineering.md b/GenAI/docs/stable-diffusion/03-prompt-engineering.md new file mode 100644 index 00000000000..2338708f41a --- /dev/null +++ b/GenAI/docs/stable-diffusion/03-prompt-engineering.md @@ -0,0 +1,28 @@ +# Prompt engineering + +Structure prompts: **subject → action → composition → camera → lighting → environment → style**. + +## Example 1 — portrait +**Prompt:** `portrait of a woman, three-quarter view, soft window light, neutral background, 85mm lens, shallow depth of field, natural skin texture, photorealistic` + +**Settings:** 512×512, steps 25, CFG 7, euler + +**Why it works:** Specifies framing, lens, and light before style token. + +**Failure mode:** `beautiful woman` alone → generic, oversaturated faces. + +**Refined:** Add `subtle catchlight in eyes, muted color palette` and negative `plastic skin, oversharpened`. + +## Example 2 — product +**Prompt:** `matte black headphones on white marble surface, top-down flat lay, soft studio softbox, minimal shadows, commercial product photography` + +**Negative:** `text, logo, watermark, cluttered background` + +## Token weighting +ComfyUI CLIP encode accepts `(keyword:1.2)` in prompt text for emphasis. + +## Iteration loop +1. Generate at 512 fast +2. Fix composition in prompt +3. Increase steps only if detail lacking +4. Raise CFG slightly for prompt adherence — watch for burned colors above 10 diff --git a/GenAI/docs/stable-diffusion/04-models-checkpoints-and-vaes.md b/GenAI/docs/stable-diffusion/04-models-checkpoints-and-vaes.md new file mode 100644 index 00000000000..3ac324c8c46 --- /dev/null +++ b/GenAI/docs/stable-diffusion/04-models-checkpoints-and-vaes.md @@ -0,0 +1,17 @@ +# Models, checkpoints, and VAEs + +## Families +- **SD 1.5**: 512 native, vast LoRA ecosystem +- **SDXL**: 1024 native, two-encoder pipeline +- **Flux / SD3**: newer architectures — verify ComfyUI node support + +## Compatibility +Do not load SDXL checkpoint in SD1.5 workflow nodes. + +## VAE +Some checkpoints include baked VAE; external VAE fixes color/face issues. Place in `models/comfyui/vae/`. + +## Comparison method +Lock seed, steps, sampler; change only checkpoint for fair test. + +See [../models/README.md](../models/README.md). diff --git a/GenAI/docs/stable-diffusion/05-loras-and-embeddings.md b/GenAI/docs/stable-diffusion/05-loras-and-embeddings.md new file mode 100644 index 00000000000..0a744f33721 --- /dev/null +++ b/GenAI/docs/stable-diffusion/05-loras-and-embeddings.md @@ -0,0 +1,12 @@ +# LoRAs and embeddings + +## LoRA +Small weight files modifying UNet and/or text encoder. Typical strength **0.6–1.0** — start at 0.8. + +Place in `models/comfyui/loras/`. Load with **LoraLoader** node in ComfyUI. + +## Embeddings +Textual inversion vectors in `models/comfyui/embeddings/`. Trigger via token in prompt. + +## Compatibility +LoRA trained for SD1.5 only works with SD1.5 base. diff --git a/GenAI/docs/stable-diffusion/06-samplers-schedulers-steps-and-cfg.md b/GenAI/docs/stable-diffusion/06-samplers-schedulers-steps-and-cfg.md new file mode 100644 index 00000000000..4573cdbf913 --- /dev/null +++ b/GenAI/docs/stable-diffusion/06-samplers-schedulers-steps-and-cfg.md @@ -0,0 +1,16 @@ +# Samplers, schedulers, steps, and CFG + +## Steps +- 15–25 often sufficient for SD1.5 euler/dpm variants +- Diminishing returns after ~30 for many samplers + +## CFG +- Low (3–5): creative, loose +- Mid (6–8): balanced default +- High (10+): harsh contrast, artifact risk + +## Fair A/B +Change one knob at a time; lock seed. + +## Oversaturation +Reduce CFG or add negative `oversaturated, hdr`. diff --git a/GenAI/docs/stable-diffusion/07-image-to-image.md b/GenAI/docs/stable-diffusion/07-image-to-image.md new file mode 100644 index 00000000000..41ceb5d7d4c --- /dev/null +++ b/GenAI/docs/stable-diffusion/07-image-to-image.md @@ -0,0 +1,9 @@ +# Image-to-image + +Feed an init image + denoising strength. + +- **0.3–0.5**: color/lighting tweaks, preserve structure +- **0.6–0.8**: stronger restyle +- **1.0**: near txt2img (weak structure retention) + +Prepare square or crop consistently. See workflow `workflows/comfyui/image-to-image.json`. diff --git a/GenAI/docs/stable-diffusion/08-inpainting-and-outpainting.md b/GenAI/docs/stable-diffusion/08-inpainting-and-outpainting.md new file mode 100644 index 00000000000..6de647d1e94 --- /dev/null +++ b/GenAI/docs/stable-diffusion/08-inpainting-and-outpainting.md @@ -0,0 +1,11 @@ +# Inpainting and outpainting + +## Inpainting +Mask the region to replace. Feather mask edges 4–8px to avoid seams. + +Use cases: object removal, face/hand fix, background cleanup. + +## Outpainting +Extend canvas; mask new areas; moderate denoise (0.7–0.85). + +Workflow: `workflows/comfyui/inpainting.json`. diff --git a/GenAI/docs/stable-diffusion/09-controlnet-and-image-guidance.md b/GenAI/docs/stable-diffusion/09-controlnet-and-image-guidance.md new file mode 100644 index 00000000000..a2913fa62d6 --- /dev/null +++ b/GenAI/docs/stable-diffusion/09-controlnet-and-image-guidance.md @@ -0,0 +1,9 @@ +# ControlNet and image guidance + +ControlNet adds edge/depth/pose maps as conditioning. + +Starter approach: **Canny edge** for composition lock. Place models in `models/comfyui/controlnet/`. + +Do not install every custom node — use stock ComfyUI ControlNet nodes when available. + +Workflow: `workflows/comfyui/controlled-generation.json`. diff --git a/GenAI/docs/stable-diffusion/10-upscaling-and-restoration.md b/GenAI/docs/stable-diffusion/10-upscaling-and-restoration.md new file mode 100644 index 00000000000..ba2700d3ab7 --- /dev/null +++ b/GenAI/docs/stable-diffusion/10-upscaling-and-restoration.md @@ -0,0 +1,12 @@ +# Upscaling and restoration + +## Latent upscale +Upscale in latent space before final VAE decode — efficient. + +## Pixel upscale +ESRGAN/UltraSharp in `models/comfyui/upscale_models/`. + +## When not to upscale +Heavily artifacted base — upscaler amplifies flaws. + +Workflow: `workflows/comfyui/upscale.json`. diff --git a/GenAI/docs/stable-diffusion/11-comfyui-fundamentals.md b/GenAI/docs/stable-diffusion/11-comfyui-fundamentals.md new file mode 100644 index 00000000000..f536ec4d8bc --- /dev/null +++ b/GenAI/docs/stable-diffusion/11-comfyui-fundamentals.md @@ -0,0 +1,18 @@ +# ComfyUI fundamentals + +## Nodes and wires +- **Nodes** = operations +- **Sockets** = typed ports (MODEL, CLIP, LATENT, IMAGE, VAE) +- Data flows left → right + +## Essential chain +CheckpointLoader → CLIP encode (+/-) → KSampler → VAEDecode → SaveImage + +## Queue +Each **Queue Prompt** runs the graph. Cached nodes skip re-execution when inputs unchanged. + +## Workflow JSON +Save/load from UI. Committed starters in `workflows/comfyui/`. + +## Missing nodes +Red nodes = unknown type — do not auto-install; review custom node source first. diff --git a/GenAI/docs/stable-diffusion/12-building-comfyui-workflows.md b/GenAI/docs/stable-diffusion/12-building-comfyui-workflows.md new file mode 100644 index 00000000000..bd4f15eae20 --- /dev/null +++ b/GenAI/docs/stable-diffusion/12-building-comfyui-workflows.md @@ -0,0 +1,9 @@ +# Building ComfyUI workflows + +1. Start from `beginner-text-to-image.json` +2. Add LoraLoader after checkpoint if needed +3. Insert ControlNetApply for guided generation +4. Use **Preview Image** before Save for fast iteration +5. Save incremental versions with descriptive names + +Export JSON to `workflows/comfyui/` for Git tracking (no embedded private images). diff --git a/GenAI/docs/stable-diffusion/13-performance-and-memory.md b/GenAI/docs/stable-diffusion/13-performance-and-memory.md new file mode 100644 index 00000000000..28e816866d6 --- /dev/null +++ b/GenAI/docs/stable-diffusion/13-performance-and-memory.md @@ -0,0 +1,16 @@ +# Performance and memory + +## Windows RTX 4090 +- SD1.5 512 batch 1: fast +- SDXL 1024: monitor VRAM in doctor/nvidia-smi +- Model load delay on first run is normal + +## Apple Silicon +- MPS warmup on first generation +- Reduce resolution if swap increases + +## Interrupt +Use ComfyUI cancel — if frozen, check logs; hard kill loses in-flight job only. + +## Intel / CPU +Expect 10–50× slower; use low resolution for prompt iteration. diff --git a/GenAI/docs/stable-diffusion/14-model-management.md b/GenAI/docs/stable-diffusion/14-model-management.md new file mode 100644 index 00000000000..f11a9a5979a --- /dev/null +++ b/GenAI/docs/stable-diffusion/14-model-management.md @@ -0,0 +1,14 @@ +# Model management in practice + +Track downloads in `config/model-manifest.json`. + +## Hashes +Verify SHA256 from model card when provided. + +## Licensing +Commercial use depends on checkpoint license — read model card. + +## Malicious models +Prefer official repos; avoid random mirror sites. + +Full guide: [../../models/README.md](../../models/README.md). diff --git a/GenAI/docs/stable-diffusion/15-troubleshooting.md b/GenAI/docs/stable-diffusion/15-troubleshooting.md new file mode 100644 index 00000000000..ec03becec8a --- /dev/null +++ b/GenAI/docs/stable-diffusion/15-troubleshooting.md @@ -0,0 +1,12 @@ +# Stable Diffusion troubleshooting + +| Symptom | Cause | Action | +|---------|-------|--------| +| Black image | VAE mismatch | Try external VAE or different checkpoint | +| CUDA OOM | Resolution too high | Lower size or batch | +| Red nodes | Missing node type | Use stock workflow or install reviewed custom node | +| Slow every gen | CPU mode | Re-run setup; verify CUDA/MPS | +| Wrong model | Filename mismatch | Fix CheckpointLoader widget | + +**Diagnostic:** `doctor.ps1` / `doctor.sh` +**Logs:** `logs/comfyui/`, ComfyUI terminal output diff --git a/GenAI/docs/testing-validation-matrix.md b/GenAI/docs/testing-validation-matrix.md new file mode 100644 index 00000000000..2e8375a80ba --- /dev/null +++ b/GenAI/docs/testing-validation-matrix.md @@ -0,0 +1,28 @@ +# Validation matrix + +Honest test status as of project scaffold (agent environment). + +| Component | Static validation | Startup tested | Functional test | Acceleration tested | +|-----------|-------------------|----------------|-----------------|---------------------| +| Config templates | PASS | — | — | — | +| upstreams.lock.json | PASS | — | — | — | +| Workflow JSON | PASS | — | — | — | +| Python validators | PASS | — | — | — | +| PowerShell scripts | PARTIAL (parser) | NOT TESTED | NOT TESTED | NOT TESTED | +| Bash scripts | PARTIAL (review) | NOT TESTED | NOT TESTED | NOT TESTED | +| ComfyUI clone+venv | NOT TESTED | NOT TESTED | NOT TESTED | NOT TESTED | +| FaceFusion clone+venv | NOT TESTED | NOT TESTED | NOT TESTED | NOT TESTED | +| Image generation | NOT TESTED | NOT TESTED | BLOCKED (no checkpoint) | NOT TESTED | +| Face swap | NOT TESTED | NOT TESTED | BLOCKED (no authorized media) | NOT TESTED | +| RTX 4090 CUDA | NOT TESTED | — | — | NOT TESTED | +| Apple MPS/CoreML | NOT TESTED | — | — | NOT TESTED | + +Run static suite: +```powershell +.\scripts\windows\smoke-test.ps1 +``` +```bash +./scripts/macos/smoke-test.sh +``` + +Functional tests require user hardware, models, and authorized media. diff --git a/GenAI/docs/windows/installation.md b/GenAI/docs/windows/installation.md new file mode 100644 index 00000000000..dc31217caed --- /dev/null +++ b/GenAI/docs/windows/installation.md @@ -0,0 +1,26 @@ +# Windows installation + +## Prerequisites +1. Windows 10/11 64-bit +2. [Git for Windows](https://git-scm.com/download/win) +3. Python 3.10–3.12 (check **Add to PATH** during install) +4. [FFmpeg](https://ffmpeg.org/download.html) on PATH (`winget install ffmpeg`) +5. Latest [NVIDIA driver](https://www.nvidia.com/drivers) for RTX 4090 + +## Install +```powershell +cd GenAI +.\scripts\windows\setup-all.ps1 +.\scripts\windows\doctor.ps1 +``` + +Setup clones ComfyUI **v0.9.2** and FaceFusion **3.6.1**, creates separate venvs, installs PyTorch **cu124** for ComfyUI, and configures FaceFusion ONNX provider per `settings.env`. + +## Optional profiles +Edit `config/settings.env`: +- `GENAI_PROFILE=compatibility|balanced|performance` +- `FACEFUSION_EXECUTION_PROVIDER=cuda|directml|cpu` + +## Next steps +- [RTX 4090 tuning](rtx-4090-configuration.md) +- [First image](../stable-diffusion/02-first-image.md) diff --git a/GenAI/docs/windows/performance.md b/GenAI/docs/windows/performance.md new file mode 100644 index 00000000000..aadfc22a12a --- /dev/null +++ b/GenAI/docs/windows/performance.md @@ -0,0 +1,22 @@ +# Windows performance (RTX 4090) + +24 GB VRAM supports most SD1.5/SDXL single-image workflows at default settings. + +## Typical VRAM +| Workflow | Approx VRAM | +|----------|-------------| +| SD1.5 512 txt2img | 4–6 GB | +| SDXL 1024 txt2img | 8–12 GB | +| + ControlNet | +2–4 GB | + +## Bottlenecks +1. First-load checkpoint read from disk +2. Attention at high resolution +3. VAE decode when upscaling + +## Tips +- Lock seed for comparisons ([06-samplers](../stable-diffusion/06-samplers-schedulers-steps-and-cfg.md)) +- Watch `nvidia-smi` during first SDXL run +- ComfyUI cancel button stops queue; kill terminal only if hung + +See also [stable-diffusion/13-performance-and-memory.md](../stable-diffusion/13-performance-and-memory.md). diff --git a/GenAI/docs/windows/rtx-4090-configuration.md b/GenAI/docs/windows/rtx-4090-configuration.md new file mode 100644 index 00000000000..e6784ab5d2b --- /dev/null +++ b/GenAI/docs/windows/rtx-4090-configuration.md @@ -0,0 +1,30 @@ +# RTX 4090 configuration + +## Verify CUDA PyTorch +After setup: +```powershell +.\runtime\environments\comfyui\Scripts\python.exe -c "import torch; print('cuda', torch.cuda.is_available()); print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'n/a')" +``` +Expected: `cuda True` and `NVIDIA GeForce RTX 4090`. + +## If CUDA is False +1. Confirm NVIDIA driver installed (`nvidia-smi`) +2. Re-run `.\scripts\windows\setup-comfyui.ps1` +3. Ensure no CPU-only torch in venv + +## Starting recommendations +| Setting | SD1.5 | SDXL | +|---------|-------|------| +| Resolution | 512–768 | 1024 | +| Batch | 1 | 1 | +| Steps | 20–30 | 25–35 | +| CFG | 6–8 | 5–7 | + +## FaceFusion on RTX 4090 +Set `FACEFUSION_EXECUTION_PROVIDER=cuda` in `config/settings.env`. +Doctor should list `CUDAExecutionProvider` in ONNX providers after setup. + +## Optional performance (not defaults) +- Increase resolution in steps once stable +- Use fp16 checkpoints when available +- Do not enable experimental flags without testing diff --git a/GenAI/docs/windows/troubleshooting.md b/GenAI/docs/windows/troubleshooting.md new file mode 100644 index 00000000000..82c1657d47f --- /dev/null +++ b/GenAI/docs/windows/troubleshooting.md @@ -0,0 +1,13 @@ +# Windows troubleshooting + +| Symptom | Likely cause | Action | +|---------|--------------|--------| +| `CUDA False` | CPU PyTorch wheel | Re-run `setup-comfyui.ps1` | +| ComfyUI won't start | Port 8188 in use | Change `COMFYUI_PORT` in settings.env | +| Red workflow nodes | Missing checkpoint | Add `.safetensors` to `models/comfyui/checkpoints/` | +| FaceFusion no CUDA | Wrong ONNX package | Re-run `setup-facefusion.ps1` with cuda provider | +| FFmpeg not found | Not on PATH | `winget install ffmpeg`, reopen terminal | +| Black output image | VAE/checkpoint mismatch | Try different VAE or checkpoint | + +**First step:** `.\scripts\windows\doctor.ps1` +**Logs:** `logs/comfyui/`, `logs/facefusion/`, ComfyUI terminal diff --git a/GenAI/inputs/.gitkeep b/GenAI/inputs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/GenAI/inputs/README.md b/GenAI/inputs/README.md new file mode 100644 index 00000000000..4af34e75bd1 --- /dev/null +++ b/GenAI/inputs/README.md @@ -0,0 +1,9 @@ +# Inputs + +Git-ignored user media. Never commit private faces or videos. + +- `comfyui/` — optional reference images +- `facefusion/source/` — authorized source faces +- `facefusion/target/` — target images/videos + +See [docs/facefusion/10-consent-provenance-and-safety.md](../docs/facefusion/10-consent-provenance-and-safety.md). diff --git a/GenAI/logs/.gitkeep b/GenAI/logs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/GenAI/logs/README.md b/GenAI/logs/README.md new file mode 100644 index 00000000000..117689fa142 --- /dev/null +++ b/GenAI/logs/README.md @@ -0,0 +1,9 @@ +# Logs + +Git-ignored setup and diagnostic logs. + +| File | Source | +|------|--------| +| `comfyui/setup.log` | setup-comfyui | +| `facefusion/setup.log` | setup-facefusion | +| `update.log` | update-all | diff --git a/GenAI/models/README.md b/GenAI/models/README.md new file mode 100644 index 00000000000..234bbac9038 --- /dev/null +++ b/GenAI/models/README.md @@ -0,0 +1,77 @@ +# Model management + +AI model weights are **never committed to Git**. This directory holds local files only. + +## Directory layout + +``` +models/ +├── comfyui/ +│ ├── checkpoints/ # .safetensors / .ckpt base models +│ ├── vae/ # Optional external VAE +│ ├── loras/ # LoRA adapters +│ ├── embeddings/ # Textual inversion +│ ├── controlnet/ # ControlNet weights +│ └── upscale_models/ # ESRGAN, etc. +└── facefusion/ # FaceFusion-downloaded ONNX (or symlink) +``` + +ComfyUI resolves paths via `extra_model_paths.yaml` generated during setup. + +## Checkpoints + +| Family | Typical file | VRAM (512px) | Notes | +|--------|--------------|--------------|-------| +| SD 1.5 | `v1-5-pruned-emaonly.safetensors` | ~4 GB | Starter tutorial default | +| SDXL | `sd_xl_base_1.0.safetensors` | ~8 GB | 1024px native | +| Flux | varies | 12+ GB | Check ComfyUI node support | + +**Prefer `.safetensors`** over `.ckpt` (no arbitrary pickle code). Safetensors are not inherently trustworthy — verify source and hash. + +## Obtaining models + +1. Read the model card license (CreativeML Open RAIL-M, etc.) +2. Accept gating on Hugging Face if required — **manual login in browser** +3. Download to the correct subfolder +4. Record in `config/model-manifest.json` (copy from `model-manifest.example.json`) + +## External shared storage + +### Windows junction (same volume) +```powershell +New-Item -ItemType Junction -Path "models\comfyui\checkpoints" -Target "D:\AI\Models\checkpoints" +``` + +### macOS symlink +```bash +ln -s ~/AI/Models/checkpoints models/comfyui/checkpoints +``` + +## FaceFusion models + +FaceFusion downloads required ONNX models on first use: + +```bash +cd runtime/facefusion +../environments/facefusion/bin/python facefusion.py force-download +``` + +(Windows: use `Scripts\python.exe`.) + +## Security + +- Download only from official repos or trusted mirrors +- Compare SHA256 when published +- Do not run checkpoint files as scripts +- Scan unfamiliar models in isolated environment + +## Storage planning + +| Content | Typical size | +|---------|--------------| +| SD 1.5 checkpoint | ~4 GB | +| SDXL base | ~6.5 GB | +| LoRA collection | 10–100+ GB | +| FaceFusion models | ~2–5 GB | + +Plan **50 GB minimum** free space for comfortable local work. diff --git a/GenAI/outputs/.gitkeep b/GenAI/outputs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/GenAI/outputs/README.md b/GenAI/outputs/README.md new file mode 100644 index 00000000000..724c505f602 --- /dev/null +++ b/GenAI/outputs/README.md @@ -0,0 +1,6 @@ +# Outputs + +Git-ignored generated media. + +- `comfyui/` — saved images from ComfyUI workflows +- `facefusion/` — processed images and videos diff --git a/GenAI/scripts/lib/GenAI-Common.ps1 b/GenAI/scripts/lib/GenAI-Common.ps1 new file mode 100644 index 00000000000..70ed2797de4 --- /dev/null +++ b/GenAI/scripts/lib/GenAI-Common.ps1 @@ -0,0 +1,203 @@ +# GenAI shared PowerShell helpers +# Dot-source from scripts in scripts/windows/ + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Get-GenAIRoot { + $scriptDir = Split-Path -Parent $MyInvocation.PSCommandPath + return (Resolve-Path (Join-Path $scriptDir '..\..')).Path +} + +function Get-GenAIConfig { + param([string]$Root = (Get-GenAIRoot)) + $pathsExample = Join-Path $Root 'config\local-paths.example.json' + $pathsFile = Join-Path $Root 'config\local-paths.json' + $settingsExample = Join-Path $Root 'config\settings.example.env' + $settingsFile = Join-Path $Root 'config\settings.env' + $lockFile = Join-Path $Root 'upstreams.lock.json' + + if (-not (Test-Path $pathsFile)) { + Copy-Item $pathsExample $pathsFile + Write-Host "Created config\local-paths.json from example." + } + if (-not (Test-Path $settingsFile)) { + Copy-Item $settingsExample $settingsFile + Write-Host "Created config\settings.env from example." + } + + $paths = Get-Content $pathsFile -Raw | ConvertFrom-Json + $settings = @{} + Get-Content $settingsFile | ForEach-Object { + if ($_ -match '^\s*#' -or $_ -match '^\s*$') { return } + $parts = $_ -split '=', 2 + if ($parts.Count -eq 2) { $settings[$parts[0].Trim()] = $parts[1].Trim() } + } + $lock = Get-Content $lockFile -Raw | ConvertFrom-Json + + return [PSCustomObject]@{ + Root = $Root + Paths = $paths + Settings = $settings + Lock = $lock + } +} + +function Resolve-GenAIPath { + param([string]$Root, [string]$RelativePath) + return (Join-Path $Root ($RelativePath -replace '/', '\')) +} + +function Ensure-GenAIDirectories { + param($Config) + $dirs = @( + $Config.Paths.comfyui.cloneDir, + $Config.Paths.comfyui.envDir, + $Config.Paths.comfyui.modelsDir, + $Config.Paths.comfyui.inputDir, + $Config.Paths.comfyui.outputDir, + $Config.Paths.comfyui.logDir, + $Config.Paths.facefusion.cloneDir, + $Config.Paths.facefusion.envDir, + $Config.Paths.facefusion.modelsDir, + $Config.Paths.facefusion.inputDir, + $Config.Paths.facefusion.outputDir, + $Config.Paths.facefusion.tempDir, + $Config.Paths.facefusion.logDir, + 'models/comfyui/checkpoints', + 'models/comfyui/vae', + 'models/comfyui/loras', + 'models/comfyui/embeddings', + 'models/comfyui/controlnet', + 'models/comfyui/upscale_models', + 'models/facefusion', + 'workflows/comfyui', + 'workflows/facefusion' + ) + foreach ($d in $dirs) { + $full = Resolve-GenAIPath $Config.Root $d + if (-not (Test-Path $full)) { + New-Item -ItemType Directory -Path $full -Force | Out-Null + } + } +} + +function Write-GenAILog { + param([string]$Path, [string]$Message) + $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Message" + Add-Content -Path $Path -Value $line + Write-Host $line +} + +function Test-CommandExists { + param([string]$Name) + return [bool](Get-Command $Name -ErrorAction SilentlyContinue) +} + +function Invoke-PipInstall { + param( + [string]$Pip, + [string[]]$PipArgs, + [string]$LogFile, + [string]$Description + ) + Write-GenAILog $LogFile "pip $Description" + $output = & $Pip @PipArgs 2>&1 + $output | ForEach-Object { Add-Content -Path $LogFile -Value $_ } + if ($LASTEXITCODE -ne 0) { + throw "pip install failed for $Description (exit code $LASTEXITCODE). See $LogFile" + } +} + +function Get-SettingOrDefault { + param([hashtable]$Settings, [string]$Key, [string]$Default) + if ($Settings.ContainsKey($Key) -and $Settings[$Key]) { return $Settings[$Key] } + return $Default +} + +function Test-PinnedClone { + param([string]$CloneDir, [string]$ExpectedCommit) + if (-not (Test-Path (Join-Path $CloneDir '.git'))) { return $null } + Push-Location $CloneDir + try { + return (git rev-parse HEAD).Trim() + } finally { + Pop-Location + } +} + +function Get-PythonCandidate { + foreach ($cmd in @('py -3.12', 'py -3.11', 'py -3.10', 'python3.12', 'python3.11', 'python3.10', 'python')) { + $name, $arg = $cmd -split ' ', 2 + if (-not (Test-CommandExists $name)) { continue } + try { + if ($arg) { + $v = & $name $arg -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>$null + } else { + $v = & $name -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" 2>$null + } + if ($v) { return @{ Command = $name; Arg = $arg; Version = $v.Trim() } } + } catch { } + } + return $null +} + +function Invoke-GitClonePinned { + param( + [string]$Repository, + [string]$Commit, + [string]$TargetDir, + [string]$LogFile + ) + if (Test-Path (Join-Path $TargetDir '.git')) { + Push-Location $TargetDir + try { + git fetch --tags --quiet origin + git checkout $Commit 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { throw "git checkout $Commit failed" } + Write-GenAILog $LogFile "Checked out pinned commit $Commit in $TargetDir" + } finally { + Pop-Location + } + } else { + if (Test-Path $TargetDir) { Remove-Item $TargetDir -Recurse -Force } + New-Item -ItemType Directory -Path (Split-Path $TargetDir) -Force | Out-Null + git clone $Repository $TargetDir 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { throw "git clone failed for $Repository" } + Push-Location $TargetDir + try { + git checkout $Commit 2>&1 | Out-Null + if ($LASTEXITCODE -ne 0) { throw "git checkout $Commit failed" } + Write-GenAILog $LogFile "Cloned and checked out $Commit" + } finally { + Pop-Location + } + } +} + +function Get-DiagnosticLine { + param([string]$Name, [string]$Status, [string]$Detail = '') + return [PSCustomObject]@{ Check = $Name; Status = $Status; Detail = $Detail } +} + +function Write-DiagnosticReport { + param([array]$Results) + $width = 52 + Write-Host ('=' * $width) + Write-Host 'GenAI Doctor Report' + Write-Host ('=' * $width) + foreach ($r in $Results) { + $color = switch ($r.Status) { + 'PASS' { 'Green' } + 'WARN' { 'Yellow' } + 'FAIL' { 'Red' } + default { 'Gray' } + } + Write-Host ("[{0}] {1}" -f $r.Status, $r.Check) -ForegroundColor $color + if ($r.Detail) { Write-Host " $($r.Detail)" } + } + Write-Host ('=' * $width) + if ($Results | Where-Object { $_.Status -eq 'FAIL' }) { exit 1 } + if ($Results | Where-Object { $_.Status -eq 'WARN' }) { exit 2 } + exit 0 +} diff --git a/GenAI/scripts/lib/common.sh b/GenAI/scripts/lib/common.sh new file mode 100644 index 00000000000..bdd277c5f58 --- /dev/null +++ b/GenAI/scripts/lib/common.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# GenAI shared bash helpers for macOS scripts +set -euo pipefail + +genai_root() { + local script_dir + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + echo "$script_dir" +} + +genai_load_config() { + GENAI_ROOT="$(genai_root)" + PATHS_FILE="$GENAI_ROOT/config/local-paths.json" + SETTINGS_FILE="$GENAI_ROOT/config/settings.env" + LOCK_FILE="$GENAI_ROOT/upstreams.lock.json" + + if [[ ! -f "$PATHS_FILE" ]]; then + cp "$GENAI_ROOT/config/local-paths.example.json" "$PATHS_FILE" + echo "Created config/local-paths.json from example." + fi + if [[ ! -f "$SETTINGS_FILE" ]]; then + cp "$GENAI_ROOT/config/settings.example.env" "$SETTINGS_FILE" + echo "Created config/settings.env from example." + fi + + export GENAI_ROOT PATHS_FILE SETTINGS_FILE LOCK_FILE + # shellcheck disable=SC1090 + set -a; source "$SETTINGS_FILE"; set +a +} + +genai_json_field() { + local section="$1" field="$2" + GENAI_JSON_SECTION="$section" GENAI_JSON_FIELD="$field" python3 - "$PATHS_FILE" <<'PY' +import json, os, sys +path = sys.argv[1] +data = json.load(open(path, encoding="utf-8")) +section = os.environ["GENAI_JSON_SECTION"] +field = os.environ["GENAI_JSON_FIELD"] +print(data[section][field]) +PY +} + +genai_resolve_config_path() { + genai_resolve_path "$(genai_json_field "$1" "$2")" +} + +genai_log() { + local logfile="$1"; shift + local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*" + echo "$msg" + mkdir -p "$(dirname "$logfile")" + echo "$msg" >> "$logfile" +} + +genai_ensure_dirs() { + local dirs=( + runtime/comfyui runtime/environments/comfyui + runtime/facefusion runtime/environments/facefusion runtime/facefusion-temp + models/comfyui/checkpoints models/comfyui/vae models/comfyui/loras + models/comfyui/embeddings models/comfyui/controlnet models/comfyui/upscale_models + models/facefusion inputs/comfyui inputs/facefusion + outputs/comfyui outputs/facefusion logs/comfyui logs/facefusion + ) + for d in "${dirs[@]}"; do + mkdir -p "$(genai_resolve_path "$d")" + done +} + +genai_git_clone_pinned() { + local repo="$1" commit="$2" target="$3" logfile="$4" + if [[ -d "$target/.git" ]]; then + git -C "$target" fetch --tags --quiet origin + git -C "$target" checkout "$commit" + genai_log "$logfile" "Checked out pinned commit $commit" + else + rm -rf "$target" + mkdir -p "$(dirname "$target")" + git clone "$repo" "$target" + git -C "$target" checkout "$commit" + genai_log "$logfile" "Cloned and checked out $commit" + fi +} + +genai_python_candidate() { + for cmd in python3.12 python3.11 python3.10 python3 python; do + if command -v "$cmd" >/dev/null 2>&1; then + echo "$cmd" + return 0 + fi + done + return 1 +} + +genai_diag_line() { + printf '[%s] %s\n' "$2" "$1" + [[ -n "${3:-}" ]] && printf ' %s\n' "$3" +} diff --git a/GenAI/scripts/macos/doctor.sh b/GenAI/scripts/macos/doctor.sh new file mode 100644 index 00000000000..c08a154e69f --- /dev/null +++ b/GenAI/scripts/macos/doctor.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../lib/common.sh" +genai_load_config + +FAIL=0 +check() { + local name="$1" status="$2" detail="${3:-}" + genai_diag_line "$name" "$status" "$detail" + [[ "$status" == "FAIL" ]] && FAIL=1 +} + +check "Repository" "PASS" "$GENAI_ROOT" +[[ -f "$PATHS_FILE" ]] && check "local-paths.json" "PASS" || check "local-paths.json" "FAIL" "Run setup-all.sh" +[[ -f "$SETTINGS_FILE" ]] && check "settings.env" "PASS" || check "settings.env" "FAIL" +command -v git >/dev/null && check "Git" "PASS" "$(git --version)" || check "Git" "FAIL" +command -v ffmpeg >/dev/null && check "FFmpeg" "PASS" || check "FFmpeg" "WARN" "brew install ffmpeg" + +ARCH="$(uname -m)" +check "Architecture" "PASS" "$ARCH" +sw_vers 2>/dev/null | while read -r line; do check "macOS" "PASS" "$line"; done + +ENV_C="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['comfyui']['envDir'])")")" +if [[ -x "$ENV_C/bin/python" ]]; then + check "ComfyUI venv" "PASS" "$ENV_C" + MPS="$("$ENV_C/bin/python" -c "import torch; print(torch.backends.mps.is_available())")" + check "PyTorch MPS" "$([[ "$MPS" == "True" ]] && echo PASS || echo WARN)" "mps=$MPS" +else + check "ComfyUI venv" "FAIL" "Run setup-comfyui.sh" +fi + +ENV_F="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['facefusion']['envDir'])")")" +if [[ -x "$ENV_F/bin/python" ]]; then + check "FaceFusion venv" "PASS" "$ENV_F" + PROV="$("$ENV_F/bin/python" -c "import onnxruntime as ort; print(','.join(ort.get_available_providers()))")" + check "ONNX providers" "PASS" "$PROV" +else + check "FaceFusion venv" "FAIL" "Run setup-facefusion.sh" +fi + +exit $FAIL diff --git a/GenAI/scripts/macos/launch-comfyui.sh b/GenAI/scripts/macos/launch-comfyui.sh new file mode 100644 index 00000000000..12af86639c2 --- /dev/null +++ b/GenAI/scripts/macos/launch-comfyui.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../lib/common.sh" +genai_load_config + +CLONE_DIR="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['comfyui']['cloneDir'])")")" +ENV_DIR="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['comfyui']['envDir'])")")" +HOST="${GENAI_HOST:-127.0.0.1}" +PORT="${COMFYUI_PORT:-8188}" + +[[ -x "$ENV_DIR/bin/python" ]] || { echo "Run setup-comfyui.sh first"; exit 1; } + +BACKEND="$("$ENV_DIR/bin/python" -c "import torch; print('MPS' if torch.backends.mps.is_available() else 'CPU')")" + +echo "=== ComfyUI Launch ===" +echo "Env: $ENV_DIR" +echo "Backend: $BACKEND" +echo "URL: http://${HOST}:${PORT}" +echo "Models: $(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['comfyui']['modelsDir'])")")" +echo "======================" + +cd "$CLONE_DIR" +exec "$ENV_DIR/bin/python" main.py --listen "$HOST" --port "$PORT" diff --git a/GenAI/scripts/macos/launch-facefusion.sh b/GenAI/scripts/macos/launch-facefusion.sh new file mode 100644 index 00000000000..78c8865de50 --- /dev/null +++ b/GenAI/scripts/macos/launch-facefusion.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../lib/common.sh" +genai_load_config + +CLONE_DIR="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['facefusion']['cloneDir'])")")" +ENV_DIR="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['facefusion']['envDir'])")")" +PROVIDER="${FACEFUSION_EXECUTION_PROVIDER:-coreml}" + +[[ -x "$ENV_DIR/bin/python" ]] || { echo "Run setup-facefusion.sh first"; exit 1; } + +case "$PROVIDER" in + coreml) PROV_ARG=coreml ;; + *) PROV_ARG=cpu ;; +esac + +echo "=== FaceFusion Launch ===" +echo "Provider: $PROV_ARG" +"$ENV_DIR/bin/python" -c "import onnxruntime as ort; print('Available:', ort.get_available_providers())" +echo "=========================" + +cd "$CLONE_DIR" +exec "$ENV_DIR/bin/python" facefusion.py run --execution-providers "$PROV_ARG" diff --git a/GenAI/scripts/macos/repair.sh b/GenAI/scripts/macos/repair.sh new file mode 100644 index 00000000000..6a03bdcbc1c --- /dev/null +++ b/GenAI/scripts/macos/repair.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +"$SCRIPT_DIR/setup-comfyui.sh" +"$SCRIPT_DIR/setup-facefusion.sh" +echo "Repair complete." diff --git a/GenAI/scripts/macos/reset-runtime.sh b/GenAI/scripts/macos/reset-runtime.sh new file mode 100644 index 00000000000..cf2eabec4c2 --- /dev/null +++ b/GenAI/scripts/macos/reset-runtime.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail +[[ "${1:-}" == "--confirm" ]] || { echo "Usage: $0 --confirm"; exit 1; } +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../lib/common.sh" +genai_load_config + +for key in comfyui.cloneDir facefusion.cloneDir comfyui.envDir facefusion.envDir facefusion.tempDir; do + section="${key%%.*}"; field="${key#*.}" + rel="$(python3 -c "import json;d=json.load(open('$PATHS_FILE'));print(d['$section']['$field'])")" + full="$(genai_resolve_path "$rel")" + [[ -d "$full" ]] && rm -rf "$full" && echo "Removed $full" +done +echo "Reset complete. Run setup-all.sh" diff --git a/GenAI/scripts/macos/setup-all.sh b/GenAI/scripts/macos/setup-all.sh new file mode 100644 index 00000000000..3bec647bfb3 --- /dev/null +++ b/GenAI/scripts/macos/setup-all.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +"$SCRIPT_DIR/setup-comfyui.sh" +"$SCRIPT_DIR/setup-facefusion.sh" +echo "GenAI setup-all complete. Run ./scripts/macos/doctor.sh" diff --git a/GenAI/scripts/macos/setup-comfyui.sh b/GenAI/scripts/macos/setup-comfyui.sh new file mode 100644 index 00000000000..0d44e420577 --- /dev/null +++ b/GenAI/scripts/macos/setup-comfyui.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# Bootstrap ComfyUI on macOS with isolated venv and MPS-capable PyTorch +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=../lib/common.sh +source "$SCRIPT_DIR/../lib/common.sh" +genai_load_config +genai_ensure_dirs + +LOG="$(genai_resolve_path "${PATHS_FILE%/*}")" +LOG="$(genai_resolve_path logs/comfyui/setup.log)" +CLONE_DIR="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['comfyui']['cloneDir'])")")" +ENV_DIR="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['comfyui']['envDir'])")")" + +COMMIT="$(python3 -c "import json;print(json.load(open('$LOCK_FILE'))['upstreams']['comfyui']['commit'])")" +REPO="$(python3 -c "import json;print(json.load(open('$LOCK_FILE'))['upstreams']['comfyui']['repository'])")" + +genai_log "$LOG" "Starting ComfyUI macOS setup" +command -v git >/dev/null || { echo "Git required"; exit 1; } +PY="$(genai_python_candidate)" || { echo "Python 3.10+ required"; exit 1; } + +genai_git_clone_pinned "$REPO" "$COMMIT" "$CLONE_DIR" "$LOG" + +if [[ ! -x "$ENV_DIR/bin/python" ]]; then + "$PY" -m venv "$ENV_DIR" +fi + +PIP="$ENV_DIR/bin/pip" +PYTHON="$ENV_DIR/bin/python" +"$PIP" install --upgrade pip wheel setuptools +"$PIP" install torch torchvision torchaudio +"$PIP" install -r "$CLONE_DIR/requirements.txt" + +MPS="$("$PYTHON" -c "import torch; print(torch.backends.mps.is_available())")" +genai_log "$LOG" "MPS available: $MPS" + +MODELS="$(genai_resolve_path "$(python3 -c "import json;print(json.load(open('$PATHS_FILE'))['comfyui']['modelsDir'])")")" +cat > "$CLONE_DIR/extra_model_paths.yaml" </dev/null || { echo "Git required"; exit 1; } +command -v ffmpeg >/dev/null || echo "WARN: ffmpeg not found — install via brew install ffmpeg" + +PY="$(genai_python_candidate)" || exit 1 +genai_git_clone_pinned "$REPO" "$COMMIT" "$CLONE_DIR" "$LOG" + +[[ -x "$ENV_DIR/bin/python" ]] || "$PY" -m venv "$ENV_DIR" +PIP="$ENV_DIR/bin/pip" +"$PIP" install --upgrade pip wheel setuptools +"$PIP" install -r "$CLONE_DIR/requirements.txt" + +if [[ "$PROVIDER" == "coreml" ]]; then + "$PIP" install onnxruntime-silicon 2>/dev/null || "$PIP" install onnxruntime 2>/dev/null || true +fi + +"$ENV_DIR/bin/python" -c "import onnxruntime as ort; print('providers:', ort.get_available_providers())" +echo "FaceFusion setup complete." diff --git a/GenAI/scripts/macos/smoke-test.sh b/GenAI/scripts/macos/smoke-test.sh new file mode 100644 index 00000000000..c7fa3180cc5 --- /dev/null +++ b/GenAI/scripts/macos/smoke-test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$ROOT" +python3 tests/validate-config.py +python3 tests/validate-upstreams.py +python3 tests/validate-workflows.py +python3 tests/smoke_test.py --static-only +echo "Smoke tests passed (static)." diff --git a/GenAI/scripts/macos/update-all.sh b/GenAI/scripts/macos/update-all.sh new file mode 100644 index 00000000000..b75ba99dda7 --- /dev/null +++ b/GenAI/scripts/macos/update-all.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../lib/common.sh" +genai_load_config +echo "Pinned ComfyUI: $(python3 -c "import json;d=json.load(open('$LOCK_FILE'));u=d['upstreams']['comfyui'];print(u['release'], u['commit'])")" +echo "Pinned FaceFusion: $(python3 -c "import json;d=json.load(open('$LOCK_FILE'));u=d['upstreams']['facefusion'];print(u['release'], u['commit'])")" +echo "Edit upstreams.lock.json and re-run setup-all.sh to update." diff --git a/GenAI/scripts/windows/doctor.ps1 b/GenAI/scripts/windows/doctor.ps1 new file mode 100644 index 00000000000..d36a994b218 --- /dev/null +++ b/GenAI/scripts/windows/doctor.ps1 @@ -0,0 +1,123 @@ +#Requires -Version 5.1 +$ErrorActionPreference = 'Continue' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +$results = @() +$cfg = Get-GenAIConfig +$root = $cfg.Root + +try { + $results += Get-DiagnosticLine 'Repository path' 'PASS' $root +} catch { + $results += Get-DiagnosticLine 'Repository path' 'FAIL' $_.Exception.Message +} + +foreach ($f in @('config\local-paths.json', 'config\settings.env', 'upstreams.lock.json')) { + $p = Join-Path $root $f + if (Test-Path $p) { $results += Get-DiagnosticLine "Config: $f" 'PASS' } + else { $results += Get-DiagnosticLine "Config: $f" 'FAIL' 'Run setup-all.ps1 to create from examples' } +} + +if (Test-CommandExists 'git') { $results += Get-DiagnosticLine 'Git' 'PASS' (git --version) } +else { $results += Get-DiagnosticLine 'Git' 'FAIL' 'Install Git for Windows' } + +$py = Get-PythonCandidate +if ($py) { $results += Get-DiagnosticLine 'Python' 'PASS' "$($py.Command) $($py.Version)" } +else { $results += Get-DiagnosticLine 'Python' 'FAIL' 'Install Python 3.10+' } + +if (Test-CommandExists 'ffmpeg') { $results += Get-DiagnosticLine 'FFmpeg' 'PASS' (ffmpeg -version 2>&1 | Select-Object -First 1) } +else { $results += Get-DiagnosticLine 'FFmpeg' 'WARN' 'Required for FaceFusion video. winget install ffmpeg' } + +# OS / GPU +$results += Get-DiagnosticLine 'OS' 'PASS' "$([System.Environment]::OSVersion.VersionString)" +if (Test-CommandExists 'nvidia-smi') { + $gpu = nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader 2>$null + $results += Get-DiagnosticLine 'NVIDIA GPU' 'PASS' ($gpu -join '; ') +} else { + $results += Get-DiagnosticLine 'NVIDIA GPU' 'WARN' 'nvidia-smi not found' +} + +# Disk +$drive = (Split-Path $root -Qualifier) +$free = (Get-PSDrive ($drive.TrimEnd(':'))).Free / 1GB +if ($free -gt 50) { $results += Get-DiagnosticLine 'Disk space' 'PASS' ("{0:N1} GB free" -f $free) } +elseif ($free -gt 20) { $results += Get-DiagnosticLine 'Disk space' 'WARN' ("{0:N1} GB free — models need more" -f $free) } +else { $results += Get-DiagnosticLine 'Disk space' 'FAIL' ("{0:N1} GB free" -f $free) } + +# ComfyUI env +$comfyPy = Join-Path (Resolve-GenAIPath $root $cfg.Paths.comfyui.envDir) 'Scripts\python.exe' +$cloneComfy = Resolve-GenAIPath $root $cfg.Paths.comfyui.cloneDir +if (Test-Path $comfyPy) { + $results += Get-DiagnosticLine 'ComfyUI venv' 'PASS' $comfyPy + try { + $tv = & $comfyPy -c "import torch; print(torch.__version__)" + $cuda = & $comfyPy -c "import torch; print(torch.cuda.is_available())" + $results += Get-DiagnosticLine 'ComfyUI PyTorch' 'PASS' "v$tv cuda=$cuda" + if ($cuda -eq 'False') { $results += Get-DiagnosticLine 'ComfyUI CUDA' 'WARN' 'CPU-only PyTorch detected on Windows' } + else { $results += Get-DiagnosticLine 'ComfyUI CUDA' 'PASS' (& $comfyPy -c "import torch; print(torch.cuda.get_device_name(0))") } + } catch { + $results += Get-DiagnosticLine 'ComfyUI PyTorch' 'FAIL' $_.Exception.Message + } +} else { + $results += Get-DiagnosticLine 'ComfyUI venv' 'FAIL' 'Run setup-comfyui.ps1' +} + +if (Test-Path (Join-Path $cloneComfy 'main.py')) { + $actual = Test-PinnedClone $cloneComfy $cfg.Lock.upstreams.comfyui.commit + if ($actual -eq $cfg.Lock.upstreams.comfyui.commit) { + $results += Get-DiagnosticLine 'ComfyUI pin' 'PASS' "$($cfg.Lock.upstreams.comfyui.release) @ $($actual.Substring(0,7))" + } elseif ($actual) { + $results += Get-DiagnosticLine 'ComfyUI pin' 'WARN' "Expected $($cfg.Lock.upstreams.comfyui.commit.Substring(0,7)) got $($actual.Substring(0,7))" + } else { + $results += Get-DiagnosticLine 'ComfyUI clone' 'PASS' $cfg.Lock.upstreams.comfyui.release + } +} else { + $results += Get-DiagnosticLine 'ComfyUI clone' 'FAIL' 'Missing runtime clone' +} + +# FaceFusion env +$ffPy = Join-Path (Resolve-GenAIPath $root $cfg.Paths.facefusion.envDir) 'Scripts\python.exe' +$cloneFf = Resolve-GenAIPath $root $cfg.Paths.facefusion.cloneDir +if (Test-Path $ffPy) { + $results += Get-DiagnosticLine 'FaceFusion venv' 'PASS' $ffPy + try { + $prov = & $ffPy -c "import onnxruntime as ort; print(','.join(ort.get_available_providers()))" + $results += Get-DiagnosticLine 'FaceFusion ONNX providers' 'PASS' $prov + } catch { + $results += Get-DiagnosticLine 'FaceFusion ONNX' 'FAIL' $_.Exception.Message + } +} else { + $results += Get-DiagnosticLine 'FaceFusion venv' 'FAIL' 'Run setup-facefusion.ps1' +} + +if (Test-Path (Join-Path $cloneFf 'facefusion.py')) { + $actual = Test-PinnedClone $cloneFf $cfg.Lock.upstreams.facefusion.commit + if ($actual -eq $cfg.Lock.upstreams.facefusion.commit) { + $results += Get-DiagnosticLine 'FaceFusion pin' 'PASS' "$($cfg.Lock.upstreams.facefusion.release) @ $($actual.Substring(0,7))" + } elseif ($actual) { + $results += Get-DiagnosticLine 'FaceFusion pin' 'WARN' "Expected $($cfg.Lock.upstreams.facefusion.commit.Substring(0,7)) got $($actual.Substring(0,7))" + } else { + $results += Get-DiagnosticLine 'FaceFusion clone' 'PASS' $cfg.Lock.upstreams.facefusion.release + } +} else { + $results += Get-DiagnosticLine 'FaceFusion clone' 'FAIL' 'Missing runtime clone' +} + +$ffPort = [int](Get-SettingOrDefault $cfg.Settings 'FACEFUSION_PORT' '7861') +if ($ffPort -eq 7860) { + $results += Get-DiagnosticLine 'A1111 port conflict' 'WARN' 'FACEFUSION_PORT=7860 conflicts with parent WebUI default; use 7861' +} + +# Ports +foreach ($entry in @(@('ComfyUI port', $cfg.Settings['COMFYUI_PORT']), @('FaceFusion port', $cfg.Settings['FACEFUSION_PORT']))) { + $port = [int]$entry[1] + try { + $inUse = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue + if ($inUse) { $results += Get-DiagnosticLine $entry[0] 'WARN' "Port $port in use" } + else { $results += Get-DiagnosticLine $entry[0] 'PASS' "Port $port available" } + } catch { + $results += Get-DiagnosticLine $entry[0] 'SKIP' "Port check unavailable: $($_.Exception.Message)" + } +} + +Write-DiagnosticReport $results diff --git a/GenAI/scripts/windows/launch-comfyui.ps1 b/GenAI/scripts/windows/launch-comfyui.ps1 new file mode 100644 index 00000000000..b17000d306c --- /dev/null +++ b/GenAI/scripts/windows/launch-comfyui.ps1 @@ -0,0 +1,65 @@ +#Requires -Version 5.1 +param( + [ValidateSet('compatibility', 'balanced', 'performance')] + [string]$Profile = '' +) +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +$cfg = Get-GenAIConfig +Ensure-GenAIDirectories $cfg + +if (-not $Profile) { + $Profile = Get-SettingOrDefault $cfg.Settings 'GENAI_PROFILE' 'balanced' +} + +$root = $cfg.Root +$cloneDir = Resolve-GenAIPath $root $cfg.Paths.comfyui.cloneDir +$envDir = Resolve-GenAIPath $root $cfg.Paths.comfyui.envDir +$inputDir = Resolve-GenAIPath $root $cfg.Paths.comfyui.inputDir +$outputDir = Resolve-GenAIPath $root $cfg.Paths.comfyui.outputDir +$python = Join-Path $envDir 'Scripts\python.exe' +$hostAddr = Get-SettingOrDefault $cfg.Settings 'GENAI_HOST' '127.0.0.1' +$port = Get-SettingOrDefault $cfg.Settings 'COMFYUI_PORT' '8188' + +if (-not (Test-Path $python)) { + throw "ComfyUI environment missing. Run: .\scripts\windows\setup-comfyui.ps1" +} + +$launchArgs = @( + (Join-Path $cloneDir 'main.py'), + '--listen', $hostAddr, + '--port', $port, + '--input-directory', $inputDir, + '--output-directory', $outputDir +) + +switch ($Profile) { + 'compatibility' { $launchArgs += @('--disable-smart-memory') } + 'performance' { $launchArgs += @('--highvram') } +} + +if ($hostAddr -notin @('127.0.0.1', 'localhost')) { + Write-Warning "ComfyUI binding to $hostAddr — ensure this is intentional. Default is localhost-only." +} + +$cudaInfo = & $python -c "import torch; print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU')" 2>$null + +Write-Host '=== ComfyUI Launch ===' +Write-Host "Version: $($cfg.Lock.upstreams.comfyui.release) ($($cfg.Lock.upstreams.comfyui.commit.Substring(0,7)))" +Write-Host "Profile: $Profile" +Write-Host "Env: $envDir" +Write-Host "Backend: $cudaInfo" +Write-Host "Address: http://${hostAddr}:${port}" +Write-Host "Input: $inputDir" +Write-Host "Output: $outputDir" +Write-Host "Models: $(Resolve-GenAIPath $root $cfg.Paths.comfyui.modelsDir)" +Write-Host "Logs: $(Resolve-GenAIPath $root $cfg.Paths.comfyui.logDir)" +Write-Host '======================' + +Push-Location $cloneDir +try { + & $python @launchArgs +} finally { + Pop-Location +} diff --git a/GenAI/scripts/windows/launch-facefusion.ps1 b/GenAI/scripts/windows/launch-facefusion.ps1 new file mode 100644 index 00000000000..625a85bda8d --- /dev/null +++ b/GenAI/scripts/windows/launch-facefusion.ps1 @@ -0,0 +1,73 @@ +#Requires -Version 5.1 +param( + [string]$ExecutionProvider = '' +) +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +$cfg = Get-GenAIConfig +Ensure-GenAIDirectories $cfg + +$root = $cfg.Root +$cloneDir = Resolve-GenAIPath $root $cfg.Paths.facefusion.cloneDir +$envDir = Resolve-GenAIPath $root $cfg.Paths.facefusion.envDir +$configPath = Join-Path $root 'config\facefusion.ini' +$python = Join-Path $envDir 'Scripts\python.exe' +$hostAddr = Get-SettingOrDefault $cfg.Settings 'GENAI_HOST' '127.0.0.1' +$port = Get-SettingOrDefault $cfg.Settings 'FACEFUSION_PORT' '7861' +$tempDir = Resolve-GenAIPath $root $cfg.Paths.facefusion.tempDir +$outputDir = Resolve-GenAIPath $root $cfg.Paths.facefusion.outputDir + +if (-not $ExecutionProvider) { + $ExecutionProvider = Get-SettingOrDefault $cfg.Settings 'FACEFUSION_EXECUTION_PROVIDER' 'cuda' +} + +if (-not (Test-Path $python)) { + throw "FaceFusion environment missing. Run: .\scripts\windows\setup-facefusion.ps1" +} + +if (-not (Test-Path $configPath)) { + Copy-Item (Join-Path $root 'config\facefusion.example.ini') $configPath +} + +$providers = switch ($ExecutionProvider) { + 'cuda' { 'cuda' } + 'directml' { 'directml' } + default { 'cpu' } +} + +if ($hostAddr -notin @('127.0.0.1', 'localhost')) { + Write-Warning "FaceFusion binding to $hostAddr — ensure this is intentional." +} + +$env:GRADIO_SERVER_NAME = $hostAddr +$env:GRADIO_SERVER_PORT = $port + +$launchArgs = @( + 'run', + '--config-path', $configPath, + '--temp-path', $tempDir, + '--output-path', $outputDir, + '--execution-providers', $providers, + '--execution-device-ids', '0' +) + +$providerList = & $python -c "import onnxruntime as ort; print(','.join(ort.get_available_providers()))" 2>$null + +Write-Host '=== FaceFusion Launch ===' +Write-Host "Version: $($cfg.Lock.upstreams.facefusion.release)" +Write-Host "Env: $envDir" +Write-Host "Provider: $ExecutionProvider (available: $providerList)" +Write-Host "UI: http://${hostAddr}:${port}" +Write-Host "Input: $(Resolve-GenAIPath $root $cfg.Paths.facefusion.inputDir)" +Write-Host "Output: $outputDir" +Write-Host "Temp: $tempDir" +Write-Host "Logs: $(Resolve-GenAIPath $root $cfg.Paths.facefusion.logDir)" +Write-Host '=========================' + +Push-Location $cloneDir +try { + & $python (Join-Path $cloneDir 'facefusion.py') @launchArgs +} finally { + Pop-Location +} diff --git a/GenAI/scripts/windows/repair.ps1 b/GenAI/scripts/windows/repair.ps1 new file mode 100644 index 00000000000..b7e10c6b4b2 --- /dev/null +++ b/GenAI/scripts/windows/repair.ps1 @@ -0,0 +1,17 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Repair environments without deleting models or outputs. +#> +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +$cfg = Get-GenAIConfig +$log = Join-Path (Resolve-GenAIPath $cfg.Root 'logs') 'repair.log' +Write-GenAILog $log 'Starting repair (preserving models and outputs)' + +& "$PSScriptRoot\setup-comfyui.ps1" +& "$PSScriptRoot\setup-facefusion.ps1" + +Write-GenAILog $log 'Repair complete' +Write-Host 'Repair finished. Run doctor.ps1 to verify.' diff --git a/GenAI/scripts/windows/reset-runtime.ps1 b/GenAI/scripts/windows/reset-runtime.ps1 new file mode 100644 index 00000000000..22262645067 --- /dev/null +++ b/GenAI/scripts/windows/reset-runtime.ps1 @@ -0,0 +1,32 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Destructive reset of runtime clones and virtual environments only. +#> +param([switch]$Confirm) +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +if (-not $Confirm) { + Write-Host 'This removes runtime clones and venvs. Models, inputs, and outputs are preserved.' + Write-Host 'Re-run with -Confirm to proceed.' + exit 1 +} + +$cfg = Get-GenAIConfig +$targets = @( + $cfg.Paths.comfyui.cloneDir, + $cfg.Paths.facefusion.cloneDir, + $cfg.Paths.comfyui.envDir, + $cfg.Paths.facefusion.envDir, + $cfg.Paths.facefusion.tempDir +) + +foreach ($rel in $targets) { + $full = Resolve-GenAIPath $cfg.Root $rel + if (Test-Path $full) { + Remove-Item $full -Recurse -Force + Write-Host "Removed $full" + } +} +Write-Host 'Reset complete. Run setup-all.ps1 to rebuild.' diff --git a/GenAI/scripts/windows/setup-all.ps1 b/GenAI/scripts/windows/setup-all.ps1 new file mode 100644 index 00000000000..f7e2183e385 --- /dev/null +++ b/GenAI/scripts/windows/setup-all.ps1 @@ -0,0 +1,9 @@ +#Requires -Version 5.1 +param( + [ValidateSet('compatibility', 'balanced', 'performance')] + [string]$Profile = 'balanced' +) +$ErrorActionPreference = 'Stop' +& "$PSScriptRoot\setup-comfyui.ps1" -Profile $Profile +& "$PSScriptRoot\setup-facefusion.ps1" +Write-Host 'GenAI setup-all complete. Run .\scripts\windows\doctor.ps1 to verify.' diff --git a/GenAI/scripts/windows/setup-comfyui.ps1 b/GenAI/scripts/windows/setup-comfyui.ps1 new file mode 100644 index 00000000000..66020e79585 --- /dev/null +++ b/GenAI/scripts/windows/setup-comfyui.ps1 @@ -0,0 +1,86 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Bootstrap ComfyUI into GenAI/runtime with an isolated Python venv. +#> +param( + [ValidateSet('compatibility', 'balanced', 'performance')] + [string]$Profile = '' +) + +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +$cfg = Get-GenAIConfig +Ensure-GenAIDirectories $cfg + +if (-not $Profile) { + $Profile = Get-SettingOrDefault $cfg.Settings 'GENAI_PROFILE' 'balanced' +} + +$root = $cfg.Root +$log = Join-Path (Resolve-GenAIPath $root $cfg.Paths.comfyui.logDir) 'setup.log' +$cloneDir = Resolve-GenAIPath $root $cfg.Paths.comfyui.cloneDir +$envDir = Resolve-GenAIPath $root $cfg.Paths.comfyui.envDir +$comfy = $cfg.Lock.upstreams.comfyui +$torchIndex = $cfg.Lock.pytorch.windows_cuda_index + +Write-GenAILog $log "Starting ComfyUI setup (profile: $Profile)" + +if (-not (Test-CommandExists 'git')) { + throw 'Git is required. Install from https://git-scm.com/download/win' +} + +$py = Get-PythonCandidate +if (-not $py) { throw 'Python 3.10+ is required. Install from https://www.python.org/downloads/' } +Write-GenAILog $log "Using Python $($py.Version) via $($py.Command)" + +Invoke-GitClonePinned -Repository $comfy.repository -Commit $comfy.commit -TargetDir $cloneDir -LogFile $log + +if (-not (Test-Path (Join-Path $envDir 'Scripts\python.exe'))) { + if ($py.Arg) { & $py.Command $py.Arg -m venv $envDir } + else { & $py.Command -m venv $envDir } + Write-GenAILog $log "Created venv at $envDir" +} + +$python = Join-Path $envDir 'Scripts\python.exe' +$pip = Join-Path $envDir 'Scripts\pip.exe' + +Invoke-PipInstall -Pip $python -PipArgs @('-m', 'pip', 'install', '--upgrade', 'pip', 'wheel', 'setuptools') -LogFile $log -Description 'pip bootstrap' + +Write-GenAILog $log "Installing PyTorch with CUDA from $torchIndex" +Invoke-PipInstall -Pip $pip -PipArgs @('install', 'torch', 'torchvision', 'torchaudio', '--index-url', $torchIndex) -LogFile $log -Description 'torch cu124' + +$req = Join-Path $cloneDir 'requirements.txt' +Invoke-PipInstall -Pip $pip -PipArgs @('install', '-r', $req) -LogFile $log -Description 'ComfyUI requirements' + +$cudaCheck = & $python -c "import torch; print('cuda=' + str(torch.cuda.is_available()) + ';device=' + (torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none'))" +Write-GenAILog $log "PyTorch check: $cudaCheck" +if ($cudaCheck -notmatch 'cuda=True') { + Write-Warning 'CUDA not available. ComfyUI will run on CPU. Verify NVIDIA driver and cu124 wheel compatibility.' +} + +$modelsRoot = Resolve-GenAIPath $root $cfg.Paths.comfyui.modelsDir +$externalRoot = $cfg.Settings['EXTERNAL_MODEL_ROOT'] +$extraPaths = @" +comfyui: + base_path: $($modelsRoot -replace '\\', '/') + checkpoints: checkpoints + vae: vae + loras: loras + embeddings: embeddings + controlnet: controlnet + upscale_models: upscale_models +"@ +if ($externalRoot) { + $extraPaths += "`nexternal:`n base_path: $($externalRoot -replace '\\', '/')`n checkpoints: checkpoints" +} +$extraPathsFile = Join-Path $cloneDir 'extra_model_paths.yaml' +Set-Content -Path $extraPathsFile -Value $extraPaths -Encoding UTF8 +Write-GenAILog $log "Wrote extra_model_paths.yaml" + +Write-Host "ComfyUI setup complete." +Write-Host " Clone: $cloneDir" +Write-Host " Env: $envDir" +Write-Host " Models: $modelsRoot" +Write-Host " Log: $log" diff --git a/GenAI/scripts/windows/setup-facefusion.ps1 b/GenAI/scripts/windows/setup-facefusion.ps1 new file mode 100644 index 00000000000..b3309c0fa69 --- /dev/null +++ b/GenAI/scripts/windows/setup-facefusion.ps1 @@ -0,0 +1,82 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Bootstrap FaceFusion into GenAI/runtime with an isolated Python venv. +#> +param( + [ValidateSet('cuda', 'directml', 'cpu')] + [string]$ExecutionProvider = '' +) + +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +$cfg = Get-GenAIConfig +Ensure-GenAIDirectories $cfg + +$root = $cfg.Root +$log = Join-Path (Resolve-GenAIPath $root $cfg.Paths.facefusion.logDir) 'setup.log' +$cloneDir = Resolve-GenAIPath $root $cfg.Paths.facefusion.cloneDir +$envDir = Resolve-GenAIPath $root $cfg.Paths.facefusion.envDir +$ff = $cfg.Lock.upstreams.facefusion +$configExample = Join-Path $root 'config\facefusion.example.ini' +$configLocal = Join-Path $root 'config\facefusion.ini' + +if (-not $ExecutionProvider) { + $ExecutionProvider = Get-SettingOrDefault $cfg.Settings 'FACEFUSION_EXECUTION_PROVIDER' 'cuda' +} + +Write-GenAILog $log "Starting FaceFusion setup (provider: $ExecutionProvider)" + +if (-not (Test-CommandExists 'git')) { throw 'Git is required.' } +if (-not (Test-CommandExists 'ffmpeg')) { + Write-Warning 'FFmpeg not found in PATH. Install from https://ffmpeg.org/download.html or winget install ffmpeg' +} + +$py = Get-PythonCandidate +if (-not $py) { throw 'Python 3.10+ is required.' } + +Invoke-GitClonePinned -Repository $ff.repository -Commit $ff.commit -TargetDir $cloneDir -LogFile $log + +if (-not (Test-Path (Join-Path $envDir 'Scripts\python.exe'))) { + if ($py.Arg) { & $py.Command $py.Arg -m venv $envDir } + else { & $py.Command -m venv $envDir } +} + +$python = Join-Path $envDir 'Scripts\python.exe' +$pip = Join-Path $envDir 'Scripts\pip.exe' + +Invoke-PipInstall -Pip $python -PipArgs @('-m', 'pip', 'install', '--upgrade', 'pip', 'wheel', 'setuptools') -LogFile $log -Description 'pip bootstrap' +Invoke-PipInstall -Pip $pip -PipArgs @('install', '-r', (Join-Path $cloneDir 'requirements.txt')) -LogFile $log -Description 'FaceFusion requirements' + +switch ($ExecutionProvider) { + 'cuda' { + Invoke-PipInstall -Pip $pip -PipArgs @('install', 'onnxruntime-gpu') -LogFile $log -Description 'onnxruntime-gpu' + } + 'directml' { + Invoke-PipInstall -Pip $pip -PipArgs @('install', 'onnxruntime-directml') -LogFile $log -Description 'onnxruntime-directml' + } + default { + Write-GenAILog $log 'Using CPU onnxruntime from requirements.txt' + } +} + +$tempPath = Resolve-GenAIPath $root $cfg.Paths.facefusion.tempDir +$outputPath = Resolve-GenAIPath $root $cfg.Paths.facefusion.outputDir +if (-not (Test-Path $configLocal)) { + Copy-Item $configExample $configLocal +} +$content = Get-Content $configLocal -Raw +$content = $content -replace '(?m)^temp_path\s*=.*', "temp_path = $($tempPath -replace '\\', '/')" +$content = $content -replace '(?m)^output_path\s*=.*', "output_path = $($outputPath -replace '\\', '/')" +Set-Content -Path $configLocal -Value $content.TrimEnd() -Encoding UTF8 +Write-GenAILog $log "Updated config/facefusion.ini paths" + +$providerCheck = & $python -c "import onnxruntime as ort; print(','.join(ort.get_available_providers()))" +Write-GenAILog $log "ONNX providers: $providerCheck" + +Write-Host "FaceFusion setup complete." +Write-Host " Clone: $cloneDir" +Write-Host " Env: $envDir" +Write-Host " Provider: $ExecutionProvider ($providerCheck)" +Write-Host " Log: $log" diff --git a/GenAI/scripts/windows/smoke-test.ps1 b/GenAI/scripts/windows/smoke-test.ps1 new file mode 100644 index 00000000000..27da11d5b7f --- /dev/null +++ b/GenAI/scripts/windows/smoke-test.ps1 @@ -0,0 +1,13 @@ +#Requires -Version 5.1 +$ErrorActionPreference = 'Stop' +$root = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path +Push-Location $root +try { + python tests\validate-config.py + python tests\validate-upstreams.py + python tests\validate-workflows.py + python tests\smoke_test.py --static-only + Write-Host 'Smoke tests passed (static validation).' +} finally { + Pop-Location +} diff --git a/GenAI/scripts/windows/update-all.ps1 b/GenAI/scripts/windows/update-all.ps1 new file mode 100644 index 00000000000..ac52dca1516 --- /dev/null +++ b/GenAI/scripts/windows/update-all.ps1 @@ -0,0 +1,28 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Interactive upstream update helper. Does not auto-upgrade major versions. +#> +param([switch]$Force) +$ErrorActionPreference = 'Stop' +. "$PSScriptRoot\..\lib\GenAI-Common.ps1" + +$cfg = Get-GenAIConfig +$lockPath = Join-Path $cfg.Root 'upstreams.lock.json' +$log = Join-Path (Resolve-GenAIPath $cfg.Root 'logs') 'update.log' +Ensure-GenAIDirectories $cfg + +Write-Host 'GenAI Update — pinned upstream management' +Write-Host "Current ComfyUI: $($cfg.Lock.upstreams.comfyui.release) @ $($cfg.Lock.upstreams.comfyui.commit)" +Write-Host "Current FaceFusion: $($cfg.Lock.upstreams.facefusion.release) @ $($cfg.Lock.upstreams.facefusion.commit)" +Write-Host '' +Write-Host 'This project pins explicit release commits. To update:' +Write-Host ' 1. Review upstream release notes on GitHub' +Write-Host ' 2. Edit upstreams.lock.json with new release tag and commit SHA' +Write-Host ' 3. Run: python tests/validate-upstreams.py' +Write-Host ' 4. Run: .\scripts\windows\setup-all.ps1' +Write-Host ' 5. Run: .\scripts\windows\doctor.ps1' +Write-Host '' +Write-Host 'Rollback: restore upstreams.lock.json from Git and re-run setup.' + +Write-GenAILog $log 'Update check displayed pinned versions (no automatic upgrade)' diff --git a/GenAI/tests/generate_docs.py b/GenAI/tests/generate_docs.py new file mode 100644 index 00000000000..f5908c3a693 --- /dev/null +++ b/GenAI/tests/generate_docs.py @@ -0,0 +1,436 @@ +#!/usr/bin/env python3 +"""Generate remaining GenAI documentation files.""" +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + +SD_TUTORIALS = { + "01-fundamentals.md": """# Stable Diffusion fundamentals + +## What diffusion does +Stable Diffusion starts from random noise in **latent space** (a compressed representation of an image) and iteratively denoises it guided by your text prompt until a recognizable image emerges. + +## Core terms + +| Term | Meaning | +|------|---------| +| **Checkpoint** | Full trained model weights (UNet + text encoder + VAE bundled or partial) | +| **Latent space** | Lower-dimensional grid the UNet denoises (not pixel space) | +| **Text encoder** | Converts prompt tokens to conditioning vectors (CLIP for SD1.5) | +| **VAE** | Encodes/decodes between pixels and latents | +| **Positive prompt** | What you want | +| **Negative prompt** | What to suppress | +| **Seed** | RNG seed — same seed + settings → reproducible image | +| **Sampler** | Algorithm for each denoise step (euler, dpmpp_2m, etc.) | +| **Scheduler** | Noise schedule paired with sampler | +| **Steps** | Number of denoise iterations (more ≠ always better) | +| **CFG / guidance** | How strongly prompt constrains image (typical 5–8) | +| **Width / height** | Output resolution (affects VRAM quadratically) | +| **Denoising strength** | img2img: how much to change input (0=none, 1=full) | +| **LoRA** | Small add-on weights for style/subject | +| **ControlNet** | Structural guidance from edges, depth, pose, etc. | + +## VRAM vs unified memory +- **NVIDIA VRAM**: dedicated GPU memory; OOM stops generation +- **Apple unified memory**: CPU and GPU share pool; pressure causes swapping + +Continue to [02-first-image.md](02-first-image.md). +""", + "02-first-image.md": """# Your first image + +## Model requirement +Download **SD 1.5** `v1-5-pruned-emaonly.safetensors` (accept Hugging Face license manually) to: +`models/comfyui/checkpoints/` + +## Steps +1. `.\scripts\windows\launch-comfyui.ps1` (or macOS equivalent) +2. Open http://127.0.0.1:8188 +3. **Load** → `workflows/comfyui/beginner-text-to-image.json` +4. Confirm checkpoint name in **CheckpointLoaderSimple** node +5. Click **Queue Prompt** + +## Exact settings in starter workflow +| Setting | Value | +|---------|-------| +| Positive | cozy reading nook, warm afternoon sunlight... | +| Negative | blurry, low quality, watermark... | +| Seed | 42 (fixed) | +| Resolution | 512 × 512 | +| Sampler | euler | +| Steps | 25 | +| CFG | 7.0 | + +## Output +Images save with prefix `genai_beginner` in ComfyUI output folder. + +## Reproduce +Keep seed **42** and same checkpoint — output should match. + +## Variations +Change seed to `randomize` or increment seed for controlled diversity. + +Next: [03-prompt-engineering.md](03-prompt-engineering.md) +""", + "03-prompt-engineering.md": """# Prompt engineering + +Structure prompts: **subject → action → composition → camera → lighting → environment → style**. + +## Example 1 — portrait +**Prompt:** `portrait of a woman, three-quarter view, soft window light, neutral background, 85mm lens, shallow depth of field, natural skin texture, photorealistic` + +**Settings:** 512×512, steps 25, CFG 7, euler + +**Why it works:** Specifies framing, lens, and light before style token. + +**Failure mode:** `beautiful woman` alone → generic, oversaturated faces. + +**Refined:** Add `subtle catchlight in eyes, muted color palette` and negative `plastic skin, oversharpened`. + +## Example 2 — product +**Prompt:** `matte black headphones on white marble surface, top-down flat lay, soft studio softbox, minimal shadows, commercial product photography` + +**Negative:** `text, logo, watermark, cluttered background` + +## Token weighting +ComfyUI CLIP encode accepts `(keyword:1.2)` in prompt text for emphasis. + +## Iteration loop +1. Generate at 512 fast +2. Fix composition in prompt +3. Increase steps only if detail lacking +4. Raise CFG slightly for prompt adherence — watch for burned colors above 10 +""", + "04-models-checkpoints-and-vaes.md": """# Models, checkpoints, and VAEs + +## Families +- **SD 1.5**: 512 native, vast LoRA ecosystem +- **SDXL**: 1024 native, two-encoder pipeline +- **Flux / SD3**: newer architectures — verify ComfyUI node support + +## Compatibility +Do not load SDXL checkpoint in SD1.5 workflow nodes. + +## VAE +Some checkpoints include baked VAE; external VAE fixes color/face issues. Place in `models/comfyui/vae/`. + +## Comparison method +Lock seed, steps, sampler; change only checkpoint for fair test. + +See [../models/README.md](../models/README.md). +""", + "05-loras-and-embeddings.md": """# LoRAs and embeddings + +## LoRA +Small weight files modifying UNet and/or text encoder. Typical strength **0.6–1.0** — start at 0.8. + +Place in `models/comfyui/loras/`. Load with **LoraLoader** node in ComfyUI. + +## Embeddings +Textual inversion vectors in `models/comfyui/embeddings/`. Trigger via token in prompt. + +## Compatibility +LoRA trained for SD1.5 only works with SD1.5 base. +""", + "06-samplers-schedulers-steps-and-cfg.md": """# Samplers, schedulers, steps, and CFG + +## Steps +- 15–25 often sufficient for SD1.5 euler/dpm variants +- Diminishing returns after ~30 for many samplers + +## CFG +- Low (3–5): creative, loose +- Mid (6–8): balanced default +- High (10+): harsh contrast, artifact risk + +## Fair A/B +Change one knob at a time; lock seed. + +## Oversaturation +Reduce CFG or add negative `oversaturated, hdr`. +""", + "07-image-to-image.md": """# Image-to-image + +Feed an init image + denoising strength. + +- **0.3–0.5**: color/lighting tweaks, preserve structure +- **0.6–0.8**: stronger restyle +- **1.0**: near txt2img (weak structure retention) + +Prepare square or crop consistently. See workflow `workflows/comfyui/image-to-image.json`. +""", + "08-inpainting-and-outpainting.md": """# Inpainting and outpainting + +## Inpainting +Mask the region to replace. Feather mask edges 4–8px to avoid seams. + +Use cases: object removal, face/hand fix, background cleanup. + +## Outpainting +Extend canvas; mask new areas; moderate denoise (0.7–0.85). + +Workflow: `workflows/comfyui/inpainting.json`. +""", + "09-controlnet-and-image-guidance.md": """# ControlNet and image guidance + +ControlNet adds edge/depth/pose maps as conditioning. + +Starter approach: **Canny edge** for composition lock. Place models in `models/comfyui/controlnet/`. + +Do not install every custom node — use stock ComfyUI ControlNet nodes when available. + +Workflow: `workflows/comfyui/controlled-generation.json`. +""", + "10-upscaling-and-restoration.md": """# Upscaling and restoration + +## Latent upscale +Upscale in latent space before final VAE decode — efficient. + +## Pixel upscale +ESRGAN/UltraSharp in `models/comfyui/upscale_models/`. + +## When not to upscale +Heavily artifacted base — upscaler amplifies flaws. + +Workflow: `workflows/comfyui/upscale.json`. +""", + "11-comfyui-fundamentals.md": """# ComfyUI fundamentals + +## Nodes and wires +- **Nodes** = operations +- **Sockets** = typed ports (MODEL, CLIP, LATENT, IMAGE, VAE) +- Data flows left → right + +## Essential chain +CheckpointLoader → CLIP encode (+/-) → KSampler → VAEDecode → SaveImage + +## Queue +Each **Queue Prompt** runs the graph. Cached nodes skip re-execution when inputs unchanged. + +## Workflow JSON +Save/load from UI. Committed starters in `workflows/comfyui/`. + +## Missing nodes +Red nodes = unknown type — do not auto-install; review custom node source first. +""", + "12-building-comfyui-workflows.md": """# Building ComfyUI workflows + +1. Start from `beginner-text-to-image.json` +2. Add LoraLoader after checkpoint if needed +3. Insert ControlNetApply for guided generation +4. Use **Preview Image** before Save for fast iteration +5. Save incremental versions with descriptive names + +Export JSON to `workflows/comfyui/` for Git tracking (no embedded private images). +""", + "13-performance-and-memory.md": """# Performance and memory + +## Windows RTX 4090 +- SD1.5 512 batch 1: fast +- SDXL 1024: monitor VRAM in doctor/nvidia-smi +- Model load delay on first run is normal + +## Apple Silicon +- MPS warmup on first generation +- Reduce resolution if swap increases + +## Interrupt +Use ComfyUI cancel — if frozen, check logs; hard kill loses in-flight job only. + +## Intel / CPU +Expect 10–50× slower; use low resolution for prompt iteration. +""", + "14-model-management.md": """# Model management in practice + +Track downloads in `config/model-manifest.json`. + +## Hashes +Verify SHA256 from model card when provided. + +## Licensing +Commercial use depends on checkpoint license — read model card. + +## Malicious models +Prefer official repos; avoid random mirror sites. + +Full guide: [../../models/README.md](../../models/README.md). +""", + "15-troubleshooting.md": """# Stable Diffusion troubleshooting + +| Symptom | Cause | Action | +|---------|-------|--------| +| Black image | VAE mismatch | Try external VAE or different checkpoint | +| CUDA OOM | Resolution too high | Lower size or batch | +| Red nodes | Missing node type | Use stock workflow or install reviewed custom node | +| Slow every gen | CPU mode | Re-run setup; verify CUDA/MPS | +| Wrong model | Filename mismatch | Fix CheckpointLoader widget | + +**Diagnostic:** `doctor.ps1` / `doctor.sh` +**Logs:** `logs/comfyui/`, ComfyUI terminal output +""", +} + +FF_TUTORIALS = { + "01-overview.md": """# FaceFusion overview + +FaceFusion detects faces in source and target media, swaps identity, optionally enhances faces/frames, and re-encodes video with audio preservation. + +## vs Stable Diffusion +SD generates images from noise; FaceFusion manipulates existing pixels/video frames. + +## Key concepts +- **Source**: face identity donor (authorized only) +- **Target**: image or video to modify +- **Face swapper model**: ONNX identity transfer +- **Execution provider**: cuda / directml / coreml / cpu +- **Jobs**: batch/headless configurations + +Launch: `launch-facefusion.ps1` or `.sh` + +No Roop in this project. +""", + "02-first-image-face-swap.md": """# First image face swap + +## Authorized media only +Use your own face or documented consent + license. + +## Requirements +- Clear frontal or three-quarter source face, well lit +- Target with visible face, similar angle when possible + +## UI steps +1. Launch FaceFusion +2. Select source image (inputs/facefusion/source/) +3. Select target image +4. Choose face swapper model (defaults from FaceFusion) +5. Execution provider: cuda (Windows NVIDIA) or coreml/cpu (Mac) +6. Output to outputs/facefusion/ +7. Run / preview + +## Failed detection +Try higher-resolution source, better lighting, or manual face selector in UI. + +Next: [03-video-face-swap.md](03-video-face-swap.md) +""", + "03-video-face-swap.md": """# Video face swap + +## Before long videos +Process **5–10 second clip** first. + +## Pipeline +1. FFmpeg extracts frames +2. FaceFusion processes each frame +3. Re-encode video; audio copied when configured + +## Disk space +Temp frames in `runtime/facefusion-temp/` — ensure 2× video size free. + +## Flicker +Caused by detection inconsistency — use consistent source angle; enable enhancement cautiously. + +## Audio missing +Verify FFmpeg on PATH; check FaceFusion output encoder settings. +""", + "04-multiple-faces.md": """# Multiple faces + +FaceFusion indexes faces left-to-right or by detection order. + +Map source faces to target indices explicitly in UI before processing group scenes. + +Preview each mapping on a single frame before full video. +""", + "05-face-selection-and-indexing.md": """# Face selection and indexing + +Lower detection confidence threshold only if faces are missed — increases false positives. + +For occluded faces, partial swaps may fail; mask/occlusion handling is limited — set realistic expectations. +""", + "06-enhancement-and-output-quality.md": """# Enhancement and output quality + +Enhancement can fix blur but causes **plastic skin** if pushed too high. + +Match lighting between source and target when possible. + +Check edge blending around hair and jaw — common artifact regions. +""", + "07-batch-and-headless-operation.md": """# Batch and headless operation + +Verify flags on pinned 3.6.1: +```bash +python facefusion.py --help +python facefusion.py run --help +python facefusion.py headless-run --help +``` + +Typical patterns: +```bash +python facefusion.py force-download +python facefusion.py headless-run -s SOURCE -t TARGET -o OUTPUT +``` + +Use placeholder paths from `workflows/facefusion/example-image-job.ini`. + +Job system commands (if available in 3.6.1): check `job-list`, `job-create` via --help output. +""", + "08-performance.md": """# FaceFusion performance + +| Platform | Provider | Relative speed | +|----------|----------|----------------| +| RTX 4090 | cuda | Fast | +| Windows | directml | Medium | +| Apple Silicon | coreml | Medium-variable | +| Any | cpu | Slow | + +Video scales linearly with frame count × resolution. +""", + "09-troubleshooting.md": """# FaceFusion troubleshooting + +| Symptom | Cause | Diagnostic | Fix | +|---------|-------|------------|-----| +| No face detected | Poor source | doctor + preview | Better source photo | +| CUDA provider missing | Wrong onnxruntime | doctor ONNX line | Re-run setup-facefusion | +| FFmpeg error | Not installed | `ffmpeg -version` | Install FFmpeg | +| Port occupied | Other Gradio app | doctor port check | Change port / stop app | +| Temp disk full | Long video | Check facefusion-temp | Clear temp, shorten clip | + +Logs: `logs/facefusion/setup.log`, terminal output. +""", + "10-consent-provenance-and-safety.md": """# Consent, provenance, and safety + +## Prohibited uses +- Non-consensual intimate imagery +- Sexualized depictions of minors +- Fraudulent impersonation or identity theft +- Fabricated legal/financial/political evidence +- Evading consent or forensic detection +- Presenting manipulated media as authentic evidence + +## Permitted sources +- Your own likeness +- Consenting adults with documented permission +- Licensed stock/media +- Synthetic/public-domain test fixtures + +## Provenance workflow +1. Keep original unedited media +2. Save FaceFusion settings / job config +3. Record app version from upstreams.lock.json +4. Date outputs; label manipulated media where sharing +5. Retain authorization records for commercial work + +Private media stays in git-ignored `inputs/` and `outputs/` only. +""", +} + +def write_if_missing(path: Path, content: str) -> None: + if not path.exists(): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content.strip() + "\n", encoding="utf-8") + print(f"created {path.relative_to(ROOT)}") + +for name, body in SD_TUTORIALS.items(): + write_if_missing(ROOT / "docs/stable-diffusion" / name, body) + +for name, body in FF_TUTORIALS.items(): + write_if_missing(ROOT / "docs/facefusion" / name, body) + +print("done") diff --git a/GenAI/tests/smoke_test.py b/GenAI/tests/smoke_test.py new file mode 100644 index 00000000000..be322de10db --- /dev/null +++ b/GenAI/tests/smoke_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""GenAI smoke and static validation orchestrator.""" +from __future__ import annotations + +import argparse +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +TESTS = [ + "validate-config.py", + "validate-upstreams.py", + "validate-workflows.py", +] + + +def run_test(name: str) -> int: + path = ROOT / "tests" / name + print(f"\n--- {name} ---") + result = subprocess.run([sys.executable, str(path)], cwd=ROOT) + return result.returncode + + +def check_git_hygiene() -> int: + print("\n--- git-hygiene ---") + result = subprocess.run( + ["git", "status", "--porcelain", "GenAI"], + cwd=ROOT.parent, + capture_output=True, + text=True, + ) + blocked_ext = (".ckpt", ".safetensors", ".onnx", ".pth", ".pt") + for line in result.stdout.splitlines(): + path = line[3:].strip() if len(line) > 3 else line + if path.lower().endswith(blocked_ext): + print(f"FAIL: tracked model-like file: {path}") + return 1 + print("PASS: no model extensions staged under GenAI") + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--static-only", action="store_true", help="Skip runtime startup tests") + args = parser.parse_args() + rc = 0 + for test in TESTS: + rc |= run_test(test) + rc |= check_git_hygiene() + if args.static_only: + print("\nSKIP: runtime startup tests (--static-only)") + else: + print("\nNOT TESTED: ComfyUI/FaceFusion startup requires local setup and models") + return 1 if rc else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/GenAI/tests/validate-config.py b/GenAI/tests/validate-config.py new file mode 100644 index 00000000000..e0cc80b145e --- /dev/null +++ b/GenAI/tests/validate-config.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""Validate GenAI local-paths and settings configuration.""" +from __future__ import annotations + +import json +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +REQUIRED_PATH_KEYS = { + "comfyui": ["cloneDir", "envDir", "modelsDir", "inputDir", "outputDir", "logDir"], + "facefusion": ["cloneDir", "envDir", "modelsDir", "inputDir", "outputDir", "tempDir", "logDir"], +} + + +def validate_paths(path: Path) -> list[str]: + errors: list[str] = [] + data = json.loads(path.read_text(encoding="utf-8")) + if data.get("schemaVersion") != 1: + errors.append("local-paths schemaVersion must be 1") + for section, keys in REQUIRED_PATH_KEYS.items(): + if section not in data: + errors.append(f"missing section: {section}") + continue + for key in keys: + if key not in data[section]: + errors.append(f"missing {section}.{key}") + return errors + + +def validate_settings_example(path: Path) -> list[str]: + errors: list[str] = [] + text = path.read_text(encoding="utf-8") + required = ["GENAI_HOST", "COMFYUI_PORT", "FACEFUSION_PORT"] + for key in required: + if not re.search(rf"^{key}=", text, re.MULTILINE): + errors.append(f"settings.example.env missing {key}") + if "127.0.0.1" not in text: + errors.append("settings.example.env should default GENAI_HOST to 127.0.0.1") + return errors + + +def main() -> int: + errors: list[str] = [] + example_paths = ROOT / "config" / "local-paths.example.json" + example_settings = ROOT / "config" / "settings.example.env" + errors.extend(validate_paths(example_paths)) + errors.extend(validate_settings_example(example_settings)) + if errors: + for e in errors: + print(f"FAIL: {e}") + return 1 + print("PASS: configuration templates valid") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/GenAI/tests/validate-upstreams.py b/GenAI/tests/validate-upstreams.py new file mode 100644 index 00000000000..441221e02d8 --- /dev/null +++ b/GenAI/tests/validate-upstreams.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +"""Validate upstreams.lock.json structure and commit pin format.""" +from __future__ import annotations + +import json +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +LOCK = ROOT / "upstreams.lock.json" +COMMIT_RE = re.compile(r"^[0-9a-f]{40}$") + + +def main() -> int: + data = json.loads(LOCK.read_text(encoding="utf-8")) + errors: list[str] = [] + if data.get("schemaVersion") != 1: + errors.append("schemaVersion must be 1") + for name in ("comfyui", "facefusion"): + upstream = data.get("upstreams", {}).get(name) + if not upstream: + errors.append(f"missing upstream {name}") + continue + for field in ("repository", "release", "commit", "license", "purpose"): + if field not in upstream: + errors.append(f"{name} missing {field}") + repo = upstream.get("repository", "") + if not repo.startswith("https://github.com/"): + errors.append(f"{name} repository must use HTTPS GitHub URL") + commit = upstream.get("commit", "") + if not COMMIT_RE.match(commit): + errors.append(f"{name} commit must be 40-char SHA") + if errors: + for e in errors: + print(f"FAIL: {e}") + return 1 + print("PASS: upstreams.lock.json valid") + print(f" ComfyUI: {data['upstreams']['comfyui']['release']} @ {data['upstreams']['comfyui']['commit'][:7]}") + print(f" FaceFusion: {data['upstreams']['facefusion']['release']} @ {data['upstreams']['facefusion']['commit'][:7]}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/GenAI/tests/validate-workflows.py b/GenAI/tests/validate-workflows.py new file mode 100644 index 00000000000..caf3e623894 --- /dev/null +++ b/GenAI/tests/validate-workflows.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Validate committed ComfyUI workflow JSON files.""" +from __future__ import annotations + +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +WORKFLOW_DIR = ROOT / "workflows" / "comfyui" + + +def validate_workflow(path: Path) -> list[str]: + errors: list[str] = [] + try: + data = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError as exc: + return [f"{path.name}: invalid JSON ({exc})"] + if "nodes" not in data or not isinstance(data["nodes"], list): + errors.append(f"{path.name}: missing nodes array") + return errors + if len(data["nodes"]) == 0: + errors.append(f"{path.name}: empty workflow") + node_ids = {n.get("id") for n in data["nodes"]} + if None in node_ids: + errors.append(f"{path.name}: node missing id") + for node in data["nodes"]: + if "type" not in node: + errors.append(f"{path.name}: node {node.get('id')} missing type") + return errors + + +def main() -> int: + if not WORKFLOW_DIR.exists(): + print("FAIL: workflows/comfyui directory missing") + return 1 + files = sorted(WORKFLOW_DIR.glob("*.json")) + if not files: + print("FAIL: no workflow JSON files found") + return 1 + all_errors: list[str] = [] + for wf in files: + all_errors.extend(validate_workflow(wf)) + if all_errors: + for e in all_errors: + print(f"FAIL: {e}") + return 1 + print(f"PASS: {len(files)} workflow(s) valid") + for wf in files: + print(f" - {wf.name}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/GenAI/upstreams.lock.json b/GenAI/upstreams.lock.json new file mode 100644 index 00000000000..3382e281405 --- /dev/null +++ b/GenAI/upstreams.lock.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "upstreams": { + "comfyui": { + "repository": "https://github.com/comfyanonymous/ComfyUI.git", + "release": "v0.9.2", + "commit": "8f40b43e0204d5b9780f3e9618e140e929e80594", + "license": "GPL-3.0", + "purpose": "Stable Diffusion workflow interface" + }, + "facefusion": { + "repository": "https://github.com/facefusion/facefusion.git", + "release": "3.6.1", + "commit": "5b7d145aa7659a5997f304dc059bce5005270c1d", + "license": "OpenRAIL-AS", + "purpose": "Local image and video face manipulation" + } + }, + "pytorch": { + "windows_cuda_index": "https://download.pytorch.org/whl/cu124", + "windows_cuda_version": "cu124", + "comfyui_python_min": "3.10", + "comfyui_python_max": "3.12", + "facefusion_python": "3.12" + } +} diff --git a/GenAI/workflows/comfyui/beginner-text-to-image.json b/GenAI/workflows/comfyui/beginner-text-to-image.json new file mode 100644 index 00000000000..c6a09bea55f --- /dev/null +++ b/GenAI/workflows/comfyui/beginner-text-to-image.json @@ -0,0 +1,126 @@ +{ + "last_node_id": 9, + "last_link_id": 12, + "nodes": [ + { + "id": 1, + "type": "CheckpointLoaderSimple", + "pos": [50, 100], + "size": [315, 98], + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + {"name": "MODEL", "type": "MODEL", "links": [1], "slot_index": 0}, + {"name": "CLIP", "type": "CLIP", "links": [2, 3], "slot_index": 1}, + {"name": "VAE", "type": "VAE", "links": [8], "slot_index": 2} + ], + "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, + "widgets_values": ["v1-5-pruned-emaonly.safetensors"] + }, + { + "id": 2, + "type": "CLIPTextEncode", + "pos": [400, 50], + "size": [400, 200], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [{"name": "clip", "type": "CLIP", "link": 2}], + "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [4], "slot_index": 0}], + "properties": {"Node name for S&R": "CLIPTextEncode"}, + "widgets_values": ["a cozy reading nook, warm afternoon sunlight through a window, soft shadows, photorealistic, 35mm photograph, shallow depth of field"] + }, + { + "id": 3, + "type": "CLIPTextEncode", + "pos": [400, 280], + "size": [400, 200], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [{"name": "clip", "type": "CLIP", "link": 3}], + "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [5], "slot_index": 0}], + "properties": {"Node name for S&R": "CLIPTextEncode"}, + "widgets_values": ["blurry, low quality, watermark, text, deformed, oversaturated"] + }, + { + "id": 4, + "type": "EmptyLatentImage", + "pos": [50, 350], + "size": [315, 106], + "flags": {}, + "order": 3, + "mode": 0, + "outputs": [{"name": "LATENT", "type": "LATENT", "links": [6], "slot_index": 0}], + "properties": {"Node name for S&R": "EmptyLatentImage"}, + "widgets_values": [512, 512, 1] + }, + { + "id": 5, + "type": "KSampler", + "pos": [850, 150], + "size": [315, 262], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + {"name": "model", "type": "MODEL", "link": 1}, + {"name": "positive", "type": "CONDITIONING", "link": 4}, + {"name": "negative", "type": "CONDITIONING", "link": 5}, + {"name": "latent_image", "type": "LATENT", "link": 6} + ], + "outputs": [{"name": "LATENT", "type": "LATENT", "links": [7], "slot_index": 0}], + "properties": {"Node name for S&R": "KSampler"}, + "widgets_values": [42, "fixed", 25, 7.0, "euler", "normal", 1.0] + }, + { + "id": 6, + "type": "VAEDecode", + "pos": [1200, 150], + "size": [210, 46], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + {"name": "samples", "type": "LATENT", "link": 7}, + {"name": "vae", "type": "VAE", "link": 8} + ], + "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [9], "slot_index": 0}], + "properties": {"Node name for S&R": "VAEDecode"} + }, + { + "id": 7, + "type": "SaveImage", + "pos": [1450, 150], + "size": [315, 270], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [{"name": "images", "type": "IMAGE", "link": 9}], + "properties": {"Node name for S&R": "SaveImage"}, + "widgets_values": ["genai_beginner"] + } + ], + "links": [ + [1, 1, 0, 5, 0, "MODEL"], + [2, 1, 1, 2, 0, "CLIP"], + [3, 1, 1, 3, 0, "CLIP"], + [4, 2, 0, 5, 1, "CONDITIONING"], + [5, 3, 0, 5, 2, "CONDITIONING"], + [6, 4, 0, 5, 3, "LATENT"], + [7, 5, 0, 6, 0, "LATENT"], + [8, 1, 2, 6, 1, "VAE"], + [9, 6, 0, 7, 0, "IMAGE"] + ], + "groups": [], + "config": {}, + "extra": { + "genai": { + "tutorial": "docs/stable-diffusion/02-first-image.md", + "checkpoint": "v1-5-pruned-emaonly.safetensors", + "resolution": "512x512" + } + }, + "version": 0.4 +} diff --git a/GenAI/workflows/comfyui/controlled-generation.json b/GenAI/workflows/comfyui/controlled-generation.json new file mode 100644 index 00000000000..b57b92d9009 --- /dev/null +++ b/GenAI/workflows/comfyui/controlled-generation.json @@ -0,0 +1,375 @@ +{ + "last_node_id": 9, + "last_link_id": 12, + "nodes": [ + { + "id": 1, + "type": "CheckpointLoaderSimple", + "pos": [ + 50, + 100 + ], + "size": [ + 315, + 98 + ], + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 1 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 2, + 3 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 8 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "v1-5-pruned-emaonly.safetensors" + ] + }, + { + "id": 2, + "type": "CLIPTextEncode", + "pos": [ + 400, + 50 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 2 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 4 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "a cozy reading nook, warm afternoon sunlight through a window, soft shadows, photorealistic, 35mm photograph, shallow depth of field" + ] + }, + { + "id": 3, + "type": "CLIPTextEncode", + "pos": [ + 400, + 280 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 3 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 5 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "blurry, low quality, watermark, text, deformed, oversaturated" + ] + }, + { + "id": 4, + "type": "EmptyLatentImage", + "pos": [ + 50, + 350 + ], + "size": [ + 315, + 106 + ], + "flags": {}, + "order": 3, + "mode": 0, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 6 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 5, + "type": "KSampler", + "pos": [ + 850, + 150 + ], + "size": [ + 315, + 262 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 1 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 4 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 5 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 6 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 7 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 42, + "fixed", + 25, + 7.0, + "euler", + "normal", + 1.0 + ] + }, + { + "id": 6, + "type": "VAEDecode", + "pos": [ + 1200, + 150 + ], + "size": [ + 210, + 46 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 7 + }, + { + "name": "vae", + "type": "VAE", + "link": 8 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 7, + "type": "SaveImage", + "pos": [ + 1450, + 150 + ], + "size": [ + 315, + 270 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "properties": { + "Node name for S&R": "SaveImage" + }, + "widgets_values": [ + "genai_beginner" + ] + } + ], + "links": [ + [ + 1, + 1, + 0, + 5, + 0, + "MODEL" + ], + [ + 2, + 1, + 1, + 2, + 0, + "CLIP" + ], + [ + 3, + 1, + 1, + 3, + 0, + "CLIP" + ], + [ + 4, + 2, + 0, + 5, + 1, + "CONDITIONING" + ], + [ + 5, + 3, + 0, + 5, + 2, + "CONDITIONING" + ], + [ + 6, + 4, + 0, + 5, + 3, + "LATENT" + ], + [ + 7, + 5, + 0, + 6, + 0, + "LATENT" + ], + [ + 8, + 1, + 2, + 6, + 1, + "VAE" + ], + [ + 9, + 6, + 0, + 7, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": { + "genai": { + "tutorial": "docs/stable-diffusion/09-controlnet-and-image-guidance.md", + "checkpoint": "v1-5-pruned-emaonly.safetensors", + "resolution": "512x512", + "mode": "controlnet-stub" + } + }, + "version": 0.4 +} diff --git a/GenAI/workflows/comfyui/image-to-image.json b/GenAI/workflows/comfyui/image-to-image.json new file mode 100644 index 00000000000..5f0ee52272f --- /dev/null +++ b/GenAI/workflows/comfyui/image-to-image.json @@ -0,0 +1,375 @@ +{ + "last_node_id": 9, + "last_link_id": 12, + "nodes": [ + { + "id": 1, + "type": "CheckpointLoaderSimple", + "pos": [ + 50, + 100 + ], + "size": [ + 315, + 98 + ], + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 1 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 2, + 3 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 8 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "v1-5-pruned-emaonly.safetensors" + ] + }, + { + "id": 2, + "type": "CLIPTextEncode", + "pos": [ + 400, + 50 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 2 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 4 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "a cozy reading nook, warm afternoon sunlight through a window, soft shadows, photorealistic, 35mm photograph, shallow depth of field" + ] + }, + { + "id": 3, + "type": "CLIPTextEncode", + "pos": [ + 400, + 280 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 3 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 5 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "blurry, low quality, watermark, text, deformed, oversaturated" + ] + }, + { + "id": 4, + "type": "EmptyLatentImage", + "pos": [ + 50, + 350 + ], + "size": [ + 315, + 106 + ], + "flags": {}, + "order": 3, + "mode": 0, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 6 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 5, + "type": "KSampler", + "pos": [ + 850, + 150 + ], + "size": [ + 315, + 262 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 1 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 4 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 5 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 6 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 7 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 42, + "fixed", + 25, + 7.0, + "euler", + "normal", + 1.0 + ] + }, + { + "id": 6, + "type": "VAEDecode", + "pos": [ + 1200, + 150 + ], + "size": [ + 210, + 46 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 7 + }, + { + "name": "vae", + "type": "VAE", + "link": 8 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 7, + "type": "SaveImage", + "pos": [ + 1450, + 150 + ], + "size": [ + 315, + 270 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "properties": { + "Node name for S&R": "SaveImage" + }, + "widgets_values": [ + "genai_beginner" + ] + } + ], + "links": [ + [ + 1, + 1, + 0, + 5, + 0, + "MODEL" + ], + [ + 2, + 1, + 1, + 2, + 0, + "CLIP" + ], + [ + 3, + 1, + 1, + 3, + 0, + "CLIP" + ], + [ + 4, + 2, + 0, + 5, + 1, + "CONDITIONING" + ], + [ + 5, + 3, + 0, + 5, + 2, + "CONDITIONING" + ], + [ + 6, + 4, + 0, + 5, + 3, + "LATENT" + ], + [ + 7, + 5, + 0, + 6, + 0, + "LATENT" + ], + [ + 8, + 1, + 2, + 6, + 1, + "VAE" + ], + [ + 9, + 6, + 0, + 7, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": { + "genai": { + "tutorial": "docs/stable-diffusion/07-image-to-image.md", + "checkpoint": "v1-5-pruned-emaonly.safetensors", + "resolution": "512x512", + "mode": "img2img-stub" + } + }, + "version": 0.4 +} diff --git a/GenAI/workflows/comfyui/inpainting.json b/GenAI/workflows/comfyui/inpainting.json new file mode 100644 index 00000000000..d38bc1a1bf0 --- /dev/null +++ b/GenAI/workflows/comfyui/inpainting.json @@ -0,0 +1,375 @@ +{ + "last_node_id": 9, + "last_link_id": 12, + "nodes": [ + { + "id": 1, + "type": "CheckpointLoaderSimple", + "pos": [ + 50, + 100 + ], + "size": [ + 315, + 98 + ], + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 1 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 2, + 3 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 8 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "v1-5-pruned-emaonly.safetensors" + ] + }, + { + "id": 2, + "type": "CLIPTextEncode", + "pos": [ + 400, + 50 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 2 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 4 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "a cozy reading nook, warm afternoon sunlight through a window, soft shadows, photorealistic, 35mm photograph, shallow depth of field" + ] + }, + { + "id": 3, + "type": "CLIPTextEncode", + "pos": [ + 400, + 280 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 3 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 5 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "blurry, low quality, watermark, text, deformed, oversaturated" + ] + }, + { + "id": 4, + "type": "EmptyLatentImage", + "pos": [ + 50, + 350 + ], + "size": [ + 315, + 106 + ], + "flags": {}, + "order": 3, + "mode": 0, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 6 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 5, + "type": "KSampler", + "pos": [ + 850, + 150 + ], + "size": [ + 315, + 262 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 1 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 4 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 5 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 6 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 7 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 42, + "fixed", + 25, + 7.0, + "euler", + "normal", + 1.0 + ] + }, + { + "id": 6, + "type": "VAEDecode", + "pos": [ + 1200, + 150 + ], + "size": [ + 210, + 46 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 7 + }, + { + "name": "vae", + "type": "VAE", + "link": 8 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 7, + "type": "SaveImage", + "pos": [ + 1450, + 150 + ], + "size": [ + 315, + 270 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "properties": { + "Node name for S&R": "SaveImage" + }, + "widgets_values": [ + "genai_beginner" + ] + } + ], + "links": [ + [ + 1, + 1, + 0, + 5, + 0, + "MODEL" + ], + [ + 2, + 1, + 1, + 2, + 0, + "CLIP" + ], + [ + 3, + 1, + 1, + 3, + 0, + "CLIP" + ], + [ + 4, + 2, + 0, + 5, + 1, + "CONDITIONING" + ], + [ + 5, + 3, + 0, + 5, + 2, + "CONDITIONING" + ], + [ + 6, + 4, + 0, + 5, + 3, + "LATENT" + ], + [ + 7, + 5, + 0, + 6, + 0, + "LATENT" + ], + [ + 8, + 1, + 2, + 6, + 1, + "VAE" + ], + [ + 9, + 6, + 0, + 7, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": { + "genai": { + "tutorial": "docs/stable-diffusion/08-inpainting-and-outpainting.md", + "checkpoint": "v1-5-pruned-emaonly.safetensors", + "resolution": "512x512", + "mode": "inpaint-stub" + } + }, + "version": 0.4 +} diff --git a/GenAI/workflows/comfyui/upscale.json b/GenAI/workflows/comfyui/upscale.json new file mode 100644 index 00000000000..22e664f356f --- /dev/null +++ b/GenAI/workflows/comfyui/upscale.json @@ -0,0 +1,375 @@ +{ + "last_node_id": 9, + "last_link_id": 12, + "nodes": [ + { + "id": 1, + "type": "CheckpointLoaderSimple", + "pos": [ + 50, + 100 + ], + "size": [ + 315, + 98 + ], + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 1 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 2, + 3 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 8 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "v1-5-pruned-emaonly.safetensors" + ] + }, + { + "id": 2, + "type": "CLIPTextEncode", + "pos": [ + 400, + 50 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 2 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 4 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "a cozy reading nook, warm afternoon sunlight through a window, soft shadows, photorealistic, 35mm photograph, shallow depth of field" + ] + }, + { + "id": 3, + "type": "CLIPTextEncode", + "pos": [ + 400, + 280 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 3 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 5 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "blurry, low quality, watermark, text, deformed, oversaturated" + ] + }, + { + "id": 4, + "type": "EmptyLatentImage", + "pos": [ + 50, + 350 + ], + "size": [ + 315, + 106 + ], + "flags": {}, + "order": 3, + "mode": 0, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 6 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 5, + "type": "KSampler", + "pos": [ + 850, + 150 + ], + "size": [ + 315, + 262 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 1 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 4 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 5 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 6 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 7 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 42, + "fixed", + 25, + 7.0, + "euler", + "normal", + 1.0 + ] + }, + { + "id": 6, + "type": "VAEDecode", + "pos": [ + 1200, + 150 + ], + "size": [ + 210, + 46 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 7 + }, + { + "name": "vae", + "type": "VAE", + "link": 8 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 7, + "type": "SaveImage", + "pos": [ + 1450, + 150 + ], + "size": [ + 315, + 270 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "properties": { + "Node name for S&R": "SaveImage" + }, + "widgets_values": [ + "genai_beginner" + ] + } + ], + "links": [ + [ + 1, + 1, + 0, + 5, + 0, + "MODEL" + ], + [ + 2, + 1, + 1, + 2, + 0, + "CLIP" + ], + [ + 3, + 1, + 1, + 3, + 0, + "CLIP" + ], + [ + 4, + 2, + 0, + 5, + 1, + "CONDITIONING" + ], + [ + 5, + 3, + 0, + 5, + 2, + "CONDITIONING" + ], + [ + 6, + 4, + 0, + 5, + 3, + "LATENT" + ], + [ + 7, + 5, + 0, + 6, + 0, + "LATENT" + ], + [ + 8, + 1, + 2, + 6, + 1, + "VAE" + ], + [ + 9, + 6, + 0, + 7, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": { + "genai": { + "tutorial": "docs/stable-diffusion/10-upscaling-and-restoration.md", + "checkpoint": "v1-5-pruned-emaonly.safetensors", + "resolution": "512x512", + "mode": "upscale-stub" + } + }, + "version": 0.4 +} diff --git a/GenAI/workflows/facefusion/README.md b/GenAI/workflows/facefusion/README.md new file mode 100644 index 00000000000..ff5511dbe29 --- /dev/null +++ b/GenAI/workflows/facefusion/README.md @@ -0,0 +1,27 @@ +# FaceFusion workflows and jobs + +FaceFusion **3.6.1** is launched from the cloned repo via `facefusion.py`. + +## Interactive +```bash +python facefusion.py run --execution-providers cuda +``` +(macOS: `coreml` or `cpu`) + +## Headless image (verify flags on your install) +```bash +python facefusion.py headless-run \ + -s inputs/facefusion/source/your-source.jpg \ + -t inputs/facefusion/target/your-target.jpg \ + -o outputs/facefusion/result.jpg +``` + +## Model download +```bash +python facefusion.py force-download +``` + +## Example config +See [example-image-job.ini](example-image-job.ini) for placeholder paths. + +Official docs: https://docs.facefusion.io/ diff --git a/GenAI/workflows/facefusion/example-image-job.ini b/GenAI/workflows/facefusion/example-image-job.ini new file mode 100644 index 00000000000..4df98034abf --- /dev/null +++ b/GenAI/workflows/facefusion/example-image-job.ini @@ -0,0 +1,10 @@ +[paths] +source = inputs/facefusion/source/example-source.jpg +target = inputs/facefusion/target/example-target.jpg +output = outputs/facefusion/example-output.jpg + +[options] +# Windows NVIDIA: cuda +# Windows fallback: directml or cpu +# macOS: coreml or cpu +execution_providers = cuda From fbdb68c1471a6c95f890c61e657ed2d92ea0a67f Mon Sep 17 00:00:00 2001 From: Quango Date: Sun, 14 Jun 2026 08:07:42 -0600 Subject: [PATCH 2/2] Fix Windows setup and validation after RTX 4090 runtime test. Handle git stderr during pinned checkout, use ASCII-safe PowerShell strings, install requests for ComfyUI startup, and update validation matrix with honest hardware test results. Co-authored-by: Cursor --- GenAI/docs/testing-validation-matrix.md | 39 +++++++++++++-------- GenAI/scripts/lib/GenAI-Common.ps1 | 6 ++++ GenAI/scripts/windows/doctor.ps1 | 5 +-- GenAI/scripts/windows/launch-comfyui.ps1 | 5 +-- GenAI/scripts/windows/launch-facefusion.ps1 | 5 +-- GenAI/scripts/windows/setup-comfyui.ps1 | 4 ++- 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/GenAI/docs/testing-validation-matrix.md b/GenAI/docs/testing-validation-matrix.md index 2e8375a80ba..48487632ad4 100644 --- a/GenAI/docs/testing-validation-matrix.md +++ b/GenAI/docs/testing-validation-matrix.md @@ -1,21 +1,32 @@ -# Validation matrix +# Validation matrix -Honest test status as of project scaffold (agent environment). +Honest test status after Windows runtime validation on RTX 4090 host (2026-06-14). Commit context: `46ddd9ab` (Add cross-platform ComfyUI and FaceFusion environment). Mac rows: **NOT TESTED** on this Windows host. | Component | Static validation | Startup tested | Functional test | Acceleration tested | |-----------|-------------------|----------------|-----------------|---------------------| -| Config templates | PASS | — | — | — | -| upstreams.lock.json | PASS | — | — | — | -| Workflow JSON | PASS | — | — | — | -| Python validators | PASS | — | — | — | -| PowerShell scripts | PARTIAL (parser) | NOT TESTED | NOT TESTED | NOT TESTED | -| Bash scripts | PARTIAL (review) | NOT TESTED | NOT TESTED | NOT TESTED | -| ComfyUI clone+venv | NOT TESTED | NOT TESTED | NOT TESTED | NOT TESTED | -| FaceFusion clone+venv | NOT TESTED | NOT TESTED | NOT TESTED | NOT TESTED | -| Image generation | NOT TESTED | NOT TESTED | BLOCKED (no checkpoint) | NOT TESTED | -| Face swap | NOT TESTED | NOT TESTED | BLOCKED (no authorized media) | NOT TESTED | -| RTX 4090 CUDA | NOT TESTED | — | — | NOT TESTED | -| Apple MPS/CoreML | NOT TESTED | — | — | NOT TESTED | +| Config templates | PASS | PASS (setup created settings) | N/A | N/A | +| upstreams.lock.json | PASS | PASS (pinned clones) | N/A | N/A | +| Workflow JSON | PASS | N/A | NOT TESTED | N/A | +| Python validators | PASS | N/A | N/A | N/A | +| PowerShell scripts | PASS (smoke-test static) | PARTIAL | NOT TESTED | NOT TESTED | +| Bash scripts | NOT TESTED (Windows host) | NOT TESTED | NOT TESTED | NOT TESTED | +| ComfyUI clone+venv | PASS | PASS (HTTP 200 @8188)* | BLOCKED (no checkpoint) | PASS (CUDA RTX 4090) | +| FaceFusion clone+venv | PASS | NOT TESTED (no authorized media) | BLOCKED (no authorized media) | PASS (CUDA/TRT ONNX providers) | +| Image generation | NOT TESTED | N/A | BLOCKED (no checkpoint) | NOT TESTED | +| Face swap | NOT TESTED | N/A | BLOCKED (no authorized media in inputs/facefusion) | NOT TESTED | +| RTX 4090 CUDA | PASS (doctor + torch) | N/A | N/A | PASS (ComfyUI cuda=True; ONNX CUDA) | +| Apple MPS/CoreML | NOT TESTED | NOT TESTED | NOT TESTED | NOT TESTED | + +* ComfyUI v0.9.2 at pinned commit omits `requests` from `requirements.txt`; first validation run needed manual `pip install requests` before HTTP 200 (~15s). `setup-comfyui.ps1` now installs `requests` after requirements on fresh setup. + +## Windows validation notes + +- **setup-all.ps1**: First run failed on PowerShell treating `git` stderr as terminating (`Invoke-GitClonePinned`); patched `GenAI-Common.ps1` to set `$ErrorActionPreference = 'Continue'` during git ops. Subsequent setup-comfyui + setup-facefusion completed successfully. +- **doctor.ps1**: Failed parse until em-dash characters replaced with ASCII hyphen (Windows encoding). After fix: all PASS except **FFmpeg WARN**. +- **launch-comfyui.ps1 / launch-facefusion.ps1**: Same em-dash parse issue fixed for startup smoke. +- **GENAI_PROFILE**: `balanced` adds no extra args; `compatibility` → `--disable-smart-memory`; `performance` → `--highvram` (matches launch-comfyui.ps1 switch). +- **models/comfyui/checkpoints/**: empty (no download performed). +- **inputs/facefusion/**: empty (no face swap run). Run static suite: ```powershell diff --git a/GenAI/scripts/lib/GenAI-Common.ps1 b/GenAI/scripts/lib/GenAI-Common.ps1 index 70ed2797de4..dc0d038ad57 100644 --- a/GenAI/scripts/lib/GenAI-Common.ps1 +++ b/GenAI/scripts/lib/GenAI-Common.ps1 @@ -149,6 +149,9 @@ function Invoke-GitClonePinned { [string]$TargetDir, [string]$LogFile ) + $prevErrorAction = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + try { if (Test-Path (Join-Path $TargetDir '.git')) { Push-Location $TargetDir try { @@ -173,6 +176,9 @@ function Invoke-GitClonePinned { Pop-Location } } + } finally { + $ErrorActionPreference = $prevErrorAction + } } function Get-DiagnosticLine { diff --git a/GenAI/scripts/windows/doctor.ps1 b/GenAI/scripts/windows/doctor.ps1 index d36a994b218..2e00f51a007 100644 --- a/GenAI/scripts/windows/doctor.ps1 +++ b/GenAI/scripts/windows/doctor.ps1 @@ -1,4 +1,4 @@ -#Requires -Version 5.1 +#Requires -Version 5.1 $ErrorActionPreference = 'Continue' . "$PSScriptRoot\..\lib\GenAI-Common.ps1" @@ -41,7 +41,7 @@ if (Test-CommandExists 'nvidia-smi') { $drive = (Split-Path $root -Qualifier) $free = (Get-PSDrive ($drive.TrimEnd(':'))).Free / 1GB if ($free -gt 50) { $results += Get-DiagnosticLine 'Disk space' 'PASS' ("{0:N1} GB free" -f $free) } -elseif ($free -gt 20) { $results += Get-DiagnosticLine 'Disk space' 'WARN' ("{0:N1} GB free — models need more" -f $free) } +elseif ($free -gt 20) { $results += Get-DiagnosticLine 'Disk space' 'WARN' ("{0:N1} GB free - models need more" -f $free) } else { $results += Get-DiagnosticLine 'Disk space' 'FAIL' ("{0:N1} GB free" -f $free) } # ComfyUI env @@ -121,3 +121,4 @@ foreach ($entry in @(@('ComfyUI port', $cfg.Settings['COMFYUI_PORT']), @('FaceFu } Write-DiagnosticReport $results + diff --git a/GenAI/scripts/windows/launch-comfyui.ps1 b/GenAI/scripts/windows/launch-comfyui.ps1 index b17000d306c..e72fb20809c 100644 --- a/GenAI/scripts/windows/launch-comfyui.ps1 +++ b/GenAI/scripts/windows/launch-comfyui.ps1 @@ -1,4 +1,4 @@ -#Requires -Version 5.1 +#Requires -Version 5.1 param( [ValidateSet('compatibility', 'balanced', 'performance')] [string]$Profile = '' @@ -40,7 +40,7 @@ switch ($Profile) { } if ($hostAddr -notin @('127.0.0.1', 'localhost')) { - Write-Warning "ComfyUI binding to $hostAddr — ensure this is intentional. Default is localhost-only." + Write-Warning "ComfyUI binding to $hostAddr - ensure this is intentional. Default is localhost-only." } $cudaInfo = & $python -c "import torch; print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU')" 2>$null @@ -63,3 +63,4 @@ try { } finally { Pop-Location } + diff --git a/GenAI/scripts/windows/launch-facefusion.ps1 b/GenAI/scripts/windows/launch-facefusion.ps1 index 625a85bda8d..6bff25d2983 100644 --- a/GenAI/scripts/windows/launch-facefusion.ps1 +++ b/GenAI/scripts/windows/launch-facefusion.ps1 @@ -1,4 +1,4 @@ -#Requires -Version 5.1 +#Requires -Version 5.1 param( [string]$ExecutionProvider = '' ) @@ -37,7 +37,7 @@ $providers = switch ($ExecutionProvider) { } if ($hostAddr -notin @('127.0.0.1', 'localhost')) { - Write-Warning "FaceFusion binding to $hostAddr — ensure this is intentional." + Write-Warning "FaceFusion binding to $hostAddr - ensure this is intentional." } $env:GRADIO_SERVER_NAME = $hostAddr @@ -71,3 +71,4 @@ try { } finally { Pop-Location } + diff --git a/GenAI/scripts/windows/setup-comfyui.ps1 b/GenAI/scripts/windows/setup-comfyui.ps1 index 66020e79585..7788f42614c 100644 --- a/GenAI/scripts/windows/setup-comfyui.ps1 +++ b/GenAI/scripts/windows/setup-comfyui.ps1 @@ -1,4 +1,4 @@ -#Requires -Version 5.1 +#Requires -Version 5.1 <# .SYNOPSIS Bootstrap ComfyUI into GenAI/runtime with an isolated Python venv. @@ -54,6 +54,8 @@ Invoke-PipInstall -Pip $pip -PipArgs @('install', 'torch', 'torchvision', 'torch $req = Join-Path $cloneDir 'requirements.txt' Invoke-PipInstall -Pip $pip -PipArgs @('install', '-r', $req) -LogFile $log -Description 'ComfyUI requirements' +Invoke-PipInstall -Pip $pip -PipArgs @('install', 'requests') -LogFile $log -Description 'requests (ComfyUI startup dep not in requirements.txt)' + $cudaCheck = & $python -c "import torch; print('cuda=' + str(torch.cuda.is_available()) + ';device=' + (torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none'))" Write-GenAILog $log "PyTorch check: $cudaCheck" if ($cudaCheck -notmatch 'cuda=True') {