2023-01-08 08:09:04 +00:00
"""
Installer user interaction
"""
import platform
from pathlib import Path
2023-01-10 21:55:57 +00:00
from prompt_toolkit import prompt
from prompt_toolkit . completion import PathCompleter
from prompt_toolkit . shortcuts import CompleteStyle
2023-01-13 06:57:06 +00:00
from prompt_toolkit . validation import Validator
2023-01-08 08:09:04 +00:00
from rich import box , print
2023-01-13 06:57:06 +00:00
from rich . console import Console , Group
2023-01-08 08:09:04 +00:00
from rich . panel import Panel
from rich . prompt import Confirm
from rich . style import Style
2023-01-14 16:24:18 +00:00
from rich . syntax import Syntax
2023-01-17 05:47:36 +00:00
from rich . text import Text
2023-01-08 08:09:04 +00:00
2023-01-16 07:38:50 +00:00
"""
INVOKE_AI_SRC = https : / / github . com / invoke - ai / InvokeAI / archive / refs / tags / $ { INVOKEAI_VERSION } . zip
INSTRUCTIONS = https : / / invoke - ai . github . io / InvokeAI / installation / INSTALL_AUTOMATED /
TROUBLESHOOTING = https : / / invoke - ai . github . io / InvokeAI / installation / INSTALL_AUTOMATED / #troubleshooting
"""
2023-01-08 08:09:04 +00:00
OS = platform . uname ( ) . system
ARCH = platform . uname ( ) . machine
2023-01-14 16:24:18 +00:00
if OS == " Windows " :
# Windows terminals look better without a background colour
console = Console ( style = Style ( color = " grey74 " ) )
else :
console = Console ( style = Style ( color = " grey74 " , bgcolor = " grey19 " ) )
2023-01-08 08:09:04 +00:00
def welcome ( ) :
console . rule ( )
print (
Panel (
2023-01-09 05:13:01 +00:00
title = " [bold wheat1]Welcome to the InvokeAI Installer " ,
2023-01-08 08:09:04 +00:00
renderable = Text (
" Some of the installation steps take a long time to run. Please be patient. If the script appears to hang for more than 10 minutes, please interrupt with control-C and retry. " ,
justify = " center " ,
) ,
box = box . DOUBLE ,
width = 80 ,
expand = False ,
padding = ( 1 , 2 ) ,
style = Style ( bgcolor = " grey23 " , color = " orange1 " ) ,
2023-01-09 05:13:01 +00:00
subtitle = f " [bold grey39] { OS } - { ARCH } " ,
2023-01-08 08:09:04 +00:00
)
)
console . line ( )
def dest_path ( init_path = None ) - > Path :
"""
Prompt the user for the destination path and create the path
: param init_path : a filesystem path , defaults to None
: type init_path : str , optional
: return : absolute path to the created installation directory
: rtype : Path
"""
2023-01-10 21:55:57 +00:00
# TODO: feels like this could be refactored for clarity (@ebr)
2023-01-08 08:09:04 +00:00
dest = init_path
2023-01-10 21:55:57 +00:00
if dest is not None :
dest = Path ( dest ) . expanduser ( ) . resolve ( )
2023-01-08 08:09:04 +00:00
dest_confirmed = False
while not dest_confirmed :
console . line ( )
2023-01-10 21:55:57 +00:00
2023-01-14 16:24:18 +00:00
old_dest = dest
2023-01-10 21:55:57 +00:00
print ( f " InvokeAI will be installed at { dest } " )
dest_confirmed = Confirm . ask ( f " Is this correct? " , default = " y " )
2023-01-14 16:24:18 +00:00
2023-01-08 08:09:04 +00:00
if not dest_confirmed :
2023-01-10 21:55:57 +00:00
# needs more thought into how to handle this nicely
# so that the first selected destination continues to shows up as
# default until the user is done selecting (potentially multiple times)
path_completer = PathCompleter (
only_directories = True ,
expanduser = True ,
get_paths = lambda : [ Path ( dest ) . parent ] ,
file_filter = lambda n : not n . startswith ( " . " ) ,
)
print ( f " Please select the destination directory for the installation \ [ { dest } ]: " )
selected = prompt (
2023-01-16 07:38:50 +00:00
" [Tab] to complete ❯ " ,
2023-01-10 21:55:57 +00:00
complete_in_thread = True ,
completer = path_completer ,
complete_style = CompleteStyle . READLINE_LIKE ,
)
if Path ( selected ) . is_absolute ( ) :
# use the absolute path directly
dest = Path ( selected )
else :
# the user entered a relative path - offer to create it as a sibling to the original destination
dest = dest . parent / Path ( selected )
dest = dest . expanduser ( ) . resolve ( )
2023-01-08 08:09:04 +00:00
if dest . exists ( ) :
2023-01-13 09:10:34 +00:00
console . line ( )
2023-01-08 08:09:04 +00:00
print ( f " :exclamation: Directory { dest } already exists. " )
2023-01-13 06:57:06 +00:00
console . line ( )
2023-01-08 08:09:04 +00:00
dest_confirmed = Confirm . ask (
" :question: Are you sure you want to (re)install in this location? " , default = " y "
)
2023-01-10 21:55:57 +00:00
if not dest_confirmed :
dest = old_dest
2023-01-08 08:09:04 +00:00
try :
dest . mkdir ( exist_ok = True , parents = True )
return dest
except PermissionError as exc :
print (
f " Failed to create directory { dest } due to insufficient permissions " ,
style = Style ( color = " red " ) ,
highlight = True ,
)
except OSError as exc :
console . print_exception ( exc )
if Confirm . ask ( " Would you like to try again? " ) :
dest_path ( init_path )
else :
console . rule ( " Goodbye! " )
2023-01-13 06:57:06 +00:00
def graphical_accelerator ( ) :
"""
Prompt the user to select the graphical accelerator in their system
This does not validate user ' s choices (yet), but only offers choices
valid for the platform .
CUDA is the fallback .
We may be able to detect the GPU driver by shelling out to ` modprobe ` or ` lspci ` ,
but this is not yet supported or reliable . Also , some users may have exotic preferences .
"""
if ARCH == " arm64 " and OS != " Darwin " :
print ( f " Only CPU acceleration is available on { ARCH } architecture. Proceeding with that. " )
return " cpu "
2023-01-13 09:10:34 +00:00
nvidia = ( " an [gold1 b]NVIDIA[/] GPU (using CUDA™) " , " cuda " , )
amd = ( " an [gold1 b]AMD[/] GPU (using ROCm™) " , " rocm " , )
2023-01-13 06:57:06 +00:00
cpu = ( " no compatible GPU, or specifically prefer to use the CPU " , " cpu " , )
idk = ( " I ' m not sure what to choose " , " idk " , )
if OS == " Windows " :
options = [ nvidia , cpu ]
if OS == " Linux " :
options = [ nvidia , amd , cpu ]
elif OS == " Darwin " :
options = [ cpu ]
# future CoreML?
if len ( options ) == 1 :
2023-01-16 07:38:50 +00:00
print ( f " Your platform [gold1] { OS } - { ARCH } [/] only supports the \" { options [ 0 ] [ 1 ] } \" driver. Proceeding with that. " )
2023-01-13 06:57:06 +00:00
return options [ 0 ] [ 1 ]
# "I don't know" is always added the last option
options . append ( idk )
options = { str ( i ) : opt for i , opt in enumerate ( options , 1 ) }
2023-01-13 09:10:34 +00:00
console . rule ( " :space_invader: GPU (Graphics Card) selection :space_invader: " )
2023-01-13 06:57:06 +00:00
console . print ( Panel (
2023-01-13 09:10:34 +00:00
Group ( " \n " . join ( [
f " Detected the [gold1] { OS } - { ARCH } [/] platform " ,
" " ,
" See [steel_blue3]https://invoke-ai.github.io/InvokeAI/#system[/] to ensure your system meets the minimum requirements. " ,
" " ,
" [red3]🠶[/] [b]Your GPU drivers must be correctly installed before using InvokeAI![/] [red3]🠴[/] " ] ) ,
2023-01-13 06:57:06 +00:00
" " ,
2023-01-13 09:10:34 +00:00
" Please select the type of GPU installed in your computer. " ,
Panel ( " \n " . join ( [ f " [spring_green3 b i] { i } [/] [dark_red]🢒[/] { opt [ 0 ] } " for ( i , opt ) in options . items ( ) ] ) , box = box . MINIMAL ) ,
2023-01-13 06:57:06 +00:00
) ,
box = box . MINIMAL ,
2023-01-13 09:10:34 +00:00
padding = ( 1 , 1 ) ,
2023-01-13 06:57:06 +00:00
)
)
choice = prompt ( " Please make your selection: " , validator = Validator . from_callable ( lambda n : n in options . keys ( ) , error_message = " Please select one the above options " ) )
2023-01-13 09:10:34 +00:00
if options [ choice ] [ 1 ] == " idk " :
console . print ( " No problem. We will try to install a version that [i]should[/i] be compatible. :crossed_fingers: " )
2023-01-13 06:57:06 +00:00
return options [ choice ] [ 1 ]
2023-01-13 09:10:34 +00:00
def simple_banner ( message : str ) - > None :
"""
A simple banner with a message , defined here for styling consistency
: param message : The message to display
: type message : str
"""
console . rule ( message )
2023-01-14 16:24:18 +00:00
# TODO this does not yet work correctly
def windows_long_paths_registry ( ) - > None :
"""
Display a message about applying the Windows long paths registry fix
"""
with open ( str ( Path ( __file__ ) . parent / " WinLongPathsEnabled.reg " ) , " r " , encoding = " utf-16le " ) as code :
syntax = Syntax ( code . read ( ) , line_numbers = True )
console . print ( Panel (
Group ( " \n " . join ( [
" We will now apply a registry fix to enable long paths on Windows. InvokeAI needs this to function correctly. We are asking your permission to modify the Windows Registry on your behalf. " ,
" " ,
" This is the change that will be applied: " ,
syntax ,
] ) ) ,
title = " Windows Long Paths registry fix " ,
box = box . HORIZONTALS ,
padding = ( 1 , 1 ) ,
) )
2023-01-13 09:10:34 +00:00
def introduction ( ) - > None :
"""
Display a banner when starting configuration of the InvokeAI application
"""
console . rule ( )
console . print ( Panel ( title = " :art: Configuring InvokeAI :art: " , renderable = Group (
" " ,
" [b]This script will: " ,
" " ,
" 1. Configure the InvokeAI application directory " ,
" 2. Help download the Stable Diffusion weight files " ,
" and other large models that are needed for text to image generation " ,
" 3. Create initial configuration files. " ,
" " ,
" [i]At any point you may interrupt this program and resume later. " ,
) ) )
2023-01-17 05:47:36 +00:00
console . line ( 2 )