haybale-models 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- haybale_models-0.3.1/.gitignore +227 -0
- haybale_models-0.3.1/PKG-INFO +11 -0
- haybale_models-0.3.1/README.md +34 -0
- haybale_models-0.3.1/haybale_models/OVERVIEW.md +30 -0
- haybale_models-0.3.1/haybale_models/QUICKREF.md +27 -0
- haybale_models-0.3.1/haybale_models/__init__.py +44 -0
- haybale_models-0.3.1/haybale_models/consumer.py +115 -0
- haybale_models-0.3.1/haybale_models/describe.py +111 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.editor.ModelsEditor.md +35 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.farmhand.fetch.md +23 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.farmhand.list.md +28 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.farmhand.needs.md +24 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.node.ResolveModelNode.md +33 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.panel.ModelsSettingsPanel.md +14 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.setting.ModelsSettings.md +9 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.state.ModelsState.md +9 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.type.MODEL_REF.md +19 -0
- haybale_models-0.3.1/haybale_models/docs/haybale-models.widget.ModelRefWidget.md +53 -0
- haybale_models-0.3.1/haybale_models/download.py +675 -0
- haybale_models-0.3.1/haybale_models/editors/__init__.py +0 -0
- haybale_models-0.3.1/haybale_models/editors/assisted_dialog.py +180 -0
- haybale_models-0.3.1/haybale_models/editors/models_editor.py +1399 -0
- haybale_models-0.3.1/haybale_models/farmhands/__init__.py +0 -0
- haybale_models-0.3.1/haybale_models/farmhands/model_tools.py +225 -0
- haybale_models-0.3.1/haybale_models/filters.py +78 -0
- haybale_models-0.3.1/haybale_models/haybale.toml +13 -0
- haybale_models-0.3.1/haybale_models/hf.py +386 -0
- haybale_models-0.3.1/haybale_models/keys.py +10 -0
- haybale_models-0.3.1/haybale_models/matcher.py +50 -0
- haybale_models-0.3.1/haybale_models/needs.py +565 -0
- haybale_models-0.3.1/haybale_models/nodes/__init__.py +0 -0
- haybale_models-0.3.1/haybale_models/nodes/resolve_model.py +66 -0
- haybale_models-0.3.1/haybale_models/panels/__init__.py +1 -0
- haybale_models-0.3.1/haybale_models/panels/models_settings_panel.py +32 -0
- haybale_models-0.3.1/haybale_models/pool.py +113 -0
- haybale_models-0.3.1/haybale_models/py.typed +0 -0
- haybale_models-0.3.1/haybale_models/receive.py +150 -0
- haybale_models-0.3.1/haybale_models/resolver.py +418 -0
- haybale_models-0.3.1/haybale_models/settings/__init__.py +0 -0
- haybale_models-0.3.1/haybale_models/settings/models_settings.py +17 -0
- haybale_models-0.3.1/haybale_models/signals.py +18 -0
- haybale_models-0.3.1/haybale_models/sources/__init__.py +48 -0
- haybale_models-0.3.1/haybale_models/sources/base.py +195 -0
- haybale_models-0.3.1/haybale_models/sources/caches.py +124 -0
- haybale_models-0.3.1/haybale_models/sources/folder.py +143 -0
- haybale_models-0.3.1/haybale_models/sources/hf.py +110 -0
- haybale_models-0.3.1/haybale_models/sources/https.py +132 -0
- haybale_models-0.3.1/haybale_models/sources/registry.py +97 -0
- haybale_models-0.3.1/haybale_models/state/__init__.py +0 -0
- haybale_models-0.3.1/haybale_models/state/models_state.py +179 -0
- haybale_models-0.3.1/haybale_models/status.py +48 -0
- haybale_models-0.3.1/haybale_models/store/__init__.py +1 -0
- haybale_models-0.3.1/haybale_models/store/index.py +302 -0
- haybale_models-0.3.1/haybale_models/store/layout.py +118 -0
- haybale_models-0.3.1/haybale_models/store/records.py +125 -0
- haybale_models-0.3.1/haybale_models/types/__init__.py +0 -0
- haybale_models-0.3.1/haybale_models/types/model_ref.py +74 -0
- haybale_models-0.3.1/haybale_models/upload_route.py +176 -0
- haybale_models-0.3.1/haybale_models/value/__init__.py +5 -0
- haybale_models-0.3.1/haybale_models/value/model_ref.py +83 -0
- haybale_models-0.3.1/haybale_models/widgets/__init__.py +0 -0
- haybale_models-0.3.1/haybale_models/widgets/model_ref_widget.py +657 -0
- haybale_models-0.3.1/pyproject.toml +24 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# The MANIFEST/*.manifest packaging-artifact rules above are unanchored and
|
|
36
|
+
# (on a case-insensitive filesystem) also match our real, tracked
|
|
37
|
+
# haywire_studio/share/manifest/ package — un-ignore it explicitly.
|
|
38
|
+
!packages/haywire-studio/src/haywire_studio/share/manifest/
|
|
39
|
+
|
|
40
|
+
# Installer logs
|
|
41
|
+
pip-log.txt
|
|
42
|
+
pip-delete-this-directory.txt
|
|
43
|
+
|
|
44
|
+
# Unit test / coverage reports
|
|
45
|
+
htmlcov/
|
|
46
|
+
.tox/
|
|
47
|
+
.nox/
|
|
48
|
+
.coverage
|
|
49
|
+
.coverage.*
|
|
50
|
+
.cache
|
|
51
|
+
nosetests.xml
|
|
52
|
+
coverage.xml
|
|
53
|
+
*.cover
|
|
54
|
+
*.py,cover
|
|
55
|
+
.hypothesis/
|
|
56
|
+
.pytest_cache/
|
|
57
|
+
cover/
|
|
58
|
+
|
|
59
|
+
# Translations
|
|
60
|
+
*.mo
|
|
61
|
+
*.pot
|
|
62
|
+
|
|
63
|
+
# Django stuff:
|
|
64
|
+
*.log
|
|
65
|
+
local_settings.py
|
|
66
|
+
db.sqlite3
|
|
67
|
+
db.sqlite3-journal
|
|
68
|
+
|
|
69
|
+
# Flask stuff:
|
|
70
|
+
instance/
|
|
71
|
+
.webassets-cache
|
|
72
|
+
|
|
73
|
+
# Scrapy stuff:
|
|
74
|
+
.scrapy
|
|
75
|
+
|
|
76
|
+
# Sphinx documentation
|
|
77
|
+
docs/_build/
|
|
78
|
+
|
|
79
|
+
# PyBuilder
|
|
80
|
+
.pybuilder/
|
|
81
|
+
target/
|
|
82
|
+
|
|
83
|
+
# Jupyter Notebook
|
|
84
|
+
.ipynb_checkpoints
|
|
85
|
+
|
|
86
|
+
# IPython
|
|
87
|
+
profile_default/
|
|
88
|
+
ipython_config.py
|
|
89
|
+
|
|
90
|
+
# pyenv
|
|
91
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
92
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
93
|
+
# .python-version
|
|
94
|
+
|
|
95
|
+
# pipenv
|
|
96
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
97
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
98
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
99
|
+
# install all needed dependencies.
|
|
100
|
+
#Pipfile.lock
|
|
101
|
+
|
|
102
|
+
# UV
|
|
103
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
104
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
105
|
+
# commonly ignored for libraries.
|
|
106
|
+
#uv.lock
|
|
107
|
+
|
|
108
|
+
# poetry
|
|
109
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
110
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
111
|
+
# commonly ignored for libraries.
|
|
112
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
113
|
+
#poetry.lock
|
|
114
|
+
|
|
115
|
+
# pdm
|
|
116
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
117
|
+
#pdm.lock
|
|
118
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
119
|
+
# in version control.
|
|
120
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
121
|
+
.pdm.toml
|
|
122
|
+
.pdm-python
|
|
123
|
+
.pdm-build/
|
|
124
|
+
|
|
125
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
126
|
+
__pypackages__/
|
|
127
|
+
|
|
128
|
+
# Celery stuff
|
|
129
|
+
celerybeat-schedule
|
|
130
|
+
celerybeat.pid
|
|
131
|
+
|
|
132
|
+
# SageMath parsed files
|
|
133
|
+
*.sage.py
|
|
134
|
+
|
|
135
|
+
# Environments
|
|
136
|
+
**/.env
|
|
137
|
+
.venv
|
|
138
|
+
env/
|
|
139
|
+
venv/
|
|
140
|
+
ENV/
|
|
141
|
+
env.bak/
|
|
142
|
+
venv.bak/
|
|
143
|
+
|
|
144
|
+
# Spyder project settings
|
|
145
|
+
.spyderproject
|
|
146
|
+
.spyproject
|
|
147
|
+
|
|
148
|
+
# Rope project settings
|
|
149
|
+
.ropeproject
|
|
150
|
+
|
|
151
|
+
# mkdocs documentation
|
|
152
|
+
/site
|
|
153
|
+
|
|
154
|
+
# mypy
|
|
155
|
+
.mypy_cache/
|
|
156
|
+
.dmypy.json
|
|
157
|
+
dmypy.json
|
|
158
|
+
|
|
159
|
+
# Pyre type checker
|
|
160
|
+
.pyre/
|
|
161
|
+
|
|
162
|
+
# pytype static type analyzer
|
|
163
|
+
.pytype/
|
|
164
|
+
|
|
165
|
+
# Cython debug symbols
|
|
166
|
+
cython_debug/
|
|
167
|
+
|
|
168
|
+
# PyCharm
|
|
169
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
170
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
171
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
172
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
173
|
+
.idea/
|
|
174
|
+
|
|
175
|
+
# Abstra
|
|
176
|
+
# Abstra is an AI-powered process automation framework.
|
|
177
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
178
|
+
# Learn more at https://abstra.io/docs
|
|
179
|
+
.abstra/
|
|
180
|
+
|
|
181
|
+
# Visual Studio Code
|
|
182
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
183
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
184
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
185
|
+
# you could uncomment the following to ignore the enitre vscode folder
|
|
186
|
+
.vscode/
|
|
187
|
+
|
|
188
|
+
# Ruff stuff:
|
|
189
|
+
.ruff_cache/
|
|
190
|
+
|
|
191
|
+
# PyPI configuration file
|
|
192
|
+
.pypirc
|
|
193
|
+
|
|
194
|
+
# Cursor
|
|
195
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
196
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
197
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
198
|
+
.cursorignore
|
|
199
|
+
.cursorindexingignore
|
|
200
|
+
|
|
201
|
+
.DS_Store
|
|
202
|
+
saves/*.*
|
|
203
|
+
|
|
204
|
+
# Dev-mode graphs (local testing only, not part of the repo)
|
|
205
|
+
graphs/*.json
|
|
206
|
+
# MkDocs build output
|
|
207
|
+
site/
|
|
208
|
+
|
|
209
|
+
# Baked docs for the haywire-core wheel (generated by scripts/bake_docs.py;
|
|
210
|
+
# force-included into the wheel at build time). The *contents* are never
|
|
211
|
+
# committed, but the directory itself is kept via a committed .gitkeep so the
|
|
212
|
+
# hatchling force-include target exists in a fresh checkout — otherwise every
|
|
213
|
+
# editable install (`uv sync`) fails the build on a missing force-include path.
|
|
214
|
+
packages/haywire-core/src/haywire/_baked_docs/*
|
|
215
|
+
!packages/haywire-core/src/haywire/_baked_docs/.gitkeep
|
|
216
|
+
|
|
217
|
+
# Local git worktrees (claude-code workflow)
|
|
218
|
+
.worktrees/
|
|
219
|
+
|
|
220
|
+
# Haywire project config (user-local, not repo state)
|
|
221
|
+
.haywire/
|
|
222
|
+
|
|
223
|
+
# Local symlinks to external dev projects (machine-specific paths)
|
|
224
|
+
barn/haybale-visiongraph
|
|
225
|
+
barn/haybale-spandrel
|
|
226
|
+
|
|
227
|
+
assets/*
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: haybale-models
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Model references, local model store, downloads and graph needs-list
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: models
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: haybale-graph-editor>=0.3.1
|
|
9
|
+
Requires-Dist: haywire-core>=0.3.1
|
|
10
|
+
Requires-Dist: httpx>=0.28
|
|
11
|
+
Requires-Dist: huggingface-hub>=2.4
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Models
|
|
2
|
+
|
|
3
|
+
<!-- marketstall:share-url:start -->
|
|
4
|
+
*Subscribe URL not yet published — run `haywire share --save`.*
|
|
5
|
+
<!-- marketstall:share-url:end -->
|
|
6
|
+
|
|
7
|
+
Model references, local model store, downloads and graph needs-list
|
|
8
|
+
|
|
9
|
+
## Nodes
|
|
10
|
+
### Models
|
|
11
|
+
- **Resolve Model** — Gives the file of a model on this machine, with its sha256 and size
|
|
12
|
+
|
|
13
|
+
## Types
|
|
14
|
+
- **Model** — A model, named by a catalog entry or by a file (url and sha256). Resolve Model turns it into a path on disk.
|
|
15
|
+
|
|
16
|
+
## Widgets
|
|
17
|
+
- **ModelRefWidget** — Pick a model
|
|
18
|
+
|
|
19
|
+
## Settings
|
|
20
|
+
- **Models** — Model download behaviour
|
|
21
|
+
|
|
22
|
+
## Farmhands
|
|
23
|
+
- **Fetch models** — Queue downloads: everything an open graph needs, or catalog entries by key (admin).
|
|
24
|
+
- **List models** — Catalog entries with per-variant status, indexed local files and download jobs.
|
|
25
|
+
- **Models a graph needs** — The models an open graph references and does not have locally.
|
|
26
|
+
|
|
27
|
+
## States
|
|
28
|
+
- **Models** — Model volumes, file index, download queue and the pool of loaded models
|
|
29
|
+
|
|
30
|
+
## Panels
|
|
31
|
+
- **Models** — Model download behaviour: how many files download in parallel.
|
|
32
|
+
|
|
33
|
+
## Editors
|
|
34
|
+
- **Models** — Model files on this machine and where they come from
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Models
|
|
2
|
+
|
|
3
|
+
Model references, local model store, downloads and graph needs-list
|
|
4
|
+
|
|
5
|
+
## Nodes
|
|
6
|
+
### Models
|
|
7
|
+
- **Resolve Model** — Gives the file of a model on this machine, with its sha256 and size
|
|
8
|
+
|
|
9
|
+
## Types
|
|
10
|
+
- **Model** — A model, named by a catalog entry or by a file (url and sha256). Resolve Model turns it into a path on disk.
|
|
11
|
+
|
|
12
|
+
## Widgets
|
|
13
|
+
- **ModelRefWidget** — Pick a model
|
|
14
|
+
|
|
15
|
+
## Settings
|
|
16
|
+
- **Models** — Model download behaviour
|
|
17
|
+
|
|
18
|
+
## Farmhands
|
|
19
|
+
- **Fetch models** — Queue downloads: everything an open graph needs, or catalog entries by key (admin).
|
|
20
|
+
- **List models** — Catalog entries with per-variant status, indexed local files and download jobs.
|
|
21
|
+
- **Models a graph needs** — The models an open graph references and does not have locally.
|
|
22
|
+
|
|
23
|
+
## States
|
|
24
|
+
- **Models** — Model volumes, file index, download queue and the pool of loaded models
|
|
25
|
+
|
|
26
|
+
## Panels
|
|
27
|
+
- **Models** — Model download behaviour: how many files download in parallel.
|
|
28
|
+
|
|
29
|
+
## Editors
|
|
30
|
+
- **Models** — Model files on this machine and where they come from
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# haybale-models — component index (v0.3.1)
|
|
2
|
+
|
|
3
|
+
## node
|
|
4
|
+
- `haybale-models:node:ResolveModelNode` — Resolve Model — Gives the file of a model on this machine, with its sha256 and size _tags: model, resolve, file, path, weights, catalog, sha256_
|
|
5
|
+
|
|
6
|
+
## type
|
|
7
|
+
- `haybale-models:type:MODEL_REF` — Model — A model, named by a catalog entry or by a file (url and sha256). Resolve Model turns it into a path on disk.
|
|
8
|
+
|
|
9
|
+
## widget
|
|
10
|
+
- `haybale-models:widget:ModelRefWidget` — ModelRefWidget — Pick a model
|
|
11
|
+
|
|
12
|
+
## setting
|
|
13
|
+
- `haybale-models:setting:ModelsSettings` — Models — Model download behaviour
|
|
14
|
+
|
|
15
|
+
## farmhand
|
|
16
|
+
- `haybale-models:farmhand:fetch` — Fetch models — Queue downloads: everything an open graph needs, or catalog entries by key (admin).
|
|
17
|
+
- `haybale-models:farmhand:list` — List models — Catalog entries with per-variant status, indexed local files and download jobs.
|
|
18
|
+
- `haybale-models:farmhand:needs` — Models a graph needs — The models an open graph references and does not have locally.
|
|
19
|
+
|
|
20
|
+
## state
|
|
21
|
+
- `haybale-models:state:ModelsState` — Models — Model volumes, file index, download queue and the pool of loaded models
|
|
22
|
+
|
|
23
|
+
## panel
|
|
24
|
+
- `haybale-models:panel:ModelsSettingsPanel` — Models — Model download behaviour: how many files download in parallel.
|
|
25
|
+
|
|
26
|
+
## editor
|
|
27
|
+
- `haybale-models:editor:ModelsEditor` — Models — Model files on this machine and where they come from
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Haybale-Models: model references, local model store, downloads and needs-list.
|
|
2
|
+
|
|
3
|
+
Component folders (registered in ``register_components``):
|
|
4
|
+
- ``state``: ``ModelsState`` with the two model volumes.
|
|
5
|
+
- ``settings``, ``types``, ``widgets``, ``nodes``, ``editors``, ``farmhands``, ``panels``.
|
|
6
|
+
|
|
7
|
+
Never imports ``torch``: variant choice comes from the consuming node's settings.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from haywire.core.farmhand import FarmhandRegistry
|
|
13
|
+
from haywire.core.library.base import BaseLibrary
|
|
14
|
+
from haywire.core.library.decorator import library
|
|
15
|
+
from haywire.core.node.registry import NodeRegistry
|
|
16
|
+
from haywire.core.settings.registry import SettingsRegistry
|
|
17
|
+
from haywire.core.state import LibraryStateRegistry
|
|
18
|
+
from haywire.core.types.registry import TypeRegistry
|
|
19
|
+
from haywire.ui.editor.registry import EditorTypeRegistry
|
|
20
|
+
from haywire.ui.panel.registry import PanelRegistry
|
|
21
|
+
from haywire.ui.widget.registry import WidgetRegistry
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@library(
|
|
25
|
+
file_watcher=True,
|
|
26
|
+
)
|
|
27
|
+
class Library(BaseLibrary):
|
|
28
|
+
"""Models library — model references, store and downloads."""
|
|
29
|
+
|
|
30
|
+
def register_components(self):
|
|
31
|
+
"""Register the component folders with their registries."""
|
|
32
|
+
base_path = Path(__file__).parent
|
|
33
|
+
|
|
34
|
+
self.add_folder_to_registry(folder_path=str(base_path / "state"), registry_cls=LibraryStateRegistry)
|
|
35
|
+
self.add_folder_to_registry(folder_path=str(base_path / "settings"), registry_cls=SettingsRegistry)
|
|
36
|
+
self.add_folder_to_registry(folder_path=str(base_path / "types"), registry_cls=TypeRegistry)
|
|
37
|
+
self.add_folder_to_registry(folder_path=str(base_path / "widgets"), registry_cls=WidgetRegistry)
|
|
38
|
+
self.add_folder_to_registry(folder_path=str(base_path / "nodes"), registry_cls=NodeRegistry)
|
|
39
|
+
self.add_folder_to_registry(folder_path=str(base_path / "editors"), registry_cls=EditorTypeRegistry)
|
|
40
|
+
self.add_folder_to_registry(folder_path=str(base_path / "farmhands"), registry_cls=FarmhandRegistry)
|
|
41
|
+
self.add_folder_to_registry(folder_path=str(base_path / "panels"), registry_cls=PanelRegistry)
|
|
42
|
+
|
|
43
|
+
def validate(self) -> bool:
|
|
44
|
+
return True
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Helpers for node authors: resolve a MODEL_REF port and hold its loaded model per run."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Hashable
|
|
6
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
7
|
+
|
|
8
|
+
from haywire.core.di.context import get_library_state_container
|
|
9
|
+
from haywire.core.node.node_warning import NodeWarning
|
|
10
|
+
|
|
11
|
+
from haybale_models.matcher import Preference
|
|
12
|
+
from haybale_models.resolver import ModelMissing, ResolvedModel, resolve
|
|
13
|
+
from haybale_models.state.models_state import ModelsState
|
|
14
|
+
from haybale_models.value.model_ref import ModelRef
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from haywire.core.node.base import BaseNode
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"KIND",
|
|
21
|
+
"MISSING_WARNING",
|
|
22
|
+
"ModelMissing",
|
|
23
|
+
"acquire_port",
|
|
24
|
+
"models_state",
|
|
25
|
+
"preference_for",
|
|
26
|
+
"release_node",
|
|
27
|
+
"resolve_port",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
T = TypeVar("T")
|
|
31
|
+
|
|
32
|
+
KIND = "model"
|
|
33
|
+
MISSING_WARNING = NodeWarning(message="Model missing — Show in Models", source_version=None, kind=KIND)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def models_state() -> ModelsState:
|
|
37
|
+
"""The app-wide :class:`ModelsState`.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
RuntimeError: If haybale-models is not enabled.
|
|
41
|
+
"""
|
|
42
|
+
state = get_library_state_container().get(ModelsState)
|
|
43
|
+
if state is None:
|
|
44
|
+
raise RuntimeError("haybale-models is not enabled")
|
|
45
|
+
return state
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def preference_for(node: BaseNode, port_id: str) -> Preference:
|
|
49
|
+
"""The variant preference for *port_id*.
|
|
50
|
+
|
|
51
|
+
The node's ``model_preference(port_id)`` if it defines one, else the port's ``prefer``
|
|
52
|
+
property, else ``[{}]``.
|
|
53
|
+
"""
|
|
54
|
+
hook = getattr(node, "model_preference", None)
|
|
55
|
+
if callable(hook):
|
|
56
|
+
return hook(port_id) # type: ignore[no-any-return]
|
|
57
|
+
prefer = node.ports[port_id].widget_config.get("properties", {}).get("prefer")
|
|
58
|
+
return list(prefer) if prefer else [{}]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def resolve_port(node: BaseNode, port_id: str) -> ResolvedModel:
|
|
62
|
+
"""Resolve the model on *port_id* and set the node's ``"model"`` warnings.
|
|
63
|
+
|
|
64
|
+
All ``MODEL_REF`` ports of one node share a single ``"model"`` warning list, so with
|
|
65
|
+
several ports the last call wins. Key the list per port when a two-port consumer lands.
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
ModelMissing: If no local file matches the reference.
|
|
69
|
+
"""
|
|
70
|
+
state = models_state()
|
|
71
|
+
value = node.ports[port_id].get_value()
|
|
72
|
+
ref = value if isinstance(value, ModelRef) else ModelRef.from_dict(value or {})
|
|
73
|
+
try:
|
|
74
|
+
resolved = resolve(
|
|
75
|
+
ref,
|
|
76
|
+
preference_for(node, port_id),
|
|
77
|
+
catalog=state.catalog,
|
|
78
|
+
index=state.index,
|
|
79
|
+
sources=state.sources,
|
|
80
|
+
)
|
|
81
|
+
except ModelMissing:
|
|
82
|
+
node.wrapper.replace_warnings(KIND, [] if ref.is_empty else [MISSING_WARNING])
|
|
83
|
+
raise
|
|
84
|
+
node.wrapper.replace_warnings(KIND, _warnings(resolved))
|
|
85
|
+
return resolved
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _warnings(resolved: ResolvedModel) -> list[NodeWarning]:
|
|
89
|
+
messages: list[str] = []
|
|
90
|
+
if resolved.better_unfetched is not None:
|
|
91
|
+
variant = resolved.better_unfetched.variant_name()
|
|
92
|
+
messages.append(f"Preferred variant {variant} is not downloaded — Show in Models")
|
|
93
|
+
if resolved.ref.is_file and not resolved.ref.sha256:
|
|
94
|
+
messages.append("Not pinned — use the pin on the card")
|
|
95
|
+
return [NodeWarning(message=m, source_version=None, kind=KIND) for m in messages]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def acquire_port(
|
|
99
|
+
node: BaseNode,
|
|
100
|
+
port_id: str,
|
|
101
|
+
*,
|
|
102
|
+
variant_key: Hashable,
|
|
103
|
+
load: Callable[[ResolvedModel], T],
|
|
104
|
+
release: Callable[[T], None],
|
|
105
|
+
) -> T:
|
|
106
|
+
"""Resolve *port_id* and hold its loaded model for *node*; nodes share one load per key."""
|
|
107
|
+
resolved = resolve_port(node, port_id)
|
|
108
|
+
return models_state().pool.acquire(
|
|
109
|
+
(resolved.key, variant_key), node, load=lambda: load(resolved), release=release
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def release_node(node: BaseNode) -> None:
|
|
114
|
+
"""Drop *node*'s holds. Call from ``on_shutdown`` and, as a backstop, ``on_teardown``."""
|
|
115
|
+
models_state().pool.release(node)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""What a catalog entry is, in words: a one-line summary, a plain-text description, and search text.
|
|
2
|
+
|
|
3
|
+
Used wherever a person chooses between models (the model picker, the Models editor),
|
|
4
|
+
so both describe an entry the same way.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from haywire.core.catalog import CatalogEntry
|
|
14
|
+
|
|
15
|
+
from haybale_models.filters import COMMERCIAL, NON_COMMERCIAL
|
|
16
|
+
|
|
17
|
+
_NOT_NON_COMMERCIAL = re.compile(r"(?<!non-)commercial")
|
|
18
|
+
|
|
19
|
+
__all__ = ["entry_description", "entry_search_text", "entry_summary", "search_matches"]
|
|
20
|
+
|
|
21
|
+
#: Tags shown in a summary line; the rest are counted (``+2``).
|
|
22
|
+
SUMMARY_TAGS = 4
|
|
23
|
+
|
|
24
|
+
#: Longest description shown in a tip, in characters; longer text ends in ``…``.
|
|
25
|
+
DESCRIPTION_LIMIT = 400
|
|
26
|
+
|
|
27
|
+
_LINK = re.compile(r"\[([^\]]+)\]\([^)]*\)")
|
|
28
|
+
_MARKUP = re.compile(r"[*_`#>]+")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def entry_summary(entry: CatalogEntry, *, scale: bool = True) -> str:
|
|
32
|
+
"""Return what tells *entry* apart at a glance, such as ``4x · ESRGAN · anime, photo +2``.
|
|
33
|
+
|
|
34
|
+
Scale, architecture and the first tags, each left out when the entry has none.
|
|
35
|
+
``scale=False`` leaves the scale out, for a place that already shows it.
|
|
36
|
+
"""
|
|
37
|
+
parts: list[str] = []
|
|
38
|
+
if scale and entry.scale:
|
|
39
|
+
parts.append(f"{entry.scale}x")
|
|
40
|
+
if entry.architecture:
|
|
41
|
+
parts.append(entry.architecture)
|
|
42
|
+
if entry.tags:
|
|
43
|
+
shown = ", ".join(entry.tags[:SUMMARY_TAGS])
|
|
44
|
+
rest = len(entry.tags) - SUMMARY_TAGS
|
|
45
|
+
parts.append(f"{shown} +{rest}" if rest > 0 else shown)
|
|
46
|
+
return " · ".join(parts)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def entry_description(entry: CatalogEntry) -> str:
|
|
50
|
+
"""Return *entry*'s description as plain text for a tip, with purpose, author and licence below.
|
|
51
|
+
|
|
52
|
+
Markdown links keep their text, other markup is dropped, and a description longer
|
|
53
|
+
than ``DESCRIPTION_LIMIT`` characters is cut at a word and ends in ``…``.
|
|
54
|
+
"""
|
|
55
|
+
text = _plain(entry.description)
|
|
56
|
+
if len(text) > DESCRIPTION_LIMIT:
|
|
57
|
+
text = text[:DESCRIPTION_LIMIT].rsplit(" ", 1)[0] + " …"
|
|
58
|
+
facts = []
|
|
59
|
+
if entry.purpose:
|
|
60
|
+
facts.append("Purpose: " + ", ".join(entry.purpose))
|
|
61
|
+
if entry.author:
|
|
62
|
+
facts.append("By " + entry.author)
|
|
63
|
+
if licence := entry_licence_text(entry):
|
|
64
|
+
facts.append("Licence: " + licence)
|
|
65
|
+
return "\n".join(part for part in (text, " · ".join(facts)) if part)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def entry_licence_text(entry: CatalogEntry) -> str:
|
|
69
|
+
"""Return *entry*'s licence, followed by ``non-commercial`` unless its author states otherwise.
|
|
70
|
+
|
|
71
|
+
Empty for a commercial entry without a licence.
|
|
72
|
+
"""
|
|
73
|
+
parts = [entry.license, "" if entry.commercial else NON_COMMERCIAL]
|
|
74
|
+
return " · ".join(part for part in parts if part)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def entry_search_text(entry: CatalogEntry) -> str:
|
|
78
|
+
"""Return the lower-case text a search over *entry* matches; see :func:`search_matches`.
|
|
79
|
+
|
|
80
|
+
Its name, scale (``4x``), architecture, purpose, tags, licence, commercial use and description.
|
|
81
|
+
"""
|
|
82
|
+
words = [
|
|
83
|
+
entry.name,
|
|
84
|
+
f"{entry.scale}x" if entry.scale else "",
|
|
85
|
+
entry.architecture,
|
|
86
|
+
*entry.purpose,
|
|
87
|
+
*entry.tags,
|
|
88
|
+
entry.license,
|
|
89
|
+
COMMERCIAL if entry.commercial else NON_COMMERCIAL,
|
|
90
|
+
_plain(entry.description),
|
|
91
|
+
]
|
|
92
|
+
return " ".join(w for w in words if w).lower()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def search_matches(query: str, text: str) -> bool:
|
|
96
|
+
"""Return whether every word of *query* appears somewhere in *text*, ignoring case.
|
|
97
|
+
|
|
98
|
+
A word may be part of a longer one, in any order. An empty query matches everything. The word
|
|
99
|
+
``commercial`` does not match ``non-commercial``.
|
|
100
|
+
"""
|
|
101
|
+
text = text.lower()
|
|
102
|
+
return all(
|
|
103
|
+
_NOT_NON_COMMERCIAL.search(text) if word == COMMERCIAL else word in text
|
|
104
|
+
for word in query.lower().split()
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _plain(markdown: str) -> str:
|
|
109
|
+
text = _LINK.sub(r"\1", markdown or "")
|
|
110
|
+
text = _MARKUP.sub("", text)
|
|
111
|
+
return " ".join(text.split())
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Models
|
|
2
|
+
|
|
3
|
+
`haybale-models:editor:ModelsEditor` · kind: editor
|
|
4
|
+
|
|
5
|
+
Model files on this machine and where they come from
|
|
6
|
+
|
|
7
|
+
## Details
|
|
8
|
+
|
|
9
|
+
- **default_slot**: `action`
|
|
10
|
+
- **opens**: `OpenBehavior.REQUIRED`
|
|
11
|
+
- **order**: `100`
|
|
12
|
+
|
|
13
|
+
## Notes
|
|
14
|
+
|
|
15
|
+
Lists the model catalog, the files in the Models volume and the Hugging Face files in use.
|
|
16
|
+
|
|
17
|
+
The **Filters** icon in the header opens the filter section. Its **Show:** row picks the sections
|
|
18
|
+
(**Catalog**, **Models**, **Huggingface**). Its search toggle opens a field that narrows every
|
|
19
|
+
section: each word must appear, in any order, and scale, licence and `commercial` count too. Its
|
|
20
|
+
**+** adds a filter on library, purpose, tags, architecture, scale, commercial use or status, which
|
|
21
|
+
narrows the catalog. Closing it switches them all off, so a closed section always means the full
|
|
22
|
+
list, and reopening it brings them back.
|
|
23
|
+
The **Needed** section is always shown: per open graph, the models it lacks; **Fetch all**
|
|
24
|
+
downloads them. Each section header counts what the section lists; hover it for the whole
|
|
25
|
+
volume's size.
|
|
26
|
+
|
|
27
|
+
A catalog row reads `non-commercial` unless its author states the model may be used commercially.
|
|
28
|
+
Each catalog resource shows its status: `local`, `HF cache`, `downloading`, `failed` or
|
|
29
|
+
`not downloaded`. **Models** lists the files in the Models volume that no catalog entry
|
|
30
|
+
occupies. **Huggingface** lists the cached files that catalog entries or open graphs use,
|
|
31
|
+
and what uses each; the Volumes editor shows where the cache lives. Download, Cancel and
|
|
32
|
+
Remove need admin access; a removal names the open graphs that use the file and the bytes
|
|
33
|
+
it frees. Cancel is offered only while a download can still be stopped: a Hugging Face
|
|
34
|
+
download, once started, runs to the end. Opening the editor and **Rescan** index the Models
|
|
35
|
+
volume on a background thread.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Fetch models
|
|
2
|
+
|
|
3
|
+
`haybale-models:farmhand:fetch` · kind: farmhand
|
|
4
|
+
|
|
5
|
+
Queue downloads: everything an open graph needs, or catalog entries by key (admin).
|
|
6
|
+
|
|
7
|
+
## Agent Instructions
|
|
8
|
+
|
|
9
|
+
Queues the downloads and returns their job ids; progress is in models_list jobs. Pass binding_id to fetch everything that graph needs, or keys=[<catalog key>, ...] to fetch the preferred variant of each entry. Needs admin access.
|
|
10
|
+
|
|
11
|
+
## Details
|
|
12
|
+
|
|
13
|
+
- **input_schema**: `{'type': 'object', 'properties': {'binding_id': {'type': 'string', 'default': None}, 'keys': {'type': 'array', 'items': {'type': 'string'}, 'default': None}}, 'required': []}`
|
|
14
|
+
- **annotations**: `{'read_only_hint': False, 'destructive_hint': False, 'idempotent_hint': False, 'open_world_hint': True}`
|
|
15
|
+
|
|
16
|
+
## Notes
|
|
17
|
+
|
|
18
|
+
Queues downloads of missing models. Needs ADMIN access.
|
|
19
|
+
|
|
20
|
+
Pass `binding_id` to fetch everything that open graph needs, or `keys` to fetch the
|
|
21
|
+
preferred variant of each catalog entry. Returns the job ids at once; the files
|
|
22
|
+
arrive in the background, so call `models_list` to follow the jobs. Downloads use
|
|
23
|
+
network and disk: call it only when the user wants the models.
|