Skip to content

Commit 6b0ca88

Browse files
authored
Merge branch 'main' into ryan/spandrel-upscale-tiling
2 parents 0428ce7 + 7ad32dc commit 6b0ca88

Some content is hidden

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

52 files changed

+4751
-1902
lines changed

docs/contributing/MODEL_MANAGER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ config = get_config()
408408
409409
logger = InvokeAILogger.get_logger(config=config)
410410
db = SqliteDatabase(config.db_path, logger)
411-
record_store = ModelRecordServiceSQL(db)
411+
record_store = ModelRecordServiceSQL(db, logger)
412412
queue = DownloadQueueService()
413413
queue.start()
414414

invokeai/app/api/dependencies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def initialize(config: InvokeAIAppConfig, event_handler_id: int, logger: Logger
9999
model_images_service = ModelImageFileStorageDisk(model_images_folder / "model_images")
100100
model_manager = ModelManagerService.build_model_manager(
101101
app_config=configuration,
102-
model_record_service=ModelRecordServiceSQL(db=db),
102+
model_record_service=ModelRecordServiceSQL(db=db, logger=logger),
103103
download_queue=download_queue_service,
104104
events=events,
105105
)

invokeai/app/api/routers/images.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,14 @@ async def get_image_workflow(
233233
)
234234
async def get_image_full(
235235
image_name: str = Path(description="The name of full-resolution image file to get"),
236-
) -> FileResponse:
236+
) -> Response:
237237
"""Gets a full-resolution image file"""
238238

239239
try:
240240
path = ApiDependencies.invoker.services.images.get_path(image_name)
241-
242-
if not ApiDependencies.invoker.services.images.validate_path(path):
243-
raise HTTPException(status_code=404)
244-
245-
response = FileResponse(
246-
path,
247-
media_type="image/png",
248-
filename=image_name,
249-
content_disposition_type="inline",
250-
)
241+
with open(path, "rb") as f:
242+
content = f.read()
243+
response = Response(content, media_type="image/png")
251244
response.headers["Cache-Control"] = f"max-age={IMAGE_MAX_AGE}"
252245
return response
253246
except Exception:
@@ -268,15 +261,14 @@ async def get_image_full(
268261
)
269262
async def get_image_thumbnail(
270263
image_name: str = Path(description="The name of thumbnail image file to get"),
271-
) -> FileResponse:
264+
) -> Response:
272265
"""Gets a thumbnail image file"""
273266

274267
try:
275268
path = ApiDependencies.invoker.services.images.get_path(image_name, thumbnail=True)
276-
if not ApiDependencies.invoker.services.images.validate_path(path):
277-
raise HTTPException(status_code=404)
278-
279-
response = FileResponse(path, media_type="image/webp", content_disposition_type="inline")
269+
with open(path, "rb") as f:
270+
content = f.read()
271+
response = Response(content, media_type="image/webp")
280272
response.headers["Cache-Control"] = f"max-age={IMAGE_MAX_AGE}"
281273
return response
282274
except Exception:

invokeai/app/api_app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ def find_port(port: int) -> int:
161161
# Taken from https://waylonwalker.com/python-find-available-port/, thanks Waylon!
162162
# https://github.com/WaylonWalker
163163
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
164+
s.settimeout(1)
164165
if s.connect_ex(("localhost", port)) == 0:
165166
return find_port(port=port + 1)
166167
else:

invokeai/app/invocations/fields.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ class FieldDescriptions:
162162
fp32 = "Whether or not to use full float32 precision"
163163
precision = "Precision to use"
164164
tiled = "Processing using overlapping tiles (reduce memory consumption)"
165-
vae_tile_size = "The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the "
166-
"model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage."
165+
vae_tile_size = "The tile size for VAE tiling in pixels (image space). If set to 0, the default tile size for the model will be used. Larger tile sizes generally produce better results at the cost of higher memory usage."
167166
detect_res = "Pixel resolution for detection"
168167
image_res = "Pixel resolution for output image"
169168
safe_mode = "Whether or not to use safe mode"

invokeai/app/services/model_records/model_records_sql.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,14 @@
4040
"""
4141

4242
import json
43+
import logging
4344
import sqlite3
4445
from math import ceil
4546
from pathlib import Path
4647
from typing import List, Optional, Union
4748

49+
import pydantic
50+
4851
from invokeai.app.services.model_records.model_records_base import (
4952
DuplicateModelException,
5053
ModelRecordChanges,
@@ -67,7 +70,7 @@
6770
class ModelRecordServiceSQL(ModelRecordServiceBase):
6871
"""Implementation of the ModelConfigStore ABC using a SQL database."""
6972

70-
def __init__(self, db: SqliteDatabase):
73+
def __init__(self, db: SqliteDatabase, logger: logging.Logger):
7174
"""
7275
Initialize a new object from preexisting sqlite3 connection and threading lock objects.
7376
@@ -76,6 +79,7 @@ def __init__(self, db: SqliteDatabase):
7679
super().__init__()
7780
self._db = db
7881
self._cursor = db.conn.cursor()
82+
self._logger = logger
7983

8084
@property
8185
def db(self) -> SqliteDatabase:
@@ -291,7 +295,20 @@ def search_by_attr(
291295
tuple(bindings),
292296
)
293297
result = self._cursor.fetchall()
294-
results = [ModelConfigFactory.make_config(json.loads(x[0]), timestamp=x[1]) for x in result]
298+
299+
# Parse the model configs.
300+
results: list[AnyModelConfig] = []
301+
for row in result:
302+
try:
303+
model_config = ModelConfigFactory.make_config(json.loads(row[0]), timestamp=row[1])
304+
except pydantic.ValidationError:
305+
# We catch this error so that the app can still run if there are invalid model configs in the database.
306+
# One reason that an invalid model config might be in the database is if someone had to rollback from a
307+
# newer version of the app that added a new model type.
308+
self._logger.warning(f"Found an invalid model config in the database. Ignoring this model. ({row[0]})")
309+
else:
310+
results.append(model_config)
311+
295312
return results
296313

297314
def search_by_path(self, path: Union[str, Path]) -> List[AnyModelConfig]:

0 commit comments

Comments
 (0)