-
-
Notifications
You must be signed in to change notification settings - Fork 66
/
enfugue.sh
executable file
·438 lines (418 loc) · 19.2 KB
/
enfugue.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
#!/usr/bin/env bash
#
# This shell script serves as a one-and-done download, update, configure and run command for enfugue.
# It's able to download enfuge via conda or in portable form.
#
# Below is the configuration for enfugue. Most values are self-explanatory, but some others need some explanation.
# See below the configuration for explanation on these values.
#
# AN IMPORTANT NOTE ABOUT NETWORKING
#
# Enfugue runs two servers, one with SSL that only listens to `https://app.enfugue.ai:45554`, and a second without
# SSL that does not require a domain name be configured.
# `app.enfugue.ai` is a registered domain name that resolves to `127.0.0.1`, the loopback domain.
# If you're running enfugue on a local machine with internet access, you should be able to access the secure endpoint this way.
# If you're running enfugue on a remote machine and want to enable SSL, you will want to change the following:
# - `server.domain` should be changed to the domain you want to access the interface through _unless_ you're using a proxy. This can be an IP address.
# - `server.secure` should be set to false _unless_ you also configure an SSL certificate. See the commented-out (#) lines in the configuration below.
# - If you're additionally using a proxy like ngrok, you should configure `server.cms.path.root` to be the proxied URL. See the commented-out (#) lines below.
# - Remember, the above only applies if you want to enable SSL. If you don't, leave secure as `false` and `domain` as null, and enfugue will set the
# domain in responses as the same as the request came in on.
cat << EOF > $PWD/config.yml
---
sandboxed: false # true = disable system management in UI
server:
host: 0.0.0.0 # listens on any connection
port: [45554, 45555] # ports < 1024 require sudo
domain: [app.enfugue.ai, null] # this is a loopback domain, null = match request
secure: [true, false] # enables SSL for first server, off for second
# If you change the domain, you must provide your own certificates or disable SSL
# key: /path/to/key.pem
# cert: /path/to/cert.pem
# chain: /path/to/chain.pem
logging:
file: ~/.cache/enfugue.log # server logs (NOT diffusion logs)
level: error # server only logs errors
# cms: # only configure when using a proxy
# path:
# root: http(s)://my-proxy-host.ngrok-or-other/
enfugue:
noauth: true # authentication default
queue: 4 # queue size default
safe: true # safety checker default
model: v1-5-pruned.ckpt # default model (repo or file)
inpainter: null # default inpainter (repo or file), null = sd15 base inpainter
refiner: null # default refiner (repo or file), null = none
refiner_start: 0.85 # default start for refining when refiner set and no args (0.0-1.0)
dtype: null # set to float32 to disable half-precision, you probably dont want to
engine:
logging:
file: ~/.cache/enfugue-engine.log # diffusion logs (shown in UI)
level: debug # logs everything, helpful for debugging
root: ~/.cache/enfugue # root engine directory, images save in /images
cache: ~/.cache/enfugue/cache # diffusers cache, controlnets, VAE
checkpoint: ~/.cache/enfugue/checkpoint # checkpoints only
lora: ~/.cache/enfugue/lora # lora only
lycoris: ~/.cache/enfugue/lycoris # lycoris only
inversion: ~/.cache/enfugue/inversion # textual inversion only
motion: ~/.cache/enfugue/motion # motion modules only
other: ~/.cache/enfugue/other # other AI models (upscalers, preprocessors, etc.)
pipeline:
switch: "offload" # See comment above
inpainter: true # See comment above
cache: null # See comment above
sequential: false # See comment above
EOF
trap "rm $PWD/config.yml" EXIT
# -----------------------
# enfugue.pipeline.switch
# -----------------------
# 'switch' determines how to swap between pipelines when required, like going from inpainting to non-inpainting or loading a refiner.
# The default behavior, 'offload,' sends unneeded pipelines to the CPU and promotes active pipelines to the GPU when requested.
# This usually provides the best balance of speed and memory usage, but can result in heavy overhead on some systems.
#
# If this proves too much, or you wish to minimize memory usage, set this to 'unload,'which will always completely unload a pipeline
# and free memory before a different pipeline is used.
#
# If you set this to 'null,' _all models will remain in memory_. This is by far thefastest but consumes the most memory, this is only
# suitable for enterprise GPUs.
#
# --------------------------
# enfugue.pipeline.inpainter
# --------------------------
# 'inpainter' determines how to inpaint when no inpainter is specified.
#
# When the value is 'true', and the user is using a stable diffusion 1.5 model for their base model, enfugue will look for another
# checkpoint with the same name but the suffix `-inpainting`, and when one is not found, it will create one using the model merger.
# Fine-tuned inpainting checkpoints perform significantly better at the task, however they are roughly equivalent in size to the
# main model, effectively doubling storage required.
#
# When the value is 'null' or 'false', Enfugue will still search for a fine-tuned inpainting model, but will not create one if it does not exist.
# Instead, enfugue will use 4-dim inpainting, which in 1.5 is less effective.
# SDXL does not have a fine-tuned inpainting model (yet,) so this procedure does not apply, and 4-dim inpainting is always used.
#
# ----------------------
# enfugue.pipeline.cache
# ----------------------
# 'cache' determines when to create diffusers caches. A diffusers cache will always load faster than a checkpoint, but is once again
# approximately the same size as the checkpoint, so this will also effectively double storage size.
#
# When the value is 'null' or 'false', diffusers caches will _only_ be made for TensorRT pipelines, as it is required. This is the default value.
#
# When the value is 'xl', enfugue will cache XL checkpoints. These load _significantly_ faster than when loading from file, between
# 2 and 3 times as quickly. You may wish to consider using this setting to speed up changing between XL checkpoints.
#
# When the value is 'true', diffusers caches will be created for all pipelines. This is not recommended as it only provides marginal
# speed advantages for 1.5 models.
#
# ---------------------------
# enfugue.pipeline.sequential
# ---------------------------
# 'sequential' enables sequential onloading and offloading of AI models.
#
# When the value is 'true', AI models will only ever be loaded to the GPU when they are needed.
# At all other times, they will be in normal memory, waiting for the next time they are requested, at which time they will be loaded
# to the GPU, and afterward unloaded.
#
# These operations take time, so this is only recommended to enable if you are experiencing issues with out-of-memory errors.
#
# -- end of configuration --
# -- start functions --
# This function prints out the help message.
usage() {
echo "USAGE: $0 [OPTIONS]"
echo "Options:"
echo " --help Display this help message."
echo " --conda / --portable Automatically set installation type (do not prompt.)"
echo " --update / --no-update Automatically apply or skip updates (do not prompt.)"
echo " --mmpose / --no-mmpose Automatically install or skip installing MMPose (do not prompt.)"
}
# This function compares version strings
# Adapted from https://stackoverflow.com/questions/4023830/how-to-compare-two-strings-in-dot-separated-version-format-in-bash
compare_versions () {
if [[ $1 == $2 ]]
then
echo "0"
fi
local IFS=.
local i ver1=($1) ver2=($2)
# fill empty fields in ver1 with zeros
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
do
ver1[i]=0
done
for ((i=0; i<${#ver1[@]}; i++))
do
if [[ -z ${ver2[i]} ]]
then
# fill empty fields in ver2 with zeros
ver2[i]=0
fi
if ((10#${ver1[i]} > 10#${ver2[i]}))
then
echo "1"
return
fi
if ((10#${ver1[i]} < 10#${ver2[i]}))
then
echo "-1"
return
fi
done
echo "0"
}
# This function compares versions and prompts you to download when relevant.
# Pass --no-update when executing this script to never check versions.
# Pass --update when executing this script to automatically update when available.
compare_prompt_update() {
COMPARE=$(compare_versions $1 $2)
if [ "$COMPARE" == "-1" ]; then
if [[ "$INSTALL_UPDATE" == "1" ]]; then
echo "1"
return
else
# A new version of enfugue is available
read -p "Version $2 of enfugue is available, you have version $1 installed. Download update? [Yes]: " DOWNLOAD_LATEST
DOWNLOAD_LATEST=${DOWNLOAD_LATEST:-Yes}
DOWNLOAD_LATEST=${DOWNLOAD_LATEST:0:1}
if [[ "${DOWNLOAD_LATEST,,}" == "t" || "${DOWNLOAD_LATEST,,}" == "y" || "${DOWNLOAD_LATEST,,}" == "1" ]]; then
echo "1"
return
fi
fi
fi
echo "0"
}
# This function downloads and extracts the latest portable version when relevant.
download_portable() {
RELEASE_PACKAGES=$(curl -s https://api.github.com/repos/painebenjamin/app.enfugue.ai/releases/latest | grep browser_download_url | grep manylinux | cut -d : -f 2,3 | tr -d ' ,\"')
mkdir -p $PORTABLE_DIR
while IFS= read -r TARBALL; do
FILENAME=$(basename $TARBALL)
curl -L $TARBALL -o $FILENAME
done <<< "$RELEASE_PACKAGES"
DOWNLOADED=$(echo "$RELEASE_PACKAGES" | sed 's|.*/||')
cat $DOWNLOADED | tar -xvz --directory $PORTABLE_DIR --strip-components=2 1>&2
rm $DOWNLOADED
}
# -- end functions --
# -- start script --
# Declare default options, then iterate through command line arguments and set variables.
INSTALL_TYPE=""
INSTALL_MMPOSE=""
INSTALL_UPDATE=""
while [ "$#" -gt 0 ]; do
case $1 in
--conda)
INSTALL_TYPE="conda"
;;
--portable)
INSTALL_TYPE="portable"
;;
--mmpose)
INSTALL_MMPOSE="1"
;;
--no-mmpose)
INSTALL_MMPOSE="0"
;;
--update)
INSTALL_UPDATE="1"
;;
--no-update)
INSTALL_UPDATE="0"
;;
--help)
usage
exit 0
;;
*)
echo "Invalid option: $1" >&2
usage
exit 1
;;
esac
shift
done
# Set portable directory and paths
PORTABLE_DIR="$PWD/enfugue-server"
export PATH=$PATH:$PWD:$PORTABLE_DIR:$HOME/miniconda3/bin
# Gather some variables from the current environment.
CONDA=$CONDA_EXE
# Make sure conda can be executed.
if [ ! -x $CONDA ]; then
CONDA="$(which conda)"
if [ ! -x $CONDA ]; then
CONDA=""
fi
fi
# Check if we can simply activate an existing conda environment.
if [[ "$CONDA" != "" ]]; then
if conda env list | grep -q enfugue; then
echo "Found enfugue environment, activating."
source $(dirname $CONDA)/activate enfugue
ENFUGUE=$(which enfugue)
fi
fi
ENFUGUE=$(which enfugue)
ENFUGUE_SERVER=$(which enfugue-server)
# Change variables if forcing portable/conda
if [ "$INSTALL_TYPE" == "conda" ]; then
ENFUGUE_SERVER=""
elif [ "$INSTALL_TYPE" == "portable" ]; then
ENFUGUE=""
fi
# These will be populated later if relevant.
ENFUGUE_INSTALLED_PIP_VERSION=""
ENFUGUE_AVAILABLE_PIP_VERSION=""
ENFUGUE_INSTALLED_PORTABLE_VERSION=""
ENFUGUE_AVAILABLE_PORTABLE_VERSION=""
# Get the current python executable
PYTHON=$(which python3)
# Check if either of the above tactics found enfugue. If so, and it's not disabled, check for updates.
if [ "$ENFUGUE" != "" ]; then
if [[ "$PYTHON" != "" && "$INSTALL_UPDATE" != "0" ]]; then
# Get installed version from pip
ENFUGUE_INSTALLED_PIP_VERSION=$($PYTHON -m pip freeze | grep enfugue | awk -F= '{print $3}' | sed -e 's/\.post/\./g')
fi
if [ "$ENFUGUE_INSTALLED_PIP_VERSION" != "" ]; then
# Enfugue was installed in this current environment and update allowed, get available versions from pip
ENFUGUE_AVAILABLE_PIP_VERSION=$($PYTHON -m pip install enfugue== 2>&1 | grep 'from versions' | awk '{n=split($0,v,/, /); print v[n]}')
ENFUGUE_AVAILABLE_PIP_VERSION=$(echo ${ENFUGUE_AVAILABLE_PIP_VERSION::-1} | sed -e 's/\.post/\./g')
if [ "$INSTALL_UPDATE" == "" ]; then
INSTALL_UPDATE=$(compare_prompt_update $ENFUGUE_INSTALLED_PIP_VERSION $ENFUGUE_AVAILABLE_PIP_VERSION)
fi
if [ "$INSTALL_UPDATE" == "1" ]; then
echo "Downloading update."
pip install --upgrade enfugue
fi
fi
elif [ "$ENFUGUE_SERVER" != "" ]; then
# Portable found
ENFUGUE_PORTABLE_DIR=$(dirname $(realpath $ENFUGUE_SERVER))
if [ "$INSTALL_UPDATE" != "0" ]; then
# Get versions
ENFUGUE_AVAILABLE_PORTABLE_VERSION=$(curl -s https://api.github.com/repos/painebenjamin/app.enfugue.ai/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d ' ,\"')
ENFUGUE_INSTALLED_PORTABLE_VERSION=$(cat $ENFUGUE_PORTABLE_DIR/enfugue/version.txt)
# Compare versions and prompt if necessary
if [ "$INSTALL_UPDATE" == "" ]; then
INSTALL_UPDATE=$(compare_prompt_update $ENFUGUE_INSTALLED_PORTABLE_VERSION $ENFUGUE_AVAILABLE_PORTABLE_VERSION)
fi
if [ "$INSTALL_UPDATE" == "1" ]; then
echo "Downloading update."
download_portable $(dirname $ENFUGUE_PORTABLE_DIR)
fi
fi
fi
# Check if we've found enfugue by now. If not, prompt what to do.
if [[ "$ENFUGUE" == "" && "$ENFUGUE_SERVER" == "" ]]; then
echo "Enfugue is not currently installed."
# Prompt how to install enfugue.
DOWNLOAD_TYPE=""
if [ "$INSTALL_TYPE" == "conda" ]; then
DOWNLOAD_TYPE="1"
elif [ "$INSTALL_TYPE" == "portable" ]; then
DOWNLOAD_TYPE="2"
fi
while [ "$DOWNLOAD_TYPE" == "" ]; do
echo "How would you like to install it?"
echo "1) Anaconda/Miniconda (Recommended)"
echo "2) Portable Installation"
read -p "Please make a selection: [1] " DOWNLOAD_TYPE
if [ "$DOWNLOAD_TYPE" == "" ]; then
DOWNLOAD_TYPE="1"
else
DOWNLOAD_TYPE=${DOWNLOAD_TYPE:0:1}
fi
if [[ "$DOWNLOAD_TYPE" != "1" && "$DOWNLOAD_TYPE" != "2" ]]; then
echo "'$DOWNLOAD_TYPE' is not a valid response."
DOWNLOAD_TYPE=""
fi
done
if [ "$DOWNLOAD_TYPE" == "1" ]; then
# Check if conda is already installed.
if [ "$CONDA" == "" ]; then
# Run the miniconda installer
MINICONDA_INSTALL_DIR="${HOME}/miniconda3"
mkdir -p $MINICONDA_INSTALL_DIR
curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o ${MINICONDA_INSTALL_DIR}/miniconda.sh --silent
bash $MINICONDA_INSTALL_DIR/miniconda.sh -b -u -p $MINICONDA_INSTALL_DIR
# Remove the miniconda installer
rm -rf $MINICONDA_INSTALL_DIR/miniconda.sh
CONDA=$MINICONDA_INSTALL_DIR/bin/conda
fi
echo "Creating enfugue environment. This can take up to 15 minutes depending on download speeds, please be patient."
# Download the latest environment file from the github
curl https://raw.githubusercontent.com/painebenjamin/app.enfugue.ai/main/environments/linux-cuda.yml -o $PWD/enfugue-environment.yml --silent
# Use conda to create the environment
$CONDA env create -f $PWD/enfugue-environment.yml
# Remove the environment file
rm $PWD/enfugue-environment.yml
# Activate the environment
source $(dirname $CONDA)/activate enfugue
ENFUGUE=$(which enfugue)
PYTHON=$(which python)
elif [ "$DOWNLOAD_TYPE" == "2" ]; then
# Download and extract the latest portable
download_portable
ENFUGUE_SERVER="$PORTABLE_DIR/enfugue-server"
fi
fi
# Now enfugue is installed, check if we can install MMPose
if [[ "$ENFUGUE" != "" && "$PYTHON" != "" && "$INSTALL_TYPE" != "portable" ]]; then
MMPOSE_INSTALLED=$($PYTHON -m pip freeze | grep mmpose)
if [[ "$MMPOSE_INSTALLED" == "" && "$INSTALL_MMPOSE" != "0" ]]; then
if [ "$INSTALL_MMPOSE" == "" ]; then
read -p "MMPose not installed. Install it? [Yes]: " INSTALL_MMPOSE
INSTALL_MMPOSE=${INSTALL_MMPOSE:-Yes}
INSTALL_MMPOSE=${INSTALL_MMPOSE:0:1}
if [[ "${INSTALL_MMPOSE,,}" == "y" || "${INSTALL_MMPOSE,,}" == "t" || "${INSTALL_MMPOSE,,}" == "1" ]]; then
INSTALL_MMPOSE="1"
else
INSTALL_MMPOSE="0"
fi
fi
if [ "$INSTALL_MMPOSE" == "1" ]; then
$PYTHON -m mim install mmengine
$PYTHON -m mim install "mmcv>=2.0.1"
$PYTHON -m mim install "mmdet>=3.1.0"
$PYTHON -m mim install "mmpose>=1.1.0"
fi
fi
fi
# Now we should have enfugue, run it.
if [[ "$ENFUGUE" != "" && "$INSTALL_TYPE" != "portable" ]]; then
# Run enfugue via python module script
$PYTHON -m enfugue run -c $PWD/config.yml -m
elif [[ "$ENFUGUE_SERVER" != "" && "$INSTALL_TYPE" != "conda" ]]; then
# Run enfugue via portable executable
PORTABLE_DIR=$(dirname $(realpath $ENFUGUE_SERVER))
# Check if this is the first launch (it will take longer)
LAUNCH_FILE=${PORTABLE_DIR}/.launched
if [ ! -f $LAUNCH_FILE ]; then
echo "First launch detected, it may take a minute or so for the server to start.";
echo "A window will be opened when the server is ready to respond to requests.";
touch $LAUNCH_FILE
fi
# Set path variables so binaries can be found
export PATH=$PORTABLE_DIR/torch/lib:$PORTABLE_DIR/tensorrt:$PATH
export LD_LIBRARY_PATH=$PORTABLE_DIR/torch/lib:$PORTABLE_DIR/tensorrt:$LD_LIBRARY_PATH
# Allow lazy CUDA loading
export CUDA_MODULA_LOADING=LAZY
# Disable unsafe legacy crytography suites (MacOS requires this)
export CRYPTOGRAPHY_OPENSSL_NO_LEGACY=1
# Enable MPS fallback (MacOS)
export PYTORCH_ENABLE_MPS_FALLBACK=1
# Allow enfugue to spawn unlimited processes
# This is essential for enfugue to operate - enfugue uses process isolation,
# so it will very frequently create and destroy processes. Without this, enfugue
# will enfugue not be able to create any more processes and it will start failing.
ulimit -n unlimited 2>/dev/null > /dev/null || true
# Echo and go
echo "Starting Enfugue server. Press Ctrl+C to exit."
ENFUGUE_CONFIG=$PWD/config.yml $PORTABLE_DIR/enfugue-server
echo "Goodbye!"
else
echo "Could not find or download enfugue. Exiting."
exit 1
fi
exit 0