Skip to content

Commit 0df018b

Browse files
author
Lincoln Stein
committed
resolve merge conflicts
2 parents 7088d56 + b03073d commit 0df018b

File tree

110 files changed

+5588
-3233
lines changed

Some content is hidden

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

110 files changed

+5588
-3233
lines changed

docs/contributing/DOWNLOAD_QUEUE.md

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ The queue operates on a series of download job objects. These objects
128128
specify the source and destination of the download, and keep track of
129129
the progress of the download.
130130

131-
The only job type currently implemented is `DownloadJob`, a pydantic object with the
131+
Two job types are defined. `DownloadJob` and
132+
`MultiFileDownloadJob`. The former is a pydantic object with the
132133
following fields:
133134

134135
| **Field** | **Type** | **Default** | **Description** |
@@ -138,7 +139,7 @@ following fields:
138139
| `dest` | Path | | Where to download to |
139140
| `access_token` | str | | [optional] string containing authentication token for access |
140141
| `on_start` | Callable | | [optional] callback when the download starts |
141-
| `on_progress` | Callable | | [optional] callback called at intervals during download progress |
142+
| `on_progress` | Callable | | [optional] callback called at intervals during download progress |
142143
| `on_complete` | Callable | | [optional] callback called after successful download completion |
143144
| `on_error` | Callable | | [optional] callback called after an error occurs |
144145
| `id` | int | auto assigned | Job ID, an integer >= 0 |
@@ -190,6 +191,33 @@ A cancelled job will have status `DownloadJobStatus.ERROR` and an
190191
`error_type` field of "DownloadJobCancelledException". In addition,
191192
the job's `cancelled` property will be set to True.
192193

194+
The `MultiFileDownloadJob` is used for diffusers model downloads,
195+
which contain multiple files and directories under a common root:
196+
197+
| **Field** | **Type** | **Default** | **Description** |
198+
|----------------|-----------------|---------------|-----------------|
199+
| _Fields passed in at job creation time_ |
200+
| `download_parts` | Set[DownloadJob]| | Component download jobs |
201+
| `dest` | Path | | Where to download to |
202+
| `on_start` | Callable | | [optional] callback when the download starts |
203+
| `on_progress` | Callable | | [optional] callback called at intervals during download progress |
204+
| `on_complete` | Callable | | [optional] callback called after successful download completion |
205+
| `on_error` | Callable | | [optional] callback called after an error occurs |
206+
| `id` | int | auto assigned | Job ID, an integer >= 0 |
207+
| _Fields updated over the course of the download task_
208+
| `status` | DownloadJobStatus| | Status code |
209+
| `download_path` | Path | | Path to the root of the downloaded files |
210+
| `bytes` | int | 0 | Bytes downloaded so far |
211+
| `total_bytes` | int | 0 | Total size of the file at the remote site |
212+
| `error_type` | str | | String version of the exception that caused an error during download |
213+
| `error` | str | | String version of the traceback associated with an error |
214+
| `cancelled` | bool | False | Set to true if the job was cancelled by the caller|
215+
216+
Note that the MultiFileDownloadJob does not support the `priority`,
217+
`job_started`, `job_ended` or `content_type` attributes. You can get
218+
these from the individual download jobs in `download_parts`.
219+
220+
193221
### Callbacks
194222

195223
Download jobs can be associated with a series of callbacks, each with
@@ -251,11 +279,40 @@ jobs using `list_jobs()`, fetch a single job by its with
251279
running jobs with `cancel_all_jobs()`, and wait for all jobs to finish
252280
with `join()`.
253281

254-
#### job = queue.download(source, dest, priority, access_token)
282+
#### job = queue.download(source, dest, priority, access_token, on_start, on_progress, on_complete, on_cancelled, on_error)
255283

256284
Create a new download job and put it on the queue, returning the
257285
DownloadJob object.
258286

287+
#### multifile_job = queue.multifile_download(parts, dest, access_token, on_start, on_progress, on_complete, on_cancelled, on_error)
288+
289+
This is similar to download(), but instead of taking a single source,
290+
it accepts a `parts` argument consisting of a list of
291+
`RemoteModelFile` objects. Each part corresponds to a URL/Path pair,
292+
where the URL is the location of the remote file, and the Path is the
293+
destination.
294+
295+
`RemoteModelFile` can be imported from `invokeai.backend.model_manager.metadata`, and
296+
consists of a url/path pair. Note that the path *must* be relative.
297+
298+
The method returns a `MultiFileDownloadJob`.
299+
300+
301+
```
302+
from invokeai.backend.model_manager.metadata import RemoteModelFile
303+
remote_file_1 = RemoteModelFile(url='http://www.foo.bar/my/pytorch_model.safetensors'',
304+
path='my_model/textencoder/pytorch_model.safetensors'
305+
)
306+
remote_file_2 = RemoteModelFile(url='http://www.bar.baz/vae.ckpt',
307+
path='my_model/vae/diffusers_model.safetensors'
308+
)
309+
job = queue.multifile_download(parts=[remote_file_1, remote_file_2],
310+
dest='/tmp/downloads',
311+
on_progress=TqdmProgress().update)
312+
queue.wait_for_job(job)
313+
print(f"The files were downloaded to {job.download_path}")
314+
```
315+
259316
#### jobs = queue.list_jobs()
260317

