Merge branch 'main' into refactor/model_manager_instantiate

This commit is contained in:
Kevin Turner 2023-08-02 17:11:23 -07:00 committed by GitHub
commit 02d2cc758d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 103 additions and 206 deletions

View File

@ -1,13 +1,15 @@
name: Black # TODO: add isort and flake8 later name: style checks
# just formatting for now
# TODO: add isort and flake8 later
on: on:
pull_request: {} pull_request:
push: push:
branches: master branches: main
tags: "*" tags: "*"
jobs: jobs:
test: black:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -19,8 +21,7 @@ jobs:
- name: Install dependencies with pip - name: Install dependencies with pip
run: | run: |
pip install --upgrade pip wheel pip install black
pip install .[test]
# - run: isort --check-only . # - run: isort --check-only .
- run: black --check . - run: black --check .

View File

@ -274,7 +274,7 @@ class InvokeAISettings(BaseSettings):
@classmethod @classmethod
def _excluded(self) -> List[str]: def _excluded(self) -> List[str]:
# internal fields that shouldn't be exposed as command line options # internal fields that shouldn't be exposed as command line options
return ["type", "initconf", "cached_root"] return ["type", "initconf"]
@classmethod @classmethod
def _excluded_from_yaml(self) -> List[str]: def _excluded_from_yaml(self) -> List[str]:
@ -290,7 +290,6 @@ class InvokeAISettings(BaseSettings):
"restore", "restore",
"root", "root",
"nsfw_checker", "nsfw_checker",
"cached_root",
] ]
class Config: class Config:
@ -356,7 +355,7 @@ class InvokeAISettings(BaseSettings):
def _find_root() -> Path: def _find_root() -> Path:
venv = Path(os.environ.get("VIRTUAL_ENV") or ".") venv = Path(os.environ.get("VIRTUAL_ENV") or ".")
if os.environ.get("INVOKEAI_ROOT"): if os.environ.get("INVOKEAI_ROOT"):
root = Path(os.environ.get("INVOKEAI_ROOT")).resolve() root = Path(os.environ["INVOKEAI_ROOT"])
elif any([(venv.parent / x).exists() for x in [INIT_FILE, LEGACY_INIT_FILE]]): elif any([(venv.parent / x).exists() for x in [INIT_FILE, LEGACY_INIT_FILE]]):
root = (venv.parent).resolve() root = (venv.parent).resolve()
else: else:
@ -403,7 +402,7 @@ class InvokeAIAppConfig(InvokeAISettings):
xformers_enabled : bool = Field(default=True, description="Enable/disable memory-efficient attention", category='Memory/Performance') xformers_enabled : bool = Field(default=True, description="Enable/disable memory-efficient attention", category='Memory/Performance')
tiled_decode : bool = Field(default=False, description="Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty)", category='Memory/Performance') tiled_decode : bool = Field(default=False, description="Whether to enable tiled VAE decode (reduces memory consumption with some performance penalty)", category='Memory/Performance')
root : Path = Field(default=_find_root(), description='InvokeAI runtime root directory', category='Paths') root : Path = Field(default=None, description='InvokeAI runtime root directory', category='Paths')
autoimport_dir : Path = Field(default='autoimport', description='Path to a directory of models files to be imported on startup.', category='Paths') autoimport_dir : Path = Field(default='autoimport', description='Path to a directory of models files to be imported on startup.', category='Paths')
lora_dir : Path = Field(default=None, description='Path to a directory of LoRA/LyCORIS models to be imported on startup.', category='Paths') lora_dir : Path = Field(default=None, description='Path to a directory of LoRA/LyCORIS models to be imported on startup.', category='Paths')
embedding_dir : Path = Field(default=None, description='Path to a directory of Textual Inversion embeddings to be imported on startup.', category='Paths') embedding_dir : Path = Field(default=None, description='Path to a directory of Textual Inversion embeddings to be imported on startup.', category='Paths')
@ -424,7 +423,6 @@ class InvokeAIAppConfig(InvokeAISettings):
log_level : Literal[tuple(["debug","info","warning","error","critical"])] = Field(default="info", description="Emit logging messages at this level or higher", category="Logging") log_level : Literal[tuple(["debug","info","warning","error","critical"])] = Field(default="info", description="Emit logging messages at this level or higher", category="Logging")
version : bool = Field(default=False, description="Show InvokeAI version and exit", category="Other") version : bool = Field(default=False, description="Show InvokeAI version and exit", category="Other")
cached_root : Path = Field(default=None, description="internal use only", category="DEPRECATED")
# fmt: on # fmt: on
def parse_args(self, argv: List[str] = None, conf: DictConfig = None, clobber=False): def parse_args(self, argv: List[str] = None, conf: DictConfig = None, clobber=False):
@ -472,15 +470,12 @@ class InvokeAIAppConfig(InvokeAISettings):
""" """
Path to the runtime root directory Path to the runtime root directory
""" """
# we cache value of root to protect against it being '.' and the cwd changing if self.root:
if self.cached_root:
root = self.cached_root
elif self.root:
root = Path(self.root).expanduser().absolute() root = Path(self.root).expanduser().absolute()
else: else:
root = self.find_root() root = self.find_root().expanduser().absolute()
self.cached_root = root self.root = root # insulate ourselves from relative paths that may change
return self.cached_root return root
@property @property
def root_dir(self) -> Path: def root_dir(self) -> Path:

