Skip to content

Commit 849e1f5

Browse files
committed
--offline-mode enhancements #52
Create vendored configs and other generated assets in refactored assetgen build system which handles generated assets for the project. Redirect to dgenerate.pipelinewrapper.hub_configs when possible in --offline-mode This allows loading single file checkpoints for SD1.5, SD2, SD3, and Flux without ever connecting to the hub even once. You must download and specify a VAE on disk manually for Flux and SD3 for single file loads. This behavior is patched into huggingface_hub and diffusers dynamically and active when --offline-mode is enabled. Patch in fix for diffusers not honoring offline mode for sharded checkpoints: huggingface/diffusers#12132
1 parent a12a157 commit 849e1f5

File tree

228 files changed

+2070573
-38
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

228 files changed

+2070573
-38
lines changed

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ recursive-include dgenerate/console/schemas *.json
66
recursive-include dgenerate/console/recipes *.recipe
77
recursive-include dgenerate/extras/hidiffusion/sd_module_key *.txt
88
recursive-include dgenerate/translators/data *.json
9-
recursive-include dgenerate/pipelinewrapper/uris/text_encoder_configs *.json
9+
recursive-include dgenerate/pipelinewrapper/hub_configs *.json *.txt *.model
Lines changed: 16 additions & 10 deletions
File renamed without changes.

doctools/build.py renamed to assetgen/build.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@
2626
from importlib.machinery import SourceFileLoader
2727
from pathlib import Path
2828

29-
from doctools.core.rst_preprocessor import RSTPreprocessor
30-
from doctools.builders.readme_builder import ReadmeBuilder
31-
from doctools.builders.docs_builder import DocsBuilder
32-
from doctools.builders.console_schema_builder import ConsoleSchemaBuilder
33-
from doctools.builders.helsinki_nlp_translation_map_builder import HelsinkiNLPTranslationMapBuilder
29+
from assetgen.core.rst_preprocessor import RSTPreprocessor
30+
from assetgen.builders.readme_builder import ReadmeBuilder
31+
from assetgen.builders.docs_builder import DocsBuilder
32+
from assetgen.builders.console_schema_builder import ConsoleSchemaBuilder
33+
from assetgen.builders.helsinki_nlp_translation_map_builder import HelsinkiNLPTranslationMapBuilder
34+
from assetgen.builders.hf_configs_builder import HfConfigsBuilder
3435

3536

3637
def get_git_revision():
@@ -72,7 +73,7 @@ def setup_argument_parser():
7273

7374
parser.add_argument(
7475
'--target',
75-
choices=['readme', 'docs', 'console-schemas', 'helsinki-nlp-translation-map', 'all'],
76+
choices=['readme', 'docs', 'console-schemas', 'helsinki-nlp-translation-map', 'hf-configs', 'all'],
7677
default='all',
7778
help='What to build (default: all)'
7879
)
@@ -91,9 +92,9 @@ def setup_argument_parser():
9192

9293
return parser
9394

94-
9595
def main():
9696
"""Main entry point for the build script."""
97+
9798
# Get project paths and info
9899
script_path = Path(__file__).absolute().parent
99100
project_dir = (script_path / '..').resolve()
@@ -119,7 +120,7 @@ def main():
119120
if disable_cache_completely:
120121
command_cache_path = None
121122
else:
122-
command_cache_path = project_dir / 'doctools' / 'cache' / 'command.cache.json'
123+
command_cache_path = project_dir / 'assetgen' / 'cache' / 'command.cache.json'
123124
command_cache_path.parent.mkdir(parents=True, exist_ok=True)
124125

125126
# Create template builder - always verbose
@@ -158,6 +159,10 @@ def main():
158159
if args.target in ['helsinki-nlp-translation-map', 'all']:
159160
translation_map_builder = HelsinkiNLPTranslationMapBuilder(project_dir)
160161
translation_map_builder.build()
162+
163+
if args.target in ['hf-configs', 'all']:
164+
hf_configs_builder = HfConfigsBuilder(project_dir)
165+
hf_configs_builder.build()
161166

162167
print("Build completed successfully!")
163168

File renamed without changes.