261318
Return a list of all active and inactive `DownloadJob`s.

docs/contributing/MODEL_MANAGER.md

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -397,26 +397,25 @@ In the event you wish to create a new installer, you may use the
397397
following initialization pattern:
398398

399399
```
400-
from invokeai.app.services.config import InvokeAIAppConfig
400+
from invokeai.app.services.config import get_config
401401
from invokeai.app.services.model_records import ModelRecordServiceSQL
402402
from invokeai.app.services.model_install import ModelInstallService
403403
from invokeai.app.services.download import DownloadQueueService
404-
from invokeai.app.services.shared.sqlite import SqliteDatabase
404+
from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase
405405
from invokeai.backend.util.logging import InvokeAILogger
406406
407-
config = InvokeAIAppConfig.get_config()
408-
config.parse_args()
407+
config = get_config()
409408
410409
logger = InvokeAILogger.get_logger(config=config)
411-
db = SqliteDatabase(config, logger)
410+
db = SqliteDatabase(config.db_path, logger)
412411
record_store = ModelRecordServiceSQL(db)
413412
queue = DownloadQueueService()
414413
queue.start()
415414
416-
installer = ModelInstallService(app_config=config,
415+
installer = ModelInstallService(app_config=config,
417416
record_store=record_store,
418-
download_queue=queue
419-
)
417+
download_queue=queue
418+
)
420419
installer.start()
421420
```
422421

@@ -1367,30 +1366,54 @@ the in-memory loaded model:
13671366
| `model` | AnyModel | The instantiated model (details below) |
13681367
| `locker` | ModelLockerBase | A context manager that mediates the movement of the model into VRAM |
13691368

1370-
Because the loader can return multiple model types, it is typed to
1371-
return `AnyModel`, a Union `ModelMixin`, `torch.nn.Module`,
1372-
`IAIOnnxRuntimeModel`, `IPAdapter`, `IPAdapterPlus`, and
1373-
`EmbeddingModelRaw`. `ModelMixin` is the base class of all diffusers
1374-
models, `EmbeddingModelRaw` is used for LoRA and TextualInversion
1375-
models. The others are obvious.
1369+
### get_model_by_key(key, [submodel]) -> LoadedModel
1370+
1371+
The `get_model_by_key()` method will retrieve the model using its
1372+
unique database key. For example:
1373+
1374+
loaded_model = loader.get_model_by_key('f13dd932c0c35c22dcb8d6cda4203764', SubModelType('vae'))
1375+
1376+
`get_model_by_key()` may raise any of the following exceptions:
1377+
1378+
* `UnknownModelException` -- key not in database
1379+
* `ModelNotFoundException` -- key in database but model not found at path
1380+
* `NotImplementedException` -- the loader doesn't know how to load this type of model
1381+
1382+
### Using the Loaded Model in Inference
13761383

13771384
`LoadedModel` acts as a context manager. The context loads the model
13781385
into the execution device (e.g. VRAM on CUDA systems), locks the model
13791386
in the execution device for the duration of the context, and returns
13801387
the model. Use it like this:
13811388

13821389
```
1383-
model_info = loader.get_model_by_key('f13dd932c0c35c22dcb8d6cda4203764', SubModelType('vae'))
1384-
with model_info as vae:
1390+
loaded_model_= loader.get_model_by_key('f13dd932c0c35c22dcb8d6cda4203764', SubModelType('vae'))
1391+
with loaded_model as vae:
13851392
image = vae.decode(latents)[0]
13861393
```
13871394