View File

@ -78,10 +78,9 @@ class InvokeAIDiffuserComponent:
self.cross_attention_control_context = None self.cross_attention_control_context = None
self.sequential_guidance = config.sequential_guidance self.sequential_guidance = config.sequential_guidance
@classmethod
@contextmanager @contextmanager
def custom_attention_context( def custom_attention_context(
cls, self,
unet: UNet2DConditionModel, # note: also may futz with the text encoder depending on requested LoRAs unet: UNet2DConditionModel, # note: also may futz with the text encoder depending on requested LoRAs
extra_conditioning_info: Optional[ExtraConditioningInfo], extra_conditioning_info: Optional[ExtraConditioningInfo],
step_count: int, step_count: int,
@ -91,18 +90,19 @@ class InvokeAIDiffuserComponent:
old_attn_processors = unet.attn_processors old_attn_processors = unet.attn_processors
# Load lora conditions into the model # Load lora conditions into the model
if extra_conditioning_info.wants_cross_attention_control: if extra_conditioning_info.wants_cross_attention_control:
cross_attention_control_context = Context( self.cross_attention_control_context = Context(
arguments=extra_conditioning_info.cross_attention_control_args, arguments=extra_conditioning_info.cross_attention_control_args,
step_count=step_count, step_count=step_count,
) )
setup_cross_attention_control_attention_processors( setup_cross_attention_control_attention_processors(
unet, unet,
cross_attention_control_context, self.cross_attention_control_context,
) )
try: try:
yield None yield None
finally: finally:
self.cross_attention_control_context = None
if old_attn_processors is not None: if old_attn_processors is not None:
unet.set_attn_processor(old_attn_processors) unet.set_attn_processor(old_attn_processors)
# TODO resuscitate attention map saving # TODO resuscitate attention map saving

View File

@ -124,7 +124,8 @@
"deleteImageBin": "Deleted images will be sent to your operating system's Bin.", "deleteImageBin": "Deleted images will be sent to your operating system's Bin.",
"deleteImagePermanent": "Deleted images cannot be restored.", "deleteImagePermanent": "Deleted images cannot be restored.",
"images": "Images", "images": "Images",
"assets": "Assets" "assets": "Assets",
"autoAssignBoardOnClick": "Auto-Assign Board on Click"
}, },
"hotkeys": { "hotkeys": {
"keyboardShortcuts": "Keyboard Shortcuts", "keyboardShortcuts": "Keyboard Shortcuts",

View File

@ -11,11 +11,14 @@ import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
const selector = createSelector( const selector = createSelector(
[stateSelector], [stateSelector],
({ gallery }) => { ({ gallery, system }) => {
const { autoAddBoardId } = gallery; const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
const { isProcessing } = system;
return { return {
autoAddBoardId, autoAddBoardId,
autoAssignBoardOnClick,
isProcessing,
}; };
}, },
defaultSelectorOptions defaultSelectorOptions
@ -23,7 +26,8 @@ const selector = createSelector(
const BoardAutoAddSelect = () => { const BoardAutoAddSelect = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { autoAddBoardId } = useAppSelector(selector); const { autoAddBoardId, autoAssignBoardOnClick, isProcessing } =
useAppSelector(selector);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const { boards, hasBoards } = useListAllBoardsQuery(undefined, { const { boards, hasBoards } = useListAllBoardsQuery(undefined, {
selectFromResult: ({ data }) => { selectFromResult: ({ data }) => {
@ -67,7 +71,7 @@ const BoardAutoAddSelect = () => {
data={boards} data={boards}
nothingFound="No matching Boards" nothingFound="No matching Boards"
itemComponent={IAIMantineSelectItemWithTooltip} itemComponent={IAIMantineSelectItemWithTooltip}
disabled={!hasBoards} disabled={!hasBoards || autoAssignBoardOnClick || isProcessing}
filter={(value, item: SelectItem) => filter={(value, item: SelectItem) =>
item.label?.toLowerCase().includes(value.toLowerCase().trim()) || item.label?.toLowerCase().includes(value.toLowerCase().trim()) ||
item.value.toLowerCase().includes(value.toLowerCase().trim()) item.value.toLowerCase().includes(value.toLowerCase().trim())

View File

@ -25,14 +25,17 @@ const BoardContextMenu = memo(
const selector = useMemo( const selector = useMemo(
() => () =>
createSelector(stateSelector, ({ gallery }) => { createSelector(stateSelector, ({ gallery, system }) => {
const isAutoAdd = gallery.autoAddBoardId === board_id; const isAutoAdd = gallery.autoAddBoardId === board_id;
return { isAutoAdd }; const isProcessing = system.isProcessing;
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
return { isAutoAdd, isProcessing, autoAssignBoardOnClick };
}), }),
[board_id] [board_id]
); );
const { isAutoAdd } = useAppSelector(selector); const { isAutoAdd, isProcessing, autoAssignBoardOnClick } =
useAppSelector(selector);
const boardName = useBoardName(board_id); const boardName = useBoardName(board_id);
const handleSetAutoAdd = useCallback(() => { const handleSetAutoAdd = useCallback(() => {
@ -59,7 +62,7 @@ const BoardContextMenu = memo(
<MenuGroup title={boardName}> <MenuGroup title={boardName}>
<MenuItem <MenuItem
icon={<FaPlus />} icon={<FaPlus />}
isDisabled={isAutoAdd} isDisabled={isAutoAdd || isProcessing || autoAssignBoardOnClick}
onClick={handleSetAutoAdd} onClick={handleSetAutoAdd}
> >
Auto-add to this Board Auto-add to this Board

View File

@ -16,7 +16,10 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDroppable from 'common/components/IAIDroppable'; import IAIDroppable from 'common/components/IAIDroppable';
import SelectionOverlay from 'common/components/SelectionOverlay'; import SelectionOverlay from 'common/components/SelectionOverlay';
import { boardIdSelected } from 'features/gallery/store/gallerySlice'; import {
autoAddBoardIdChanged,
boardIdSelected,
} from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { FaUser } from 'react-icons/fa'; import { FaUser } from 'react-icons/fa';
import { useUpdateBoardMutation } from 'services/api/endpoints/boards'; import { useUpdateBoardMutation } from 'services/api/endpoints/boards';
@ -38,18 +41,25 @@ const GalleryBoard = memo(
() => () =>
createSelector( createSelector(
stateSelector, stateSelector,
({ gallery }) => { ({ gallery, system }) => {
const isSelectedForAutoAdd = const isSelectedForAutoAdd =
board.board_id === gallery.autoAddBoardId; board.board_id === gallery.autoAddBoardId;
const autoAssignBoardOnClick = gallery.autoAssignBoardOnClick;
const isProcessing = system.isProcessing;
return { isSelectedForAutoAdd }; return {
isSelectedForAutoAdd,
autoAssignBoardOnClick,
isProcessing,
};
}, },
defaultSelectorOptions defaultSelectorOptions
), ),
[board.board_id] [board.board_id]
); );
const { isSelectedForAutoAdd } = useAppSelector(selector); const { isSelectedForAutoAdd, autoAssignBoardOnClick, isProcessing } =
useAppSelector(selector);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const handleMouseOver = useCallback(() => { const handleMouseOver = useCallback(() => {
setIsHovered(true); setIsHovered(true);
@ -66,7 +76,10 @@ const GalleryBoard = memo(
const handleSelectBoard = useCallback(() => { const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(board_id)); dispatch(boardIdSelected(board_id));
}, [board_id, dispatch]); if (autoAssignBoardOnClick && !isProcessing) {
dispatch(autoAddBoardIdChanged(board_id));
}
}, [board_id, autoAssignBoardOnClick, isProcessing, dispatch]);
const [updateBoard, { isLoading: isUpdateBoardLoading }] = const [updateBoard, { isLoading: isUpdateBoardLoading }] =
useUpdateBoardMutation(); useUpdateBoardMutation();

View File

@ -7,7 +7,10 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import InvokeAILogoImage from 'assets/images/logo.png'; import InvokeAILogoImage from 'assets/images/logo.png';
import IAIDroppable from 'common/components/IAIDroppable'; import IAIDroppable from 'common/components/IAIDroppable';
import SelectionOverlay from 'common/components/SelectionOverlay'; import SelectionOverlay from 'common/components/SelectionOverlay';
import { boardIdSelected } from 'features/gallery/store/gallerySlice'; import {
boardIdSelected,
autoAddBoardIdChanged,
} from 'features/gallery/store/gallerySlice';
import { memo, useCallback, useMemo, useState } from 'react'; import { memo, useCallback, useMemo, useState } from 'react';
import { useBoardName } from 'services/api/hooks/useBoardName'; import { useBoardName } from 'services/api/hooks/useBoardName';
import AutoAddIcon from '../AutoAddIcon'; import AutoAddIcon from '../AutoAddIcon';
@ -18,20 +21,25 @@ interface Props {
const selector = createSelector( const selector = createSelector(
stateSelector, stateSelector,
({ gallery }) => { ({ gallery, system }) => {
const { autoAddBoardId } = gallery; const { autoAddBoardId, autoAssignBoardOnClick } = gallery;
return { autoAddBoardId }; const { isProcessing } = system;
return { autoAddBoardId, autoAssignBoardOnClick, isProcessing };
}, },
defaultSelectorOptions defaultSelectorOptions
); );
const NoBoardBoard = memo(({ isSelected }: Props) => { const NoBoardBoard = memo(({ isSelected }: Props) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { autoAddBoardId } = useAppSelector(selector); const { autoAddBoardId, autoAssignBoardOnClick, isProcessing } =
useAppSelector(selector);
const boardName = useBoardName(undefined); const boardName = useBoardName(undefined);
const handleSelectBoard = useCallback(() => { const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(undefined)); dispatch(boardIdSelected(undefined));
}, [dispatch]); if (autoAssignBoardOnClick && !isProcessing) {
dispatch(autoAddBoardIdChanged(undefined));
}
}, [dispatch, autoAssignBoardOnClick, isProcessing]);
const [isHovered, setIsHovered] = useState(false); const [isHovered, setIsHovered] = useState(false);
const handleMouseOver = useCallback(() => { const handleMouseOver = useCallback(() => {
setIsHovered(true); setIsHovered(true);

View File

@ -8,6 +8,7 @@ import IAIPopover from 'common/components/IAIPopover';
import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox';
import IAISlider from 'common/components/IAISlider'; import IAISlider from 'common/components/IAISlider';
import { import {
autoAssignBoardOnClickChanged,
setGalleryImageMinimumWidth, setGalleryImageMinimumWidth,
shouldAutoSwitchChanged, shouldAutoSwitchChanged,
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
@ -19,11 +20,16 @@ import BoardAutoAddSelect from './Boards/BoardAutoAddSelect';
const selector = createSelector( const selector = createSelector(
[stateSelector], [stateSelector],
(state) => { (state) => {
const { galleryImageMinimumWidth, shouldAutoSwitch } = state.gallery; const {
galleryImageMinimumWidth,
shouldAutoSwitch,
autoAssignBoardOnClick,
} = state.gallery;
return { return {
galleryImageMinimumWidth, galleryImageMinimumWidth,
shouldAutoSwitch, shouldAutoSwitch,
autoAssignBoardOnClick,
}; };
}, },
defaultSelectorOptions defaultSelectorOptions
@ -33,7 +39,7 @@ const GallerySettingsPopover = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const { galleryImageMinimumWidth, shouldAutoSwitch } = const { galleryImageMinimumWidth, shouldAutoSwitch, autoAssignBoardOnClick } =
useAppSelector(selector); useAppSelector(selector);
const handleChangeGalleryImageMinimumWidth = (v: number) => { const handleChangeGalleryImageMinimumWidth = (v: number) => {
@ -69,6 +75,13 @@ const GallerySettingsPopover = () => {
dispatch(shouldAutoSwitchChanged(e.target.checked)) dispatch(shouldAutoSwitchChanged(e.target.checked))
} }
/> />
<IAISimpleCheckbox
label={t('gallery.autoAssignBoardOnClick')}
isChecked={autoAssignBoardOnClick}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
dispatch(autoAssignBoardOnClickChanged(e.target.checked))
}
/>
<BoardAutoAddSelect /> <BoardAutoAddSelect />
</Flex> </Flex>
</IAIPopover> </IAIPopover>

View File

@ -8,6 +8,7 @@ export const initialGalleryState: GalleryState = {
selection: [], selection: [],
shouldAutoSwitch: true, shouldAutoSwitch: true,
autoAddBoardId: undefined, autoAddBoardId: undefined,
autoAssignBoardOnClick: true,
galleryImageMinimumWidth: 96, galleryImageMinimumWidth: 96,
selectedBoardId: undefined, selectedBoardId: undefined,
galleryView: 'images', galleryView: 'images',
@ -66,6 +67,9 @@ export const gallerySlice = createSlice({
setGalleryImageMinimumWidth: (state, action: PayloadAction<number>) => { setGalleryImageMinimumWidth: (state, action: PayloadAction<number>) => {
state.galleryImageMinimumWidth = action.payload; state.galleryImageMinimumWidth = action.payload;
}, },
autoAssignBoardOnClickChanged: (state, action: PayloadAction<boolean>) => {
state.autoAssignBoardOnClick = action.payload;
},
boardIdSelected: (state, action: PayloadAction<BoardId>) => { boardIdSelected: (state, action: PayloadAction<BoardId>) => {
state.selectedBoardId = action.payload; state.selectedBoardId = action.payload;
state.galleryView = 'images'; state.galleryView = 'images';
@ -140,6 +144,7 @@ export const {
imageSelectionToggled, imageSelectionToggled,
imageSelected, imageSelected,
shouldAutoSwitchChanged, shouldAutoSwitchChanged,
autoAssignBoardOnClickChanged,
setGalleryImageMinimumWidth, setGalleryImageMinimumWidth,
boardIdSelected, boardIdSelected,
isBatchEnabledChanged, isBatchEnabledChanged,

View File

@ -18,6 +18,7 @@ export type GalleryState = {
selection: string[]; selection: string[];
shouldAutoSwitch: boolean; shouldAutoSwitch: boolean;
autoAddBoardId: string | undefined; autoAddBoardId: string | undefined;
autoAssignBoardOnClick: boolean;
galleryImageMinimumWidth: number; galleryImageMinimumWidth: number;
selectedBoardId: BoardId; selectedBoardId: BoardId;
galleryView: GalleryView; galleryView: GalleryView;

162
shell.nix
View File

@ -1,162 +0,0 @@
{ pkgs ? import <nixpkgs> {}
, lib ? pkgs.lib
, stdenv ? pkgs.stdenv
, fetchurl ? pkgs.fetchurl
, runCommand ? pkgs.runCommand
, makeWrapper ? pkgs.makeWrapper
, mkShell ? pkgs.mkShell
, buildFHSUserEnv ? pkgs.buildFHSUserEnv
, frameworks ? pkgs.darwin.apple_sdk.frameworks
}:
# Setup InvokeAI environment using nix
# Simple usage:
# nix-shell
# python3 scripts/preload_models.py
# python3 scripts/invoke.py -h
let
conda-shell = { url, sha256, installPath, packages, shellHook }:
let
src = fetchurl { inherit url sha256; };
libPath = lib.makeLibraryPath ([] ++ lib.optionals (stdenv.isLinux) [ pkgs.zlib ]);
condaArch = if stdenv.system == "aarch64-darwin" then "osx-arm64" else "";
installer =
if stdenv.isDarwin then
runCommand "conda-install" {
nativeBuildInputs = [ makeWrapper ];
} ''
mkdir -p $out/bin
cp ${src} $out/bin/miniconda-installer.sh
chmod +x $out/bin/miniconda-installer.sh
makeWrapper \
$out/bin/miniconda-installer.sh \
$out/bin/conda-install \
--add-flags "-p ${installPath}" \
--add-flags "-b"
''
else if stdenv.isLinux then
runCommand "conda-install" {
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ pkgs.zlib ];
}
# on line 10, we have 'unset LD_LIBRARY_PATH'
# we have to comment it out however in a way that the number of bytes in the
# file does not change. So we replace the 'u' in the line with a '#'
# The reason is that the binary payload is encoded as number
# of bytes from the top of the installer script
# and unsetting the library path prevents the zlib library from being discovered
''
mkdir -p $out/bin
sed 's/unset LD_LIBRARY_PATH/#nset LD_LIBRARY_PATH/' ${src} > $out/bin/miniconda-installer.sh
chmod +x $out/bin/miniconda-installer.sh
makeWrapper \
$out/bin/miniconda-installer.sh \
$out/bin/conda-install \
--add-flags "-p ${installPath}" \
--add-flags "-b" \
--prefix "LD_LIBRARY_PATH" : "${libPath}"
''
else {};
hook = ''
export CONDA_SUBDIR=${condaArch}
'' + shellHook;
fhs = buildFHSUserEnv {
name = "conda-shell";
targetPkgs = pkgs: [ stdenv.cc pkgs.git installer ] ++ packages;
profile = hook;
runScript = "bash";
};
shell = mkShell {
shellHook = if stdenv.isDarwin then hook else "conda-shell; exit";
packages = if stdenv.isDarwin then [ pkgs.git installer ] ++ packages else [ fhs ];
};
in shell;
packages = with pkgs; [
cmake
protobuf
libiconv
rustc
cargo
rustPlatform.bindgenHook
];
env = {
aarch64-darwin = {
envFile = "environment-mac.yml";
condaPath = (builtins.toString ./.) + "/.conda";
ptrSize = "8";
};
x86_64-linux = {
envFile = "environment.yml";
condaPath = (builtins.toString ./.) + "/.conda";
ptrSize = "8";
};
};
envFile = env.${stdenv.system}.envFile;
installPath = env.${stdenv.system}.condaPath;
ptrSize = env.${stdenv.system}.ptrSize;
shellHook = ''
conda-install
# tmpdir is too small in nix
export TMPDIR="${installPath}/tmp"
# Add conda to PATH
export PATH="${installPath}/bin:$PATH"
# Allows `conda activate` to work properly
source ${installPath}/etc/profile.d/conda.sh
# Paths for gcc if compiling some C sources with pip
export NIX_CFLAGS_COMPILE="-I${installPath}/include -I$TMPDIR/include"
export NIX_CFLAGS_LINK="-L${installPath}/lib $BINDGEN_EXTRA_CLANG_ARGS"
export PIP_EXISTS_ACTION=w
# rust-onig fails (think it writes config.h to wrong location)
mkdir -p "$TMPDIR/include"
cat <<'EOF' > "$TMPDIR/include/config.h"
#define HAVE_PROTOTYPES 1
#define STDC_HEADERS 1
#define HAVE_STRING_H 1
#define HAVE_STDARG_H 1
#define HAVE_STDLIB_H 1
#define HAVE_LIMITS_H 1
#define HAVE_INTTYPES_H 1
#define SIZEOF_INT 4
#define SIZEOF_SHORT 2
#define SIZEOF_LONG ${ptrSize}
#define SIZEOF_VOIDP ${ptrSize}
#define SIZEOF_LONG_LONG 8
EOF
conda env create -f "${envFile}" || conda env update --prune -f "${envFile}"
conda activate invokeai
'';
version = "4.12.0";
conda = {
aarch64-darwin = {
shell = conda-shell {
inherit shellHook installPath;
url = "https://repo.anaconda.com/miniconda/Miniconda3-py39_${version}-MacOSX-arm64.sh";
sha256 = "4bd112168cc33f8a4a60d3ef7e72b52a85972d588cd065be803eb21d73b625ef";
packages = [ frameworks.Security ] ++ packages;
};
};
x86_64-linux = {
shell = conda-shell {
inherit shellHook installPath;
url = "https://repo.continuum.io/miniconda/Miniconda3-py39_${version}-Linux-x86_64.sh";
sha256 = "78f39f9bae971ec1ae7969f0516017f2413f17796670f7040725dd83fcff5689";
packages = with pkgs; [ libGL glib ] ++ packages;
};
};
};
in conda.${stdenv.system}.shell

View File

@ -84,6 +84,21 @@ def test_env_override():
assert conf.max_cache_size == 20 assert conf.max_cache_size == 20
def test_root_resists_cwd():
previous = os.environ["INVOKEAI_ROOT"]
cwd = Path(os.getcwd()).resolve()
os.environ["INVOKEAI_ROOT"] = "."
conf = InvokeAIAppConfig.get_config()
conf.parse_args([])
assert conf.root_path == cwd
os.chdir("..")
assert conf.root_path == cwd
os.environ["INVOKEAI_ROOT"] = previous
os.chdir(cwd)
def test_type_coercion(): def test_type_coercion():
conf = InvokeAIAppConfig().get_config() conf = InvokeAIAppConfig().get_config()
conf.parse_args(argv=["--root=/tmp/foobar"]) conf.parse_args(argv=["--root=/tmp/foobar"])