doctools/builders/docs_builder.py renamed to assetgen/builders/docs_builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def __init__(self, preprocessor: RSTPreprocessor, project_dir: Optional[Path] =
4040
"""
4141
self.preprocessor = preprocessor
4242
self.project_dir = project_dir or Path.cwd()
43-
self.template_dir = self.project_dir / 'doctools' / 'templates' / 'docs'
43+
self.template_dir = self.project_dir / 'assetgen' / 'templates' / 'docs'
4444
self.output_dir = self.project_dir / 'docs'
4545

4646
def build(self):
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2023, Teriks
3+
#
4+
# dgenerate is distributed under the following BSD 3-Clause License
5+
#
6+
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7+
#
8+
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
9+
#
10+
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in
11+
# the documentation and/or other materials provided with the distribution.
12+
#
13+
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
14+
# from this software without specific prior written permission.
15+
#
16+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18+
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22+
23+
import shutil
24+
from pathlib import Path
25+
from typing import Optional
26+
27+
try:
28+
from huggingface_hub import snapshot_download
29+
except ImportError:
30+
snapshot_download = None
31+
32+
33+
class HfConfigsBuilder:
34+
"""Builder for downloading Hugging Face model configuration files."""
35+
36+
def __init__(self, project_dir: Optional[Path] = None):
37+
"""
38+
Initialize the HfConfigsBuilder.
39+
40+
:param project_dir: Project directory (defaults to current working directory)
41+
:type project_dir: Optional[Path]
42+
"""
43+
self.project_dir = project_dir or Path.cwd()
44+
self.output_dir = self.project_dir / 'dgenerate' / 'pipelinewrapper' / 'hub_configs'
45+
46+
# List of repositories to download from
47+
self.repositories = [
48+
"stable-diffusion-v1-5/stable-diffusion-v1-5",
49+
"stabilityai/stable-diffusion-2-1",
50+
"stabilityai/stable-diffusion-xl-base-1.0",
51+
"black-forest-labs/FLUX.1-dev",
52+
"black-forest-labs/FLUX.1-Fill-dev",
53+
"black-forest-labs/FLUX.1-schnell",
54+
"stabilityai/stable-diffusion-3-medium-diffusers",
55+
"stabilityai/stable-diffusion-3.5-large",
56+
"stabilityai/stable-diffusion-3.5-medium"
57+
]
58+
59+
def build(self):
60+
"""Download essential JSON configuration files from Hugging Face repositories."""
61+
print("Downloading Hugging Face model configuration files...")
62+
63+
64+
# Create the target directory if it doesn't exist
65+
self.output_dir.mkdir(parents=True, exist_ok=True)
66+
67+
print(f"Target directory: {self.output_dir}")
68+
print(f"Number of repositories: {len(self.repositories)}")
69+
print("-" * 60)
70+
71+
success_count = 0
72+
failed_repos = []
73+
74+
# Download .json config files from each repository
75+
for i, repo in enumerate(self.repositories, 1):
76+
print(f"[{i}/{len(self.repositories)}] Processing: {repo}")
77+
78+
try:
79+
# Create a directory structure: models--org--name
80+
repo_dir_name = f"models--{repo.replace('/', '--')}"
81+
repo_path = self.output_dir / repo_dir_name
82+
83+
# Download only essential .json config files
84+
essential_patterns = [
85+
"model_index.json", # Main model configuration
86+
"*/config.json", # Component configs (unet, vae, text_encoder, etc.)
87+
"*/scheduler_config.json", # Scheduler configuration
88+
"*/tokenizer_config.json", # Tokenizer configuration
89+
"*/special_tokens_map.json", # Tokenizer special tokens
90+
"*/vocab.json", # Tokenizer vocabulary
91+
"*/tokenizer.json", # Tokenizer data
92+
# Ensure BPE/SentencePiece tokenizers are complete
93+
"merges.txt",
94+
"*/merges.txt",
95+
"**/merges.txt",
96+
"spiece.model",
97+
"*/spiece.model",
98+
"**/spiece.model",
99+
"*/preprocessor_config.json", # Feature extractor config
100+
"*.safetensors.index.json", # Model weight indices
101+
"*.safetensors.index.fp16.json" # FP16 model weight indices
102+
]
103+
104+
snapshot_download(
105+
repo_id=repo,
106+
allow_patterns=essential_patterns,
107+
local_dir=repo_path,
108+
local_dir_use_symlinks=False # Use actual files instead of symlinks
109+
)
110+
111+
# Remove any .cache directories that might have been created
112+
cache_dirs = list(repo_path.rglob(".cache"))
113+
for cache_dir in cache_dirs:
114+
if cache_dir.is_dir():
115+
shutil.rmtree(cache_dir)
116+
print(f" ✓ Removed checkpoint .cache directory")
117+
118+
# Count downloaded JSON files
119+
json_files = list(repo_path.rglob("*.json"))
120+
print(f" ✓ Downloaded {len(json_files)} JSON files")
121+
success_count += 1
122+
123+
except Exception as e:
124+
print(f" ✗ Failed to download from {repo}: {str(e)}")
125+
failed_repos.append(repo)
126+
continue
127+
128+
print()
129+
130+
# Summary
131+
print("=" * 60)
132+
print(f"Download Summary:")
133+
print(f" Successfully processed: {success_count}/{len(self.repositories)} repositories")
134+
135+
if failed_repos:
136+
print(f" Failed repositories:")
137+
for repo in failed_repos:
138+
print(f" - {repo}")
139+
print("✗ Some repositories failed to download")
140+
else:
141+
print(f" ✓ All repositories downloaded successfully!")
142+
143+
print(f"\nJSON config files are now available in: {self.output_dir}")
144+
145+
def get_output_path(self) -> Path:
146+
"""Get the path to the hub configs output directory."""
147+
return self.output_dir

doctools/builders/readme_builder.py renamed to assetgen/builders/readme_builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def __init__(self, preprocessor: RSTPreprocessor, project_dir: Optional[Path] =
4040
"""
4141
self.preprocessor = preprocessor
4242
self.project_dir = project_dir or Path.cwd()
43-
self.template_dir = self.project_dir / 'doctools' / 'templates' / 'readme'
43+
self.template_dir = self.project_dir / 'assetgen' / 'templates' / 'readme'
4444
self.output_file = self.project_dir / 'README.rst'
4545

4646
def build(self):

0 commit comments

Comments
 (0)