WIP: nixified webui

currently packaging as a nixosModule so should be easy to integrate with
nixos generators in the future. Also looking at making a docker image
without systemd
This commit is contained in:
TakodaS 2025-03-10 17:51:53 +00:00
parent 82a973c043
commit 149824a515
22 changed files with 4214 additions and 19 deletions

2
.envrc Normal file
View File

@ -0,0 +1,2 @@
use flake
watch_file uv.lock

2
.gitignore vendored
View File

@ -42,3 +42,5 @@ notification.mp3
/cache
trace.json
/sysinfo-????-??-??-??-??.json
.direnv
result

170
flake.lock generated Normal file
View File

@ -0,0 +1,170 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1741010256,
"narHash": "sha256-WZNlK/KX7Sni0RyqLSqLPbK8k08Kq7H7RijPJbq9KHM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ba487dbc9d04e0634c64e3b1f0d25839a0a68246",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1725103162,
"narHash": "sha256-Ym04C5+qovuQDYL/rKWSR+WESseQBbNAe5DsXNx5trY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "12228ff1752d7b7624a54e9c1af4b222b3c1073b",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pyproject-build-systems": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"pyproject-nix": [
"pyproject-nix"
],
"uv2nix": [
"uv2nix"
]
},
"locked": {
"lastModified": 1740362541,
"narHash": "sha256-S8Mno07MspggOv/xIz5g8hB2b/C5HPiX8E+rXzKY+5U=",
"owner": "pyproject-nix",
"repo": "build-system-pkgs",
"rev": "e151741c848ba92331af91f4e47640a1fb82be19",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "build-system-pkgs",
"type": "github"
}
},
"pyproject-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1740921768,
"narHash": "sha256-4d27TdYoJ8B99b4kU7qESB4QVjiV9gPSuP2/MDqjDWo=",
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"rev": "ca5d23f044943a23cc4274b2d3dea45682dc025f",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"pyproject-build-systems": "pyproject-build-systems",
"pyproject-nix": "pyproject-nix",
"uv2nix": "uv2nix",
"uv2nix-hammer-overrides": "uv2nix-hammer-overrides"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"id": "systems",
"type": "indirect"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1725271838,
"narHash": "sha256-VcqxWT0O/gMaeWTTjf1r4MOyG49NaNxW4GHTO3xuThE=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "9fb342d14b69aefdf46187f6bb80a4a0d97007cd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"uv2nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"pyproject-nix": [
"pyproject-nix"
]
},
"locked": {
"lastModified": 1740497536,
"narHash": "sha256-K+8wsVooqhaqyxuvew3+62mgOfRLJ7whv7woqPU3Ypo=",
"owner": "pyproject-nix",
"repo": "uv2nix",
"rev": "d01fd3a141755ad5d5b93dd9fcbd76d6401f5bac",
"type": "github"
},
"original": {
"owner": "pyproject-nix",
"repo": "uv2nix",
"type": "github"
}
},
"uv2nix-hammer-overrides": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"systems": "systems",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1738911992,
"narHash": "sha256-qM+5AhrAzoVYPgFJLW+XJLMpmg0hRimqiDVsopcEDBo=",
"owner": "TyberiusPrime",
"repo": "uv2nix_hammer_overrides",
"rev": "85d4ac52d5e78963a4803be8e26eeb41d5882868",
"type": "github"
},
"original": {
"owner": "TyberiusPrime",
"repo": "uv2nix_hammer_overrides",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

76
flake.nix Normal file
View File

@ -0,0 +1,76 @@
{
description = "Django application using uv2nix";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
pyproject-nix = {
url = "github:pyproject-nix/pyproject.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
uv2nix = {
url = "github:pyproject-nix/uv2nix";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
pyproject-build-systems = {
url = "github:pyproject-nix/build-system-pkgs";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.uv2nix.follows = "uv2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
uv2nix-hammer-overrides = {
url = "github:TyberiusPrime/uv2nix_hammer_overrides";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs@{
self,
nixpkgs,
pyproject-nix,
pyproject-build-systems,
uv2nix,
uv2nix-hammer-overrides,
...
}:
let
lib = nixpkgs.lib.extend (
self: _: {
flake = import ./nix/lib (
{
lib = self;
}
// inputs
);
}
);
package-name = "stable-diffusion-webui";
pythonSets = import ./pythonSets.nix ({ inherit lib; } // inputs);
allArgs = inputs // {
inherit lib package-name pythonSets;
};
nixDirs = lib.flake.getSubdirs ./nix;
importFolder = (
name: {
name = name;
value =
import ./nix/${name} allArgs;
}
);
in
{
asgiApp = "django_webapp.asgi:application";
settingsModules = {
prod = "django_webapp.settings";
};
}
// builtins.listToAttrs (map importFolder nixDirs);
}

19
nix/checks/default.nix Normal file
View File

@ -0,0 +1,19 @@
{
lib,
self,
package-name,
...
}:
lib.flake.forAllSystems (
system:
let
mainPkg = self.packages.${system}.${package-name};
in
rec {
inherit (mainPkg) tests;
inherit (mainPkg.tests) mypy pytest nixos;
default = pytest;
}
)

34
nix/devShells/default.nix Normal file
View File

@ -0,0 +1,34 @@
args@{
lib,
self,
package-name,
pythonSets,
nixpkgs,
...
}:
lib.flake.forAllSystems (
system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
pythonSet = pythonSets.${system};
folders = lib.flake.getSubdirs ./.;
folderAttrs = (
name: {
name = name;
value = import ./${name} (
args
// {
inherit pkgs system pythonSet;
}
); # You can replace this with any value
}
);
in
builtins.listToAttrs (map folderAttrs folders)
// {
default = self.devShells.${system}.${package-name};
}
)

View File

@ -0,0 +1,25 @@
{
pkgs,
lib,
system,
self,
...
}:
let
inherit (self.packages.${system}) venv;
in
pkgs.mkShell {
packages = [
pkgs.uv
pkgs.python310
];
env = {
UV_NO_SYNC = "1";
UV_PYTHON = "${pkgs.python310}";
UV_PYTHON_DOWNLOADS = "never";
};
shellHook = ''
unset PYTHONPATH
export REPO_ROOT=$(git rev-parse --show-toplevel)
'';
}

View File

@ -0,0 +1,25 @@
{
pkgs,
lib,
self,
system,
...
}:
let
venv = self.packages.${system}.venv;
in
pkgs.mkShell {
packages = [
venv
pkgs.uv
];
env = {
UV_NO_SYNC = "1";
UV_PYTHON = "${venv}/bin/python";
UV_PYTHON_DOWNLOADS = "never";
};
shellHook = ''
unset PYTHONPATH
export REPO_ROOT=$(git rev-parse --show-toplevel)
'';
}

15
nix/lib/default.nix Normal file
View File

@ -0,0 +1,15 @@
{
lib,
...
}:
{
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
getSubdirs =
dir:
let
dirContents = builtins.readDir dir; # Reads the current directory
folders = builtins.attrNames (lib.attrsets.filterAttrs (_: type: type == "directory") dirContents);
in
folders;
}

View File

@ -0,0 +1,135 @@
{
lib,
self,
nixpkgs,
package-name,
...
}:
with lib;
let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
config = self.nixosModules.options;
in
{
"${package-name}" =
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.${package-name};
inherit (pkgs) system;
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf;
in
{
options.services.${package-name} = {
enable = mkOption {
type = lib.types.bool;
default = false;
description = ''
Enable ${package-name}
'';
};
args = mkOption {
default = [
"--skip-prepare-environment"
"--xformers"
];
type = types.listOf (types.str);
description = "Command line arguments to pass to launch.py";
};
venv = mkOption {
type = lib.types.package;
default = self.packages.${system}.venv;
description = ''
${package-name} virtual environment package
'';
};
stateDir = mkOption {
default = "/var/lib/${package-name}";
type = types.str;
description = "${package-name} data directory.";
};
repositoryRoot = mkOption {
type = types.str;
default = "${cfg.stateDir}/src";
description = "Path to the git repositories.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [
cfg.venv
pkgs.git
pkgs.cacert
pkgs.rsync
];
users.users.${package-name} = {
description = "${package-name}-user";
home = cfg.stateDir;
group = package-name;
isSystemUser = true;
};
users.groups.${package-name} = { };
systemd.services.${package-name} = {
description = "${package-name} server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [
gitAndTools.git
cacert
neovim
];
preStart = ''
mkdir -p ${cfg.repositoryRoot}/repositories
mkdir -p ${cfg.stateDir}/.config/matplotlib
${lib.getExe pkgs.rsync} -r -a ${
self.packages.${system}.${package-name}.static
}/* ${cfg.repositoryRoot}
chown -hR ${package-name} ${cfg.stateDir}
chmod -R +775 ${cfg.stateDir}
'';
serviceConfig = {
ExecStart = ''
${cfg.venv}/bin/python ${cfg.repositoryRoot}/launch.py ${builtins.concatStringsSep " " cfg.args}
'';
Restart = "on-failure";
User = package-name;
# DynamicUser = true;
StateDirectory = "${cfg.stateDir}";
StateDirectoryMode = 775;
RuntimeDirectory = "${cfg.repositoryRoot}";
RuntimeDirectoryMode = 775;
PermissionsStartOnly = true;
# BindReadOnlyPaths = [
# "${
# config.environment.etc."ssl/certs/ca-certificates.crt".source
# }:/etc/ssl/certs/ca-certificates.crt"
# builtins.storeDir
# "-/etc/resolv.conf"
# "-/etc/nsswitch.conf"
# "-/etc/hosts"
# "-/etc/localtime"
# ];
};
};
};
};
}

34
nix/packages/default.nix Normal file
View File

@ -0,0 +1,34 @@
args@{
lib,
self,
pythonSets,
package-name,
nixpkgs,
...
}:
lib.flake.forAllSystems (
system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
pythonSet = pythonSets.${system};
folders = lib.flake.getSubdirs ./.;
folderAttrs = (
name: {
name = name;
value = import ./${name} (
args
// {
inherit pkgs system pythonSet;
}
); # You can replace this with any value
}
);
in
builtins.listToAttrs (map folderAttrs folders)
// {
default = self.packages.${system}.${package-name}.static;
}
)

View File

@ -0,0 +1,66 @@
{ pkgs, lib, ... }:
let
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
deps = {
stable-diffusion-webui-assets = {
owner = "AUTOMATIC1111";
repo = "stable-diffusion-webui-assets";
rev = "6f7db241d2f8ba7457bac5ca9753331f0c266917";
hash = "sha256-gos24/VHz+Es834ZfMVdu3L9m04CR0cLi54bgTlWLJk=";
};
stable-diffusion-stability-ai = {
owner = "Stability-AI";
repo = "stablediffusion";
rev = "cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf";
hash = "sha256-yEtrz/JTq53JDI4NZI26KsD8LAgiViwiNaB2i1CBs/I=";
};
generative-models = {
owner = "Stability-AI";
repo = "generative-models";
rev = "45c443b316737a4ab6e40413d7794a7f5657c19f";
hash = "sha256-qaZeaCfOO4vWFZZAyqNpJbTttJy17GQ5+DM05yTLktA=";
};
k-diffusion = {
owner = "crowsonkb";
repo = "k-diffusion";
rev = "ab527a9a6d347f364e3d185ba6d714e22d80cb3c";
hash = "sha256-tOWDFt0/hGZF5HENiHPb9a2pBlXdSvDvCNTsCMZljC4=";
};
BLIP = {
owner = "salesforce";
repo = "BLIP";
rev = "48211a1594f1321b00f14c9f7a5b4813144b2fb9";
hash = "sha256-0IO+3M/Gy4VrNBFYYgZB2CzWhT3PTGBXNKPad61px5k=";
};
};
depsOut = lib.attrsets.mapAttrs (
name: value:
pkgs.srcOnly {
inherit name;
src = pkgs.fetchFromGitHub {
inherit (value)
owner
repo
rev
hash
;
};
}
) deps;
in
pkgs.srcOnly {
pname = "deps";
version = "1.0";
stdenv = pkgs.stdenvNoCC;
sourceRoot = ".";
# Empty derivation, nothing to build
srcs = lib.attrsets.attrValues depsOut;
# Attach other derivations or values
passthru = depsOut;
}

View File

@ -0,0 +1,25 @@
{
lib,
pkgs,
system,
package-name,
self,
...
}:
let
venv = self.packages.${system}.venv;
in
lib.optionalAttrs pkgs.stdenv.isLinux
# Expose Docker container in packages
pkgs.dockerTools.buildLayeredImage
{
name = "${package-name}";
contents = [ pkgs.cacert ];
config = {
Cmd = [
"${venv}/bin/python"
];
Env = [
];
};
}

View File

@ -0,0 +1,52 @@
{ pkgs, ... }:
{
url,
name,
hash ? "",
sha256 ? "",
...
}@args:
pkgs.fetchurl (
args
// {
name = "model";
passthru = (
{
inherit name;
}
// args.passthru
);
curlOpts = "-H @/build/ACTIVE_TOKEN";
netrcImpureEnvVars = [
"HF_TOKEN"
"CIVITAI_API_TOKEN"
];
netrcPhase = ''
# Warn if HF_TOKEN or CIVITAI_API_TOKEN are not set or didn't work, in
# the case of failure to fetch
warnEmptyTokensHook() {
if [ -z "$HF_TOKEN" ]; then
echo "Warning: HF_TOKEN is not set. Please set it to access gated Hugging Face models."
elif [ -n "$HF_TOKEN" ]; then
echo "Warning: HF_TOKEN is set, but fetching didn't seem to work, check your token!"
fi
if [ -z "$CIVITAI_API_TOKEN" ]; then
echo "Warning: CIVITAI_API_TOKEN is not set. Please set it to access gated CivitAI resources."
elif [ -n "$CIVITAI_API_TOKEN" ]; then
echo "Warning: CIVITAI_API_TOKEN is set, but fetching didn't seem to work, check your token!"
fi
}
failureHooks+=(warnEmptyTokensHook)
# echo is a bash internal and doesn't create a process in /proc/*/cmdline
if [[ '${url}' == *huggingface* ]]; then
echo "Authorization: Bearer $HF_TOKEN" > /build/ACTIVE_TOKEN
elif [[ '${url}' == *civitai* ]]; then
echo "Authorization: Bearer $CIVITAI_API_TOKEN" > /build/ACTIVE_TOKEN
else
> /build/ACTIVE_TOKEN
fi
'';
inherit url hash sha256;
}
)

View File

@ -0,0 +1,53 @@
{ pkgs, lib, ... }:
let
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
models = {
# opt-350 = {
# # from `pkgs`, not `builtins`, may not matter?
# url = "https://huggingface.co/facebook/opt-350m";
# rev = "08ab08cc4b72ff5593870b5d527cf4230323703c";
# hash = "sha256-tqPLcxtZ6WSNzFIVxUZ52LnXYFijDp6KzA6WMRVnMJM=";
# };
Stable-diffusion = {
# from `pkgs`, not `builtins`, may not matter?
file = "v1-5-pruned-emaonly.safetensors";
url = "https://huggingface.co/sd-legacy/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors";
hash = "sha256-bOAWFomzhTrKoDd57JPq/nWgL0ztZZvuA/UHl4Bvovo=";
};
};
getModels = lib.attrsets.foldlAttrs (
acc: name: value:
let
outDir = "$out/${name}";
model = pkgs.fetchurl {
inherit (value)
url
hash
;
};
in
acc + "mkdir -p ${outDir} && cp -r ${model} ${outDir}/${value.file} \n"
) "" models;
in
pkgs.stdenvNoCC.mkDerivation {
pname = "models";
version = "1.0";
dontUnpack = true;
# Empty derivation, nothing to build
installPhase = ''
${getModels}
'';
passthru = lib.attrsets.mapAttrs (
name: value:
pkgs.fetchurl {
inherit (value)
url
hash
;
}
) models;
}

View File

@ -0,0 +1,150 @@
args@{
package-name,
lib,
pkgs,
system,
pythonSet,
self,
...
}:
let
inherit (pkgs) stdenv stdenvNoCC;
inherit (self.packages.${system}) venv;
src = lib.cleanSource "${self}";
# Run mypy checks
mypy =
let
venv = self.packages.${system}.venv.typing;
in
stdenv.mkDerivation {
name = "${package-name}-test-mypy";
inherit src;
nativeBuildInputs = [
venv
];
dontConfigure = true;
dontInstall = true;
buildPhase = ''
mkdir $out
mypy --strict . --junit-xml $out/junit.xml
'';
};
# Run pytest with coverage reports installed into build output
pytest =
let
venv = self.packages.${system}.venv.test;
in
stdenv.mkDerivation {
name = "${package-name}-test-pytest";
inherit src;
nativeBuildInputs = [
venv
];
dontConfigure = true;
buildPhase = ''
runHook preBuild
pytest --cov tests --cov-report html tests
runHook postBuild
'';
installPhase = ''
runHook preInstall
mv htmlcov $out
runHook postInstall
'';
};
nixos =
let
venv = self.packages.${system}.venv.test;
in
lib.optionalAttrs stdenv.isLinux
# NixOS module test
pkgs.nixosTest
{
name = "${package-name}-nixos-test";
nodes.machine =
{ ... }:
{
virtualisation.memorySize = 6000;
imports = [
self.nixosModules.${package-name}
];
services.${package-name} = {
enable = true;
inherit venv;
};
system.stateVersion = "24.11";
};
testScript = ''
machine.wait_for_unit("${package-name}.service")
with subtest("Web interface getting ready"):
machine.wait_until_succeeds("curl -fs localhost:7860")
'';
};
testAttrs = {
inherit
mypy
pytest
nixos
;
};
tests = pkgs.symlinkJoin rec {
name = "test";
paths = builtins.attrValues passthru;
passthru = testAttrs;
};
in
pythonSet.${package-name}.overrideAttrs (old: {
# Add tests to passthru.tests
#
# These attribute are used in Flake checks.
passthru = old.passthru // {
tests = (old.tests or { }) // tests;
static = import ./static args;
};
src = lib.cleanSource old.src;
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.makeWrapper ];
# postBuild =
#
# let
# getDeps = lib.attrsets.foldlAttrs (
# acc: name: value:
# acc + "ln -s ${value} repositories/${name} \n"
# ) "" self.packages.${system}.deps.passthru;
# in
# ''
# mkdir repositories
# ${getDeps}
# '';
postInstall =
let
script = pkgs.writeShellScriptBin "${old.pname}" ''
python ${src}/launch.py --skip-prepare-environment "$@"
'';
in
''
mkdir -p $out/bin
cp -r ${script}/bin/* $out/bin/
'';
postFixup = ''
wrapProgram $out/bin/${old.pname} \
--set PATH ${
lib.makeBinPath [
venv
]
}
'';
})

View File

@ -0,0 +1,61 @@
{
lib,
pkgs,
system,
package-name,
pythonSet,
self,
...
}:
let
venv = self.packages.${system}.venv;
in
pkgs.stdenv.mkDerivation rec {
name = "${package-name}-static";
inherit (pythonSet.${package-name}) src;
dontConfigure = true;
dontBuild = true;
nativeBuildInputs = with pkgs; [
venv
makeWrapper
cacert
];
buildInputs = with pkgs; [ git ];
installPhase =
let
repoDir = "$out/";
getDeps = lib.attrsets.foldlAttrs (
acc: name: value:
acc + "ln -s ${value} ${repoDir}/repositories/${name} \n"
) "" self.packages.${system}.deps.passthru;
# getModels = lib.attrsets.foldlAttrs (
# acc: name: value:
# acc + "ln -s ${value} ${repoDir}/models/${name} \n"
# ) "" self.packages.${system}.models.passthru;
script = pkgs.writeShellScriptBin "${name}" ''
${venv}/bin/python launch.py --skip-prepare-environment --skip-install "$@"
'';
in
''
mkdir -p ${repoDir}/repositories
mkdir -p ${repoDir}/models/Stable-diffusion
mkdir -p $out/bin
cp -r $src/* ${repoDir}
${getDeps}
cp ${lib.getExe script} $out/bin
'';
postFixup = ''
wrapProgram $out/bin/${name} \
--set PATH ${
lib.makeBinPath [
venv
pkgs.coreutils
pkgs.git
]
}
'';
}

View File

@ -0,0 +1,39 @@
{
lib,
pkgs,
package-name,
pythonSet,
self,
pyproject-nix,
pyproject-build-systems,
uv2nix,
...
}:
let
asgiApp = "django_webapp.asgi:application";
settingsModules = {
prod = "django_webapp.settings";
};
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = "${self}"; };
overlay = workspace.mkPyprojectOverlay {
sourcePreference = "wheel";
};
editableOverlay = workspace.mkEditablePyprojectOverlay {
root = "$REPO_ROOT";
};
# Python sets grouped per system
envs = lib.attrsets.genAttrs workspace.deps.all.${package-name} (
name: pythonSet.mkVirtualEnv "${package-name}-${name}-env" { ${package-name} = [ name ]; }
);
in
(pythonSet.mkVirtualEnv "${package-name}-env" workspace.deps.default).overrideAttrs (
self: super: {
passthru = envs;
}
)

View File

@ -1,34 +1,84 @@
[project]
name = "stable-diffusion-webui"
version = "0.0.1"
requires-python = ">=3.10"
dependencies = [
"accelerate>=1.4.0",
"blendmodes>=2024",
"clean-fid>=0.1.35",
"clip",
"cv-3>=1.2.0",
"diskcache>=5.6.3",
"einops>=0.8.1",
"facexlib>=0.3.0",
"gitpython>=3.1.44",
"gradio==3.41.2",
"inflection>=0.5.1",
"jsonmerge>=1.9.2",
"kornia>=0.8.0",
"lark>=1.2.2",
"omegaconf>=2.3.0",
"open-clip-torch>=2.31.0",
"piexif>=1.1.3",
"pillow-avif-plugin>=1.4.6",
"psutil>=7.0.0",
"pydantic~=1.10",
"pytorch-lightning~=1.9",
"requests>=2.32.3",
"resize-right>=0.0.2",
"safetensors>=0.5.3",
"scikit-image>=0.25.2",
"scipy>=1.15.2",
"sgm",
"tomesd>=0.1.3",
"torch==2.4.1",
"torchdiffeq>=0.2.5",
"torchsde>=0.2.6",
"transformers>=4.49.0",
"xformers>=0.0.28.post1",
]
[dependency-groups]
dev = [
{ include-group = "test" },
{ include-group = "typing" },
{ include-group = "lint" },
]
typing = ["mypy>=1.13.0"]
test = ["pytest-cov>=6.0.0", "pytest>=8.3.3"]
lint = ["ruff>=0.7.2"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
include = ["**/*.py", "**/*.js", "**/*.html", "/tests"]
# exclude = ["*.json", "pkg/_compat.py"]
[tool.ruff]
target-version = "py39"
[tool.ruff.lint]
extend-select = [
"B",
"C",
"I",
"W",
]
extend-select = ["B", "C", "I", "W"]
exclude = [
"extensions",
"extensions-disabled",
]
exclude = ["extensions", "extensions-disabled"]
ignore = [
"E501", # Line too long
"E721", # Do not compare types, use `isinstance`
"E731", # Do not assign a `lambda` expression, use a `def`
"I001", # Import block is un-sorted or un-formatted
"C901", # Function is too complex
"C408", # Rewrite as a literal
"W605", # invalid escape sequence, messes with some docstrings
"E501", # Line too long
"E721", # Do not compare types, use `isinstance`
"E731", # Do not assign a `lambda` expression, use a `def`
"I001", # Import block is un-sorted or un-formatted
"C901", # Function is too complex
"C408", # Rewrite as a literal
"W605", # invalid escape sequence, messes with some docstrings
]
[tool.ruff.lint.per-file-ignores]
"webui.py" = ["E402"] # Module level import not at top of file
"webui.py" = ["E402"] # Module level import not at top of file
[tool.ruff.lint.flake8-bugbear]
# Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`.
@ -36,3 +86,7 @@ extend-immutable-calls = ["fastapi.Depends", "fastapi.security.HTTPBasic"]
[tool.pytest.ini_options]
base_url = "http://127.0.0.1:7860"
[tool.uv.sources]
sgm = { git = "https://github.com/Stability-AI/generative-models", rev = "45c443b316737a4ab6e40413d7794a7f5657c19f" }
clip = { git = "https://github.com/openai/CLIP.git" }

61
pythonSets.nix Normal file
View File

@ -0,0 +1,61 @@
{
lib,
self,
nixpkgs,
pyproject-nix,
pyproject-build-systems,
uv2nix,
uv2nix-hammer-overrides,
...
}:
# Python sets grouped per system
lib.flake.forAllSystems (
system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = "${self}"; };
overlay = workspace.mkPyprojectOverlay {
sourcePreference = "wheel";
};
editableOverlay = workspace.mkEditablePyprojectOverlay {
root = "$REPO_ROOT";
};
# Base Python package set from pyproject.nix
baseSet = pkgs.callPackage pyproject-nix.build.packages {
python = pkgs.python310;
};
# An overlay of build fixups & test additions
pyprojectOverrides = final: prev: {
sgm = prev.sgm.overrideAttrs (old: {
nativeBuildInputs =
(old.nativeBuildInputs or [ ])
++ final.resolveBuildSystem {
hatchling = [ ];
};
});
cv-3 = prev.cv-3.overrideAttrs (old: {
nativeBuildInputs =
(old.nativeBuildInputs or [ ])
++ final.resolveBuildSystem {
setuptools = [ ];
};
});
};
in
(baseSet.overrideScope (
lib.composeManyExtensions [
pyproject-build-systems.overlays.default
overlay
(uv2nix-hammer-overrides.overrides pkgs)
pyprojectOverrides
]
))
)

7
tests/test_webapp.py Normal file
View File

@ -0,0 +1,7 @@
from django.test import Client
def test_index(client: Client) -> None:
resp = client.get("/")
assert resp.status_code == 200
assert resp.content == b"Hello from index"

3090
uv.lock generated Normal file

File diff suppressed because it is too large Load Diff