├── .dockerignore ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── feature-request.yml │ └── user-support.yml └── images │ └── swarmui.jpg ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── SwarmUI.sln ├── colab ├── README.md └── colab-notebook.ipynb ├── docs ├── API.md ├── APIRoutes │ ├── AdminAPI.md │ ├── BackendAPI.md │ ├── BasicAPIFeatures.md │ ├── ComfyUIWebAPI.md │ ├── GridGeneratorExtension.md │ ├── ImageBatchToolExtension.md │ ├── ModelsAPI.md │ ├── T2IAPI.md │ └── UtilAPI.md ├── Advanced Usage.md ├── Basic Usage.md ├── Command Line Arguments.md ├── Docker.md ├── Extensions.md ├── Features │ ├── Autocompletions.md │ ├── Comfy-Workflows.md │ ├── ControlNet.md │ ├── ImagePrompting.md │ ├── Presets.md │ ├── Prompt Syntax.md │ ├── README.md │ ├── UISounds.md │ ├── Video.md │ └── Webhooks.md ├── Image Metadata Format.md ├── Making Extensions.md ├── Model Support.md ├── Motivations.md ├── Privacy.md ├── README.md ├── Sharing Your Swarm.md ├── Troubleshooting.md ├── User Settings.md ├── Using More GPUs.md ├── Video Model Support.md ├── Why Use Swarm.md └── images │ ├── add-swarm-backend.png │ ├── alternate-cat-dog.jpg │ ├── autocompletions.png │ ├── clear-cat.png │ ├── config-swarm-backend.png │ ├── draggable.png │ ├── fromto-cat-dog.jpg │ ├── grids.png │ ├── lan-access.png │ ├── local-network.png │ ├── lora-arcane-cat.jpg │ ├── models │ ├── auraflow-02.jpg │ ├── cascade.jpg │ ├── flux-dev.jpg │ ├── flux-schnell.jpg │ ├── hidream-i1-dev.jpg │ ├── lumina-2.png │ ├── pixart-sigma-xl-2.jpg │ ├── sana-1600m.jpg │ ├── sd15.jpg │ ├── sd35l.jpg │ ├── sd35m.jpg │ ├── sd3m.jpg │ └── sdxl.jpg │ ├── presets.png │ ├── prompt-weight.jpg │ ├── queue-running.png │ ├── random-cats.jpg │ ├── refiners.png │ ├── remote-machine-open.png │ ├── repeat-random.jpg │ ├── sdxl_catdog.jpg │ ├── sdxl_object_catdog.jpg │ ├── segment-ref.jpg │ ├── servermodelpath.png │ ├── setvar-cat.jpg │ ├── style-preset-cats.jpg │ ├── trigger-arcane-cat.jpg │ ├── usecomfy.png │ ├── wildcards-cats.jpg │ └── yourfirstprompt.png ├── languages ├── ar.json ├── de.json ├── en.json ├── es.json ├── fr.json ├── hi.json ├── it.json ├── ja.json ├── nl.json ├── pt.json ├── ru.json ├── sv.json ├── vi.json └── zh.json ├── launch-linux-dev.sh ├── launch-linux.sh ├── launch-macos.sh ├── launch-windows-dev.ps1 ├── launch-windows.bat ├── launchtools ├── 7z │ └── win │ │ ├── 7za.dll │ │ ├── 7za.exe │ │ ├── 7zxa.dll │ │ ├── License.txt │ │ └── readme.txt ├── OpenDockerfile.docker ├── StandardDockerfile.docker ├── comfy-install-linux.sh ├── docker-open-inner.sh ├── docker-standard-inner.sh ├── example-docker-compose.yml ├── extension_list.fds ├── install-linux.sh ├── install-windows.bat ├── launch-open-docker.sh ├── launch-standard-docker.sh ├── linux-build-logic.sh ├── linux-path-fix.sh ├── pickle-to-safetensors.py ├── pickle_module.py ├── windows-open-docker.bat └── windows-standard-docker.bat └── src ├── Accounts ├── Permissions.cs ├── Role.cs ├── Session.cs ├── SessionHandler.cs └── User.cs ├── Backends ├── AbstractT2IBackend.cs ├── BackendHandler.cs ├── NetworkBackendUtils.cs └── SwarmSwarmBackend.cs ├── BuiltinExtensions ├── AutoWebUIBackend │ ├── AutoWebUIAPIAbstractBackend.cs │ ├── AutoWebUIAPIBackend.cs │ ├── AutoWebUIBackendExtension.cs │ ├── AutoWebUISelfStartBackend.cs │ └── README.md ├── ComfyUIBackend │ ├── .gitignore │ ├── Assets │ │ ├── comfy_workflow_editor.css │ │ └── comfy_workflow_editor_helper.js │ ├── ComfyUIAPIAbstractBackend.cs │ ├── ComfyUIAPIBackend.cs │ ├── ComfyUIBackendExtension.cs │ ├── ComfyUIRedirectHelper.cs │ ├── ComfyUISelfStartBackend.cs │ ├── ComfyUIWebAPI.cs │ ├── ExampleWorkflows │ │ └── Basic SDXL.json │ ├── ExtraNodes │ │ ├── SwarmComfyCommon │ │ │ ├── SwarmBlending.py │ │ │ ├── SwarmClipSeg.py │ │ │ ├── SwarmExtractLora.py │ │ │ ├── SwarmImages.py │ │ │ ├── SwarmInputNodes.py │ │ │ ├── SwarmInternalUtil.py │ │ │ ├── SwarmKSampler.py │ │ │ ├── SwarmLatents.py │ │ │ ├── SwarmLoadImageB64.py │ │ │ ├── SwarmLoraLoader.py │ │ │ ├── SwarmMasks.py │ │ │ ├── SwarmMath.py │ │ │ ├── SwarmReference.py │ │ │ ├── SwarmSaveImageWS.py │ │ │ ├── SwarmTextHandling.py │ │ │ ├── SwarmTiling.py │ │ │ ├── SwarmUnsampler.py │ │ │ ├── __init__.py │ │ │ └── web │ │ │ │ └── swarmhelper.js │ │ └── SwarmComfyExtra │ │ │ ├── SwarmRemBg.py │ │ │ ├── SwarmSaveAnimationWS.py │ │ │ ├── SwarmYolo.py │ │ │ ├── __init__.py │ │ │ └── requirements.txt │ ├── README.md │ ├── Tabs │ │ └── Text2Image │ │ │ └── Comfy Workflow.html │ ├── ThemeCSS │ │ ├── modern_dark.css │ │ └── punked.css │ ├── WorkflowGenerator.cs │ └── WorkflowGeneratorSteps.cs ├── DynamicThresholding │ ├── DynamicThresholdingExtension.cs │ ├── README.md │ └── assets │ │ └── dyn_thresh.js ├── GridGenerator │ ├── Assets │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.min.css │ │ ├── grid.schema.json │ │ ├── grid_gen.css │ │ ├── grid_gen.js │ │ ├── jquery.min.js │ │ ├── jsgif.js │ │ ├── page.html │ │ ├── placeholder.png │ │ ├── proc.js │ │ ├── styles-user.css │ │ ├── styles.css │ │ └── swarmui_gridgen_local.js │ ├── GridGenCore.cs │ ├── GridGeneratorExtension.cs │ └── README.md ├── ImageBatchTool │ ├── Assets │ │ └── image_batcher.js │ ├── ImageBatchToolExtension.cs │ └── README.md └── Scorers │ ├── README.md │ ├── ScorersExtension.cs │ ├── christoph_aesthetic.py │ ├── requirements.txt │ └── scorer_engine.py ├── Core ├── Extension.cs ├── ExtensionsManager.cs ├── InstallableFeatures.cs ├── Installation.cs ├── Program.cs ├── Settings.cs └── WebServer.cs ├── DataHolders ├── DataHolder.cs └── NetConfigMetadata.cs ├── Extensions └── put_extensions_here ├── GlobalSuppressions.cs ├── GlobalUsings.cs ├── Pages ├── Error │ ├── 404.cshtml │ ├── BasicAPI.cshtml │ ├── Internal.cshtml │ ├── NoGetAPI.cshtml │ └── Permissions.cshtml ├── Install.cshtml ├── Login.cshtml ├── Shared │ └── _Layout.cshtml ├── Text2Image.cshtml ├── _Generate │ ├── GenTabModals.cshtml │ ├── GenerateTab.cshtml │ ├── ServerTab.cshtml │ ├── SimpleTab.cshtml │ ├── UserTab.cshtml │ └── UtilitiesTab.cshtml ├── _ViewImports.cshtml └── _ViewStart.cshtml ├── SwarmUI.csproj ├── Text2Image ├── CommonModels.cs ├── T2IEngine.cs ├── T2IModel.cs ├── T2IModelClass.cs ├── T2IModelClassSorter.cs ├── T2IModelHandler.cs ├── T2IMultiStepObjectBuilder.cs ├── T2IParamInput.cs ├── T2IParamSet.cs ├── T2IParamTypes.cs ├── T2IPreset.cs └── T2IPromptHandling.cs ├── Utils ├── AutoCompleteListHelper.cs ├── CliplikeTokenizer.cs ├── GeneratePageModel.cs ├── Image.cs ├── ImageMetadataTracker.cs ├── LanguagesHelper.cs ├── Logs.cs ├── MemCleaner.cs ├── MemoryNum.cs ├── NvidiaUtil.cs ├── PromptRegion.cs ├── ProxyHandler.cs ├── PythonLaunchHelper.cs ├── SwarmExceptions.cs ├── SystemStatusMonitor.cs ├── UserSoundHelper.cs ├── Utilities.cs ├── WebUtil.cs ├── WebhookManager.cs └── WildcardsHelper.cs ├── WebAPI ├── API.cs ├── APICall.cs ├── APICallReflectBuilder.cs ├── AdminAPI.cs ├── BackendAPI.cs ├── BasicAPIFeatures.cs ├── ModelsAPI.cs ├── T2IAPI.cs ├── UserUpstreamApiKeys.cs └── UtilAPI.cs ├── jsconfig.json ├── srcdata └── Tokensets │ └── clip.txt.gz └── wwwroot ├── css ├── bootstrap.min.css ├── bootstrap_light.min.css ├── genpage.css ├── installer.css ├── loginpage.css ├── select2.min.css ├── select2_bootstrap.min.css ├── site.css └── themes │ ├── beweish.css │ ├── cyber_swarm.css │ ├── dark_dreams.css │ ├── eyesear_white.css │ ├── gravity_blue.css │ ├── modern.css │ ├── modern_dark.css │ ├── modern_light.css │ ├── punked.css │ ├── solarized.css │ └── swarmpunk.css ├── favicon.ico ├── fonts ├── Inter.woff2 ├── MaterialSymbolsOutlined.LICENSE.txt ├── MaterialSymbolsOutlined.woff2 └── unifont-12.0.01.woff2 ├── imgs ├── automatic.jpg ├── canvas_move.png ├── canvas_rotate.png ├── dotdotdot.png ├── eraser.png ├── flags │ ├── ar.jpg │ ├── de.jpg │ ├── en.jpg │ ├── es.jpg │ ├── fr.jpg │ ├── hi.jpg │ ├── it.jpg │ ├── ja.jpg │ ├── nl.jpg │ ├── pt.jpg │ ├── ru.jpg │ ├── sv.jpg │ ├── vi.jpg │ └── zh.jpg ├── folder.png ├── html.jpg ├── icon-attrib.txt ├── legacy_ckpt.jpg ├── model_placeholder.jpg ├── mouse.png ├── move.png ├── noise.png ├── none.jpg ├── paintbrush.png ├── paintbucket.png └── select.png └── js ├── genpage ├── gentab │ ├── currentimagehandler.js │ ├── generatecontrols.js │ ├── imagehistory.js │ ├── layout.js │ ├── models.js │ ├── params.js │ ├── presets.js │ ├── prompttools.js │ ├── welcomemessages.js │ └── wildcards.js ├── helpers │ ├── browsers.js │ ├── generatehandler.js │ ├── image_editor.js │ ├── metadatahelpers.js │ ├── settings_editor.js │ └── ui_improvements.js ├── main.js ├── server │ ├── backends.js │ ├── logs.js │ └── servertab.js ├── simpletab.js ├── usertab.js └── utiltab.js ├── installer.js ├── lib ├── bootstrap.min.js ├── exifr.min.js ├── jquery.min.js └── select2.min.js ├── loginpage.js ├── permissions.js ├── site.js ├── translator.js └── util.js /.dockerignore: -------------------------------------------------------------------------------- 1 | # C# 2 | bin/ 3 | obj/ 4 | .vs/ 5 | .vscode/ 6 | *.user 7 | *.suo 8 | Properties/ 9 | 10 | # This project 11 | Output/ 12 | Data/ 13 | Models/ 14 | tmp/ 15 | dlbackend/ 16 | 17 | # Python 18 | __pycache__/ 19 | venv/ 20 | *.pth 21 | *.safetensors 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | Dockerfile text eol=lf 3 | *.docker text eol=lf 4 | .dockerignore text eol=lf 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: mcmonkey4eva 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: "Something is broken inside of SwarmUI. (Do not use this if you're just having issues and need help)" 3 | labels: [ "Bug" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before submitting a **Bug Report**, please ensure the following: 9 | 10 | **1:** You are running the latest version of SwarmUI. You can go to `Server` -> then press `Update and Restart` to update. 11 | **2:** You have looked at the existing bug reports and made sure this isn't already reported. 12 | **3:** This is an actual bug in SwarmUI, not just a support question and not caused by an extension. A bug is when you can specify exact steps to replicate what went wrong and others will be able to repeat your steps and see the same issue happen. 13 | 14 | If unsure, ask on the [SwarmUI Discord](https://discord.gg/q2y38cqjNw) first. 15 | - type: textarea 16 | attributes: 17 | label: Expected Behavior 18 | description: "What you expected to happen." 19 | validations: 20 | required: true 21 | - type: textarea 22 | attributes: 23 | label: Actual Behavior 24 | description: "What actually happened. Please include a screenshot of the issue if possible." 25 | validations: 26 | required: true 27 | - type: textarea 28 | attributes: 29 | label: Steps to Reproduce 30 | description: "How to reproduce the issue. Any specific parameters to use, any required extensions, etc." 31 | validations: 32 | required: true 33 | - type: textarea 34 | attributes: 35 | label: Debug Logs 36 | description: "Please go to `Server` -> `Logs` -> then click the `Pastebin` button and copy the link it generates for you to here. Alternately, set `View Type` to `Debug`, then copypaste all the text into here. If Swarm isn't starting, copy the logs from the terminal window if you can." 37 | validations: 38 | required: true 39 | - type: textarea 40 | attributes: 41 | label: Other 42 | description: "Any other additional information you think might be helpful." 43 | validations: 44 | required: false 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: SwarmUI Discord 4 | url: https://discord.gg/q2y38cqjNw 5 | about: Please join the Discord and ask there before using GitHub issues. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: "You have an idea for something new you would like to see added to Swarm." 3 | labels: [ "Feature" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before submitting a **Feature Request**, please ensure the following: 9 | 10 | **1:** You are running the latest version of SwarmUI. You can go to `Server` -> then press `Update and Restart` to update. 11 | **2:** You have looked to make sure there is not already a feature that does what you need, and there is not already a Feature Request listed for the same idea. 12 | **3:** This is something that makes sense to add to SwarmUI, as opposed to eg being a ComfyUI core feature. 13 | 14 | If unsure, maybe ask on the [SwarmUI Discord](https://discord.gg/q2y38cqjNw) first. 15 | - type: textarea 16 | attributes: 17 | label: Feature Idea 18 | description: "Describe the feature you want to see." 19 | validations: 20 | required: true 21 | - type: textarea 22 | attributes: 23 | label: Other 24 | description: "Any other additional information you think might be helpful." 25 | validations: 26 | required: false 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/user-support.yml: -------------------------------------------------------------------------------- 1 | name: User Support 2 | description: "Use this if you need help with something, or you're experiencing an issue." 3 | labels: [ "User Support" ] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before submitting a **User Report** issue, please ensure the following: 9 | 10 | **1:** You are running the latest version of SwarmUI. You can go to `Server` -> then press `Update and Restart` to update. 11 | **2:** You have made an effort to find public answers to your question before asking here. In other words, you googled it first, and scrolled through recent help topics. It's also wise to do a CTRL+F search on the [SwarmUI Discord](https://discord.gg/q2y38cqjNw). 12 | 13 | In general we recommend most support questions go through the [SwarmUI Discord](https://discord.gg/q2y38cqjNw). 14 | - type: textarea 15 | attributes: 16 | label: Your question 17 | description: "Post your question here. Please be as detailed as possible." 18 | validations: 19 | required: true 20 | - type: textarea 21 | attributes: 22 | label: Logs 23 | description: "If your question relates to an issue you're experiencing, please go to `Server` -> `Logs` -> then click the `Pastebin` button and copy the link it generates for you to here. Alternately, set `View Type` to `Debug` as well, then copypaste all the text into here." 24 | validations: 25 | required: false 26 | - type: textarea 27 | attributes: 28 | label: Other 29 | description: "Any other additional information you think might be helpful." 30 | validations: 31 | required: false 32 | -------------------------------------------------------------------------------- /.github/images/swarmui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/.github/images/swarmui.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C# and related tools 2 | bin/ 3 | obj/ 4 | .vs/ 5 | .vscode/ 6 | .idea/ 7 | *.user 8 | *.suo 9 | Properties/ 10 | .dotnet/ 11 | .aspnet/ 12 | .local/ 13 | 14 | # This project 15 | /Output 16 | /Data 17 | /Models 18 | tmp/ 19 | /src/Extensions 20 | /dlbackend 21 | *.debug 22 | custom-launch-docker.sh 23 | dotnet-install.sh 24 | docker-compose.yml 25 | 26 | # Python 27 | __pycache__/ 28 | venv/ 29 | *.pth 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to SwarmUI 2 | 3 | Please open an Issue or Discussion, or ask on [Discord](https://discord.gg/q2y38cqjNw) before opening a pull request, to make sure your work doesn't overlap with others. 4 | 5 | (TODO: More general contributing info) 6 | 7 | ## Extensions 8 | 9 | Want to make something out of scope for Swarm's core systems? Make an extension! See the [Making Extensions Doc](/docs/Making%20Extensions.md) 10 | 11 | If your extension is ready to go, post it in the `#extensions` channel on Discord, and open a pull request adding it to the file `launchtools/extension_list.fds` 12 | 13 | ## Languages 14 | 15 | Want to help translate Swarm into another language? 16 | 17 | - First: you're going to have to speak English. The English text is the "one true root" language that all other languages are derived from, it would be problematic to translate a translation. 18 | - Are you just helping improve an existing language? 19 | - Great! Just edit the file in `languages/(language-code).json` and improve the translations included 20 | - Do you want to add a new language? 21 | - See example commit here: https://github.com/mcmonkeyprojects/SwarmUI/commit/20fd27a20127b6529a2837eb838a0cfae80c20b8 22 | - In short: copy/paste `languages/en.json` to `languages/(your-code).json`, fill out the info at the top, and start translating keys. 23 | - Also add `src/wwwroot/imgs/flags/(your-code).jpg` as a small icon image of a flag that represents the language. 24 | - You can use https://github.com/mcmonkeyprojects/translate-tool to fill out any keys you can't be bothered filling in yourself with automatic AI-powered translation 25 | - Are you adding new translatable keys? 26 | - I use the hidden webconsole call `debugSubmitTranslatables()` to generate `languages/en.debug` which contains a raw key list, and then use `--add-json` to add it in with the translate tool. 27 | 28 | ## Themes 29 | 30 | Want to add a new theme or change an existing one? 31 | 32 | - First: make sure it's reasonable and relevant to add to the core. If you're doing crazy experimental stuff, maybe it should be an extension. 33 | - For extensions, see [Making Extensions Doc](/docs/Making%20Extensions.md) 34 | - Theme files are in `src/wwwroot/css/themes` 35 | - You can add any new theme to the registered theme list in `WebServer.cs`, `PreInit()` block. 36 | - Most core themes should only edit colors, other CSS edits should only be with good well considered reasons. 37 | - If you must modify non-color CSS, make the minimum possible changes. 38 | - Non-color CSS should be in a separate file from the color changes, similar to how `modern.css` is a separate file for major edits. 39 | - All new themes should use `modern.css` as the first stylesheet entry. Themes not built atop `modern.css` will not be accepted to the core. 40 | - Themes should ideally be added to Install page, but not strictly required. 41 | 42 | # Legal 43 | 44 | By submitting a contribution to this repo, you agree to grant a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license to Alex "mcmonkey" Goodwin to use, copy, modify, and distribute your contribution under the terms of the MIT License, view [LICENSE.txt](/LICENSE.txt) for details, and under any future license we may change to. 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024-2025 Alex "mcmonkey" Goodwin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SwarmUI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33530.505 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwarmUI", "src\SwarmUI.csproj", "{2A621042-196A-4F9C-88EA-10369EAEE774}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {2A621042-196A-4F9C-88EA-10369EAEE774}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {2A621042-196A-4F9C-88EA-10369EAEE774}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {2A621042-196A-4F9C-88EA-10369EAEE774}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {2A621042-196A-4F9C-88EA-10369EAEE774}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {FD662A7C-77C1-49E7-B0AC-EF986731A226} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /colab/README.md: -------------------------------------------------------------------------------- 1 | This folder is for Google-Colab related files. 2 | 3 | View the notebook on Google Colab: https://colab.research.google.com/github/mcmonkeyprojects/SwarmUI/blob/master/colab/colab-notebook.ipynb 4 | -------------------------------------------------------------------------------- /colab/colab-notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "#### PICK ONE: This node for temporary install\n", 10 | "SWARMPATH = '/content/'" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "#### PICK ONE: *OR* this node for install in google drive\n", 20 | "from google.colab import drive\n", 21 | "drive.mount('/content/drive')\n", 22 | "SWARMPATH = '/content/drive/MyDrive'\n", 23 | "\n", 24 | "# Colab breaks venvs, and doesn't save anything valid to drive, so just screw it do a global install of pip reqs if we used a comfy backend in the drive\n", 25 | "!if [[ -f \"$SWARMPATH/SwarmUI/dlbackend/ComfyUI/requirements.txt\" ]]; then rm -rf $SWARMPATH/SwarmUI/dlbackend/ComfyUI/venv/; pip install -r $SWARMPATH/SwarmUI/dlbackend/ComfyUI/requirements.txt; fi" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": { 32 | "id": "Sg0HeYwkM48V" 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "# Install dotnet dependencies\n", 37 | "!wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh\n", 38 | "!chmod +x dotnet-install.sh\n", 39 | "!./dotnet-install.sh --channel 8.0\n", 40 | "\n", 41 | "# Install Clouldflared\n", 42 | "!wget https://github.com/cloudflare/cloudflared/releases/download/2024.8.2/cloudflared-linux-amd64.deb\n", 43 | "!dpkg -i cloudflared-linux-amd64.deb\n", 44 | "\n", 45 | "# use the chosen path\n", 46 | "import os\n", 47 | "os.environ['SWARMPATH'] = SWARMPATH\n", 48 | "%cd $SWARMPATH\n", 49 | "\n", 50 | "# Colab breaks venv, so, tell swarm to not make a venv\n", 51 | "os.environ['SWARM_NO_VENV'] = 'true'\n", 52 | "\n", 53 | "# Download SwarmUI\n", 54 | "url = \"https://github.com/mcmonkeyprojects/SwarmUI\"\n", 55 | "!git clone $url" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": { 62 | "id": "K9Oer9TVObYA" 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "# Alright, launch it! Watch the output for a Cloudflare URL\n", 67 | "%cd $SWARMPATH/SwarmUI/\n", 68 | "\n", 69 | "# (Colab stupid-proofing: aggressive git ultraforce pull)\n", 70 | "!git fetch\n", 71 | "!git reset --hard origin/master\n", 72 | "!git pull --autostash\n", 73 | "# (Colab stupid-proofing: drive wonks the perms so this needs an aggressive rebuild)\n", 74 | "!rm -rf ./src/bin/live_release\n", 75 | "\n", 76 | "!bash ./launch-linux.sh --launch_mode none --cloudflared-path cloudflared" 77 | ] 78 | } 79 | ], 80 | "metadata": { 81 | "accelerator": "GPU", 82 | "colab": { 83 | "gpuType": "T4", 84 | "provenance": [] 85 | }, 86 | "kernelspec": { 87 | "display_name": "Python 3", 88 | "name": "python3" 89 | }, 90 | "language_info": { 91 | "name": "python" 92 | } 93 | }, 94 | "nbformat": 4, 95 | "nbformat_minor": 0 96 | } 97 | -------------------------------------------------------------------------------- /docs/APIRoutes/ImageBatchToolExtension.md: -------------------------------------------------------------------------------- 1 | # SwarmUI API Documentation - ImageBatchToolExtension 2 | 3 | > This is a subset of the API docs, see [/docs/API.md](/docs/API.md) for general info. 4 | 5 | (CLASS DESCRIPTION NOT SET) 6 | 7 | #### Table of Contents: 8 | 9 | - WebSocket Route [ImageBatchRun](#websocket-route-apiimagebatchrun) 10 | 11 | ## WebSocket Route /API/ImageBatchRun 12 | 13 | #### Description 14 | 15 | (ROUTE DESCRIPTION NOT SET) 16 | 17 | #### Permission Flag 18 | 19 | `imagebatcher_use_image_batcher` - `[Image Batch Tool] Use Image Batcher` in group `User` 20 | 21 | #### Parameters 22 | 23 | | Name | Type | Description | Default | 24 | | --- | --- | --- | --- | 25 | | rawInput | JObject | (PARAMETER DESCRIPTION NOT SET) | **(REQUIRED)** | 26 | | input_folder | String | (PARAMETER DESCRIPTION NOT SET) | **(REQUIRED)** | 27 | | output_folder | String | (PARAMETER DESCRIPTION NOT SET) | **(REQUIRED)** | 28 | | init_image | Boolean | (PARAMETER DESCRIPTION NOT SET) | **(REQUIRED)** | 29 | | revision | Boolean | (PARAMETER DESCRIPTION NOT SET) | **(REQUIRED)** | 30 | | controlnet | Boolean | (PARAMETER DESCRIPTION NOT SET) | **(REQUIRED)** | 31 | | resMode | String | (PARAMETER DESCRIPTION NOT SET) | **(REQUIRED)** | 32 | 33 | #### Return Format 34 | 35 | ```js 36 | (RETURN INFO NOT SET) 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /docs/APIRoutes/UtilAPI.md: -------------------------------------------------------------------------------- 1 | # SwarmUI API Documentation - UtilAPI 2 | 3 | > This is a subset of the API docs, see [/docs/API.md](/docs/API.md) for general info. 4 | 5 | General utility API routes. 6 | 7 | #### Table of Contents: 8 | 9 | - HTTP Route [CountTokens](#http-route-apicounttokens) 10 | - HTTP Route [Pickle2SafeTensor](#http-route-apipickle2safetensor) 11 | - HTTP Route [TokenizeInDetail](#http-route-apitokenizeindetail) 12 | - HTTP Route [WipeMetadata](#http-route-apiwipemetadata) 13 | 14 | ## HTTP Route /API/CountTokens 15 | 16 | #### Description 17 | 18 | Count the CLIP-like tokens in a given text prompt. 19 | 20 | #### Permission Flag 21 | 22 | `use_tokenizer` - `Use Tokenizer` in group `User` 23 | 24 | #### Parameters 25 | 26 | | Name | Type | Description | Default | 27 | | --- | --- | --- | --- | 28 | | text | String | The text to tokenize. | **(REQUIRED)** | 29 | | skipPromptSyntax | Boolean | If false, processing prompt syntax (things like ` `Extensions` 8 | - If you like the sound one, look at its readme to make sure you want it. If so, hit the "Install" button. 9 | 10 | See also the Extensions channel in The SwarmUI Discord. 11 | -------------------------------------------------------------------------------- /docs/Features/Autocompletions.md: -------------------------------------------------------------------------------- 1 | # SwarmUI Autocompletions Engine 2 | 3 | When you're typing into a prompt box within Swarm, the **autocompletions engine** is looking for ways to help you. 4 | 5 | ### Prompt Syntax 6 | 7 | The first way it will try to help you is with regards to advanced prompt syntax, as documented in [Basic Usage](/docs/Basic%20Usage.md). As soon as you type the `<` symbol, you'll see suggestions for prompt syntax options: 8 | 9 | ![image](/docs/images/autocompletions.png) 10 | 11 | - You can start typing the name of one to narrow down to just that option. 12 | - You can hit *tab* or click an option to immediately complete it. 13 | - For syntax features with more options/configuration, it will complete to eg ` `User Settings` 25 | - find the option `AutoCompletionsSource` and select your word list file of choice. 26 | - Go back to the generate tab, and start typing! Words will pop up and are tab completable or clickable. 27 | -------------------------------------------------------------------------------- /docs/Features/Comfy-Workflows.md: -------------------------------------------------------------------------------- 1 | # Custom Comfy Workflows In SwarmUI 2 | 3 | (Placeholder) 4 | 5 | **TODO:** 6 | - generate outlinks to info about Comfy itself 7 | - how to work with custom comfy workflows in general inside Swarm 8 | - notably the workflow browser tool and all 9 | - multi-backend split usage 10 | - how to push to the Generate tab 11 | - considerations like how save nodes work 12 | - how to use power features like Grid generation with comfy workflows 13 | - how to use the Comfy Workflow parameter to grid different workflows at once 14 | - how to remove the comfy link 15 | - Generate tab parameters: 16 | - how the default workflow parameter linker works 17 | - how to link default parameters (primitives) 18 | - how to build custom parameters and groups (`SwarmInput` nodes) 19 | - how to use the Simple tab (how to add/configure workflows there, how to share them with others) 20 | -------------------------------------------------------------------------------- /docs/Features/ImagePrompting.md: -------------------------------------------------------------------------------- 1 | # SwarmUI Image Prompting - IP-Adapter, Revision, Etc. 2 | 3 | - To use image-prompting features in Swarm, simply drag an image into the prompt box, or copy an image and while in the prompt box press CTRL+V to paste. 4 | - When you do this, the Image Prompting control panel will open on the left at the top of the parameters listing. 5 | 6 | ## ReVision 7 | 8 | - ReVision is an SDXL-specific image-prompting technique that direct passes your image to the SDXL model. This has a light effect, but biases the model towards your prompt. 9 | - If you want to use other image-prompt features without ReVision, simply drag the "ReVision Strength" slider down to `0`. 10 | - ReVision works better if you leave your prompt blank than if you specify a prompt, as prompts tend to overpower the ReVision guidance. 11 | - There's an additional `ReVision Model` parameter hidden under `Advanced Sampling` parameter group. 12 | 13 | ## IP-Adapter 14 | 15 | - IP-Adapter is a technique developed by [TenCent AI Lab](https://github.com/tencent-ailab/IP-Adapter) to bias Stable Diffusion models strongly towards matching the content of an image. 16 | - This is similar to [ControlNet](/docs/Features/ControlNet.md), but where ControlNets match images features (such as canny lines, depth maps, etc), IP Adapter matches vaguer concepts (such as the general concept of an image, or the face of a person, or etc). 17 | - IP-Adapter support in Swarm is powered by a [ComfyUI addon developed by Matteo Spinelli aka cubiq](https://github.com/cubiq/ComfyUI_IPAdapter_plus). They've gone above and beyond in extended IP-Adapter beyond its initial state from TenCent. They have a [GitHub sponsors page](https://github.com/sponsors/cubiq) worth supporting if you use IP-Adapter often. 18 | - In a default Swarm install, you will have an "`Install IP Adapter`" button in the parameter list. 19 | - Simply click this button, and accept the confirmation prompt, to install IP-Adapter to your ComfyUI backend. 20 | - This may take a minute to download, install, and restart your backend. 21 | - Once it's done, the UI will update and display IP-Adapter parameters. 22 | - If it gets stuck for weirdly long, check the Server Logs to see if something errored, or if it finished without updating the page (if so, simply refresh the page). 23 | - When you have IP-Adapter installed, you have the option "Use IP-Adapter" under ReVision. 24 | - This is a listing of model types with short descriptions, simply select the one that fits your needs. 25 | - Whenever you select a model category you haven't tried before, Swarm will automatically download the model files for it. 26 | - Depending on your network speed, this may take a moment. Check the server logs to see a progress report of the download. 27 | - After being downloaded once, the models are stored in `(Swarm)/Models/ipadapter` (or wherever your models dir is), and won't need to be downloaded again. 28 | - You can use non-standard IPAdapter models by saving them to `(Swarm)/Models/ipadapter`. If they don't use CLIPVision G, you will need to select the correct vision model under `Advanced Model Addons` -> `ReVision Model`. 29 | 30 | # Style Models 31 | 32 | - Style models, such as Flux Redux, can be used through the same interface. 33 | - See [the model support doc for Flux Redux specifics](/docs/Model%20Support.md#flux1-tools) 34 | -------------------------------------------------------------------------------- /docs/Features/Presets.md: -------------------------------------------------------------------------------- 1 | # Presets 2 | 3 | (TODO: Info about how presets work and all that) 4 | 5 | # Official SDXL Preset Pack 6 | 7 | These are the presets used on DreamStudio. 8 | 9 | - Download this file: [SDXL Official Presets.json](https://github.com/mcmonkeyprojects/SwarmUI/releases/download/0.6.5-Beta/SDXL.Official.Presets.json) and save it somewhere (anywhere, desktop is fine) 10 | - Open SwarmUI and open the `Presets` tab at the bottom 11 | - Click on the `Import Presets` button 12 | - drag the presets file in to the box that appears 13 | - Click the `Import` button at the bottom 14 | - close the box, look at your presets list - there is now a folder labeled `sdxlofficial` on the side. Click into that. 15 | 16 | ![img](/docs/images/presets.png) 17 | 18 | - You can click on any preset to use it, or unclick it to disable it. 19 | - (Note: if it behaves weird, refresh the page. Alpha projects do silly things sometimes!) 20 | -------------------------------------------------------------------------------- /docs/Features/README.md: -------------------------------------------------------------------------------- 1 | # SwarmUI Feature List & Documentation 2 | 3 | See [The Docs Readme](/docs/README.md) for general listing of documentation and basic usage info, this section here is a subfolder for specific individual features listed out file-by-file. This is not a complete list of all features in SwarmUI, just the ones that need their one big doc file. 4 | 5 | - [Presets](/docs/Features/Presets.md) for info about presets, and a download of an official list of high quality SDXL presets. 6 | - [Prompt Syntax](/docs/Features/Prompt%20Syntax.md) for info about prompt syntax - the various features available by just typing into the prompt box. 7 | - [Autocompletions](/docs/Features/Autocompletions.md) for details about the prompt autocompletions engine. 8 | - [Image Prompting](/docs/Features/ImagePrompting.md) for details about image-prompting with IP-Adapter, ReVision, etc. 9 | - [ControlNet](/docs/Features/ControlNet.md) for info about using ControlNets. 10 | - [Video](/docs/Features/Video.md) for info about generating videos. 11 | - [Custom Comfy Workflows](/docs/Features/Comfy-Workflows.md) for info about getting the most out of custom Comfy workflows in SwarmUI. 12 | - [Webhooks](/docs/Features/Webhooks.md) for info about custom defined webhooks triggered by your SwarmUI server. 13 | - [UISounds](/docs/Features/UISounds.md) for info about sound playback in the UI (eg a sound to play after generations complete). 14 | -------------------------------------------------------------------------------- /docs/Features/UISounds.md: -------------------------------------------------------------------------------- 1 | # UI Sounds in SwarmUI 2 | 3 | If you want to to have the UI play sounds in specific situations, you just need to add a sound file and select it for the relevant context. 4 | 5 | ### Add a sound file 6 | 7 | - Pick any `.wav`, `.mp3`, `.ogg`, ... sound file. 8 | - Here's an online collection of ding sounds if you want https://bigsoundbank.com/search?q=notification 9 | - Store the file in `SwarmUI/Data/Audio` 10 | - Refresh your Swarm page or restart Swarm (file listing does not currently auto-update, TBD) 11 | - Go to `User` -> `User Settings` 12 | - Find the `Audio` category, pick your saved file for whichever sound event you want 13 | - `Completion Sound` plays when all current queued generations complete 14 | - Optionally set the volume too 15 | - hit Save 16 | - Go trigger the event to see it (eg generate things to hear the completion sound) 17 | - You can change the audio event back to the blank entry at the top of the dropdown list to disable sound playback 18 | -------------------------------------------------------------------------------- /docs/Features/Video.md: -------------------------------------------------------------------------------- 1 | # Video Generation in SwarmUI 2 | 3 | - Video generation in SwarmUI is powered by [Stable Video Diffusion](https://arxiv.org/abs/2311.15127) 4 | - You can find the official SVD XT 1.1 model [here](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) (requires a HuggingFace account to download) 5 | - Store the model in your Stable-Diffusion models folder 6 | - To use Video, simply enable the Video parameter group 7 | - It should automatically select your Video model, but you can select one manually. 8 | - The default settings all work great, but customize at will. 9 | - Setting your Sampler to AlignYourSteps might be beneficial to use lower step counts with SVD. 10 | - A lot of extra parameters here are hidden beneath "Display Advanced Options" 11 | - By default, you'll get live animated previews (webp-animation). These incur a slight performance penalty but give you a very clear view of the video that is generating (vs traditional single-frame preview). 12 | - For the final video format, "webp" is most convenient for usage within Swarm, but can be awkward for sharing as not all programs support webp (eg Discord doesn't). h264-mp4 is easier to share, but may misbehave slightly in the UI. 13 | - Frame interpolation can make your videos a bit nicer - a repo from [Fannovel16](https://github.com/Fannovel16/ComfyUI-Frame-Interpolation) helps with this. The bottom of the video parameter listing has a button to automatically install it for you if you want to use frame interpolation. 14 | - When installed, simply set the "Frame Interpolation Multiplier" to a higher value (eg 2, to interpolate to twice as many frames as SVD generated), and pick between the Methods available (they are similar, differences are subjective.) 15 | - Got bad video results? Well... yeah. Unfortunately, AI video tech is still young, and there's a lot of trial-and-error, as well as a lot of luck-of-seeds. 16 | - Reducing the Motion Bucket can help reduce corruption. The AlignYourSteps sampler can also help. Naturally, higher Steps helps too (at the cost of taking much longer to generate). 17 | -------------------------------------------------------------------------------- /docs/Motivations.md: -------------------------------------------------------------------------------- 1 | # Motivations 2 | 3 | This document explains the reasoning behind some core choices for the design of the project. These are not necessarily sweeping statements, reasons to make the same choices, or even absolutely true facts - these are the reasons that choices were made, and nothing more. 4 | 5 | ## Origin 6 | 7 | This project was originally built inside Stability AI as a project to build a better UI that can handle both the internal needs at Stability AI (eg large multi-GPU cluster support, thus the 'Swarm' naming), and the growing needs of the broader Image Generation community. 8 | 9 | Since then it has migrated to become a fully independent project, made by and for the general image generation community. 10 | 11 | ## Language Choice 12 | 13 | This project is built with a C# backend server to maximize performance while only minimally increasing code complexity. While most ML projects tend to be written in Python, that language is simply insufficient to meet the performance goals of this project\* (ie to provide a very fast and responsive multi-user-ready multi-backend service), notably it lacks "true" multithreading capabilities (due to Python GIL), which was deemed strongly necessary for SwarmUI (it must be able to use available CPU cores while serving user requests and managing internal data to be able to respond to all requests as quickly as possible). 14 | 15 | It is also hoped that building Stable Diffusion tools in C# will enable a wider range of developers to make use of Stable Diffusion (vs being limited to the Python ecosystem). 16 | 17 | \* This is not meant to be an insult to python, simply an explanation of why C# was chosen as being more ideal for the goals of the project than python is. [Some users have disagreed](https://github.com/Stability-AI/StableSwarmUI/issues/3) with this reasoning. 18 | 19 | ## Modularity 20 | 21 | The project was designed to be heavily modular, such that backends are fully separated from the middle-layer which is fully separated from the frontend UI, and all components are interswappable. This is to enable extensibility and customization. For example, an extension can easily provide alternative backend generators (this project comes with several built-in, such as ComfyUI, Auto WebUI, ...) without having to edit anything else to work. 22 | 23 | The limitation of this approach is some tools may not easily be intercompatible, limiting power users to only the most well supported tools. 24 | 25 | ## Comfy 26 | 27 | For the goal of maximizing capabilities, a 'main' backend needed to be chosen to focus initial development on. ComfyUI was chosen because: 28 | - It is itself an extremely modular and extensible system. 29 | - It is highly performant and compatible. 30 | - The code inside is extremely clean and well written. 31 | - It provides bonus features that other UIs can't match (ie: the workflow node editor). 32 | - The lead developer of Comfy was hired to Stability, and was able to directly help in ensuring the SwarmUI-ComfyUI integration works as best it can. 33 | - (June 2024 note: Comfy is now maintained by [Comfy Org](https://www.comfy.org/), and SwarmUI is now independent from Stability AI) 34 | 35 | ## Web Frontend 36 | 37 | A completely custom HTML/JS frontend was built with the goal of allowing detailed and thorough customization of the UI (as opposed to eg being locked in to the way Gradio generates things). 38 | 39 | Non-web based solutions are possible, but, well, ... everyone does webapps these days, and so all the convenient tools are built for webapps, and it's really nice for things like remote hosting the UI itself (and in the future for sharing it). So, wasn't really a choice, webapp frontend was the way to go. 40 | -------------------------------------------------------------------------------- /docs/Privacy.md: -------------------------------------------------------------------------------- 1 | # Privacy of SwarmUI 2 | 3 | SwarmUI is free-and-open-source pure-local runnable software. That means, in short, the answer to "Swarm has good privacy?" is "Yes Swarm is very private." 4 | 5 | ## Longer Answer 6 | 7 | ### The Good News 8 | 9 | - Swarm itself has no native autoenabled phone home nor data tracking. 10 | - Swarm has a variety of dependencies, none of which *should* be phoning home. 11 | - Swarm actively emits multiple environment variables telling all known phone-homer dependencies to shut up and not do that (eg `DISABLE_TELEMETRY` to disable HuggingFace telemetry, `YOLO_OFFLINE` to disable Ultralytics YOLOv8 segment model telemetry, etc.) 12 | - Swarm literally does not have any central servers to contact. The rare cases where remote connections are needed (see below) are to general public services from other providers (eg GitHub). 13 | - As long as you're running locally, your prompts, generated images, etc. are never sent to remote servers. 14 | - Swarm's primary backend provider, ComfyUI, is developed by an author who has consistently maintained a similar stance of pure privacy and has made a public promise that ComfyUI will never phone home. 15 | 16 | ### The Considerations 17 | 18 | - Naturally when installing, you pull software from `github.com`, `pypi` servers, `nuget` servers, etc. 19 | - Swarm will generally try to auto-update ComfyUI and custom nodes, this will perform a `git pull` to `github.com`, which GitHub has anonymous statistic counters for events. 20 | - You can disable auto updating in Server->Backends->edit the backend->disable AutoUpdate 21 | - Swarm does not auto-update itself by default (though you can enable this), but you can manual update in a couple clicks, and this of course downloads from `github.com` and `nuget` servers 22 | - Swarm cannot rigidly guarantee no dependencies ever phone home. If you discover any start doing that, please report it immediately on the SwarmUI GitHub issues page or Discord, we will take corrective action. 23 | - If you run on a remote server, obviously the provider of that server has access to your data, and potentially the networking in between may be interceptable. 24 | - Notably for example if you use CloudFlare Tunnels, CloudFlare may be able to scan your data. 25 | - If you open your server to public or external access, remote users of course can read the data available through the Swarm instance. 26 | - When installing custom nodes or Swarm extensions, they may have their own functionality, which may make remote connections. 27 | - For Swarm Extensions, Swarm [has a rule](/docs/Making%20Extensions.md) against unnecessary web connections (but allows necessary ones). 28 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # SwarmUI Documentation 2 | 3 | - [Basic Usage](/docs/Basic%20Usage.md) to learn how to get started. 4 | - [Why Use Swarm](/docs/Why%20Use%20Swarm.md) to see what advanced Swarm offers you 5 | - [Advanced Usage](/docs/Advanced%20Usage.md) for various advanced usage topics (use from external device, comfy workflows, ...) 6 | - [Features](/docs/Features/README.md) for a sub-listing of various specific Swarm features and how to use them. 7 | - [Docker](/docs/Docker.md) for using SwarmUI inside a Docker container. 8 | - [Using More GPUs](/docs/Using%20More%20GPUs.md) for more info about splitting generations between multiple GPUs. 9 | - [Sharing Your Swarm](/docs/Sharing%20Your%20Swarm.md) for info about sharing your SwarmUI instance with others. 10 | - [Model Support](/docs/Model%20Support.md) for details about support of different image model types. 11 | - [Video Model Support](/docs/Video%20Model%20Support.md) for details about video model types. 12 | - TODO: Settings guide (server & user) 13 | - [Command line arguments](/docs/Command%20Line%20Arguments.md) describes the available command line arguments in detail. 14 | - [Extensions](/docs/Extensions.md) for info about how to use extensions. 15 | - [Making Extensions](/docs/Making%20Extensions.md) explains how to make extensions. 16 | - TODO: Backends (what a 'backend' is, supported options, usage, etc) 17 | - [ComfyUI Backend Extension](/src/BuiltinExtensions/ComfyUIBackend/README.md) 18 | - Legacy [Auto WebUI Backend Extension](/src/BuiltinExtensions/AutoWebUIBackend/README.md) 19 | - TODO: Tools (what a tool is, how to use em, etc) 20 | - [GridGenerator](/src/BuiltinExtensions/GridGenerator/README.md) 21 | - [ImageBatchTool](/src/BuiltinExtensions/ImageBatchTool/README.md) 22 | - Other: 23 | - [Dynamic Thresholding](/src/BuiltinExtensions/DynamicThresholding/README.md) 24 | - [API](/docs/API.md) for details about the HTTP&WebSocket network API, to use swarm externally. 25 | - [Image Metadata Format](/docs/Image%20Metadata%20Format.md) for details about the format of metadata Swarm adds to generated images by default. 26 | - [Privacy](/docs/Privacy.md) contains notes for privacy-concerned Swarm users. 27 | - [Troubleshooting](/docs/Troubleshooting.md) for information about how to solve common problems. 28 | -------------------------------------------------------------------------------- /docs/User Settings.md: -------------------------------------------------------------------------------- 1 | # User Settings in SwarmUI 2 | 3 | (TODO: general info about settings) 4 | 5 | ## Path Format 6 | 7 | `User -> OutpathBuilder -> Format` accepts the following format keys: 8 | 9 | - `[year]`: 4-digit year, eg 2023 10 | - `[month]`: 2-digit month, eg 07 11 | - `[month_name]`: full month name, eg July 12 | - `[day]`: 2-digit day, eg 29 13 | - `[day_name]`: full day name, eg Saturday 14 | - `[hour]`: 2-digit hour, eg 12 15 | - `[minute]`: 2-digit minute, eg 04 16 | - `[second]`: 2-digit second, eg 30 17 | - `[millisecond]`: 3-digit millisecond, eg 057 18 | - `[request_time_inc]`: an arbitrary incrementing number of requests to force orderly names, pair as `[hour][minute][request_time_inc]` to get unique linear id prefixes. 19 | - `[prompt]`: the prompt (often cut off by `MaxLenPerPart`) 20 | - `[negative_prompt]`: the negative prompt (often cut off by `MaxLenPerPart`) 21 | - `[prompthash]`: a short (8 character) SHA256 hash prefix of the prompt 22 | - `[negativeprompthash]`: a short (8 character) SHA256 hash prefix of the negative prompt 23 | - `[seed]`: the seed number parameter 24 | - `[cfg_scale]`: the CFG Scale parameter 25 | - `[width]`: the Width parameter 26 | - `[height]`: the Height parameter 27 | - `[steps]`: the Steps number parameter 28 | - `[model]`: the filename of the model 29 | - `[model_title]`: the metadata title of the model 30 | - `[user_name]`: the name of the user 31 | - `[batch_id]`: the index # of this image within the batch 32 | - `[some parameter name here]`: the value of the parameter named. Must have exact parameter name. For example `[refinermodel]` will get you the name of the refiner model. 33 | 34 | If names overlap, a numeric index will be appended to the end, eg if `123-a cat.jpg` is your output but it already exists, `123-a cat-1.jpg` will be used. 35 | -------------------------------------------------------------------------------- /docs/images/add-swarm-backend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/add-swarm-backend.png -------------------------------------------------------------------------------- /docs/images/alternate-cat-dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/alternate-cat-dog.jpg -------------------------------------------------------------------------------- /docs/images/autocompletions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/autocompletions.png -------------------------------------------------------------------------------- /docs/images/clear-cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/clear-cat.png -------------------------------------------------------------------------------- /docs/images/config-swarm-backend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/config-swarm-backend.png -------------------------------------------------------------------------------- /docs/images/draggable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/draggable.png -------------------------------------------------------------------------------- /docs/images/fromto-cat-dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/fromto-cat-dog.jpg -------------------------------------------------------------------------------- /docs/images/grids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/grids.png -------------------------------------------------------------------------------- /docs/images/lan-access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/lan-access.png -------------------------------------------------------------------------------- /docs/images/local-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/local-network.png -------------------------------------------------------------------------------- /docs/images/lora-arcane-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/lora-arcane-cat.jpg -------------------------------------------------------------------------------- /docs/images/models/auraflow-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/auraflow-02.jpg -------------------------------------------------------------------------------- /docs/images/models/cascade.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/cascade.jpg -------------------------------------------------------------------------------- /docs/images/models/flux-dev.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/flux-dev.jpg -------------------------------------------------------------------------------- /docs/images/models/flux-schnell.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/flux-schnell.jpg -------------------------------------------------------------------------------- /docs/images/models/hidream-i1-dev.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/hidream-i1-dev.jpg -------------------------------------------------------------------------------- /docs/images/models/lumina-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/lumina-2.png -------------------------------------------------------------------------------- /docs/images/models/pixart-sigma-xl-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/pixart-sigma-xl-2.jpg -------------------------------------------------------------------------------- /docs/images/models/sana-1600m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/sana-1600m.jpg -------------------------------------------------------------------------------- /docs/images/models/sd15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/sd15.jpg -------------------------------------------------------------------------------- /docs/images/models/sd35l.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/sd35l.jpg -------------------------------------------------------------------------------- /docs/images/models/sd35m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/sd35m.jpg -------------------------------------------------------------------------------- /docs/images/models/sd3m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/sd3m.jpg -------------------------------------------------------------------------------- /docs/images/models/sdxl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/models/sdxl.jpg -------------------------------------------------------------------------------- /docs/images/presets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/presets.png -------------------------------------------------------------------------------- /docs/images/prompt-weight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/prompt-weight.jpg -------------------------------------------------------------------------------- /docs/images/queue-running.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/queue-running.png -------------------------------------------------------------------------------- /docs/images/random-cats.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/random-cats.jpg -------------------------------------------------------------------------------- /docs/images/refiners.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/refiners.png -------------------------------------------------------------------------------- /docs/images/remote-machine-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/remote-machine-open.png -------------------------------------------------------------------------------- /docs/images/repeat-random.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/repeat-random.jpg -------------------------------------------------------------------------------- /docs/images/sdxl_catdog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/sdxl_catdog.jpg -------------------------------------------------------------------------------- /docs/images/sdxl_object_catdog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/sdxl_object_catdog.jpg -------------------------------------------------------------------------------- /docs/images/segment-ref.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/segment-ref.jpg -------------------------------------------------------------------------------- /docs/images/servermodelpath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/servermodelpath.png -------------------------------------------------------------------------------- /docs/images/setvar-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/setvar-cat.jpg -------------------------------------------------------------------------------- /docs/images/style-preset-cats.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/style-preset-cats.jpg -------------------------------------------------------------------------------- /docs/images/trigger-arcane-cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/trigger-arcane-cat.jpg -------------------------------------------------------------------------------- /docs/images/usecomfy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/usecomfy.png -------------------------------------------------------------------------------- /docs/images/wildcards-cats.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/wildcards-cats.jpg -------------------------------------------------------------------------------- /docs/images/yourfirstprompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/docs/images/yourfirstprompt.png -------------------------------------------------------------------------------- /launch-linux-dev.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Ensure correct local path. 4 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 5 | cd "$SCRIPT_DIR" 6 | 7 | source ./launchtools/linux-path-fix.sh 8 | 9 | # Cycle build folder forward 10 | rm -rf ./src/bin/live_release_backup 11 | mv ./src/bin/live_release ./src/bin/live_release_backup 12 | rm ./src/bin/must_rebuild 13 | rm ./src/bin/last_build 14 | 15 | # Build the program 16 | dotnet build src/SwarmUI.csproj --configuration Debug -o ./src/bin/live_release 17 | 18 | # Default env configuration, gets overwritten by the C# code's settings handler 19 | export ASPNETCORE_ENVIRONMENT="Production" 20 | export ASPNETCORE_URLS="http://*:7801" 21 | 22 | # Actual runner. 23 | ./src/bin/live_release/SwarmUI $@ 24 | 25 | # Exit code 42 means restart, anything else = don't. 26 | if [ $? == 42 ]; then 27 | exec ./launch-linux-dev.sh $@ 28 | fi 29 | -------------------------------------------------------------------------------- /launch-linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Ensure correct local path. 4 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 5 | cd "$SCRIPT_DIR" 6 | 7 | source ./launchtools/linux-build-logic.sh 8 | 9 | # Allow restarting to be forwarded (for docker) 10 | FORWARD_RESTART="" 11 | if [ "$1" == "--forward_restart" ]; then 12 | FORWARD_RESTART="true" 13 | shift 14 | fi 15 | 16 | # Actual runner. 17 | ./src/bin/live_release/SwarmUI $@ 18 | 19 | # Exit code 42 means restart, anything else = don't. 20 | if [ $? == 42 ]; then 21 | if [ "$FORWARD_RESTART" == "true" ]; then 22 | exit 42 23 | else 24 | exec ./launch-linux.sh $@ 25 | fi 26 | fi 27 | -------------------------------------------------------------------------------- /launch-macos.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Ensure correct local path. 4 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 5 | cd "$SCRIPT_DIR" 6 | 7 | # Try to encourage Mac to use the correct python version (ie don't use global default which is often 3.13 for mac users, instead use 11, 10, or 12) 8 | export PATH="/opt/homebrew/opt/python@3.11/libexec/bin:/opt/homebrew/opt/python@3.10/libexec/bin:/opt/homebrew/opt/python@3.12/libexec/bin:$PATH" 9 | 10 | # PyTorch MPS fallback to CPU, so incompatible comfy nodes can still work. 11 | export PYTORCH_ENABLE_MPS_FALLBACK=1 12 | 13 | source ./launchtools/linux-build-logic.sh 14 | 15 | # Actual runner. 16 | ./src/bin/live_release/SwarmUI $@ 17 | 18 | # Exit code 42 means restart, anything else = don't. 19 | if [ $? == 42 ]; then 20 | . ./launch-macos.sh $@ 21 | fi 22 | -------------------------------------------------------------------------------- /launch-windows-dev.ps1: -------------------------------------------------------------------------------- 1 | # Ensure correct local path. 2 | $thisPath = Split-Path $MyInvocation.MyCommand.Path -Parent 3 | cd $thisPath 4 | 5 | # Visual Studio likes to generate invalid files here for some reason, so autonuke it 6 | if (Test-Path "src/Properties/launchSettings.json") { 7 | rm src/Properties/launchSettings.json 8 | } 9 | 10 | # Nuke build files to ensure our build is fresh and won't skip past errors 11 | Remove-Item 'src/bin/' -Recurse 12 | Remove-Item 'src/obj/' -Recurse 13 | 14 | # Building first is more reliable than running directly from src 15 | dotnet build src/SwarmUI.csproj --configuration Debug -o src/bin/live_release 16 | 17 | # Default env configuration, gets overwritten by the C# code's settings handler 18 | $Env:ASPNETCORE_ENVIRONMENT = "Development" 19 | $Env:ASPNETCORE_URLS = "http://*:7801" 20 | 21 | # Actual runner. 22 | .\src\bin\live_release\SwarmUI.exe --environment dev @args 23 | 24 | # Exit code 42 means restart, anything else = don't. 25 | if ($LASTEXITCODE -eq 42) { 26 | .\launch-windows-dev.ps1 @args 27 | } 28 | -------------------------------------------------------------------------------- /launch-windows.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal ENABLEDELAYEDEXPANSION 3 | 4 | rem Ensure correct local path. 5 | cd /D "%~dp0" 6 | 7 | rem Microsoft borked the dotnet installer/path handler, so force x64 to be read first 8 | set PATH=C:\Program Files\dotnet;%PATH% 9 | 10 | set DOTNET_CLI_TELEMETRY_OPTOUT=1 11 | 12 | rem Server settings option 13 | if exist .\src\bin\always_pull ( 14 | echo Pulling latest changes... 15 | git pull 16 | ) 17 | 18 | if not exist .git ( 19 | echo. 20 | echo. 21 | echo WARNING: YOU DID NOT CLONE FROM GIT. THIS WILL BREAK SOME SYSTEMS. PLEASE INSTALL PER THE README. 22 | echo. 23 | echo. 24 | timeout 5 25 | ) else ( 26 | for /f "delims=" %%i in ('git rev-parse HEAD') do set CUR_HEAD=%%i 27 | set /p BUILT_HEAD=.\src\bin\must_rebuild 35 | ) 36 | ) 37 | 38 | if exist .\src\bin\must_rebuild ( 39 | echo Rebuilding... 40 | if exist .\src\bin\live_release ( 41 | rmdir /s /q .\src\bin\live_release_backup 42 | move .\src\bin\live_release .\src\bin\live_release_backup 43 | ) 44 | del .\src\bin\must_rebuild 45 | ) 46 | 47 | rem Build the program if it isn't already built 48 | if not exist src\bin\live_release\SwarmUI.exe ( 49 | rem For some reason Microsoft's nonsense is missing the official nuget source? So forcibly add that to be safe. 50 | dotnet nuget add source https://api.nuget.org/v3/index.json --name "NuGet official package source" 51 | 52 | dotnet build src/SwarmUI.csproj --configuration Release -o src/bin/live_release 53 | for /f "delims=" %%i in ('git rev-parse HEAD') do set CUR_HEAD2=%%i 54 | echo !CUR_HEAD2!> src/bin/last_build 55 | ) 56 | 57 | if not exist src\bin\live_release\SwarmUI.exe if exist src\bin\live_release_backup\SwarmUI.exe ( 58 | echo. 59 | echo. 60 | echo WARNING: BUILD FAILED? Restoring backup... 61 | echo. 62 | echo. 63 | timeout 5 64 | rmdir /s /q src\bin\live_release 65 | move src\bin\live_release_backup src\bin\live_release 66 | ) 67 | 68 | rem Default env configuration, gets overwritten by the C# code's settings handler 69 | set ASPNETCORE_ENVIRONMENT="Production" 70 | set ASPNETCORE_URLS="http://*:7801" 71 | 72 | .\src\bin\live_release\SwarmUI.exe %* 73 | 74 | rem Exit code 42 means restart, anything else = don't. 75 | if %ERRORLEVEL% EQU 42 ( 76 | echo Restarting... 77 | call launch-windows.bat %* 78 | ) 79 | 80 | IF %ERRORLEVEL% NEQ 0 ( pause ) 81 | -------------------------------------------------------------------------------- /launchtools/7z/win/7za.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/launchtools/7z/win/7za.dll -------------------------------------------------------------------------------- /launchtools/7z/win/7za.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/launchtools/7z/win/7za.exe -------------------------------------------------------------------------------- /launchtools/7z/win/7zxa.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/launchtools/7z/win/7zxa.dll -------------------------------------------------------------------------------- /launchtools/7z/win/License.txt: -------------------------------------------------------------------------------- 1 | 7-Zip Extra 2 | ~~~~~~~~~~~ 3 | License for use and distribution 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Copyright (C) 1999-2023 Igor Pavlov. 7 | 8 | 7-Zip Extra files are under the GNU LGPL license. 9 | 10 | 11 | Notes: 12 | You can use 7-Zip Extra on any computer, including a computer in a commercial 13 | organization. You don't need to register or pay for 7-Zip. 14 | 15 | It is allowed to digitally sign DLL files included into this package 16 | with arbitrary signatures of third parties." 17 | 18 | 19 | GNU LGPL information 20 | -------------------- 21 | 22 | This library is free software; you can redistribute it and/or 23 | modify it under the terms of the GNU Lesser General Public 24 | License as published by the Free Software Foundation; either 25 | version 2.1 of the License, or (at your option) any later version. 26 | 27 | This library is distributed in the hope that it will be useful, 28 | but WITHOUT ANY WARRANTY; without even the implied warranty of 29 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 | Lesser General Public License for more details. 31 | 32 | You can receive a copy of the GNU Lesser General Public License from 33 | http://www.gnu.org/ 34 | 35 | -------------------------------------------------------------------------------- /launchtools/OpenDockerfile.docker: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim 2 | 3 | ARG UID=1000 4 | 5 | # Install python 6 | RUN apt update 7 | RUN apt install -y git wget build-essential python3.11 python3.11-venv python3.11-dev ffmpeg 8 | 9 | # Install dependencies for controlnet preprocessors 10 | RUN apt install -y libglib2.0-0 libgl1 11 | 12 | # Stupidproofing on git calls from inside docker 13 | RUN git config --global --add safe.directory '*' 14 | 15 | # Ensure a user exists within docker for the given uid 16 | RUN useradd -u $UID swarmui 17 | 18 | # Expose the port for other containers (to use Swarm as an API if you want) 19 | EXPOSE 7801 20 | 21 | # Set the run file to the launch script 22 | ENTRYPOINT ["bash", "/SwarmUI/launchtools/docker-open-inner.sh"] 23 | -------------------------------------------------------------------------------- /launchtools/StandardDockerfile.docker: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim 2 | 3 | ARG UID=1000 4 | 5 | # Install python 6 | RUN apt update 7 | RUN apt install -y git wget build-essential python3.11 python3.11-venv python3.11-dev ffmpeg 8 | 9 | # Install dependencies for controlnet preprocessors 10 | RUN apt install -y libglib2.0-0 libgl1 11 | 12 | # Copy swarm's files into the docker container 13 | COPY . ./SwarmUI 14 | 15 | RUN chown -R $UID:$UID ./SwarmUI 16 | 17 | # Stupidproofing on git calls from inside docker 18 | RUN git config --global --add safe.directory '*' 19 | 20 | # Ensure a user exists within docker for the given uid 21 | RUN useradd -u $UID swarmui 22 | 23 | # Expose the port for other containers (to use Swarm as an API if you want) 24 | EXPOSE 7801 25 | 26 | # Set the run file to the launch script 27 | ENTRYPOINT ["bash", "/SwarmUI/launchtools/docker-standard-inner.sh"] 28 | -------------------------------------------------------------------------------- /launchtools/comfy-install-linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check if GPU type is provided 4 | if [ $# -eq 0 ]; then 5 | >&2 echo "Error: GPU type not specified. Please use 'amd' or 'nv' as an argument." 6 | exit 1 7 | fi 8 | 9 | GPU_TYPE=$1 10 | 11 | # Validate GPU type 12 | if [ "$GPU_TYPE" != "amd" ] && [ "$GPU_TYPE" != "nv" ]; then 13 | >&2 echo "Error: Invalid GPU type. Please use 'amd' or 'nv'." 14 | exit 1 15 | fi 16 | 17 | mkdir dlbackend 18 | 19 | cd dlbackend 20 | 21 | git clone https://github.com/comfyanonymous/ComfyUI 22 | 23 | cd ComfyUI 24 | 25 | # Try to find a good python executable, and dodge unsupported python versions 26 | for pyvers in python3.11 python3.10 python3.12 python3 python 27 | do 28 | python=`which $pyvers` 29 | if [ "$python" != "" ]; then 30 | break 31 | fi 32 | done 33 | if [ "$python" == "" ]; then 34 | >&2 echo "ERROR: cannot find python3" 35 | >&2 echo "Please follow the install instructions in the readme!" 36 | exit 1 37 | fi 38 | 39 | # Validate venv 40 | venv=`$python -m venv 2>&1` 41 | case $venv in 42 | *usage*) 43 | : 44 | ;; 45 | *) 46 | >&2 echo "ERROR: python venv is not installed" 47 | >&2 echo "Please follow the install instructions in the readme!" 48 | >&2 echo "If on Ubuntu/Debian, you may need: sudo apt install python3-venv" 49 | exit 1 50 | ;; 51 | esac 52 | 53 | # Make and activate the venv. "python3" in the venv is now the python executable. 54 | if [ -z "${SWARM_NO_VENV}" ]; then 55 | echo "Making venv..." 56 | $python -s -m venv venv 57 | source venv/bin/activate 58 | python=python3 59 | else 60 | echo "swarm_no_venv set, will not create venv" 61 | fi 62 | 63 | # Install PyTorch based on GPU type 64 | if [ "$GPU_TYPE" == "nv" ]; then 65 | echo "install nvidia torch..." 66 | $python -s -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu124 67 | elif [ "$GPU_TYPE" == "amd" ]; then 68 | echo "install amd torch..." 69 | $python -s -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.3 70 | fi 71 | 72 | echo "install general requirements..." 73 | $python -s -m pip install -r requirements.txt 74 | 75 | echo "Installation completed for $GPU_TYPE GPU." 76 | -------------------------------------------------------------------------------- /launchtools/docker-open-inner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is the inner script for docker runs, don't run this directly, use launch-docker.sh instead. 4 | 5 | cd /SwarmUI 6 | 7 | # Add a fake home path, because docker defaults it to '/' 8 | HOME=/SwarmUI/dlbackend/linuxhome 9 | 10 | # Launch as normal, just ensure launch mode is off and host is global (to expose it out of the container) 11 | bash /SwarmUI/launch-linux.sh $@ --launch_mode none --host 0.0.0.0 12 | -------------------------------------------------------------------------------- /launchtools/docker-standard-inner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is the inner script for docker runs, don't run this directly, use launch-docker.sh instead. 4 | 5 | cd /SwarmUI 6 | 7 | # Arg to fix the permissions (from legacy root) 8 | if [[ "$1" == "fixch" ]] 9 | then 10 | echo "Fixing perms, owning to UID $2" 11 | chown -R $2:$2 /SwarmUI/dlbackend /SwarmUI/Data /SwarmUI/src /SwarmUI/Output 12 | chown $2:$2 /SwarmUI 13 | # Scrap any database files rather than reperm (to reduce conflicts, they regen anyway) 14 | rm /SwarmUI/Models/**/model_metadata.ldb 2> /dev/null 15 | echo "Perms fixed, launch as normal now" 16 | exit 17 | fi 18 | 19 | # Check to see if a 'fixch' call is needed 20 | if [ "$EUID" -ne 0 ] && [ "$(stat -c '%U' "/SwarmUI/dlbackend")" = "root" ] 21 | then 22 | echo "Detected folder ownership issue. Please run the docker script as './launchtools/launch-standard-docker.sh fixch' to fix permissions." 23 | echo "(This happens because the script used to run as root inside the docker, but it now runs as your local user)" 24 | exit 25 | fi 26 | 27 | # Add a fake home path, because docker defaults it to '/' 28 | HOME=/SwarmUI/dlbackend/linuxhome 29 | 30 | # Launch as normal, just ensure launch mode is off and host is global (to expose it out of the container) 31 | bash /SwarmUI/launch-linux.sh $@ --launch_mode none --host 0.0.0.0 32 | -------------------------------------------------------------------------------- /launchtools/example-docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | swarmui: 3 | image: swarmui 4 | user: ${HOST_UID:-1000}:${HOST_GID:-1000} 5 | cap_drop: 6 | - ALL 7 | build: 8 | context: . 9 | args: 10 | UID: ${HOST_UID:-1000} 11 | dockerfile: launchtools/StandardDockerfile.docker 12 | container_name: swarmui 13 | # uncomment `network_mode: host` if you want to access other services on the host network (eg a separated comfy instance) 14 | # network_mode: host 15 | volumes: 16 | - swarmdata:/SwarmUI/Data 17 | - swarmbackend:/SwarmUI/dlbackend 18 | - swarmdlnodes:/SwarmUI/src/BuiltinExtensions/ComfyUIBackend/DLNodes 19 | - ./Models:/SwarmUI/Models 20 | - ./Output:/SwarmUI/Output 21 | - ./src/BuiltinExtensions/ComfyUIBackend/CustomWorkflows:/SwarmUI/src/BuiltinExtensions/ComfyUIBackend/CustomWorkflows 22 | ports: 23 | - "7801:7801" 24 | deploy: 25 | resources: 26 | reservations: 27 | devices: 28 | - driver: nvidia 29 | # change the count to the number of GPUs you want to use. 30 | count: 1 31 | capabilities: [gpu] 32 | volumes: 33 | swarmdata: 34 | name: swarmdata 35 | swarmbackend: 36 | name: swarmbackend 37 | swarmdlnodes: 38 | name: swarmdlnodes 39 | -------------------------------------------------------------------------------- /launchtools/install-linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Ensure correct local path. 4 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 5 | cd "$SCRIPT_DIR" 6 | 7 | # Accidental run prevention 8 | if [ -d "SwarmUI" ]; then 9 | echo "SwarmUI already exists in this directory. Please remove it before installing." 10 | exit 1 11 | fi 12 | if [ -f "SwarmUI.sln" ]; then 13 | echo "SwarmUI already exists in this directory. Please remove it before installing." 14 | exit 1 15 | fi 16 | 17 | # Download swarm 18 | git clone https://github.com/mcmonkeyprojects/SwarmUI 19 | cd SwarmUI 20 | 21 | # install dotnet 22 | cd launchtools 23 | rm dotnet-install.sh 24 | # https://learn.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#scripted-install 25 | wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh 26 | chmod +x dotnet-install.sh 27 | cd .. 28 | 29 | # Note: manual installers that want to avoid home dir, add to both of the below lines: --install-dir "$SCRIPT_DIR/.dotnet" 30 | ./launchtools/dotnet-install.sh --channel 8.0 --runtime aspnetcore 31 | ./launchtools/dotnet-install.sh --channel 8.0 32 | 33 | rm ./launchtools/dotnet-install.sh 34 | 35 | # Launch 36 | ./launch-linux.sh $@ 37 | -------------------------------------------------------------------------------- /launchtools/install-windows.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | cd /d "%~dp0" 4 | 5 | if exist SwarmUI ( 6 | echo SwarmUI is already installed in this folder. If this is incorrect, delete the 'SwarmUI' folder and try again. 7 | pause 8 | exit /b 9 | ) 10 | 11 | if exist SwarmUI.sln ( 12 | echo SwarmUI is already installed in this folder. If this is incorrect, delete 'SwarmUI.sln' and try again. 13 | pause 14 | exit /b 15 | ) 16 | 17 | set "tempfile=%TEMP%\swarm_dotnet_sdklist.tmp" 18 | dotnet --list-sdks > "%tempfile%" 19 | findstr "8.0." "%tempfile%" > nul 20 | if %ERRORLEVEL% neq 0 ( 21 | echo DotNet SDK 8 is not installed, will install from WinGet... 22 | winget install Microsoft.DotNet.SDK.8 --accept-source-agreements --accept-package-agreements 23 | ) 24 | del "%tempfile%" 25 | 26 | WHERE git 27 | IF %ERRORLEVEL% NEQ 0 ( 28 | winget install --id Git.Git -e --source winget --accept-source-agreements --accept-package-agreements 29 | ) 30 | 31 | git clone https://github.com/mcmonkeyprojects/SwarmUI 32 | cd SwarmUI 33 | 34 | cmd /c .\launch-windows.bat --launch_mode webinstall 35 | 36 | IF %ERRORLEVEL% NEQ 0 ( pause ) 37 | -------------------------------------------------------------------------------- /launchtools/launch-open-docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Note: This is an example file, do not edit `launch-open-docker.sh`. Instead, duplicate the file and edit your duplicate. 4 | # `custom-launch-docker.sh` is reserved in gitignore for if you want to use that. 5 | 6 | # Run script automatically in Swarm's dir regardless of how it was triggered 7 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 8 | cd "$SCRIPT_DIR/.." 9 | 10 | docker build --build-arg UID=$UID -f launchtools/OpenDockerfile.docker -t swarmui . 11 | 12 | # add "--network=host" if you want to access other services on the host network (eg a separated comfy instance) 13 | docker run -it \ 14 | --rm \ 15 | --user $UID:$(id -g) --cap-drop=ALL \ 16 | --name swarmui \ 17 | -v "$PWD:/SwarmUI" \ 18 | --gpus=all -p 7801:7801 swarmui --forward_restart $@ 19 | 20 | if [ $? == 42 ]; then 21 | exec "$SCRIPT_DIR/launch-open-docker.sh" $@ 22 | fi 23 | -------------------------------------------------------------------------------- /launchtools/launch-standard-docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Note: This is an example file, do not edit `launch-standard-docker.sh`. Instead, duplicate the file and edit your duplicate. 4 | # `custom-launch-docker.sh` is reserved in gitignore for if you want to use that. 5 | 6 | # Run script automatically in Swarm's dir regardless of how it was triggered 7 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 8 | cd "$SCRIPT_DIR/.." 9 | 10 | docker build --build-arg UID=$UID -f launchtools/StandardDockerfile.docker -t swarmui . 11 | 12 | # Run this script with 'fixch' to run as root in the container and chown to the correct user 13 | SETUSER="--user $UID:$(id -g) --cap-drop=ALL" 14 | POSTARG="--forward_restart $@" 15 | if [[ "$1" == "fixch" ]] 16 | then 17 | SETUSER="" 18 | POSTARG="fixch $UID" 19 | fi 20 | 21 | # add "--network=host" if you want to access other services on the host network (eg a separated comfy instance) 22 | docker run -it \ 23 | --rm \ 24 | $SETUSER \ 25 | --name swarmui \ 26 | --mount source=swarmdata,target=/SwarmUI/Data \ 27 | --mount source=swarmbackend,target=/SwarmUI/dlbackend \ 28 | --mount source=swarmdlnodes,target=/SwarmUI/src/BuiltinExtensions/ComfyUIBackend/DLNodes \ 29 | -v "$PWD/Models:/SwarmUI/Models" \ 30 | -v "$PWD/Output:/SwarmUI/Output" \ 31 | -v "$PWD/src/BuiltinExtensions/ComfyUIBackend/CustomWorkflows:/SwarmUI/src/BuiltinExtensions/ComfyUIBackend/CustomWorkflows" \ 32 | --gpus=all -p 7801:7801 swarmui $POSTARG 33 | 34 | if [ $? == 42 ]; then 35 | exec "$SCRIPT_DIR/launch-standard-docker.sh" $@ 36 | fi 37 | -------------------------------------------------------------------------------- /launchtools/linux-build-logic.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source ./launchtools/linux-path-fix.sh 4 | 5 | # Server settings option 6 | if [ -f ./src/bin/always_pull ]; then 7 | echo "Pulling latest changes..." 8 | git pull 9 | fi 10 | 11 | if [ -d .git ]; then 12 | cur_head=`git rev-parse HEAD` 13 | built_head=`cat src/bin/last_build` 14 | if [ "$cur_head" != "$built_head" ]; then 15 | printf "\n\nWARNING: You did a git pull without building. Will now build for you...\n\n" 16 | touch ./src/bin/must_rebuild 17 | fi 18 | else 19 | printf "\n\nWARNING: YOU DID NOT CLONE FROM GIT. THIS WILL BREAK SOME SYSTEMS. PLEASE INSTALL PER THE README.\n\n" 20 | sleep 5 21 | fi 22 | 23 | if [ -f ./src/bin/must_rebuild ]; then 24 | echo "Rebuilding..." 25 | if [ -d ./src/bin/live_release ]; then 26 | rm -rf ./src/bin/live_release_backup 27 | mv ./src/bin/live_release ./src/bin/live_release_backup 28 | fi 29 | rm ./src/bin/must_rebuild 30 | fi 31 | 32 | # Build the program if it isn't already built 33 | if [ ! -f src/bin/live_release/SwarmUI.dll ]; then 34 | dotnet build src/SwarmUI.csproj --configuration Release -o ./src/bin/live_release 35 | cur_head=`git rev-parse HEAD` 36 | echo $cur_head > src/bin/last_build 37 | fi 38 | 39 | if [ ! -f src/bin/live_release/SwarmUI.dll ] && [ -f src/bin/live_release_backup/SwarmUI.dll ]; then 40 | printf "\n\nWARNING: BUILD FAILED? Restoring backup...\n\n" 41 | sleep 5 42 | rm -rf ./src/bin/live_release 43 | mv ./src/bin/live_release_backup ./src/bin/live_release 44 | fi 45 | 46 | # Default env configuration, gets overwritten by the C# code's settings handler 47 | export ASPNETCORE_ENVIRONMENT="Production" 48 | export ASPNETCORE_URLS="http://*:7801" 49 | -------------------------------------------------------------------------------- /launchtools/linux-path-fix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # No telemetry, no localization 4 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 5 | export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 6 | 7 | # Add dotnet non-admin-install to path 8 | export PATH="$SCRIPT_DIR/.dotnet:$HOME/.dotnet:/usr/lib/dotnet:/usr/share/dotnet:$PATH" 9 | 10 | # Try to set the expected runtime root by parsing dotnet cli output 11 | if [ -z "$DOTNET_ROOT" ]; then 12 | runtime_path=$(dotnet --list-runtimes | head -n 1 | awk -F'[\\[\\]]' '{print $2}') 13 | if [ -d "$runtime_path" ]; then 14 | actual_path="$(dirname "$(dirname "$runtime_path")")" 15 | export DOTNET_ROOT="$actual_path" 16 | export DOTNET_ROOT_X64="$actual_path" 17 | fi 18 | fi 19 | 20 | # Fallback to a list of expected locations it could also be in 21 | if [ -z "$DOTNET_ROOT" ]; then 22 | expected_location=( 23 | "$SCRIPT_DIR/.dotnet" 24 | "$HOME/.dotnet" 25 | "/usr/lib/dotnet" 26 | "/usr/share/dotnet" 27 | "/opt/homebrew/Cellar/dotnet/8.0.0/libexec" 28 | "/opt/homebrew/opt/dotnet/libexec" 29 | ) 30 | 31 | for location in "${expected_location[@]}"; do 32 | if [ -d "$location" ]; then 33 | export DOTNET_ROOT="$location" 34 | export DOTNET_ROOT_X64="$location" 35 | break 36 | fi 37 | done 38 | fi 39 | 40 | if [ -z "$DOTNET_ROOT" ]; then 41 | echo "Could not find dotnet runtime path, please report on Discord @ https://discord.gg/q2y38cqjNw with info about your dotnet installation" 42 | fi 43 | -------------------------------------------------------------------------------- /launchtools/pickle-to-safetensors.py: -------------------------------------------------------------------------------- 1 | # Internally called by SwarmUI 2 | # python -s launchtools/pickle-to-safetensors.py 3 | 4 | import os, sys, glob, traceback 5 | 6 | sys.path.append(os.path.dirname(__file__)) 7 | 8 | try: 9 | import torch 10 | except ImportError: 11 | os.system('python -m pip install torch') 12 | try: 13 | import safetensors 14 | except ImportError: 15 | os.system('python -m pip install safetensors') 16 | import torch 17 | from safetensors.torch import save_file 18 | 19 | fp16 = sys.argv[2].lower() == 'true' 20 | 21 | path = sys.argv[1] 22 | def get_all(ext): 23 | return glob.glob(f"{path}/**/*.{ext}", recursive=True) 24 | files = get_all('ckpt') + get_all('pt') + get_all('bin') + get_all('pth') 25 | 26 | import pickle_module 27 | 28 | for file in files: 29 | try: 30 | if '/backups/' in file.replace('\\', '/'): 31 | continue 32 | print(f"Will convert {file}...") 33 | last_dot = file.rindex('.') 34 | fname_clean = file[:last_dot] 35 | with open(file, 'rb') as f: 36 | tens = torch.load(f, map_location='cpu', pickle_module=pickle_module) 37 | metadata = {} 38 | # Stable-Diffusion checkpoint model data 39 | if "state_dict" in tens: 40 | tens = tens["state_dict"] 41 | # TI Embedding data 42 | if "string_to_param" in tens: 43 | vals = next(iter(tens["string_to_param"].values())) 44 | if isinstance(vals, torch.nn.ParameterDict): 45 | vals = {k: v.data for k, v in vals.items()} 46 | if isinstance(vals, torch.nn.Parameter): 47 | vals = vals.data 48 | tens["emb_params"] = vals 49 | del tens["string_to_param"] 50 | if "name" in tens: 51 | name = str(tens["name"]) 52 | if name: 53 | metadata["modelspec.title"] = name 54 | del tens["name"] 55 | if "sd_checkpoint" in tens: 56 | ckpt_name = str(tens["sd_checkpoint"]) 57 | if ckpt_name: 58 | metadata["modelspec.description"] = f"Embedding trained against '{ckpt_name}'" 59 | del tens["sd_checkpoint"] 60 | if "sd_checkpoint_name" in tens: 61 | ckpt_name = str(tens["sd_checkpoint_name"]) 62 | if ckpt_name: 63 | metadata["modelspec.description"] = f"Embedding trained against '{ckpt_name}'" 64 | del tens["sd_checkpoint_name"] 65 | # General cleanup 66 | for k, v in dict(tens).items(): 67 | if k.startswith("loss."): # VAE stray data 68 | del tens[k] 69 | elif k.startswith("model_ema."): # Stable-Diffusion checkpoint model stray data 70 | del tens[k] 71 | elif type(v) != torch.Tensor: 72 | raw_data = str(v) 73 | if (len(raw_data) > 100): 74 | raw_data = raw_data[:100] + "..." 75 | print(f"discard {k} = {raw_data}") 76 | del tens[k] 77 | elif fp16: 78 | tens[k] = tens[k].half() 79 | save_file(tens, fname_clean + '.safetensors', metadata=metadata) 80 | rel = os.path.relpath(file, path) 81 | os.makedirs(os.path.dirname(f"{path}/backups/{rel}"), exist_ok=True) 82 | os.rename(file, f"{path}/backups/{rel}") 83 | except Exception as e: 84 | print(f"Failed to convert {file}:") 85 | traceback.print_exc() 86 | -------------------------------------------------------------------------------- /launchtools/pickle_module.py: -------------------------------------------------------------------------------- 1 | # This entire file is copied from ComfyUI and is just a micro-hack to avoid a dependency on PyTorch Lightning 2 | import pickle 3 | 4 | load = pickle.load 5 | 6 | class Empty: 7 | pass 8 | 9 | class Unpickler(pickle.Unpickler): 10 | def find_class(self, module, name): 11 | if module.startswith("pytorch_lightning"): 12 | return Empty 13 | return super().find_class(module, name) 14 | -------------------------------------------------------------------------------- /launchtools/windows-open-docker.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem Ensure correct local path. 4 | cd /D "%~dp0/.." 5 | set current_dir=%cd% 6 | 7 | docker build -f launchtools/OpenDockerfile.docker -t swarmui . 8 | 9 | rem add "--network=host" if you want to access other services on the host network (eg a separated comfy instance) 10 | docker run -it --rm --name swarmui -v "%current_dir%":/SwarmUI --gpus=all -p 7801:7801 swarmui 11 | -------------------------------------------------------------------------------- /launchtools/windows-standard-docker.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem Ensure correct local path. 4 | cd /D "%~dp0/.." 5 | set current_dir=%cd% 6 | 7 | docker build -f launchtools/StandardDockerfile.docker -t swarmui . 8 | 9 | rem add "--network=host" if you want to access other services on the host network (eg a separated comfy instance) 10 | docker run -it --rm --name swarmui -v "%current_dir%"/Models:/SwarmUI/Models -v "%current_dir%"/Output:/SwarmUI/Output -v "%current_dir%"/src/BuiltinExtensions/ComfyUIBackend/CustomWorkflows:/SwarmUI/src/BuiltinExtensions/ComfyUIBackend/CustomWorkflows --mount source=swarmbackend,target=/SwarmUI/dlbackend --mount source=swarmdata,target=/SwarmUI/Data --mount source=swarmdlnodes,target=/SwarmUI/src/BuiltinExtensions/ComfyUIBackend/DLNodes --gpus=all -p 7801:7801 swarmui 11 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/AutoWebUIBackend/AutoWebUIAPIBackend.cs: -------------------------------------------------------------------------------- 1 | using FreneticUtilities.FreneticDataSyntax; 2 | using SwarmUI.DataHolders; 3 | using SwarmUI.Backends; 4 | 5 | namespace SwarmUI.Builtin_AutoWebUIExtension; 6 | 7 | public class AutoWebUIAPIBackend : AutoWebUIAPIAbstractBackend 8 | { 9 | public class AutoWebUIAPISettings : AutoConfiguration 10 | { 11 | /// Base web address of the auto webui instance. 12 | [SuggestionPlaceholder(Text = "WebUI's address...")] 13 | [ConfigComment("The address of the WebUI, eg 'http://localhost:7860'.")] 14 | public string Address = ""; 15 | } 16 | 17 | public override string Address => (SettingsRaw as AutoWebUIAPISettings).Address.TrimEnd('/'); 18 | 19 | public override Task Init() 20 | { 21 | return InitInternal(false); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/AutoWebUIBackend/AutoWebUIBackendExtension.cs: -------------------------------------------------------------------------------- 1 | using FreneticUtilities.FreneticToolkit; 2 | using Newtonsoft.Json.Linq; 3 | using SwarmUI.Core; 4 | using SwarmUI.DataHolders; 5 | using SwarmUI.Text2Image; 6 | 7 | namespace SwarmUI.Builtin_AutoWebUIExtension; 8 | 9 | /// Main class for the Automatic1111 Stable-Diffusion-WebUI Backend extension. 10 | public class AutoWebUIBackendExtension : Extension 11 | { 12 | 13 | /// List of actions to run when generating an image, primarily to alter the input data. 14 | public static List> OtherGenHandlers = []; 15 | 16 | /// Set of all feature-ids supported by Auto WebUI backends. 17 | public static HashSet FeaturesSupported = ["variation_seed", "autowebui"]; 18 | 19 | public static T2IRegisteredParam SamplerParam; 20 | 21 | public static List Samplers = ["Euler a", "Euler"]; 22 | 23 | public static LockObject ExtBackLock = new(); 24 | 25 | public static void LoadSamplerList(List newSamplers) 26 | { 27 | lock (ExtBackLock) 28 | { 29 | Samplers = [.. Samplers.Union(newSamplers).Distinct()]; 30 | } 31 | } 32 | 33 | public override void OnInit() 34 | { 35 | T2IParamGroup autoWebuiGroup = new("Auto WebUI", Toggles: false, Open: false); 36 | SamplerParam = T2IParamTypes.Register(new("[AutoWebUI] Sampler", "Sampler type (for AutoWebUI)", 37 | "Euler", Toggleable: true, FeatureFlag: "autowebui", Group: autoWebuiGroup, 38 | GetValues: (_) => Samplers 39 | )); 40 | Program.Backends.RegisterBackendType("auto_webui_api", "Auto1111 SD-WebUI API By URL", 41 | "A backend powered by a pre-existing installation of the AUTOMATIC1111/Stable-Diffusion-WebUI launched in '--api' mode, referenced via API base URL.", true); 42 | Program.Backends.RegisterBackendType("auto_webui_selfstart", "Auto111 SD-WebUI Self-Starting", 43 | "A backend powered by a pre-existing installation of the AUTOMATIC1111/Stable-Diffusion-WebUI, automatically launched and managed by this UI server."); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/AutoWebUIBackend/AutoWebUISelfStartBackend.cs: -------------------------------------------------------------------------------- 1 | using FreneticUtilities.FreneticDataSyntax; 2 | using FreneticUtilities.FreneticExtensions; 3 | using SwarmUI.Core; 4 | using SwarmUI.DataHolders; 5 | using SwarmUI.Utils; 6 | using System.Diagnostics; 7 | using SwarmUI.Backends; 8 | using System.IO; 9 | 10 | namespace SwarmUI.Builtin_AutoWebUIExtension; 11 | 12 | public class AutoWebUISelfStartBackend : AutoWebUIAPIAbstractBackend 13 | { 14 | public class AutoWebUISelfStartSettings : AutoConfiguration 15 | { 16 | [ConfigComment("The location of the 'webui.sh' or 'webui.bat' file.")] 17 | public string StartScript = ""; 18 | 19 | [ConfigComment("Any arguments to include in the launch script.")] 20 | public string ExtraArgs = ""; 21 | 22 | [ConfigComment("Which GPU to use, if multiple are available.")] 23 | public int GPU_ID = 0; // TODO: Determine GPU count and provide correct max 24 | } 25 | 26 | public Process RunningProcess; 27 | 28 | public int Port; 29 | 30 | public override string Address => $"http://localhost:{Port}"; 31 | 32 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 33 | public override async Task Init() 34 | { 35 | AutoWebUISelfStartSettings settings = SettingsRaw as AutoWebUISelfStartSettings; 36 | settings.StartScript = settings.StartScript.Trim(' ', '"', '\'', '\n', '\r', '\t'); 37 | if (settings.StartScript.AfterLast('/').BeforeLast('.') == "webui-user" && File.Exists(settings.StartScript)) 38 | { 39 | if (settings.StartScript.EndsWith(".sh")) // On Linux, webui-user.sh is not a valid launcher at all 40 | { 41 | Logs.Error($"Refusing init of AutoWebUI with 'webui-user.sh' target script. Please use the 'webui.sh' script instead."); 42 | Status = BackendStatus.ERRORED; 43 | return; 44 | } 45 | string scrContent = File.ReadAllText(settings.StartScript); 46 | if (!scrContent.Contains("%*") && !scrContent.Contains("%~")) // on Windows, it's only valid if you forward swarm's CLI args 47 | { 48 | Logs.Error($"Refusing init of AutoWebUI with 'webui-user.bat' target script. Please use the 'webui.bat' script instead. (If webui-user.bat usage is intentional, please forward CLI args, eg 'COMMANDLINE_ARGS=%*'."); 49 | Status = BackendStatus.ERRORED; 50 | return; 51 | } 52 | } 53 | await NetworkBackendUtils.DoSelfStart(settings.StartScript, this, $"AutoWebUI-{BackendData.ID}", $"backend-{BackendData.ID}", $"{settings.GPU_ID}", settings.ExtraArgs + " --api --port={PORT}", InitInternal, (p, r) => { Port = p; RunningProcess = r; }); 54 | } 55 | 56 | public override async Task Shutdown() 57 | { 58 | await base.Shutdown(); 59 | try 60 | { 61 | if (RunningProcess is null || RunningProcess.HasExited) 62 | { 63 | return; 64 | } 65 | Logs.Info($"Shutting down self-start Auto WebUI (port={Port}) process #{RunningProcess.Id}..."); 66 | Utilities.KillProcess(RunningProcess, 10); 67 | Logs.Info("Auto WebUI process shut down."); 68 | } 69 | catch (Exception ex) 70 | { 71 | Logs.Error($"Error stopping Auto WebUI process: {ex.ReadableString()}"); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/AutoWebUIBackend/README.md: -------------------------------------------------------------------------------- 1 | # Automatic1111 Stable-Diffusion-WebUI Backend Extension for SwarmUI 2 | 3 | 4 | This extension enables the use of [AUTOMATIC1111/Stable-Diffusion-WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) ("Auto WebUI") as a backend provider for SwarmUI. 5 | 6 | ### API vs Self-Start 7 | 8 | (TODO) 9 | 10 | ### Installation (API) 11 | 12 | (TODO) 13 | 14 | ### Installation (Self-Start) 15 | 16 | (TODO) 17 | 18 | ### Basic Usage Within SwarmUI 19 | 20 | (TODO) 21 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/.gitignore: -------------------------------------------------------------------------------- 1 | CustomWorkflows/ 2 | Workflows/ 3 | DLNodes/ 4 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/Assets/comfy_workflow_editor.css: -------------------------------------------------------------------------------- 1 | .comfy_workflow_frame { 2 | width: 100%; 3 | height: calc(100% - 3rem); 4 | } 5 | .comfy_workflow_frameholder { 6 | width: 100%; 7 | height: 100%; 8 | } 9 | .comfy_workflow_buttons { 10 | position: absolute; 11 | width: 0; 12 | height: 0; 13 | } 14 | .comfy_workflow_buttons_actual { 15 | position: relative; 16 | top: 1rem; 17 | width: fit-content; 18 | height: fit-content; 19 | padding: 1rem; 20 | border: 1px solid var(--shadow); 21 | border-radius: 1rem; 22 | background-color: var(--background-soft); 23 | white-space: nowrap; 24 | min-width: 3rem; 25 | min-height: 3rem; 26 | } 27 | .comfy_workflow_buttons_left { 28 | display: inline-block; 29 | width: fit-content; 30 | height: fit-content; 31 | white-space: normal; 32 | vertical-align: top; 33 | } 34 | .comfy_workflow_buttons_right { 35 | display: inline-block; 36 | width: fit-content; 37 | height: fit-content; 38 | white-space: normal; 39 | padding-right: 2rem; /* this is a hack, idk why this is needed */ 40 | vertical-align: top; 41 | } 42 | .comfy-small-button { 43 | font-size: 80%; 44 | } 45 | .comfy_buttons_closer_wrapper { 46 | position: relative; 47 | width: 0; 48 | height: 0; 49 | } 50 | .comfy_buttons_closer { 51 | font-size: 80%; 52 | position: absolute; 53 | top: 0rem; 54 | left: 0rem; 55 | cursor: pointer; 56 | border-radius: 0.5rem; 57 | border: 1px solid var(--button-border); 58 | background-color: var(--button-background); 59 | color: var(--button-text); 60 | width: fit-content; 61 | padding: 0.2rem; 62 | } 63 | .comfy_buttons_closer:hover { 64 | background-color: var(--button-background-hover); 65 | color: var(--button-foreground-hover); 66 | } 67 | .comfy_quickload { 68 | font-size: 80%; 69 | } 70 | .comfy_quickload select { 71 | border: none; 72 | padding: 0; 73 | margin: 0; 74 | float: right; 75 | text-align: right; 76 | } 77 | .comfy_multigpu { 78 | font-size: 80%; 79 | } 80 | .comfy_multigpu select { 81 | border: none; 82 | padding: 0; 83 | margin: 0; 84 | float: right; 85 | margin-left: 1rem; 86 | } 87 | .comfy-disable-button { 88 | margin: auto; 89 | display: block; 90 | } 91 | .comfy-second-button-row { 92 | height: 1.5rem; 93 | width: 100%; 94 | } 95 | .comfy-left-button { 96 | float: left; 97 | } 98 | .comfy_workflow_topbar_holder { 99 | width: 100%; 100 | margin: 0; 101 | padding: 0; 102 | height: 20rem; 103 | border-bottom: 7px solid var(--background); 104 | } 105 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ComfyUIAPIBackend.cs: -------------------------------------------------------------------------------- 1 |  2 | using FreneticUtilities.FreneticDataSyntax; 3 | using SwarmUI.DataHolders; 4 | 5 | namespace SwarmUI.Builtin_ComfyUIBackend; 6 | 7 | public class ComfyUIAPIBackend : ComfyUIAPIAbstractBackend 8 | { 9 | public class ComfyUIAPISettings : AutoConfiguration 10 | { 11 | /// Base web address of the ComfyUI instance. 12 | [SuggestionPlaceholder(Text = "ComfyUI's address...")] 13 | [ConfigComment("The address of the ComfyUI instance, eg 'http://localhost:8188'.")] 14 | public string Address = ""; 15 | 16 | [ConfigComment("Whether the backend is allowed to revert to an 'idle' state if the API address is unresponsive.\nAn idle state is not considered an error, but cannot generate.\nIt will automatically return to 'running' if the API becomes available.")] 17 | public bool AllowIdle = false; 18 | 19 | [ConfigComment("How many extra requests may queue up on this backend while one is processing.")] 20 | public int OverQueue = 1; 21 | 22 | [ConfigComment("If true, the backend address will use '/api/' for API calls to enable passthrough of a frontend NPM dev server.")] 23 | public bool EnableFrontendDev = false; 24 | } 25 | 26 | public ComfyUIAPISettings Settings => SettingsRaw as ComfyUIAPISettings; 27 | 28 | public override string APIAddress => Settings.Address.TrimEnd('/') + (Settings.EnableFrontendDev ? "/api" : ""); 29 | 30 | public override string WebAddress => Settings.Address.TrimEnd('/'); 31 | 32 | public override bool CanIdle => Settings.AllowIdle; 33 | 34 | public override int OverQueue => Settings.OverQueue; 35 | 36 | public override Task Init() 37 | { 38 | return InitInternal(CanIdle); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmBlending.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | class SwarmLatentBlendMasked: 4 | @classmethod 5 | def INPUT_TYPES(s): 6 | return { 7 | "required": { 8 | "samples0": ("LATENT",), 9 | "samples1": ("LATENT",), 10 | "mask": ("MASK",), 11 | "blend_factor": ("FLOAT", { "default": 0.5, "min": 0, "max": 1, "step": 0.01, "tooltip": "The blend factor between the two samples. 0 means entirely use sample0, 1 means entirely sample1, 0.5 means 50/50 of each." }), 12 | } 13 | } 14 | 15 | RETURN_TYPES = ("LATENT",) 16 | FUNCTION = "blend" 17 | CATEGORY = "SwarmUI/images" 18 | DESCRIPTION = "Blends two latent images together within a masked region." 19 | 20 | def blend(self, samples0, samples1, blend_factor, mask): 21 | samples_out = samples0.copy() 22 | samples0 = samples0["samples"] 23 | samples1 = samples1["samples"] 24 | while mask.ndim < 4: 25 | mask = mask.unsqueeze(0) 26 | 27 | if samples0.shape != samples1.shape: 28 | samples1 = torch.nn.functional.interpolate(samples1, size=(samples0.shape[2], samples0.shape[3]), mode="bicubic") 29 | if samples0.shape != mask.shape: 30 | mask = torch.nn.functional.interpolate(mask, size=(samples0.shape[2], samples0.shape[3]), mode="bicubic") 31 | 32 | samples_blended = samples0 * (1 - mask * blend_factor) + samples1 * (mask * blend_factor) 33 | samples_out["samples"] = samples_blended 34 | return (samples_out,) 35 | 36 | 37 | NODE_CLASS_MAPPINGS = { 38 | "SwarmLatentBlendMasked": SwarmLatentBlendMasked, 39 | } 40 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmClipSeg.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from PIL import Image 3 | import numpy as np 4 | from transformers import CLIPSegProcessor, CLIPSegForImageSegmentation 5 | import folder_paths 6 | import os, requests 7 | 8 | def get_path(): 9 | if "clipseg" in folder_paths.folder_names_and_paths: 10 | paths = folder_paths.folder_names_and_paths["clipseg"] 11 | return paths[0][0] 12 | else: 13 | # Jank backup path if you're not running properly in Swarm 14 | path = os.path.dirname(os.path.realpath(__file__)) + "/models" 15 | return path 16 | 17 | 18 | # Manual download of the model from a safetensors conversion. 19 | # Done manually to guarantee it's only a safetensors file ever and not a pickle 20 | def download_model(path, urlbase): 21 | if os.path.exists(path): 22 | return 23 | for file in ["config.json", "merges.txt", "model.safetensors", "preprocessor_config.json", "special_tokens_map.json", "tokenizer_config.json", "vocab.json"]: 24 | os.makedirs(path, exist_ok=True) 25 | filepath = path + file 26 | if not os.path.exists(filepath): 27 | with open(filepath, "wb") as f: 28 | print(f"[SwarmClipSeg] Downloading '{file}'...") 29 | f.write(requests.get(f"{urlbase}{file}").content) 30 | 31 | 32 | class SwarmClipSeg: 33 | @classmethod 34 | def INPUT_TYPES(s): 35 | return { 36 | "required": { 37 | "images": ("IMAGE",), 38 | "match_text": ("STRING", {"multiline": True, "tooltip": "A short description (a few words) to describe something within the image to find and mask."}), 39 | "threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step":0.01, "round": False, "tooltip": "Threshold to apply to the mask, higher values will make the mask more strict. Without sufficient thresholding, CLIPSeg may include random stray content around the edges."}), 40 | } 41 | } 42 | 43 | CATEGORY = "SwarmUI/masks" 44 | RETURN_TYPES = ("MASK",) 45 | FUNCTION = "seg" 46 | DESCRIPTION = "Segment an image using CLIPSeg, creating a mask of what part of an image appears to match the given text." 47 | 48 | def seg(self, images, match_text, threshold): 49 | # TODO: Batch support? 50 | i = 255.0 * images[0].cpu().numpy() 51 | img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) 52 | # TODO: Cache the model in RAM in some way? 53 | path = get_path() + "/clipseg-rd64-refined-fp16-safetensors/" 54 | download_model(path, "https://huggingface.co/mcmonkey/clipseg-rd64-refined-fp16/resolve/main/") 55 | processor = CLIPSegProcessor.from_pretrained(path) 56 | model = CLIPSegForImageSegmentation.from_pretrained(path) 57 | with torch.no_grad(): 58 | mask = model(**processor(text=match_text, images=img, return_tensors="pt", padding=True))[0] 59 | mask = torch.nn.functional.threshold(mask.sigmoid(), threshold, 0) 60 | mask -= mask.min() 61 | max = mask.max() 62 | if max > 0: 63 | mask /= max 64 | while mask.ndim < 4: 65 | mask = mask.unsqueeze(0) 66 | mask = torch.nn.functional.interpolate(mask, size=(images.shape[1], images.shape[2]), mode="bilinear").squeeze(0).squeeze(0) 67 | return (mask,) 68 | 69 | NODE_CLASS_MAPPINGS = { 70 | "SwarmClipSeg": SwarmClipSeg, 71 | } 72 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmInternalUtil.py: -------------------------------------------------------------------------------- 1 | import comfy, folder_paths, execution 2 | from comfy import samplers 3 | import functools 4 | 5 | # This is purely a hack to provide a list of embeds in the object_info report. 6 | # Code referenced from Comfy VAE impl. Probably does nothing useful in an actual workflow. 7 | class SwarmEmbedLoaderListProvider: 8 | @classmethod 9 | def INPUT_TYPES(s): 10 | return { 11 | "required": { 12 | "embed_name": (folder_paths.get_filename_list("embeddings"), ) 13 | } 14 | } 15 | 16 | CATEGORY = "SwarmUI/internal" 17 | RETURN_TYPES = ("EMBEDDING",) 18 | FUNCTION = "load_embed" 19 | DESCRIPTION = "Internal node just intended to provide a list of currently known embeddings to Swarm. You can also use it to blindly load an embedding file if you need to." 20 | 21 | def load_embed(self, embed_name): 22 | embed_path = folder_paths.get_full_path("embedding", embed_name) 23 | sd = comfy.utils.load_torch_file(embed_path) 24 | return (sd,) 25 | 26 | 27 | class SwarmJustLoadTheModelPlease: 28 | @classmethod 29 | def INPUT_TYPES(s): 30 | return { 31 | "required": { 32 | "model": ("MODEL",), 33 | "clip": ("CLIP,GEMMA",), 34 | "vae": ("VAE",), 35 | } 36 | } 37 | 38 | CATEGORY = "SwarmUI/internal" 39 | RETURN_TYPES = () 40 | FUNCTION = "just_load" 41 | OUTPUT_NODE = True 42 | DESCRIPTION = "Internal node that acts as a final output for a model/clip/vae. This allows swarm to load models when needed without generating anything." 43 | 44 | def just_load(self, model, clip, vae): 45 | if model is None: 46 | raise ValueError("The model failed to load") 47 | if clip is None: 48 | raise ValueError("The text encoders (CLIP) failed to load") 49 | if vae is None: 50 | raise ValueError("The VAE failed to load") 51 | return {} 52 | 53 | 54 | NODE_CLASS_MAPPINGS = { 55 | "SwarmEmbedLoaderListProvider": SwarmEmbedLoaderListProvider, 56 | "SwarmJustLoadTheModelPlease": SwarmJustLoadTheModelPlease 57 | } 58 | 59 | 60 | # This is a dirty hack to shut up the errors from Dropdown combo mismatch, pending Comfy upstream fix 61 | ORIG_EXECUTION_VALIDATE = execution.validate_inputs 62 | def validate_inputs(prompt, item, validated): 63 | raw_result = ORIG_EXECUTION_VALIDATE(prompt, item, validated) 64 | if raw_result is None: 65 | return None 66 | (did_succeed, errors, unique_id) = raw_result 67 | if did_succeed: 68 | return raw_result 69 | for error in errors: 70 | if error['type'] == "return_type_mismatch": 71 | o_id = error['extra_info']['linked_node'][0] 72 | o_class_type = prompt[o_id]['class_type'] 73 | if o_class_type == "SwarmInputModelName" or o_class_type == "SwarmInputDropdown": 74 | errors.remove(error) 75 | did_succeed = len(errors) == 0 76 | return (did_succeed, errors, unique_id) 77 | 78 | execution.validate_inputs = validate_inputs 79 | 80 | # Comfy's app logger has broken terminal compat, so violently force it to auto-flush 81 | try: 82 | from app import logger 83 | def patch_interceptor(interceptor): 84 | if interceptor: 85 | orig = interceptor.write 86 | def write(self, data): 87 | orig(data) 88 | self.flush() 89 | interceptor.write = functools.partial(write, interceptor) 90 | # Force UTF-8 too, to prevent encoding errors (Comfy will full crash outputting some languages) 91 | # (Swarm's C# engine has code to forcibly assume UTF-8, so this is safe. Otherwise it would wonk the terminal if the terminal isn't set to UTF-8) 92 | interceptor.reconfigure(encoding='utf-8') 93 | patch_interceptor(logger.stdout_interceptor) 94 | patch_interceptor(logger.stderr_interceptor) 95 | except Exception as e: 96 | import traceback 97 | traceback.print_exc() 98 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmLatents.py: -------------------------------------------------------------------------------- 1 | import comfy, torch 2 | 3 | class SwarmOffsetEmptyLatentImage: 4 | def __init__(self): 5 | self.device = comfy.model_management.intermediate_device() 6 | 7 | @classmethod 8 | def INPUT_TYPES(s): 9 | return { 10 | "required": { 11 | "width": ("INT", {"default": 512, "min": 16, "max": 4096, "step": 8}), 12 | "height": ("INT", {"default": 512, "min": 16, "max": 4096, "step": 8}), 13 | "off_a": ("INT", {"default": 0, "min": -10, "max": 10, "step": 0.0001}), 14 | "off_b": ("INT", {"default": 0, "min": -10, "max": 10, "step": 0.0001}), 15 | "off_c": ("INT", {"default": 0, "min": -10, "max": 10, "step": 0.0001}), 16 | "off_d": ("INT", {"default": 0, "min": -10, "max": 10, "step": 0.0001}), 17 | "batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}) 18 | } 19 | } 20 | 21 | CATEGORY = "SwarmUI/latents" 22 | RETURN_TYPES = ("LATENT",) 23 | FUNCTION = "generate" 24 | DESCRIPTION = "Generates a latent image with 4 channels, each channel filled with a different offset value. Designed to allow alternate empty value offsets for SDv1 and SDXL." 25 | 26 | def generate(self, width, height, off_a, off_b, off_c, off_d, batch_size=1): 27 | latent = torch.zeros([batch_size, 4, height // 8, width // 8], device=self.device) 28 | latent[:, 0, :, :] = off_a 29 | latent[:, 1, :, :] = off_b 30 | latent[:, 2, :, :] = off_c 31 | latent[:, 3, :, :] = off_d 32 | return ({"samples":latent}, ) 33 | 34 | 35 | NODE_CLASS_MAPPINGS = { 36 | "SwarmOffsetEmptyLatentImage": SwarmOffsetEmptyLatentImage 37 | } 38 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmLoadImageB64.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageOps 2 | import numpy as np 3 | import torch, base64, io 4 | 5 | def b64_to_img_and_mask(image_base64): 6 | imageData = base64.b64decode(image_base64) 7 | i = Image.open(io.BytesIO(imageData)) 8 | if hasattr(i, 'is_animated') and i.is_animated: 9 | images = [] 10 | for frame in range(i.n_frames): 11 | i.seek(frame) 12 | images.append(i.convert("RGB")) 13 | i.seek(0) 14 | image = np.array(images).astype(np.float32) / 255.0 15 | image = torch.from_numpy(image) 16 | else: 17 | i = ImageOps.exif_transpose(i) 18 | image = i.convert("RGB") 19 | image = np.array(image).astype(np.float32) / 255.0 20 | image = torch.from_numpy(image)[None,] 21 | if 'A' in i.getbands(): 22 | mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0 23 | mask = 1. - torch.from_numpy(mask) 24 | else: 25 | mask = torch.zeros((64,64), dtype=torch.float32, device="cpu") 26 | return (image, mask.unsqueeze(0)) 27 | 28 | class SwarmLoadImageB64: 29 | @classmethod 30 | def INPUT_TYPES(s): 31 | return { 32 | "required": { 33 | "image_base64": ("STRING", {"multiline": True}) 34 | } 35 | } 36 | 37 | CATEGORY = "SwarmUI/images" 38 | RETURN_TYPES = ("IMAGE", "MASK") 39 | FUNCTION = "load_image_b64" 40 | DESCRIPTION = "Loads an image from a base64 string. Works like a regular LoadImage node, but with input format designed to be easier to use through automated calls, including SwarmUI with custom workflows." 41 | 42 | def load_image_b64(self, image_base64): 43 | return b64_to_img_and_mask(image_base64) 44 | 45 | NODE_CLASS_MAPPINGS = { 46 | "SwarmLoadImageB64": SwarmLoadImageB64, 47 | } 48 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmLoraLoader.py: -------------------------------------------------------------------------------- 1 | import comfy 2 | import folder_paths 3 | 4 | class SwarmLoraLoader: 5 | def __init__(self): 6 | self.loaded_lora = None 7 | 8 | @classmethod 9 | def INPUT_TYPES(s): 10 | return { 11 | "required": { 12 | "model": ("MODEL", ), 13 | "clip": ("CLIP", ), 14 | "lora_names": ("STRING", {"multiline": True, "tooltip": "Comma separated list of lora names to load."}), 15 | "lora_weights": ("STRING", {"multiline": True, "tooltip": "Comma separated list of lora weights to apply to each lora. Must match the number of loras."}), 16 | } 17 | } 18 | 19 | CATEGORY = "SwarmUI/models" 20 | RETURN_TYPES = ("MODEL", "CLIP") 21 | FUNCTION = "load_loras" 22 | DESCRIPTION = "Like a regular LoRA Loader, but designed to take a dynamic list of loras and weights, to allow easier integration with SwarmUI custom workflows." 23 | 24 | def load_loras(self, model, clip, lora_names, lora_weights): 25 | if lora_names.strip() == "": 26 | return (model, clip) 27 | 28 | lora_names = lora_names.split(",") 29 | lora_weights = lora_weights.split(",") 30 | lora_weights = [float(x.strip()) for x in lora_weights] 31 | 32 | for i in range(len(lora_names)): 33 | lora_name = lora_names[i].strip() 34 | weight = lora_weights[i] 35 | if weight == 0: 36 | continue 37 | # This section copied directly from default comfy LoraLoader 38 | lora_path = folder_paths.get_full_path("loras", lora_name) 39 | lora = None 40 | if self.loaded_lora is not None: 41 | if self.loaded_lora[0] == lora_path: 42 | lora = self.loaded_lora[1] 43 | else: 44 | temp = self.loaded_lora 45 | self.loaded_lora = None 46 | del temp 47 | if lora is None: 48 | lora = comfy.utils.load_torch_file(lora_path, safe_load=True) 49 | self.loaded_lora = (lora_path, lora) 50 | model, clip = comfy.sd.load_lora_for_models(model, clip, lora, weight, weight) 51 | 52 | return (model, clip) 53 | 54 | NODE_CLASS_MAPPINGS = { 55 | "SwarmLoraLoader": SwarmLoraLoader, 56 | } 57 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmMath.py: -------------------------------------------------------------------------------- 1 | 2 | class SwarmIntAdd: 3 | @classmethod 4 | def INPUT_TYPES(s): 5 | return { 6 | "required": { 7 | "a": ("INT", {"default": 0, "min": -2147483647, "max": 2147483647}), 8 | "b": ("INT", {"default": 0, "min": -2147483647, "max": 2147483647}) 9 | } 10 | } 11 | 12 | CATEGORY = "SwarmUI/math" 13 | RETURN_TYPES = ("INT",) 14 | FUNCTION = "add" 15 | DESCRIPTION = "Adds two integers. Use a negative number to subtract." 16 | 17 | def add(self, a, b): 18 | return (a + b,) 19 | 20 | 21 | NODE_CLASS_MAPPINGS = { 22 | "SwarmIntAdd": SwarmIntAdd 23 | } 24 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmReference.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | # This code copied from https://github.com/comfyanonymous/ComfyUI_experiments/blob/master/reference_only.py 4 | # And modified to work better in Swarm generated workflows 5 | 6 | class SwarmReferenceOnly: 7 | @classmethod 8 | def INPUT_TYPES(s): 9 | return { 10 | "required": { 11 | "model": ("MODEL",), 12 | "reference": ("LATENT",), 13 | "latent": ("LATENT",) 14 | } 15 | } 16 | 17 | CATEGORY = "SwarmUI/sampling" 18 | RETURN_TYPES = ("MODEL", "LATENT") 19 | FUNCTION = "reference_only" 20 | DESCRIPTION = "Applies 'reference only' image-prompting to the generation. Must forward the new model, and the new latent, to work properly." 21 | 22 | def reference_only(self, model, reference, latent): 23 | model_reference = model.clone() 24 | reference["samples"] = torch.nn.functional.interpolate(reference["samples"], size=(latent["samples"].shape[2], latent["samples"].shape[3]), mode="bilinear") 25 | 26 | batch = latent["samples"].shape[0] + reference["samples"].shape[0] 27 | def reference_apply(q, k, v, extra_options): 28 | k = k.clone().repeat(1, 2, 1) 29 | offset = 0 30 | if q.shape[0] > batch: 31 | offset = batch 32 | 33 | for o in range(0, q.shape[0], batch): 34 | for x in range(1, batch): 35 | k[x + o, q.shape[1]:] = q[o,:] 36 | 37 | return q, k, k 38 | 39 | model_reference.set_model_attn1_patch(reference_apply) 40 | out_latent = torch.cat((reference["samples"], latent["samples"])) 41 | if "noise_mask" in latent: 42 | mask = latent["noise_mask"] 43 | else: 44 | mask = torch.ones((64,64), dtype=torch.float32, device="cpu") 45 | 46 | if len(mask.shape) < 3: 47 | mask = mask.unsqueeze(0) 48 | if mask.shape[0] < latent["samples"].shape[0]: 49 | mask = mask.repeat(latent["samples"].shape[0], 1, 1) 50 | 51 | out_mask = torch.zeros((1,mask.shape[1],mask.shape[2]), dtype=torch.float32, device="cpu") 52 | return (model_reference, {"samples": out_latent, "noise_mask": torch.cat((out_mask, mask))}) 53 | 54 | NODE_CLASS_MAPPINGS = { 55 | "SwarmReferenceOnly": SwarmReferenceOnly, 56 | } 57 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmTiling.py: -------------------------------------------------------------------------------- 1 | import torch, copy 2 | from torch.nn import functional as F 3 | 4 | def make_circular_assym(m, assym_mode): 5 | def _conv_forward(self, input, weight, bias): 6 | if self.padding_mode == "x_circular": 7 | padded = F.pad(input, (self._reversed_padding_repeated_twice[0], self._reversed_padding_repeated_twice[1], 0, 0), "circular") 8 | padded = F.pad(padded, (0, 0, self._reversed_padding_repeated_twice[2], self._reversed_padding_repeated_twice[3]), "constant", 0) 9 | return F.conv2d(padded, weight, bias, self.stride, (0, 0), self.dilation, self.groups) 10 | elif self.padding_mode == "y_circular": 11 | padded = F.pad(input, (self._reversed_padding_repeated_twice[0], self._reversed_padding_repeated_twice[1], 0, 0), "constant", 0) 12 | padded = F.pad(padded, (0, 0, self._reversed_padding_repeated_twice[2], self._reversed_padding_repeated_twice[3]), "circular") 13 | return F.conv2d(padded, weight, bias, self.stride, (0, 0), self.dilation, self.groups) 14 | elif self.padding_mode != "zeros": 15 | padded = F.pad(input, self._reversed_padding_repeated_twice, mode=self.padding_mode) 16 | return F.conv2d(padded, weight, bias, self.stride, (0, 0), self.dilation, self.groups) 17 | else: 18 | return F.conv2d(input, weight, bias, self.stride, self.padding, self.dilation, self.groups) 19 | if isinstance(m, torch.nn.Conv2d): 20 | m._conv_forward = _conv_forward.__get__(m, torch.nn.Conv2d) 21 | m.padding_mode = assym_mode 22 | 23 | def make_circular(m): 24 | if isinstance(m, torch.nn.Conv2d): 25 | m.padding_mode = "circular" 26 | 27 | class SwarmModelTiling: 28 | @classmethod 29 | def INPUT_TYPES(s): 30 | return { 31 | "required": { 32 | "model": ("MODEL", ), 33 | }, 34 | "optional": { 35 | "tile_axis": (["Both", "X", "Y"], ) 36 | } 37 | } 38 | 39 | CATEGORY = "SwarmUI/sampling" 40 | RETURN_TYPES = ("MODEL",) 41 | FUNCTION = "adapt" 42 | DESCRIPTION = "Adapts a model to use circular padding to enable tiled image results. Only works on UNet based models (eg SDv1, SDXL), not on DiT models (eg SD3, Flux). Use with SwarmTileableVAE." 43 | 44 | def adapt(self, model, tile_axis=None): 45 | m = copy.deepcopy(model) 46 | if tile_axis is not None and tile_axis != "Both": 47 | if tile_axis == "X": 48 | m.model.apply(lambda x: make_circular_assym(x, "x_circular")) 49 | elif tile_axis == "Y": 50 | m.model.apply(lambda x: make_circular_assym(x, "y_circular")) 51 | else: 52 | m.model.apply(make_circular) 53 | return (m,) 54 | 55 | class SwarmTileableVAE: 56 | @classmethod 57 | def INPUT_TYPES(s): 58 | return { 59 | "required": { 60 | "vae": ("VAE", ) 61 | }, 62 | "optional": { 63 | "tile_axis": (["Both", "X", "Y"], ) 64 | } 65 | } 66 | 67 | CATEGORY = "SwarmUI/sampling" 68 | RETURN_TYPES = ("VAE",) 69 | FUNCTION = "adapt" 70 | DESCRIPTION = "Adapts a VAE to use circular padding to enable tiled image results. Use with SwarmModelTiling." 71 | 72 | def adapt(self, vae, tile_axis=None): 73 | vae = copy.deepcopy(vae) 74 | if tile_axis is not None and tile_axis != "Both": 75 | if tile_axis == "X": 76 | vae.first_stage_model.apply(lambda x: make_circular_assym(x, "x_circular")) 77 | elif tile_axis == "Y": 78 | vae.first_stage_model.apply(lambda x: make_circular_assym(x, "y_circular")) 79 | else: 80 | vae.first_stage_model.apply(make_circular) 81 | return (vae,) 82 | 83 | NODE_CLASS_MAPPINGS = { 84 | "SwarmModelTiling": SwarmModelTiling, 85 | "SwarmTileableVAE": SwarmTileableVAE, 86 | } 87 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/SwarmUnsampler.py: -------------------------------------------------------------------------------- 1 | import torch, comfy 2 | from .SwarmKSampler import make_swarm_sampler_callback 3 | 4 | class SwarmUnsampler: 5 | @classmethod 6 | def INPUT_TYPES(s): 7 | return { 8 | "required": { 9 | "model": ("MODEL",), 10 | "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), 11 | "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), 12 | "scheduler": (["turbo"] + comfy.samplers.KSampler.SCHEDULERS, ), 13 | "positive": ("CONDITIONING", ), 14 | "negative": ("CONDITIONING", ), 15 | "latent_image": ("LATENT", ), 16 | "start_at_step": ("INT", {"default": 0, "min": 0, "max": 10000}), 17 | "previews": (["default", "none", "one"], ) 18 | } 19 | } 20 | 21 | CATEGORY = "SwarmUI/sampling" 22 | RETURN_TYPES = ("LATENT",) 23 | FUNCTION = "unsample" 24 | DESCRIPTION = "Runs sampling in reverse. The function of this is to create noise that matches an image, such that you can the run forward sampling with an altered version of the unsampling prompt to get a closely altered image. May not work on all models, may not work perfectly. Input values should largely match your Sampler inputs." 25 | 26 | def unsample(self, model, steps, sampler_name, scheduler, positive, negative, latent_image, start_at_step, previews): 27 | device = comfy.model_management.get_torch_device() 28 | latent_samples = latent_image["samples"].to(device) 29 | 30 | noise = torch.zeros(latent_samples.size(), dtype=latent_samples.dtype, layout=latent_samples.layout, device=device) 31 | noise_mask = None 32 | if "noise_mask" in latent_image: 33 | noise_mask = latent_image["noise_mask"] 34 | 35 | sampler = comfy.samplers.KSampler(model, steps=steps, device=device, sampler=sampler_name, scheduler=scheduler, denoise=1.0, model_options=model.model_options) 36 | sigmas = sampler.sigmas.flip(0) + 0.0001 37 | 38 | callback = make_swarm_sampler_callback(steps, device, model, previews) 39 | 40 | samples = comfy.sample.sample(model, noise, steps, 1, sampler_name, scheduler, positive, negative, latent_samples, 41 | denoise=1.0, disable_noise=False, start_step=0, last_step=steps - start_at_step, 42 | force_full_denoise=False, noise_mask=noise_mask, sigmas=sigmas, callback=callback, seed=0) 43 | out = latent_image.copy() 44 | out["samples"] = samples 45 | return (out, ) 46 | 47 | 48 | NODE_CLASS_MAPPINGS = { 49 | "SwarmUnsampler": SwarmUnsampler, 50 | } 51 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/__init__.py: -------------------------------------------------------------------------------- 1 | import os, folder_paths 2 | 3 | from . import SwarmBlending, SwarmClipSeg, SwarmImages, SwarmInternalUtil, SwarmKSampler, SwarmLoadImageB64, SwarmLoraLoader, SwarmMasks, SwarmSaveImageWS, SwarmTiling, SwarmExtractLora, SwarmUnsampler, SwarmLatents, SwarmInputNodes, SwarmTextHandling, SwarmReference, SwarmMath 4 | 5 | WEB_DIRECTORY = "./web" 6 | 7 | NODE_CLASS_MAPPINGS = ( 8 | SwarmBlending.NODE_CLASS_MAPPINGS 9 | | SwarmClipSeg.NODE_CLASS_MAPPINGS 10 | | SwarmImages.NODE_CLASS_MAPPINGS 11 | | SwarmInternalUtil.NODE_CLASS_MAPPINGS 12 | | SwarmKSampler.NODE_CLASS_MAPPINGS 13 | | SwarmLoadImageB64.NODE_CLASS_MAPPINGS 14 | | SwarmLoraLoader.NODE_CLASS_MAPPINGS 15 | | SwarmMasks.NODE_CLASS_MAPPINGS 16 | | SwarmSaveImageWS.NODE_CLASS_MAPPINGS 17 | | SwarmTiling.NODE_CLASS_MAPPINGS 18 | | SwarmExtractLora.NODE_CLASS_MAPPINGS 19 | | SwarmUnsampler.NODE_CLASS_MAPPINGS 20 | | SwarmLatents.NODE_CLASS_MAPPINGS 21 | | SwarmInputNodes.NODE_CLASS_MAPPINGS 22 | | SwarmTextHandling.NODE_CLASS_MAPPINGS 23 | | SwarmReference.NODE_CLASS_MAPPINGS 24 | | SwarmMath.NODE_CLASS_MAPPINGS 25 | ) 26 | 27 | # TODO: Why is there no comfy core register method? 0.o 28 | def register_model_folder(name): 29 | if name not in folder_paths.folder_names_and_paths: 30 | folder_paths.folder_names_and_paths[name] = ([os.path.join(folder_paths.models_dir, name)], folder_paths.supported_pt_extensions) 31 | else: 32 | folder_paths.folder_names_and_paths[name] = (folder_paths.folder_names_and_paths[name][0], folder_paths.supported_pt_extensions) 33 | 34 | register_model_folder("yolov8") 35 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyCommon/web/swarmhelper.js: -------------------------------------------------------------------------------- 1 | import { api } from '../../scripts/api.js'; 2 | 3 | window.swarmApiDirect = api; 4 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyExtra/SwarmRemBg.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import numpy as np 3 | import torch 4 | 5 | class SwarmRemBg: 6 | @classmethod 7 | def INPUT_TYPES(s): 8 | return { 9 | "required": { 10 | "images": ("IMAGE",), 11 | } 12 | } 13 | 14 | CATEGORY = "SwarmUI/images" 15 | RETURN_TYPES = ("IMAGE", "MASK",) 16 | FUNCTION = "rem" 17 | 18 | def rem(self, images): 19 | from rembg import remove 20 | 21 | output = [] 22 | masks = [] 23 | for image in images: 24 | i = 255.0 * image.cpu().numpy() 25 | img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) 26 | img = img.convert("RGBA") 27 | img = remove(img, post_process_mask=True) 28 | output.append(np.array(img).astype(np.float32) / 255.0) 29 | if 'A' in img.getbands(): 30 | mask = np.array(img.getchannel('A')).astype(np.float32) / 255.0 31 | masks.append(1. - mask) 32 | else: 33 | masks.append(np.zeros((64,64), dtype=np.float32)) 34 | return (torch.from_numpy(np.array(output)), torch.from_numpy(np.array(masks))) 35 | 36 | NODE_CLASS_MAPPINGS = { 37 | "SwarmRemBg": SwarmRemBg, 38 | } 39 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyExtra/__init__.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | NODE_CLASS_MAPPINGS = {} 4 | 5 | # RemBg doesn't work on all python versions and OS's 6 | try: 7 | from . import SwarmRemBg 8 | NODE_CLASS_MAPPINGS.update(SwarmRemBg.NODE_CLASS_MAPPINGS) 9 | except ImportError: 10 | print("Error: [Swarm] RemBg not available") 11 | traceback.print_exc() 12 | # This uses FFMPEG which doesn't install itself properly on Macs I guess? 13 | try: 14 | from . import SwarmSaveAnimationWS 15 | NODE_CLASS_MAPPINGS.update(SwarmSaveAnimationWS.NODE_CLASS_MAPPINGS) 16 | except ImportError: 17 | print("Error: [Swarm] SaveAnimationWS not available") 18 | traceback.print_exc() 19 | # Yolo uses Ultralytics, which is cursed 20 | try: 21 | from . import SwarmYolo 22 | NODE_CLASS_MAPPINGS.update(SwarmYolo.NODE_CLASS_MAPPINGS) 23 | except ImportError: 24 | print("Error: [Swarm] Yolo not available") 25 | traceback.print_exc() 26 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ExtraNodes/SwarmComfyExtra/requirements.txt: -------------------------------------------------------------------------------- 1 | rembg 2 | dill 3 | ultralytics 4 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ComfyUIBackend/ThemeCSS/modern_dark.css: -------------------------------------------------------------------------------- 1 | /** Example file: put custom ComfyUI css here. */ 2 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/DynamicThresholding/README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Thresholding 2 | 3 | SwarmUI extension that implements support for [Dynamic Thresholding](https://github.com/mcmonkeyprojects/sd-dynamic-thresholding), built in by default to Swarm. 4 | 5 | Dynamic Thresholding is a technique that allows for generating at very large CFG Scale values without the image burning. 6 | 7 | Functions as a set of optional parameters only visible when you check `Display Advanced Options`. 8 | 9 | This extension also has parameters for related features such as CFG Scale Scheduling. 10 | 11 | As a bonus, this extension also serves as a dedicated example extension, for SwarmUI extension developers to see how to write simple Comfy Node based extensions. 12 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/DynamicThresholding/assets/dyn_thresh.js: -------------------------------------------------------------------------------- 1 | postParamBuildSteps.push(() => { 2 | let dynThreshGroup = document.getElementById('input_group_content_dynamicthresholding'); 3 | if (dynThreshGroup && !currentBackendFeatureSet.includes('dynamic_thresholding')) { 4 | dynThreshGroup.append(createDiv(`dynamic_thresholding_install_button`, 'keep_group_visible', ``)); 5 | } 6 | }); 7 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/GridGenerator/Assets/grid_gen.css: -------------------------------------------------------------------------------- 1 | .grid-gen-axis-wrapper { 2 | width: 100%; 3 | margin-bottom: 0.5rem; 4 | } 5 | .grid-gen-run-button { 6 | background-color: var(--button-background); 7 | color: var(--button-text); 8 | border: 1px solid var(--button-border); 9 | font-size: 200%; 10 | font-weight: bold; 11 | padding: 1rem; 12 | border-radius: 1rem; 13 | } 14 | .grid-gen-run-button:hover { 15 | background-color: var(--button-background-hover); 16 | color: var(--button-foreground-hover); 17 | } 18 | .grid-gen-axis-area { 19 | border-radius: 1rem; 20 | margin-top: 0.5rem; 21 | width: 99%; 22 | margin-left: 0.5%; 23 | padding: 0.5rem; 24 | border: 1px solid var(--light-border); 25 | } 26 | .grid-gen-axis-input { 27 | width: calc(max(60%, 25rem)); 28 | vertical-align: top; 29 | display: inline-block; 30 | border: 1px solid var(--light-border); 31 | background-color: var(--background-soft); 32 | white-space: pre-wrap; 33 | } 34 | .grid-gen-axis-input-value { 35 | display: inline-block; 36 | border: 1px solid gray; 37 | background-color: rgba(127, 127, 127, 0.25); 38 | border-radius: 0.5rem; 39 | margin-left: 0.25rem; 40 | margin-right: 0.25rem; 41 | } 42 | .grid-gen-axis-input-value-invalid { 43 | background-color: rgba(255, 0, 0, 0.25); 44 | } 45 | .grid-gen-axis-input-value-skipped { 46 | opacity: 0.5; 47 | } 48 | .grid-gen-axis-input-separator { 49 | font-weight: bold; 50 | color: #ffff55; 51 | } 52 | .grid-gen-selector { 53 | vertical-align: top; 54 | margin-right: 0.5rem; 55 | } 56 | .grid-gen-axis-fill-button { 57 | margin-left: 0.5rem; 58 | border-radius: 0.2rem; 59 | font-weight: bold; 60 | } 61 | .gridgen_warn { 62 | color: red; 63 | font-weight: bold; 64 | } 65 | .grid-gen-checkboxes { 66 | display: flex; 67 | flex-wrap: wrap; 68 | } 69 | .grid-gen-checkboxes .auto-input-flex { 70 | width: fit-content; 71 | margin-right: 1rem; 72 | } 73 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/GridGenerator/Assets/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/BuiltinExtensions/GridGenerator/Assets/placeholder.png -------------------------------------------------------------------------------- /src/BuiltinExtensions/GridGenerator/Assets/styles-user.css: -------------------------------------------------------------------------------- 1 | /* 2 | Add your own CSS rules to this file (instead of modifying styles.css) 3 | */ 4 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/GridGenerator/Assets/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | If you want to customize your stylesheet, please edit 'styles-user.css' instead of this. 3 | */ 4 | 5 | .sel_table tr td { 6 | border: 1px solid black; 7 | padding-right: 16px; 8 | padding-left: 16px; 9 | } 10 | .sel_table { 11 | min-width: 60em; 12 | width: 100%; 13 | } 14 | .secondary { 15 | background-color: #202020; 16 | } 17 | .emptytab { 18 | width: 0px; 19 | } 20 | .primary { 21 | background-color: #303030; 22 | } 23 | .axis_table_cell { 24 | width: calc(min(40em, max(10em, 100vw - 40em))); 25 | max-height: 8em; 26 | overflow: auto; 27 | } 28 | .tabval_subdiv { 29 | min-width: calc(100vw - 5em - min(40em, max(10em, 100vw - 40em))); 30 | max-height: 8em; 31 | overflow: auto; 32 | } 33 | table tr td, 34 | table tr th { 35 | vertical-align: top; 36 | border: 1px solid black; 37 | word-break: break-all; 38 | } 39 | th { 40 | position: sticky; 41 | top: -1px; 42 | background-color: #202020; 43 | background-clip: padding-box; 44 | } 45 | table tr td:first-child { 46 | position: sticky; 47 | left: -1px; 48 | } 49 | .nostickytable tr:first-child { 50 | position: static !important; 51 | } 52 | .nostickytable tr th { 53 | position: static !important; 54 | } 55 | .nostickytable tr td:first-child { 56 | position: static !important; 57 | } 58 | .popup_modal_background { 59 | font-family: monospace, monospace; 60 | background-color: rgb(50, 50, 50, 0.7); 61 | text-align: center; 62 | } 63 | .popup_modal_img { 64 | height: 100%; 65 | width: 100%; 66 | max-height: calc(min(100vw, 100vh - 8em)); 67 | max-width: 90vw; 68 | position: relative; 69 | margin: auto; 70 | object-fit: contain; 71 | } 72 | .popup_modal_undertext { 73 | background-color: #404060; 74 | overflow-wrap: break-word; 75 | } 76 | .modal_inner_div { 77 | max-width: 90vw; 78 | margin: auto; 79 | } 80 | .advanced_settings_section { 81 | max-width: calc(max(1500px, 90em)); 82 | text-align: left; 83 | margin: auto; 84 | } 85 | .timer_range { 86 | vertical-align: bottom; 87 | } 88 | .tab_hidden { 89 | display: none; 90 | } 91 | .nav-link:focus { 92 | background-color: #909050 !important; 93 | } 94 | .axis_label_td { 95 | left: 0px; 96 | background-color: #202020; 97 | background-clip: padding-box; 98 | word-break: break-all; 99 | min-width: 10em; 100 | } 101 | .superaxis_second { 102 | background-color: #303030; 103 | } 104 | .axis_selectors { 105 | text-align: right; 106 | margin: auto; 107 | width: fit-content; 108 | } 109 | .sticky_top { 110 | position: sticky; 111 | top: 0; 112 | } 113 | .accordion-button { 114 | padding: 0.5rem; 115 | } 116 | .save_image_area { 117 | text-align: center; 118 | } 119 | .save_image_area img { 120 | margin-right: 1rem; 121 | margin-left: 1rem; 122 | border: 2px solid orange; 123 | border-radius: 0.5rem; 124 | } 125 | .content_box { 126 | display: inline-block; 127 | } 128 | .top_nav_bar { 129 | position: sticky; 130 | left: 2.5vw; 131 | width: 95vw; 132 | } 133 | .image_table_box { 134 | display: inline-block; 135 | min-width: 100%; 136 | } 137 | .image_table { 138 | margin: auto; 139 | } 140 | .save_image_output { 141 | text-align: center; 142 | } 143 | .save_image_output_img { 144 | max-width: 100%; 145 | max-height: 70vh; 146 | } 147 | .advanced-checkbox { 148 | display: inline-block; 149 | border-radius: 0.2rem; 150 | border: 1px dashed rgba(255, 255, 255, 0.25); 151 | margin: 0.1rem; 152 | } 153 | label { 154 | display: inline; 155 | } 156 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/GridGenerator/Assets/swarmui_gridgen_local.js: -------------------------------------------------------------------------------- 1 | // TODO 2 | 3 | let all_metadata = {}; 4 | 5 | function getScoreFor(img) { 6 | let meta = all_metadata[img] || {}; 7 | return ((meta['sui_image_params'] || {})['scoring'] || {})['average'] || ((meta['sui_extra_data'] || {})['scoring'] || {})['average'] || null; 8 | } 9 | 10 | function getMetadataScriptFor(slashed) { 11 | return `${slashed}.metadata.js`; 12 | } 13 | 14 | function getMetadataForImage(img) { 15 | let data = all_metadata[img.dataset.img_path]; 16 | if (!data) { 17 | return ""; 18 | } 19 | return formatMetadata(data); 20 | } 21 | 22 | function formatMetadata(metadata) { 23 | if (!metadata || !metadata['sui_image_params']) { 24 | return ''; 25 | } 26 | let result = ''; 27 | function appendObject(obj) { 28 | for (let key of Object.keys(obj)) { 29 | let val = obj[key]; 30 | if (val) { 31 | if (typeof val == 'object') { 32 | result += `${key}: `; 33 | appendObject(val); 34 | result += `, `; 35 | } 36 | else { 37 | result += `${key}: ${val}, `; 38 | } 39 | } 40 | } 41 | }; 42 | appendObject(metadata.sui_image_params); 43 | if ('sui_extra_data' in metadata) { 44 | appendObject(metadata.sui_extra_data); 45 | } 46 | return result; 47 | } 48 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/GridGenerator/README.md: -------------------------------------------------------------------------------- 1 | # Grid Generator Extension 2 | 3 | Infinite-dimensional multi-axis image grid generator tool for SwarmUI. 4 | 5 | ### Concept 6 | 7 | Operates as a "Tool" within the "Tools" UI, built into Swarm by default as an official reference Extension, lets you generate grids of images to compare prompts or parameters. 8 | 9 | - Grids are infinite dimensional, you can add as many axes as you want. 10 | - Grids display as a webpage that you can open and select axis display settings dynamically. You can save an image from the grid view. 11 | - You can opt to generate "Just Images", a "Grid Image", or a "Web Page" 12 | - "Just Images" as the name implies, just gives you images. They are stored in the normal image path. 13 | - "Grid Image" gives you one single final image at the end. This currently only works up to 3 axes (X, Y, and Y2), and will fail with more. 14 | - "Web Page" will generate a special web page with a dynamic advanced grid live-viewer, that lets you reorganize the view freely, and display up to 4 axes at a time (and easily swap to other ones). 15 | - You can save/load grid configurations at will to reuse them. 16 | - **WARNING**: The time it takes to generate a grid grows exponentially. 17 | - Lets say it takes you 1 second per image. You have one axis with 10 values... that's 10 seconds. Now you add another axis with 10 values... that's now 10\*10 = 100 seconds. Now you add a third axis with 10 values... now it takes 1000 seconds (17 minutes). Add a fourth and you're at 10k seconds (3 hours). Keep going and soon you'll be taking days, weeks, ... past a certain scale, it doesn't make sense unless you have several GPUs backing your generations to counteract the scaling. 18 | 19 | ### Tricks 20 | 21 | - When using numbered parameters, for example `Seed`, you can input `..` between numbers to automatically fill that space, for example `1, 2, .., 10` 22 | - Must have two numbers before (to identify the start and step), and one number after (to identify the end) 23 | - For example: `1, 2, .., 5` fills to `1, 2, 3, 4, 5` 24 | - For example: `1, 3, 5, 6, 6.5, .., 9, 11, 13` fills to `1, 3, 5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 11, 13` 25 | - Any parameters may have `SKIP:` in front of them (all caps!) to skip that value. 26 | - For example, `1, 2, SKIP: 3, 4` will output a grid that has all of 1,2,3,4, but only has images in 1,2,4. 27 | - This is useful particularly for when you're reusing grid pages and want to leave a placeholder, or overwrite some images but leave the rest as they were. 28 | - Want to use prompts that have a `,` in them? Just separate by `||` instead. 29 | - For example: `a cat, red || a cat, blue` will be parsed correctly as containing two text prompts. 30 | - `[Grid Gen] Prompt Replace` is pretty powerful. 31 | - If your prompt is `a photo of a cat`, you can do `cat, dog, wolf` to generate `a photo of a cat`, `a photo of a dog`, `a photo of a wolf` 32 | - But you can also do `cat=cat, cat=dog, photo=drawing` to generate `a photo of a cat`, `a photo of a dog`, `a drawing of a cat` 33 | - But *also* you can have prompt `a photo of a cat ` and then prompt replace `mylora, myotherlora, mythirdlora` 34 | - Or you can do `a photo of a cat` and then `cat, cat , dog, dog ` 35 | - You can potentially do this with any parameter or combination of parameters you want by building Presets in the Presets tab and then using `` in the prompt replacement 36 | - `[Grid Gen] Presets` is a list 37 | - You can do `mypreset || mypreset, mypreset2 || mypreset2` to compare two presets and also the combination thereof. 38 | 39 | ### History 40 | 41 | The first version of this tool was [Infinity Grid Generator for Automatic1111's Stable-Diffusion-WebUI](https://github.com/mcmonkeyprojects/sd-infinity-grid-generator-script). It has a very special place in my heart as it was used by Stability employees, which was a key factor that led to me (Alex "mcmonkey" Goodwin) getting hired by Stability, and being given the opportunity to build bigger-and-better tools like SwarmUI! 42 | 43 | That version had custom config files to enable added metadata (titles, descriptions, etc) for grid axis values - this will eventually be reimplemented to the Swarm version. 44 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ImageBatchTool/Assets/image_batcher.js: -------------------------------------------------------------------------------- 1 | 2 | class ImageBatcherClass { 3 | 4 | doGenerate() { 5 | resetBatchIfNeeded(); 6 | let batch_id = mainGenHandler.getBatchId(); 7 | let inData = { 8 | 'baseParams': getGenInput(), 9 | 'input_folder': getRequiredElementById('ext_image_batcher_inputfolder').value, 10 | 'output_folder': getRequiredElementById('ext_image_batcher_outputfolder').value, 11 | 'init_image': getRequiredElementById('ext_image_batcher_use_as_init').checked, 12 | 'revision': getRequiredElementById('ext_image_batcher_use_as_revision').checked, 13 | 'controlnet': getRequiredElementById('ext_image_batcher_use_as_controlnet').checked, 14 | 'append_filename_to_prompt': getRequiredElementById('ext_image_batcher_append_filename_to_prompt').checked, 15 | 'resMode': getRequiredElementById('ext_image_batcher_res_mode').value 16 | }; 17 | makeWSRequestT2I('ImageBatchRun', inData, data => { 18 | if (data.image) { 19 | gotImageResult(data.image, data.metadata, `${batch_id}_${data.batch_index}`); 20 | } 21 | }); 22 | } 23 | 24 | register() { 25 | let doGenWrapper = () => { 26 | setCurrentModel(() => { 27 | if (document.getElementById('current_model').value == '') { 28 | showError("Cannot run generate batch, no model selected."); 29 | return; 30 | } 31 | this.doGenerate(); 32 | }); 33 | }; 34 | this.mainDiv = registerNewTool('image_batcher', 'Image Edit Batcher', 'Run Batch', doGenWrapper); 35 | this.mainDiv.innerHTML = `The Image Batcher tool lets you run a batch of images from an arbitrary local file folder through SD and export to another folder. Use the settings below to pick which folders, and which values the images shall be fed as inputs to, then click the primary Generate button above.
IMPORTANT: make sure the parameters you're using are enabled. If you're using batched Inits, you need the Init Image parameter group enabled!
` 36 | + makeTextInput(null, 'ext_image_batcher_inputfolder', '', 'Input Folder', 'Folder path for input images.', '', 'normal', 'Folder path for input images.\nThis folder should contain a non-recursive single layer of image files (png/jpg).', false, true, true) 37 | + makeTextInput(null, 'ext_image_batcher_outputfolder', '', 'Output Folder', 'Folder path for image output.', '', 'normal', 'Folder path for image output.\nIt is highly recommended that this is an empty folder.', false, true, true) 38 | + makeCheckboxInput(null, 'ext_image_batcher_use_as_init', '', 'Use As Init', 'Whether to use the image as the Init Image parameter.', true, false, true, true) 39 | + makeCheckboxInput(null, 'ext_image_batcher_use_as_controlnet', '', 'Use As ControlNet Input', 'Whether to use the image as input to ControlNet (only applies if a ControlNet model is enabled).', true, false, true, true) 40 | + makeCheckboxInput(null, 'ext_image_batcher_use_as_revision', '', 'Use As Image Prompt', 'Whether to use the image as an Image Prompting input.', false, false, true, true) 41 | + makeCheckboxInput(null, 'ext_image_batcher_append_filename_to_prompt', '', 'Append Filename to Prompt', 'Whether to append the filename to the prompt.', false, false, true, true) 42 | + `Resolution: `; 43 | toolSelector.addEventListener('change', () => { 44 | if (toolSelector.value == 'image_batcher') { 45 | showRevisionInputs(); 46 | } 47 | else { 48 | autoRevealRevision(); 49 | } 50 | }); 51 | revisionRevealerSources.push(() => { 52 | return toolSelector.value == 'image_batcher'; 53 | }); 54 | } 55 | } 56 | 57 | let extensionImageBatcher = new ImageBatcherClass(); 58 | sessionReadyCallbacks.push(() => { 59 | extensionImageBatcher.register(); 60 | }); 61 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/ImageBatchTool/README.md: -------------------------------------------------------------------------------- 1 | # Image Batch Editor Tool Extension 2 | 3 | Operates as a "Tool" within the "Tools" UI, lets you edit batches of input images directly and quickly. 4 | 5 | (TODO: More info) 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/Scorers/README.md: -------------------------------------------------------------------------------- 1 | # Scorers Extension 2 | 3 | Adds image-scoring tools (PickScore, Aesthetic scores). 4 | 5 | These by default get stored in the image metadata, but can then be further processed from there (for example, the Grid Generator tool can display heatmaps of the scores) 6 | 7 | Currently, this uses the local current default GPU. TODO: This should have a backend registration system just like T2I does (and/or get integrated into the self-contained tooling). 8 | 9 | ## Installation 10 | 11 | TODO: This needs to automate itself way. 12 | 13 | For now: 14 | 15 | - You must have python3 installed 16 | - open the `src/BuiltinExtensions/Scorers` folder 17 | - Recommend that you create a venv 18 | - `python3 -m venv venv` 19 | - `source venv/bin/activate` for Linux, or `venv\Scripts\activate` for Windows 20 | - `pip install -r requirements.txt` 21 | - After that it should just work from the UI. 22 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/Scorers/christoph_aesthetic.py: -------------------------------------------------------------------------------- 1 | ### Based on https://github.com/christophschuhmann/improved-aesthetic-predictor/blob/main/simple_inference.py 2 | ### Under Apache license 2.0 3 | 4 | import torch 5 | import pytorch_lightning as pl 6 | import torch.nn as nn 7 | import clip 8 | import numpy as np 9 | 10 | class MLP(pl.LightningModule): 11 | def __init__(self, input_size, xcol='emb', ycol='avg_rating'): 12 | super().__init__() 13 | self.input_size = input_size 14 | self.xcol = xcol 15 | self.ycol = ycol 16 | self.layers = nn.Sequential( 17 | nn.Linear(self.input_size, 1024), 18 | nn.Dropout(0.2), 19 | nn.Linear(1024, 128), 20 | nn.Dropout(0.2), 21 | nn.Linear(128, 64), 22 | nn.Dropout(0.1), 23 | nn.Linear(64, 16), 24 | nn.Linear(16, 1) 25 | ) 26 | 27 | def forward(self, x): 28 | return self.layers(x) 29 | 30 | def normalized(a, axis=-1, order=2): 31 | l2 = np.atleast_1d(np.linalg.norm(a, order, axis)) 32 | l2[l2 == 0] = 1 33 | return a / np.expand_dims(l2, axis) 34 | 35 | class AestheticPredictor(): 36 | model = None 37 | model2 = None 38 | 39 | def to(self, dev): 40 | self.model.to(dev) 41 | self.model2.to(dev) 42 | 43 | def load(self, name, device): 44 | self.model = MLP(768) # CLIP embedding dim is 768 for CLIP ViT L 14 45 | s = torch.load(name) 46 | self.model.load_state_dict(s) 47 | self.model.eval().to(device) 48 | self.model2, self.preprocess = clip.load("ViT-L/14", device=device) #RN50x64 49 | self.model2.eval().to(device) 50 | 51 | def predict(self, img): 52 | image = self.preprocess(img).unsqueeze(0).to(self.model.device).to(self.model.dtype) 53 | with torch.no_grad(), torch.autocast('cuda', self.model.dtype): 54 | image_features = self.model2.encode_image(image) 55 | im_emb_arr = normalized(image_features.cpu().detach().numpy()) 56 | prediction = self.model(torch.from_numpy(im_emb_arr).to(self.model.device).type(torch.cuda.FloatTensor)) 57 | return prediction.cpu().detach().numpy() 58 | -------------------------------------------------------------------------------- /src/BuiltinExtensions/Scorers/requirements.txt: -------------------------------------------------------------------------------- 1 | torch 2 | pytorch-lightning 3 | numpy 4 | pillow 5 | transformers 6 | # CLIP is a mess, so, hack it via raw git ref 7 | https://github.com/openai/CLIP/archive/d50d76daa670286dd6cacf3bcd80b5e4823fc8e1.zip 8 | -------------------------------------------------------------------------------- /src/Core/InstallableFeatures.cs: -------------------------------------------------------------------------------- 1 | namespace SwarmUI.Core; 2 | 3 | /// Handler for registries of installable features. 4 | public static class InstallableFeatures 5 | { 6 | /// Represents a comfy based feature you can install. 7 | /// Human-readable display name for this feature. 8 | /// Internal registration identifier. Keep it short, simple, lowercase, no spaces. 9 | /// Name of the author of the relevant comfy nodes. 10 | /// Optional HTML ID for a button element that handles this installer normally. 11 | public record class ComfyInstallableFeature(string DisplayName, string ID, string URL, string Author, string Notice = null, bool SkipPipCache = false, bool AutoInstall = false); 12 | 13 | /// Mapping of all known installable comfy based features. 14 | public static Dictionary ComfyFeatures = []; 15 | 16 | /// Register a new installable comfy based features. 17 | public static void RegisterInstallableFeature(ComfyInstallableFeature feature) 18 | { 19 | if (string.IsNullOrWhiteSpace(feature.Notice)) 20 | { 21 | feature = feature with { Notice = $"This will install {feature.URL} which is a third-party extension maintained by community developer '{feature.Author}'.\nWe cannot make any guarantees about it.\nDo you wish to install?" }; 22 | } 23 | ComfyFeatures[feature.ID] = feature; 24 | } 25 | 26 | static InstallableFeatures() 27 | { 28 | RegisterInstallableFeature(new("IP Adapter", "ipadapter", "https://github.com/cubiq/ComfyUI_IPAdapter_plus", "cubiq")); 29 | RegisterInstallableFeature(new("ControlNet Preprocessors", "controlnet_preprocessors", "https://github.com/Fannovel16/comfyui_controlnet_aux", "Fannovel16")); 30 | RegisterInstallableFeature(new("Frame Interpolation Utilities", "frame_interpolation", "https://github.com/Fannovel16/ComfyUI-Frame-Interpolation", "Fannovel16")); 31 | RegisterInstallableFeature(new("GIMM Video Frame Interpolator", "gimm_vfi", "https://github.com/kijai/ComfyUI-GIMM-VFI", "kijai")); 32 | RegisterInstallableFeature(new("TensorRT", "comfyui_tensorrt", "https://github.com/comfyanonymous/ComfyUI_TensorRT", "comfyanonymous + NVIDIA", "This will install TensorRT support developed by Comfy and NVIDIA.\nDo you wish to install?", true)); 33 | RegisterInstallableFeature(new("Segment Anything 2", "sam2", "https://github.com/kijai/ComfyUI-segment-anything-2", "kijai")); 34 | RegisterInstallableFeature(new("Bits-n-Bytes NF4", "bnb_nf4", "https://github.com/silveroxides/ComfyUI_bnb_nf4_fp4_Loaders", "silveroxides", "This will install BnB NF4 support developed by silveroxides, comfyanonymous, and lllyasviel (AGPL License).\nDo you wish to install?")); 35 | RegisterInstallableFeature(new("GGUF", "gguf", "https://github.com/city96/ComfyUI-GGUF", "city96", "This will install GGUF support developed by city96.\nDo you wish to install?")); 36 | RegisterInstallableFeature(new("ExtraModels", "extramodels", "https://github.com/city96/ComfyUI_ExtraModels", "city96", "This will install ExtraModels support developed by city96.\nDo you wish to install?")); 37 | RegisterInstallableFeature(new("Nunchaku", "nunchaku", "https://github.com/mit-han-lab/ComfyUI-nunchaku", "mit-han-lab")); 38 | RegisterInstallableFeature(new("TeaCache", "teacache", "https://github.com/welltop-cn/ComfyUI-TeaCache", "welltop-cn")); 39 | RegisterInstallableFeature(new("Stability API", "sai_api", "https://github.com/Stability-AI/ComfyUI-SAI_API", "Stability AI", AutoInstall: true)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/DataHolders/DataHolder.cs: -------------------------------------------------------------------------------- 1 | using FreneticUtilities.FreneticExtensions; 2 | using System.Collections.Concurrent; 3 | using System.Reflection; 4 | 5 | namespace SwarmUI.DataHolders; 6 | 7 | public interface IDataHolder 8 | { 9 | [AttributeUsage(AttributeTargets.Field)] 10 | public class NetData : Attribute 11 | { 12 | public string Name; 13 | 14 | public bool Required; 15 | } 16 | 17 | public static ConcurrentDictionary Helpers = new(); 18 | 19 | public static DataHolderHelper GetHelper(Type t) 20 | { 21 | return Helpers.GetOrCreate(t, () => Activator.CreateInstance(typeof(DataHolderHelper<>).MakeGenericType(t)) as DataHolderHelper); 22 | } 23 | 24 | IDataHolder Clone(); 25 | } 26 | 27 | public class DataHolderHelper 28 | { 29 | public Type T; 30 | 31 | public record struct FieldData(FieldInfo Field, IDataHolder.NetData Data) 32 | { 33 | public readonly Type Type => Field.FieldType; 34 | public readonly string Name => Data.Name; 35 | public readonly bool Required => Data.Required; 36 | } 37 | 38 | public FieldData[] Fields; 39 | 40 | public DataHolderHelper(Type t) 41 | { 42 | T = t; 43 | Fields = [.. T.GetFields().Select(f => new FieldData(f, f.GetCustomAttribute())).Where(f => f.Data is not null)]; 44 | } 45 | } 46 | 47 | public class DataHolderHelper : DataHolderHelper where T : IDataHolder 48 | { 49 | public static readonly DataHolderHelper Instance = new(); 50 | 51 | public DataHolderHelper() : base(typeof(T)) 52 | { 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/DataHolders/NetConfigMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace SwarmUI.DataHolders; 2 | 3 | [AttributeUsage(AttributeTargets.Field)] 4 | public class SuggestionPlaceholder : Attribute 5 | { 6 | public string Text; 7 | } 8 | -------------------------------------------------------------------------------- /src/Extensions/put_extensions_here: -------------------------------------------------------------------------------- 1 | This is the extensions folder, put any extensions for Swarm you want into here. 2 | -------------------------------------------------------------------------------- /src/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | // From FrenUtil ref. 4 | [assembly: SuppressMessage("Usage", "CA2211:Non-constant fields should not be visible", Justification = "Counter-productive to quality code")] 5 | [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Endless false marking of methods whose parameters are defined by delegate/Func/Action usage")] 6 | [assembly: SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "WTF MICROSOFT???")] 7 | 8 | // Local 9 | [assembly: SuppressMessage("Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time")] 10 | [assembly: SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible")] 11 | [assembly: SuppressMessage("Performance", "CA1806:Do not ignore method results")] 12 | [assembly: SuppressMessage("Performance", "CA1860:Avoid using 'Enumerable.Any()' extension method")] 13 | [assembly: SuppressMessage("Usage", "ASP0018:Unused route parameter")] 14 | [assembly: SuppressMessage("Performance", "CA1861:Avoid constant arrays as arguments")] 15 | [assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure", Justification = "Extensions have intentional mismatch")] 16 | [assembly: SuppressMessage("Style", "IDE0019:Use pattern matching", Justification = "Often wants to change to pretty bad code")] 17 | [assembly: SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "the description of this message has no relation to the random things it marks, and it seems to just be actively wrong in what it marks")] 18 | 19 | // Special 20 | [assembly: SuppressMessage("Performance", "CA1854:Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method", Justification = "Detection is bad here, but a good warning normally", Scope = "member", Target = "~M:SwarmUI.Accounts.SessionHandler.#ctor")] 21 | -------------------------------------------------------------------------------- /src/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Concurrent; 3 | global using System.Collections.Generic; 4 | global using System.Linq; 5 | global using System.Text; 6 | global using System.Threading; 7 | global using System.Threading.Tasks; 8 | -------------------------------------------------------------------------------- /src/Pages/Error/404.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | ViewData["Title"] = "Error 404: File Not Found"; 4 | Response.StatusCode = 404; 5 | } 6 | 7 |
8 |

Error 404: File Not Found

9 |

That request doesn't seem to lead anywhere, unfortunately.

10 |
11 | -------------------------------------------------------------------------------- /src/Pages/Error/BasicAPI.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | ViewData["Title"] = "Error: Basic API Usage Error"; 4 | } 5 | 6 |
7 |

Error: Basic API Usage Error

8 |

Your access to the API is misformatted or invalid.

9 |
Please check the API Usage Docs.
10 |
11 | -------------------------------------------------------------------------------- /src/Pages/Error/Internal.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | ViewData["Title"] = "Error 500: Internal Server Error"; 4 | Response.StatusCode = 500; 5 | } 6 | 7 |
8 |

Error 500: Internal Server Error

9 |

Something went wrong. If this is your server, check the console for an error message.

10 |
11 | -------------------------------------------------------------------------------- /src/Pages/Error/NoGetAPI.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | ViewData["Title"] = "Error: Cannot 'GET' The API"; 4 | } 5 | 6 |
7 |

Error: Cannot 'GET' the API

8 |

You must use 'POST' requests to access the API.

9 |
If you're not trying to automate internal access, use the normal webpages instead.
10 |
11 | -------------------------------------------------------------------------------- /src/Pages/Error/Permissions.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | ViewData["Title"] = "Error: Permission Denied"; 4 | } 5 | 6 |
7 |

Error: Permission Denied

8 |

You do not have permission to access this API.

9 |
10 | -------------------------------------------------------------------------------- /src/Pages/Login.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @{ 3 | ViewData["Title"] = "Login"; 4 | HtmlString notice = new(Program.ServerSettings.UserAuthorization.LoginNotice); 5 | } 6 | @section Header { 7 | 8 | } 9 | @if (WebUtil.HasValidLogin(HttpContext)) 10 | { 11 | 14 | return; 15 | } 16 | 17 | 27 | 39 | 44 | 45 | @section Scripts { 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewData["Title"] - SwarmUI - @Program.ServerSettings.UserAuthorization.InstanceTitle 7 | @{ 8 | string themeId = "modern_dark"; 9 | // TODO: Identify user by auth header and use their settings to determine theme 10 | if (ViewContext.HttpContext.Request.Cookies.TryGetValue("sui_theme_id", out string themeCookie) && Program.Web.RegisteredThemes.ContainsKey(themeCookie)) { 11 | themeId = themeCookie; 12 | } 13 | WebServer.ThemeData theme = Program.Web.RegisteredThemes[themeId]; 14 | string bsUrl = theme.IsDark ? "css/bootstrap.min.css" : "css/bootstrap_light.min.css"; 15 | 16 | foreach (string path in theme.CSSPaths) 17 | { 18 | string themeUrl = $"{path}?vary={Utilities.VaryID}"; 19 | 20 | } 21 | } 22 | 23 | 24 | 25 | 26 | @RenderSection("Header", required: false) 27 | @WebServer.PageHeaderExtra 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 | @WebUtil.Toast("error_toast_box", "Error", "", "error_toast_content", "", false) 38 |
39 | @RenderBody() 40 | 41 | 42 |
SwarmUI v@Utilities.Version (@Program.CurrentGitDate)
43 | @RenderSection("Scripts", required: false) 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/Pages/_Generate/SimpleTab.cshtml: -------------------------------------------------------------------------------- 1 | @model GeneratePageModel 2 | 3 |
4 | 36 |
37 | -------------------------------------------------------------------------------- /src/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using SwarmUI.Core 2 | @using SwarmUI.Utils 3 | @using Microsoft.AspNetCore.Html 4 | @using SwarmUI.Text2Image 5 | @using SwarmUI.WebAPI 6 | @using SwarmUI.Accounts 7 | @using FreneticUtilities.FreneticExtensions 8 | @namespace SwarmUI.Pages 9 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 10 | -------------------------------------------------------------------------------- /src/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /src/SwarmUI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | 0.9.6.1 6 | CS1998 7 | true 8 | SwarmUI 9 | SwarmUI 10 | SwarmUI 11 | Alex "mcmonkey" Goodwin 12 | Copyright (c) 2024-2025 Alex "mcmonkey" Goodwin 13 | SwarmUI, an AI Image Generation User-Interface 14 | exe 15 | wwwroot/favicon.ico 16 | Major 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Text2Image/T2IModelClass.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace SwarmUI.Text2Image; 4 | 5 | /// Represents a class of models (eg SDv1). 6 | public record class T2IModelClass 7 | { 8 | /// Standard resolution for this model class. 9 | public int StandardWidth, StandardHeight; 10 | 11 | /// ID of this model type. 12 | public string ID; 13 | 14 | /// A clean name for this model class. 15 | public string Name; 16 | 17 | /// An identifier for a compatibility-class this class falls within (eg all SDv1 classes have the same compat class). 18 | public string CompatClass; 19 | 20 | /// Matcher, return true if the model x safetensors header is the given class, or false if not. 21 | public Func IsThisModelOfClass; 22 | } 23 | -------------------------------------------------------------------------------- /src/Text2Image/T2IPreset.cs: -------------------------------------------------------------------------------- 1 | using LiteDB; 2 | using Newtonsoft.Json.Linq; 3 | using SwarmUI.Utils; 4 | 5 | namespace SwarmUI.Text2Image; 6 | 7 | /// User-saved Text2Image preset. 8 | public class T2IPreset 9 | { 10 | [BsonId] 11 | public string ID { get; set; } 12 | 13 | /// The user who made this. 14 | public string Author { get; set; } 15 | 16 | /// User-written title of the preset. 17 | public string Title { get; set; } 18 | 19 | /// User-written description of the preset. 20 | public string Description { get; set; } 21 | 22 | /// Preview image URL for the preset, as a local path, usually within "Output". 23 | public string PreviewImage { get; set; } 24 | 25 | /// Mapping of parameters to values. 26 | public Dictionary ParamMap { get; set; } = []; 27 | 28 | /// Gets networkable data about this preset. 29 | public JObject NetData() 30 | { 31 | return new JObject() 32 | { 33 | ["author"] = Author, 34 | ["title"] = Title, 35 | ["description"] = Description, 36 | ["preview_image"] = PreviewImage, 37 | ["param_map"] = JObject.FromObject(ParamMap) 38 | }; 39 | } 40 | 41 | /// Automatically applies the entire preset over top of a input. 42 | public void ApplyTo(T2IParamInput user_input) 43 | { 44 | foreach ((string key, string val) in ParamMap) 45 | { 46 | if (T2IParamTypes.TryGetType(key, out _, user_input)) 47 | { 48 | T2IParamTypes.ApplyParameter(key, val, user_input); 49 | } 50 | else 51 | { 52 | Logs.Warning($"Invalid preset parameter: {key}, for preset '{ID}' created by '{Author}'"); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Utils/AutoCompleteListHelper.cs: -------------------------------------------------------------------------------- 1 | using FreneticUtilities.FreneticExtensions; 2 | using SwarmUI.Core; 3 | using System.IO; 4 | 5 | namespace SwarmUI.Utils; 6 | 7 | /// Helper for custom word autocomplete lists. 8 | public class AutoCompleteListHelper 9 | { 10 | /// Set of all filenames of auto complete files. 11 | public static HashSet FileNames = []; 12 | 13 | /// Map between filenames and actual wordlists. 14 | public static ConcurrentDictionary AutoCompletionLists = new(); 15 | 16 | /// Gets the correct folder path to use. 17 | public static string FolderPath; 18 | 19 | /// Initializes the helper. 20 | public static void Init() 21 | { 22 | Reload(); 23 | Program.ModelRefreshEvent += Reload; 24 | } 25 | 26 | /// Reloads the list of files. 27 | public static void Reload() 28 | { 29 | try 30 | { 31 | FolderPath = $"{Program.DataDir}/Autocompletions"; 32 | HashSet files = []; 33 | Directory.CreateDirectory(FolderPath); 34 | foreach (string file in Directory.GetFiles(FolderPath, "*", SearchOption.AllDirectories)) 35 | { 36 | if (file.EndsWith(".txt") || file.EndsWith(".csv")) 37 | { 38 | string path = Path.GetRelativePath(FolderPath, file).Replace("\\", "/").TrimStart('/'); 39 | files.Add(path); 40 | } 41 | } 42 | FileNames = files; 43 | AutoCompletionLists.Clear(); 44 | } 45 | catch (Exception ex) 46 | { 47 | Logs.Error($"Error while refreshing autocomplete lists: {ex.ReadableString()}"); 48 | } 49 | } 50 | 51 | /// Gets a specific data list. 52 | public static string[] GetData(string name, bool escapeParens, string suffix, string spaceMode) 53 | { 54 | if (!FileNames.Contains(name)) 55 | { 56 | return null; 57 | } 58 | string[] result = AutoCompletionLists.GetOrCreate(name, () => 59 | { 60 | return [.. File.ReadAllText($"{FolderPath}/{name}").Replace('\r', '\n').SplitFast('\n').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s) && !s.StartsWithFast('#'))]; 61 | }); 62 | bool doSpace = spaceMode == "Spaces"; 63 | bool doUnderscore = spaceMode == "Underscores"; 64 | result = [.. result]; 65 | for (int i = 0; i < result.Length; i++) 66 | { 67 | string[] parts = Utilities.SplitStandardCsv(result[i]); 68 | if (parts.Length == 2 && long.TryParse(parts[1], out _)) 69 | { 70 | parts = [parts[0], "0", parts[1], ""]; 71 | } 72 | string word = parts[0]; 73 | if (doSpace) 74 | { 75 | word = word.Replace("_", " "); 76 | } 77 | else if (doUnderscore) 78 | { 79 | word = word.Replace(" ", "_"); 80 | } 81 | word += suffix; 82 | if (escapeParens) 83 | { 84 | word = word.Replace("(", "\\(").Replace(")", "\\)"); 85 | } 86 | result[i] = $"{word}\n{parts.JoinString("\n")}"; 87 | } 88 | return result; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Utils/GeneratePageModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using SwarmUI.Accounts; 3 | using SwarmUI.Core; 4 | 5 | namespace SwarmUI.Utils; 6 | 7 | public class GeneratePageModel 8 | { 9 | public HttpContext HttpContext; 10 | 11 | public string AlwaysRefreshOnLoad => Program.ServerSettings.Backends.AlwaysRefreshOnLoad ? "true" : "false"; 12 | 13 | public string ExperimentalHide => Program.ServerSettings.ShowExperimentalFeatures ? "" : "secretexperimental"; 14 | 15 | public string InstanceTitle => Program.ServerSettings.UserAuthorization.InstanceTitle; 16 | 17 | public bool IsLoginEnabled => Program.ServerSettings.UserAuthorization.AuthorizationRequired; 18 | 19 | public User User; 20 | 21 | public GeneratePageModel(HttpContext context) 22 | { 23 | HttpContext = context; 24 | User = IsLoginEnabled ? WebUtil.GetValidLogin(HttpContext) : null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Utils/MemCleaner.cs: -------------------------------------------------------------------------------- 1 | using SwarmUI.Core; 2 | using SwarmUI.WebAPI; 3 | 4 | namespace SwarmUI.Utils; 5 | 6 | /// Simple utility class that keeps memory cleared up automatically over time based on user settings. 7 | public static class MemCleaner 8 | { 9 | public static long TimeSinceLastGen = 0; 10 | 11 | public static bool HasClearedVRAM = false, HasClearedSysRAM = false; 12 | 13 | public static void TickIsGenerating() 14 | { 15 | TimeSinceLastGen = 0; 16 | HasClearedVRAM = false; 17 | HasClearedSysRAM = false; 18 | } 19 | 20 | public static void TickNoGenerations() 21 | { 22 | if (TimeSinceLastGen == 0) 23 | { 24 | TimeSinceLastGen = Environment.TickCount64; 25 | } 26 | else if (Environment.TickCount64 - TimeSinceLastGen > Program.ServerSettings.Backends.ClearVRAMAfterMinutes * 60 * 1000 && !HasClearedVRAM) 27 | { 28 | BackendAPI.FreeBackendMemory(null, false).Wait(); 29 | HasClearedVRAM = true; 30 | } 31 | else if (Environment.TickCount64 - TimeSinceLastGen > Program.ServerSettings.Backends.ClearSystemRAMAfterMinutes * 60 * 1000 && !HasClearedSysRAM) 32 | { 33 | BackendAPI.FreeBackendMemory(null, false).Wait(); 34 | HasClearedSysRAM = true; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Utils/MemoryNum.cs: -------------------------------------------------------------------------------- 1 | namespace SwarmUI.Utils; 2 | 3 | /// Mini-struct to hold data about a memory (bytes) size number. 4 | public struct MemoryNum(long inBytes) 5 | { 6 | public long InBytes = inBytes; 7 | 8 | public readonly float KiB => InBytes / 1024f; 9 | 10 | public readonly float MiB => (float)(InBytes / (1024.0 * 1024)); 11 | 12 | public readonly float GiB => (float)(InBytes / (1024.0 * 1024 * 1024)); 13 | 14 | public readonly float TiB => GiB / 1024f; 15 | 16 | public override readonly string ToString() 17 | { 18 | if (InBytes > 1024L * 1024 * 1024 * 1024) 19 | { 20 | return $"{TiB:0.00} TiB"; 21 | } 22 | else if (InBytes > 1024L * 1024 * 1024) 23 | { 24 | return $"{GiB:0.00} GiB"; 25 | } 26 | else if (InBytes > 1024L * 1024) 27 | { 28 | return $"{MiB:0.00} MiB"; 29 | } 30 | else if (InBytes > 1024L) 31 | { 32 | return $"{KiB:0.00} KiB"; 33 | } 34 | else 35 | { 36 | return $"{InBytes} B"; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Utils/SwarmExceptions.cs: -------------------------------------------------------------------------------- 1 | namespace SwarmUI.Utils; 2 | 3 | /// Represents an error inside swarm with a plaintext English message to display. 4 | public class SwarmReadableErrorException(string message) : InvalidOperationException(message) 5 | { 6 | } 7 | 8 | /// Represents an error caused by the user, with a plaintext English message to display. 9 | public class SwarmUserErrorException(string message) : SwarmReadableErrorException(message) 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /src/Utils/SystemStatusMonitor.cs: -------------------------------------------------------------------------------- 1 | using Hardware.Info; 2 | using System.Diagnostics; 3 | 4 | namespace SwarmUI.Utils; 5 | 6 | /// Utility to monitor system usage. 7 | public static class SystemStatusMonitor 8 | { 9 | /// The current process. 10 | public static Process SelfProc = Process.GetCurrentProcess(); 11 | 12 | /// Last estimated process usage. 13 | public static double ProcessCPUUsage = 0; 14 | 15 | /// Tracker for CPU processor usage 16 | public static long LastProcessorTime = 0; 17 | 18 | /// Last time this monitor was ticked. 19 | public static long LastTick = Environment.TickCount64; 20 | 21 | /// General hardware info provider. 22 | public static HardwareInfo HardwareInfo = new(); 23 | 24 | /// Semaphore to prevent the monitor tick firing off overlapping. 25 | public static SemaphoreSlim DeDuplicator = new(1, 1); 26 | 27 | /// How many recent hardware info reports to store in the . 28 | public static int QueueSize = 10; 29 | 30 | /// Holds recent hardware info reports for the last seconds. 31 | public static ConcurrentQueue HardwareInfoQueue = new(); 32 | 33 | /// Updates system status. 34 | public static void Tick() 35 | { 36 | Task.Run(() => 37 | { 38 | if (DeDuplicator.CurrentCount == 0) 39 | { 40 | return; 41 | } 42 | DeDuplicator.Wait(); 43 | try 44 | { 45 | long newProcessorTime = SelfProc.TotalProcessorTime.Milliseconds; 46 | long newTick = Environment.TickCount64; 47 | ProcessCPUUsage = Math.Max(0, (newProcessorTime - LastProcessorTime) / (double)(newTick - LastTick)); 48 | LastProcessorTime = newProcessorTime; 49 | LastTick = newTick; 50 | HardwareInfo newInfo = new(); 51 | newInfo.RefreshMemoryStatus(); 52 | HardwareInfo = newInfo; 53 | HardwareInfoQueue.Enqueue(newInfo); 54 | if (HardwareInfoQueue.Count > QueueSize) 55 | { 56 | HardwareInfoQueue.TryDequeue(out _); 57 | } 58 | } 59 | finally 60 | { 61 | DeDuplicator.Release(); 62 | } 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Utils/UserSoundHelper.cs: -------------------------------------------------------------------------------- 1 | using SwarmUI.Core; 2 | using System.IO; 3 | 4 | namespace SwarmUI.Utils; 5 | 6 | /// Helper for playable sound effects (eg sound after gen complete). 7 | public class UserSoundHelper 8 | { 9 | /// Set of all filenames of audio files. 10 | public static HashSet Filenames = []; 11 | 12 | /// Gets the correct folder path to use. 13 | public static string FolderPath; 14 | 15 | /// Initializes the helper. 16 | public static void Init() 17 | { 18 | Reload(); 19 | Program.ModelRefreshEvent += Reload; 20 | } 21 | 22 | /// Reloads the list of files. 23 | public static void Reload() 24 | { 25 | try 26 | { 27 | FolderPath = $"{Program.DataDir}/Audio"; 28 | HashSet files = []; 29 | Directory.CreateDirectory(FolderPath); 30 | string[] supportedExtensions = [".wav", ".wave", ".mp3", ".aac", ".ogg", ".flac"]; 31 | foreach (string file in Directory.GetFiles(FolderPath, "*", SearchOption.AllDirectories)) 32 | { 33 | if (supportedExtensions.Any(file.EndsWith)) 34 | { 35 | string path = Path.GetRelativePath(FolderPath, file).Replace('\\', '/').TrimStart('/'); 36 | files.Add(path); 37 | } 38 | } 39 | Filenames = files; 40 | } 41 | catch (Exception ex) 42 | { 43 | Logs.Error($"Error while refreshing audio lists: {ex.ReadableString()}"); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/WebAPI/APICall.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Newtonsoft.Json.Linq; 3 | using SwarmUI.Accounts; 4 | using System.Net.WebSockets; 5 | using System.Reflection; 6 | 7 | namespace SwarmUI.WebAPI; 8 | 9 | 10 | /// Represents an API Call route and associated core data (permissions, etc). 11 | /// The name, ie the call path, in full. 12 | /// The original method that this call was generated from. 13 | /// Actual call function: an async function that takes the HttpContext and the JSON input, and returns JSON output. 14 | /// Whether this call is for websockets. If false, normal HTTP API. 15 | /// If true, this is considered a 'user update' behavior of some form. Use false for basic getters or automated actions. 16 | /// Optional required permission for this API call. 17 | public record class APICall(string Name, MethodInfo Original, Func> Call, bool IsWebSocket, bool IsUserUpdate, PermInfo Permission) 18 | { 19 | } 20 | -------------------------------------------------------------------------------- /src/WebAPI/UserUpstreamApiKeys.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Html; 2 | 3 | namespace SwarmUI.WebAPI; 4 | 5 | /// 6 | /// Registry class for API keys for upstream APIs, for which the keys are tracked per-user. 7 | /// 8 | public static class UserUpstreamApiKeys 9 | { 10 | /// Info about a valid API Key type. 11 | /// Short ID for data storage, eg 'stability_api'. Keep it very simple. 12 | /// JavaScript input prefix, eg 'stability'. Keep it very simple. 13 | /// Clear simple user-readable title, eg 'Stability AI'. 14 | /// Link for where to create the API key, or to docs about it. 15 | /// Info about the key. Specify what it's for, when the user would want to use it, etc. 16 | public record class ApiKeyInfo(string KeyType, string JSPrefix, string Title, string CreateLink, HtmlString InfoHtml); 17 | 18 | /// The actual registry. 19 | public static ConcurrentDictionary KeysByType = []; 20 | 21 | /// Register an API key. 22 | public static void Register(ApiKeyInfo keyInfo) 23 | { 24 | if (!KeysByType.TryAdd(keyInfo.KeyType, keyInfo)) 25 | { 26 | throw new ArgumentException($"Key with type '{keyInfo.KeyType}' already registered."); 27 | } 28 | } 29 | 30 | static UserUpstreamApiKeys() 31 | { 32 | Register(new("stability_api", "stability", "Stability AI", "https://platform.stability.ai/account/keys", new("To use the Stability AI API in SwarmUI (via the comfy nodes or simple tab), you must set your key."))); 33 | Register(new("civitai_api", "civitai", "Civitai", "https://civitai.com/user/account", new("If you plan to use the Model Downloader utility to download content from Civitai, you will want to set your Civitai API key below.\n
This will allow you to download gated or early-access content that your Civitai account has access to."))); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/jsconfig.json: -------------------------------------------------------------------------------- 1 | /* This file is here to allow enabling checks manually when mucking with JS. In the future this will be enabled properly, just not til the codebase is cleaned up more for it. */ 2 | { 3 | "compilerOptions": { 4 | "checkJs": false, 5 | "allowJs": true, 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "target": "esnext" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/srcdata/Tokensets/clip.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/srcdata/Tokensets/clip.txt.gz -------------------------------------------------------------------------------- /src/wwwroot/css/loginpage.css: -------------------------------------------------------------------------------- 1 | 2 | .login-container { 3 | width: calc(min(90vw, 800px)); 4 | margin: 0 auto; 5 | padding: 20px; 6 | border-radius: 10px; 7 | margin-top: 40px; 8 | margin-bottom: 40px; 9 | background-color: var(--background-soft); 10 | border: 1px solid var(--light-border); 11 | color: var(--text); 12 | } 13 | 14 | .login-header-container { 15 | font-size: 120%; 16 | line-height: 1.5; 17 | } 18 | 19 | .big-header { 20 | font-size: 200%; 21 | font-weight: bold; 22 | text-align: center; 23 | } 24 | 25 | .small-header { 26 | font-size: 80%; 27 | text-align: center; 28 | font-style: italic; 29 | color: var(--text-soft); 30 | } 31 | 32 | .login-form-container { 33 | line-height: 2; 34 | } 35 | 36 | .login-input-block { 37 | display: flex; 38 | align-items: baseline; 39 | margin-top: 5px; 40 | } 41 | 42 | .login-message-block { 43 | white-space: pre-wrap; 44 | } 45 | 46 | .login-error-block { 47 | color: color-mix(in srgb, var(--red) 30%, var(--text)); 48 | } 49 | 50 | .login-footer-container { 51 | color: var(--text-soft); 52 | line-height: 1.3; 53 | font-size: 90%; 54 | } 55 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/beweish.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* Core colors */ 3 | --background: #0e0e2c; 4 | --background-soft: #16213e; 5 | --background-gray: #0f3460; 6 | --background-gray-danger: #361f1f; 7 | --background-danger: #200b0b; 8 | --shadow: #2a4170; 9 | --light-border: #2a4170; 10 | 11 | /* Text colors */ 12 | --text: #ffffff; 13 | --text-soft: #e0e0e0; 14 | --popup-front: #ffffff; 15 | 16 | /* UI Elements */ 17 | --emphasis: #e94560; 18 | --emphasis-soft: #ff647f; 19 | --emphasis-text: #ffffff; 20 | --border-color: #2a4170; 21 | 22 | /* Buttons */ 23 | --button-text: #ffffff; 24 | --button-background: #0f3460; 25 | --button-background-hover: #16213e; 26 | --button-foreground-hover: #e0e0e0; 27 | --button-foreground-disabled: #666666; 28 | --button-background-disabled: #0f3460; 29 | --button-border: #2a4170; 30 | 31 | /* Danger buttons */ 32 | --danger-button-background-hover: color-mix(in srgb, #550000 70%, black); 33 | --danger-button-foreground-hover: #aaaaaa; 34 | --danger-button-border: #3f3f46; 35 | --danger-button-foreground: #ffffff; 36 | --danger-button-background: #550000; 37 | 38 | /* Panels and backgrounds */ 39 | --background-panel: #16213e; 40 | --background-panel-subtle: rgba(233, 69, 96, 0.05); 41 | 42 | /* Input elements */ 43 | --range-track-color: #16213e; 44 | --range-thumb-color: #e94560; 45 | 46 | /* Other UI elements */ 47 | --qbutton: var(--emphasis); 48 | --batch-0: rgba(233, 69, 96, 0.3); 49 | --batch-1: rgba(255, 100, 127, 0.3); 50 | --popup-back: var(--background); 51 | 52 | /* Selection colors */ 53 | --box-selected-border: rgba(233, 69, 96, 0.8); 54 | --box-selected-background: rgba(233, 69, 96, 0.2); 55 | --box-selected-border-stronger: rgba(233, 69, 96, 1.0); 56 | --box-selected-background-stronger: rgba(233, 69, 96, 0.3); 57 | 58 | /* Standard colors */ 59 | --red: #f44336; 60 | --green: #4caf50; 61 | 62 | /* Shadow variables */ 63 | --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3); 64 | --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4); 65 | --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.5); 66 | } 67 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/cyber_swarm.css: -------------------------------------------------------------------------------- 1 | .input-group .basic-button { 2 | border-radius: 1rem; 3 | padding-top: 0.1em; 4 | padding-left: 0.3em; 5 | padding-right: 0.3em; 6 | } 7 | .nav-tabs .nav-link { 8 | border-radius: 0px; 9 | } 10 | .nav-tabs { 11 | --bs-nav-tabs-link-active-bg: var(--emphasis); 12 | --bs-nav-tabs-link-active-border-color: var(--emphasis); 13 | --bs-nav-tabs-link-hover-border-color: var(--emphasis); 14 | --bs-nav-tabs-border-color: var(--shadow); 15 | } 16 | .comfy_workflow_buttons * { 17 | border-radius: 0px !important; 18 | } 19 | .comfy_workflow_buttons_actual { 20 | left: 0.5rem !important; 21 | top: 0.5rem !important; 22 | } 23 | :root { 24 | --background: #18181b; 25 | --background-soft: #27272a; 26 | --background-gray: #27272a; 27 | --background-gray-danger: #361f1f; 28 | --background-danger: #200b0b; 29 | --shadow: #3f3f46; 30 | --emphasis: #00bc8c; 31 | --light-border: #3f3f46; 32 | --red: #ff0000; 33 | --green: #00ff00; 34 | --qbutton: var(--emphasis); 35 | --popup-back: #000000; 36 | --popup-front: #ffffff; 37 | --button-background-hover: color-mix(in srgb, var(--emphasis) 70%, black); 38 | --button-foreground-hover: #aaaaaa; 39 | --button-border: #3f3f46; 40 | --button-text: #ffffff; 41 | --button-background: var(--emphasis); 42 | --danger-button-background-hover: color-mix(in srgb, #550000 70%, black); 43 | --danger-button-foreground-hover: #aaaaaa; 44 | --danger-button-border: #3f3f46; 45 | --danger-button-foreground: #ffffff; 46 | --danger-button-background: #550000; 47 | --button-background-disabled: #3f3f46; 48 | --button-foreground-disabled: #aaaaaa; 49 | --batch-0: rgba(200, 200, 200, 0.3); 50 | --batch-1: rgba(200, 200, 200, 0.3); 51 | --text: #eeeeee; 52 | --text-soft: #aaaaaa; 53 | --box-selected-border: rgba(120, 85, 225, 0.8); 54 | --box-selected-background: rgba(120, 85, 225, 0.2); 55 | --box-selected-border-stronger: rgba(120, 85, 225, 1.0); 56 | --box-selected-background-stronger: rgba(120, 85, 225, 0.3); 57 | } 58 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/dark_dreams.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background: #18181b; 3 | --background-soft: #27272a; 4 | --background-gray: #222226; 5 | --background-gray-danger: #361f1f; 6 | --background-danger: #200b0b; 7 | --shadow: #3f3f46; 8 | --emphasis: #7855e1; 9 | --light-border: #3f3f46; 10 | --red: #ff0000; 11 | --green: #00ff00; 12 | --qbutton: #7855e1; 13 | --popup-back: #000000; 14 | --popup-front: #ffffff; 15 | --button-background-hover: color-mix(in srgb, #7855e1 70%, black); 16 | --button-foreground-hover: #aaaaaa; 17 | --button-border: #3f3f46; 18 | --button-text: #ffffff; 19 | --button-background: #7855e1; 20 | --danger-button-background-hover: color-mix(in srgb, #550000 70%, black); 21 | --danger-button-foreground-hover: #aaaaaa; 22 | --danger-button-border: #3f3f46; 23 | --danger-button-foreground: #ffffff; 24 | --danger-button-background: #550000; 25 | --button-background-disabled: #3f3f46; 26 | --button-foreground-disabled: #aaaaaa; 27 | --batch-0: rgba(200, 200, 200, 0.3); 28 | --batch-1: rgba(200, 200, 200, 0.3); 29 | --text: #eeeeee; 30 | --text-soft: #aaaaaa; 31 | --box-selected-border: rgba(120, 85, 225, 0.8); 32 | --box-selected-background: rgba(120, 85, 225, 0.2); 33 | --box-selected-border-stronger: rgba(120, 85, 225, 1.0); 34 | --box-selected-background-stronger: rgba(120, 85, 225, 0.3); 35 | } 36 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/eyesear_white.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background: #f0f0f0; 3 | --background-soft: #c0c0c0; 4 | --background-gray: #d0d0d0; 5 | --background-gray-danger: #e08585; 6 | --background-danger: #200b0b; 7 | --shadow: #cbcbfd; 8 | --emphasis: #c7966b; 9 | --light-border: #728787; 10 | --red: #ff0000; 11 | --green: #00ff00; 12 | --qbutton: #8b6445; 13 | --popup-back: #aaaaaa; 14 | --popup-front: #101010; 15 | --button-background-hover: color-mix(in srgb, orange 70%, #a1a1a1); 16 | --button-foreground-hover: #101010; 17 | --button-border: #3f3f46; 18 | --button-text: #101010; 19 | --button-background: #a1a1a1; 20 | --danger-button-background-hover: color-mix(in srgb, #550000 70%, #a1a1a1); 21 | --danger-button-foreground-hover: #aaaaaa; 22 | --danger-button-border: #3f3f46; 23 | --danger-button-foreground: #ffffff; 24 | --danger-button-background: #550000; 25 | --button-background-disabled: #3f3f46; 26 | --button-foreground-disabled: #101010; 27 | --batch-0: rgba(200, 200, 200, 0.3); 28 | --batch-1: rgba(200, 200, 200, 0.3); 29 | --text: #101010; 30 | --text-soft: #303030; 31 | --box-selected-border: rgba(225, 162, 85, 0.8); 32 | --box-selected-background: rgba(255, 162, 85, 0.2); 33 | --box-selected-border-stronger: rgba(255, 162, 85, 1.0); 34 | --box-selected-background-stronger: rgba(255, 162, 85, 0.3); 35 | } 36 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/gravity_blue.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background: #0b0f20; 3 | --background-soft: #203040; 4 | --background-gray: #1f2336; 5 | --background-gray-danger: #361f1f; 6 | --background-danger: #200b0b; 7 | --shadow: #404080; 8 | --emphasis: #c7966b; 9 | --light-border: #728787; 10 | --red: #ff0000; 11 | --green: #00ff00; 12 | --qbutton: #8b6445; 13 | --popup-back: #000000; 14 | --popup-front: #ffffff; 15 | --button-background-hover: color-mix(in srgb, orange 70%, black); 16 | --button-foreground-hover: #aaaaaa; 17 | --button-border: #3f3f46; 18 | --button-text: #101010; 19 | --button-background: orange; 20 | --danger-button-background-hover: color-mix(in srgb, #550000 70%, black); 21 | --danger-button-foreground-hover: #aaaaaa; 22 | --danger-button-border: #3f3f46; 23 | --danger-button-foreground: #ffffff; 24 | --danger-button-background: #550000; 25 | --button-background-disabled: #3f3f46; 26 | --button-foreground-disabled: #aaaaaa; 27 | --batch-0: rgba(200, 200, 200, 0.3); 28 | --batch-1: rgba(200, 200, 200, 0.3); 29 | --text: #eeeeee; 30 | --text-soft: #aaaaaa; 31 | --box-selected-border: rgba(225, 162, 85, 0.8); 32 | --box-selected-background: rgba(255, 162, 85, 0.2); 33 | --box-selected-border-stronger: rgba(255, 162, 85, 1.0); 34 | --box-selected-background-stronger: rgba(255, 162, 85, 0.3); 35 | } 36 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/modern_dark.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background-soft: #27272a; 3 | --background-gray: #222226; 4 | --background-gray-danger: #361f1f; 5 | --shadow: #3f3f46; 6 | --light-border: #3f3f46; 7 | --popup-front: #ffffff; 8 | --button-foreground-disabled: #aaaaaa; 9 | --text-soft: #aaa; 10 | --background: #161616; 11 | --text: #E4E4E4; 12 | --emphasis: #7855e1; 13 | --emphasis-soft: #665694; 14 | --button-text: #fff; 15 | --background-panel: #202020; 16 | --background-panel-subtle: rgba(255, 255, 255, 0.03); 17 | --emphasis-text: var(--button-text); 18 | --border-color: #4C4C4C; 19 | --button-background: #4b4b4b; 20 | --range-track-color: #1d1d1d; 21 | --range-thumb-color: #4b4b4b; 22 | } 23 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/modern_light.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background-soft: #c0c0c0; 3 | --background-gray: #d0d0d0; 4 | --background-gray-danger: #e08585; 5 | --shadow: #cbcbfd; 6 | --light-border: #728787; 7 | --popup-front: hsl(0, 0%, 6%); 8 | --button-foreground-disabled: #fff; 9 | --text-soft: #525252; 10 | --background: #aaa; 11 | --text: #101010; 12 | --emphasis: #c7966b; 13 | --emphasis-soft: #c7966b; 14 | --button-text: #101010; 15 | --background-panel: #D9D9D9; 16 | --background-panel-subtle: rgba(0, 0, 0, 0.03); 17 | --emphasis-text: #fff; 18 | --border-color: #aaa; 19 | --button-background: #a1a1a1; 20 | --range-track-color: #B1B1B1; 21 | --range-thumb-color: #a1a1a1; 22 | --qbutton: #8b6445; 23 | } 24 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/solarized.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background-soft: #D9D2C3; 3 | --background-gray: #FDF6E4; 4 | --background-gray-danger: #e08585; 5 | --shadow: #EEE8D6; 6 | --light-border: #A4A4A4; 7 | --popup-front: hsl(0, 0%, 6%); 8 | --button-foreground-disabled: #fff; 9 | --text-soft: #525252; 10 | --background: #d3cdc0; 11 | --text: #101010; 12 | --emphasis: #c76bbb; 13 | --emphasis-soft: #c76bbb; 14 | --button-text: #101010; 15 | --background-panel: #ccc6b9; 16 | --background-panel-subtle: rgba(0, 0, 0, 0.03); 17 | --emphasis-text: #fff; 18 | --border-color: #e4dece; 19 | --button-background: #a1a1a1; 20 | --range-track-color: #B1B1B1; 21 | --range-thumb-color: #a1a1a1; 22 | --qbutton: #8b6445; 23 | } 24 | body { 25 | scrollbar-color: #a1a1a1 #dbd4c3; 26 | } 27 | -------------------------------------------------------------------------------- /src/wwwroot/css/themes/swarmpunk.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* Core colors */ 3 | --background: #0a0a1f; 4 | --background-soft: #0a0a1f; 5 | --background-gray: #1a1a3a; 6 | --background-gray-danger: #361f1f; 7 | --background-danger: #200b0b; 8 | --shadow: #00ff9d40; 9 | --light-border: #00ff9d40; 10 | 11 | /* Text colors */ 12 | --text: #00ff9d; 13 | --text-soft: #00ccff; 14 | --popup-front: #00ff9d; 15 | 16 | /* UI Elements */ 17 | --emphasis: #ff00ff; 18 | --emphasis-soft: #cc00cc; 19 | --emphasis-text: #00ff9d; 20 | --border-color: #00ff9d40; 21 | 22 | /* Buttons */ 23 | --button-text: #00ff9d; 24 | --button-background: #12122a; 25 | --button-background-hover: #1a1a3a; 26 | --button-foreground-hover: #00ccff; 27 | --button-foreground-disabled: #005544; 28 | --button-background-disabled: #0a0a1f; 29 | --button-border: #00ff9d40; 30 | 31 | /* Danger buttons */ 32 | --danger-button-background-hover: color-mix(in srgb, #550000 70%, black); 33 | --danger-button-foreground-hover: #aaaaaa; 34 | --danger-button-border: #3f3f46; 35 | --danger-button-foreground: #ffffff; 36 | --danger-button-background: #550000; 37 | 38 | /* Panels and backgrounds */ 39 | --background-panel: #12122a; 40 | --background-panel-subtle: rgba(0, 255, 157, 0.05); 41 | 42 | /* Input elements */ 43 | --range-track-color: #12122a; 44 | --range-thumb-color: #ff00ff; 45 | 46 | /* Other UI elements */ 47 | --qbutton: var(--emphasis); 48 | --batch-0: rgba(0, 255, 157, 0.3); 49 | --batch-1: rgba(0, 204, 255, 0.3); 50 | --popup-back: var(--background); 51 | 52 | /* Selection colors */ 53 | --box-selected-border: rgba(255, 0, 255, 0.8); 54 | --box-selected-background: rgba(255, 0, 255, 0.2); 55 | --box-selected-border-stronger: rgba(255, 0, 255, 1.0); 56 | --box-selected-background-stronger: rgba(255, 0, 255, 0.3); 57 | 58 | /* Standard colors */ 59 | --red: #ff0000; 60 | --green: #00ff00; 61 | } 62 | -------------------------------------------------------------------------------- /src/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/wwwroot/fonts/Inter.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/fonts/Inter.woff2 -------------------------------------------------------------------------------- /src/wwwroot/fonts/MaterialSymbolsOutlined.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/fonts/MaterialSymbolsOutlined.woff2 -------------------------------------------------------------------------------- /src/wwwroot/fonts/unifont-12.0.01.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/fonts/unifont-12.0.01.woff2 -------------------------------------------------------------------------------- /src/wwwroot/imgs/automatic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/automatic.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/canvas_move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/canvas_move.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/canvas_rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/canvas_rotate.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/dotdotdot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/dotdotdot.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/eraser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/eraser.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/ar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/ar.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/de.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/de.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/en.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/en.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/es.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/es.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/fr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/fr.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/hi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/hi.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/it.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/it.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/ja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/ja.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/nl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/nl.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/pt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/pt.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/ru.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/ru.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/sv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/sv.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/vi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/vi.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/flags/zh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/flags/zh.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/folder.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/html.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/html.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/icon-attrib.txt: -------------------------------------------------------------------------------- 1 | The icon files: 2 | - eraser 3 | - mouse 4 | - paintbrush 5 | - paintbucket 6 | 7 | were created by Cristian Munoz and released under CC-BY-4.0 at https://www.figma.com/community/file/1311159026125960259/7000-free-ui-icons 8 | -------------------------------------------------------------------------------- /src/wwwroot/imgs/legacy_ckpt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/legacy_ckpt.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/model_placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/model_placeholder.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/mouse.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/move.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/noise.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/none.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/none.jpg -------------------------------------------------------------------------------- /src/wwwroot/imgs/paintbrush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/paintbrush.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/paintbucket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/paintbucket.png -------------------------------------------------------------------------------- /src/wwwroot/imgs/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcmonkeyprojects/SwarmUI/57dde7ba11bb6b61191cf2b098117091ab9a1f80/src/wwwroot/imgs/select.png -------------------------------------------------------------------------------- /src/wwwroot/js/loginpage.js: -------------------------------------------------------------------------------- 1 | class LoginHandler { 2 | constructor() { 3 | this.usernameInput = document.getElementById('username_input'); 4 | this.passwordInput = document.getElementById('password_input'); 5 | this.loginButton = document.getElementById('login_button'); 6 | this.loginErrorBlock = document.getElementById('login_error_block'); 7 | this.errorNoUsername = translatable("Please enter a valid username."); 8 | this.errorNoPassword = translatable("Please enter a valid password."); 9 | this.errorLoginFailedUnknown = translatable("Login failed (reason unknown), please check your inputs and try again.\nIf this issue persists, please contact the instance owner."); 10 | this.errorLoginFailedGeneral = translatable("Login failed (incorrect username or password), please check your inputs and try again."); 11 | this.errorLoginFailedRatelimit = translatable("Login failed (ratelimit reached), please wait a minute before trying again."); 12 | this.messageLoggingIn = translatable("Logging in, please wait..."); 13 | this.messageLoginSuccess = translatable("Login successful! Redirecting..."); 14 | } 15 | 16 | showError(message) { 17 | this.loginErrorBlock.classList.add('login-error-block'); 18 | this.loginErrorBlock.textContent = message; 19 | } 20 | 21 | showMessage(message) { 22 | this.loginErrorBlock.classList.remove('login-error-block'); 23 | this.loginErrorBlock.textContent = message; 24 | } 25 | 26 | async doLogin() { 27 | let username = this.usernameInput.value; 28 | let password = this.passwordInput.value; 29 | if (username.length < 3) { 30 | this.showError(this.errorNoUsername.get()); 31 | return; 32 | } 33 | if (password.length < 8) { 34 | this.showError(this.errorNoPassword.get()); 35 | return; 36 | } 37 | this.showMessage(this.messageLoggingIn.get()); 38 | let inData = { 39 | username: username, 40 | password: await doPasswordClientPrehash(username, password) 41 | }; 42 | this.loginButton.disabled = true; 43 | sendJsonToServer(`API/Login`, inData, (status, data) => { 44 | data ??= {}; 45 | if (data.success) { 46 | this.showMessage(this.messageLoginSuccess.get()); 47 | setTimeout(() => { 48 | this.loginErrorBlock.innerHTML = `(Click here if you haven't already been redirected)`; 49 | window.location.href = './'; 50 | }, 1000); 51 | return; 52 | } 53 | this.loginButton.disabled = false; 54 | if (data.error_id == 'invalid_login') { 55 | this.showError(this.errorLoginFailedGeneral.get()); 56 | } 57 | else if (data.error_id == 'ratelimit') { 58 | this.showError(this.errorLoginFailedRatelimit.get()); 59 | } 60 | else { 61 | this.showError(this.errorLoginFailedUnknown.get()); 62 | } 63 | }); 64 | } 65 | } 66 | 67 | let loginHandler = new LoginHandler(); 68 | -------------------------------------------------------------------------------- /src/wwwroot/js/permissions.js: -------------------------------------------------------------------------------- 1 | /** Helper class for tracking the user's permissions. */ 2 | class Permissions { 3 | constructor() { 4 | this.permissions = {}; 5 | this.permissionedDivs = []; 6 | this.hasLoaded = false; 7 | setTimeout(() => { 8 | this.gather(); 9 | }, 0); 10 | } 11 | 12 | gather() { 13 | this.permissionedDivs = [...document.querySelectorAll('[data-requiredpermission]')]; 14 | this.apply(); 15 | } 16 | 17 | updateFrom(set) { 18 | this.permissions = {}; 19 | for (let key of set) { 20 | this.permissions[key] = true; 21 | } 22 | this.hasLoaded = true; 23 | this.apply(); 24 | } 25 | 26 | apply() { 27 | if (!this.hasLoaded) { 28 | return; 29 | } 30 | for (let div of this.permissionedDivs) { 31 | let key = div.dataset.requiredpermission; 32 | if (!this.hasPermission(key)) { 33 | div.style.display = 'none'; 34 | } 35 | else { 36 | div.style.display = ''; 37 | } 38 | } 39 | } 40 | 41 | hasPermission(key) { 42 | if (!this.hasLoaded) { 43 | return true; 44 | } 45 | if (key.includes(',')) { 46 | for (let k of key.split(',')) { 47 | if (this.hasPermission(k)) { 48 | return true; 49 | } 50 | } 51 | return false; 52 | } 53 | return this.permissions[key] || this.permissions['*']; 54 | } 55 | } 56 | 57 | permissions = new Permissions(); 58 | --------------------------------------------------------------------------------