1388-
`get_model_by_key()` may raise any of the following exceptions:
1395+
The object returned by the LoadedModel context manager is an
1396+
`AnyModel`, which is a Union of `ModelMixin`, `torch.nn.Module`,
1397+
`IAIOnnxRuntimeModel`, `IPAdapter`, `IPAdapterPlus`, and
1398+
`EmbeddingModelRaw`. `ModelMixin` is the base class of all diffusers
1399+
models, `EmbeddingModelRaw` is used for LoRA and TextualInversion
1400+
models. The others are obvious.
1401+
1402+
In addition, you may call `LoadedModel.model_on_device()`, a context
1403+
manager that returns a tuple of the model's state dict in CPU and the
1404+
model itself in VRAM. It is used to optimize the LoRA patching and
1405+
unpatching process:
1406+
1407+
```
1408+
loaded_model_= loader.get_model_by_key('f13dd932c0c35c22dcb8d6cda4203764', SubModelType('vae'))
1409+
with loaded_model.model_on_device() as (state_dict, vae):
1410+
image = vae.decode(latents)[0]
1411+
```
1412+
1413+
Since not all models have state dicts, the `state_dict` return value
1414+
can be None.
1415+
13891416

1390-
* `UnknownModelException` -- key not in database
1391-
* `ModelNotFoundException` -- key in database but model not found at path
1392-
* `NotImplementedException` -- the loader doesn't know how to load this type of model
1393-
13941417
### Emitting model loading events
13951418

13961419
When the `context` argument is passed to `load_model_*()`, it will
@@ -1578,3 +1601,59 @@ This method takes a model key, looks it up using the
15781601
`ModelRecordServiceBase` object in `mm.store`, and passes the returned
15791602
model configuration to `load_model_by_config()`. It may raise a
15801603
`NotImplementedException`.
1604+
1605+
## Invocation Context Model Manager API
1606+
1607+
Within invocations, the following methods are available from the
1608+
`InvocationContext` object:
1609+
1610+
### context.download_and_cache_model(source) -> Path
1611+
1612+
This method accepts a `source` of a remote model, downloads and caches
1613+
it locally, and then returns a Path to the local model. The source can
1614+
be a direct download URL or a HuggingFace repo_id.
1615+
1616+
In the case of HuggingFace repo_id, the following variants are
1617+
recognized:
1618+
1619+
* stabilityai/stable-diffusion-v4 -- default model
1620+
* stabilityai/stable-diffusion-v4:fp16 -- fp16 variant
1621+
* stabilityai/stable-diffusion-v4:fp16:vae -- the fp16 vae subfolder
1622+
* stabilityai/stable-diffusion-v4:onnx:vae -- the onnx variant vae subfolder
1623+
1624+
You can also point at an arbitrary individual file within a repo_id
1625+
directory using this syntax:
1626+
1627+
* stabilityai/stable-diffusion-v4::/checkpoints/sd4.safetensors
1628+
1629+
### context.load_local_model(model_path, [loader]) -> LoadedModel
1630+
1631+
This method loads a local model from the indicated path, returning a
1632+
`LoadedModel`. The optional loader is a Callable that accepts a Path
1633+
to the object, and returns a `AnyModel` object. If no loader is
1634+
provided, then the method will use `torch.load()` for a .ckpt or .bin
1635+
checkpoint file, `safetensors.torch.load_file()` for a safetensors
1636+
checkpoint file, or `cls.from_pretrained()` for a directory that looks
1637+
like a diffusers directory.
1638+
1639+
### context.load_remote_model(source, [loader]) -> LoadedModel
1640+
1641+
This method accepts a `source` of a remote model, downloads and caches
1642+
it locally, loads it, and returns a `LoadedModel`. The source can be a
1643+
direct download URL or a HuggingFace repo_id.
1644+
1645+
In the case of HuggingFace repo_id, the following variants are
1646+
recognized:
1647+
1648+
* stabilityai/stable-diffusion-v4 -- default model
1649+
* stabilityai/stable-diffusion-v4:fp16 -- fp16 variant
1650+
* stabilityai/stable-diffusion-v4:fp16:vae -- the fp16 vae subfolder
1651+
* stabilityai/stable-diffusion-v4:onnx:vae -- the onnx variant vae subfolder
1652+
1653+
You can also point at an arbitrary individual file within a repo_id
1654+
directory using this syntax:
1655+
1656+
* stabilityai/stable-diffusion-v4::/checkpoints/sd4.safetensors
1657+
1658+
1659+

invokeai/app/api/dependencies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def initialize(config: InvokeAIAppConfig, event_handler_id: int, logger: Logger
9393
conditioning = ObjectSerializerForwardCache(
9494
ObjectSerializerDisk[ConditioningFieldData](output_folder / "conditioning", ephemeral=True)
9595
)
96-
download_queue_service = DownloadQueueService(event_bus=events)
96+
download_queue_service = DownloadQueueService(app_config=configuration, event_bus=events)
9797
model_images_service = ModelImageFileStorageDisk(model_images_folder / "model_images")
9898
model_manager = ModelManagerService.build_model_manager(
9999
app_config=configuration,

0 commit comments

Comments
 (0)