drawthings-py 0.2.0__tar.gz → 0.3.0__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.
Files changed (62) hide show
  1. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/PKG-INFO +1 -1
  2. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/pyproject.toml +2 -2
  3. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/__init__.py +2 -2
  4. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/_dt_service.py +9 -1
  5. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/cli_service.py +7 -1
  6. drawthings_py-0.3.0/src/drawthings_py/configs/_util.py +70 -0
  7. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/config_dict.py +7 -1
  8. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/config_prop.py +33 -2
  9. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/configs.py +3 -24
  10. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/gen_config.py +61 -16
  11. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/presets.py +33 -1
  12. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/types.py +1 -1
  13. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/drawthings.py +1 -1
  14. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/grpc}/grpc_service.py +28 -9
  15. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/image}/_metadata.py +1 -1
  16. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/image}/_png_writer.py +1 -1
  17. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/image}/_preview_decoders.py +1 -1
  18. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/image}/image_buffer.py +2 -2
  19. drawthings_py-0.3.0/src/drawthings_py/models/__init__.py +6 -0
  20. drawthings_py-0.3.0/src/drawthings_py/models/grpc_models_source.py +46 -0
  21. drawthings_py-0.3.0/src/drawthings_py/models/types.py +121 -0
  22. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/request_builder.py +5 -5
  23. drawthings_py-0.3.0/src/drawthings_py/util/mixins.py +10 -0
  24. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/LICENSE +0 -0
  25. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/README.md +0 -0
  26. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/__init__.py +0 -0
  27. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/enums.py +0 -0
  28. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/configs/prop_schema.py +0 -0
  29. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/generated/dt_grpc/__init__.py +0 -0
  30. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/generated/dt_grpc/config_generated.py +0 -0
  31. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/generated/dt_grpc/config_generated.pyi +0 -0
  32. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/generated/dt_grpc/image_service/__init__.py +0 -0
  33. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/py.typed +0 -0
  34. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/config_props.yaml +0 -0
  35. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/anima_preview_3.json +0 -0
  36. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/chroma_hd.json +0 -0
  37. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/default.json +0 -0
  38. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/ernie_image_base.json +0 -0
  39. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/ernie_image_turbo.json +0 -0
  40. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_1_dev.json +0 -0
  41. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_1_fill_dev.json +0 -0
  42. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_1_schnell.json +0 -0
  43. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_2_dev_with_turbo.json +0 -0
  44. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_2_klein_4b.json +0 -0
  45. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_2_klein_4b_base.json +0 -0
  46. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_2_klein_9b.json +0 -0
  47. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_2_klein_9b_base.json +0 -0
  48. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/flux_2_klein_9b_kv.json +0 -0
  49. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/qwen_image_2512.json +0 -0
  50. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/qwen_image_2512_lightning.json +0 -0
  51. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/qwen_image_edit_2511.json +0 -0
  52. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/qwen_image_edit_2511_lightning.json +0 -0
  53. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/sdxl.json +0 -0
  54. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/stable_diffusion.json +0 -0
  55. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/z_image_base.json +0 -0
  56. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/configs/z_image_turbo.json +0 -0
  57. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/enums.yaml +0 -0
  58. {drawthings_py-0.2.0 → drawthings_py-0.3.0}/src/drawthings_py/resources/root_ca.crt +0 -0
  59. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/util}/_errors.py +0 -0
  60. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/util}/_util.py +0 -0
  61. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/util}/filename_pattern.py +0 -0
  62. {drawthings_py-0.2.0/src/drawthings_py → drawthings_py-0.3.0/src/drawthings_py/util}/seed_provider.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drawthings-py
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Python SDK for automating Draw Things image generation over gRPC.
5
5
  License-Expression: GPL-3.0-only
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "drawthings-py"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Python SDK for automating Draw Things image generation over gRPC."
5
5
  authors = [{ name = "Kelly Jerrell", email = "kcjerrell@gmail.com" }]
6
6
  readme = "README.md"
@@ -83,7 +83,7 @@ ignore_missing_imports = true
83
83
  include = ["src"]
84
84
  exclude = [
85
85
  "src/drawthings_py/generated",
86
- "src/drawthings_py/_metadata.py",
86
+ "src/drawthings_py/image/_metadata.py",
87
87
  "src/drawthings_py/configs/gen_config_generated.py",
88
88
  "scripts/gen"
89
89
  ]
@@ -4,8 +4,8 @@ Draw Things Python SDK
4
4
 
5
5
  import drawthings_py.drawthings as DrawThings
6
6
  from .configs import Configs, ConfigDict, Presets
7
- from .filename_pattern import FilenamePattern
8
- from .image_buffer import ImageBuffer
7
+ from .util.filename_pattern import FilenamePattern
8
+ from .image.image_buffer import ImageBuffer
9
9
  from .request_builder import RequestBuilder
10
10
 
11
11
  __all__ = [
@@ -6,7 +6,9 @@ from __future__ import annotations
6
6
  from abc import ABC, abstractmethod
7
7
  from types import TracebackType
8
8
 
9
- from .image_buffer import ImageBuffer
9
+ from drawthings_py.models.types import ModelsInfo
10
+
11
+ from .image.image_buffer import ImageBuffer
10
12
  from .request_builder import RequestBuilder
11
13
 
12
14
 
@@ -21,6 +23,12 @@ class DrawThingsService(ABC):
21
23
  Generate an image from the provided request builder
22
24
  """
23
25
 
26
+ @abstractmethod
27
+ async def get_models(self) -> ModelsInfo:
28
+ """
29
+ Get models from the service
30
+ """
31
+
24
32
  @abstractmethod
25
33
  def _dispose(self):
26
34
  """
@@ -1,7 +1,9 @@
1
1
  from typing_extensions import override
2
2
 
3
+ from drawthings_py.models.types import ModelsInfo
4
+
3
5
  from ._dt_service import DrawThingsService
4
- from .image_buffer import ImageBuffer
6
+ from .image.image_buffer import ImageBuffer
5
7
  from .request_builder import RequestBuilder
6
8
 
7
9
 
@@ -16,6 +18,10 @@ class CliService(DrawThingsService):
16
18
  """
17
19
  raise NotImplementedError("this is not implemented yet")
18
20
 
21
+ @override
22
+ async def get_models(self) -> ModelsInfo:
23
+ raise NotImplementedError("this is not implemented yet")
24
+
19
25
  @override
20
26
  async def generate_image(self, request: RequestBuilder) -> list[ImageBuffer]:
21
27
  raise NotImplementedError("this is not implemented yet")
@@ -0,0 +1,70 @@
1
+ from drawthings_py.configs.config_dict import ConfigDict
2
+ from drawthings_py.configs.gen_config import GenConfig
3
+ from drawthings_py.configs.enums import sampler_type_from_value
4
+
5
+
6
+ def describe_config(config: GenConfig | ConfigDict) -> str:
7
+ cd = config._d if isinstance(config, GenConfig) else config # pyright: ignore[reportPrivateUsage]
8
+
9
+ # flux_klein.cktp, 1024x1024, Strength: 0.9, 20 steps.
10
+ # EulerA sampler, CFG: 2.5, Shift: auto, flux_turbo.ckpt and
11
+ # flux_boobs.skpt lora
12
+
13
+ leading: list[str] = []
14
+ if model := cd.get("model"):
15
+ leading.append(model)
16
+ if (width := cd.get("width")) and (height := cd.get("height")):
17
+ leading.append(f"{width}x{height}")
18
+
19
+ # Strength (omit when 1.0) and steps
20
+ mid: list[str] = []
21
+ if (strength := cd.get("strength")) is not None and float(strength) != 1.0:
22
+ mid.append(f"Strength: {strength}")
23
+ if steps := cd.get("steps"):
24
+ mid.append(f"{steps} steps")
25
+
26
+ # Sampler, CFG (guidance), shift, and loras
27
+ tail: list[str] = []
28
+ if (sampler := cd.get("sampler")) is not None:
29
+ tail.append(f"{sampler_type_from_value(sampler)} sampler")
30
+ if (guidance := cd.get("guidance")) is not None:
31
+ tail.append(f"CFG: {guidance}")
32
+
33
+ # Shift: "auto" when resolution_dependent_shift is True
34
+ if cd.get("resolution_dependent_shift"):
35
+ shift_val: str | float | None = "auto"
36
+ else:
37
+ shift_val = cd.get("shift")
38
+ if shift_val is not None:
39
+ tail.append(f"Shift: {shift_val}")
40
+
41
+ # Loras: list filenames
42
+ lora_files: list[str] = []
43
+ if loras := cd.get("loras"):
44
+ for lora in loras:
45
+ f = lora.get("file")
46
+ if f:
47
+ lora_files.append(f)
48
+ if lora_files:
49
+ if len(lora_files) == 1:
50
+ lf = lora_files[0]
51
+ elif len(lora_files) == 2:
52
+ lf = f"{lora_files[0]} and {lora_files[1]}"
53
+ else:
54
+ lf = ", ".join(lora_files[:-1]) + f", and {lora_files[-1]}"
55
+ tail.append(f"{lf} lora")
56
+
57
+ # Build the final description
58
+ parts: list[str] = []
59
+ if leading:
60
+ first = ", ".join(leading)
61
+ if mid:
62
+ first = f"{first}, {', '.join(mid)}."
63
+ else:
64
+ first = f"{first}."
65
+ parts.append(first)
66
+
67
+ if tail:
68
+ parts.append(", ".join(tail))
69
+
70
+ return " ".join(parts).strip()
@@ -1,4 +1,4 @@
1
- from typing import TypedDict, TypeAlias, Literal
1
+ from typing import TypeGuard, TypedDict, TypeAlias, Literal, get_args
2
2
  from drawthings_py.configs.types import (
3
3
  CompressionMethod,
4
4
  ControlDict,
@@ -111,6 +111,12 @@ ConfigKey = Literal[
111
111
  "compression_artifacts_quality",
112
112
  ]
113
113
 
114
+ _CONFIG_KEYS = frozenset(get_args(ConfigKey))
115
+
116
+
117
+ def is_config_key(value: str) -> TypeGuard[ConfigKey]:
118
+ return value in _CONFIG_KEYS
119
+
114
120
 
115
121
  class ConfigDict(TypedDict, total=False):
116
122
  id: int
@@ -1,4 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
+ import json
2
3
  from types import NoneType
3
4
  from typing import Callable, Generic, Protocol, TypeVar, cast
4
5
 
@@ -19,14 +20,14 @@ from drawthings_py.generated.dt_grpc.config_generated import (
19
20
  GenerationConfigurationT,
20
21
  LoRAT,
21
22
  )
22
- from drawthings_py._util import (
23
+ from drawthings_py.util._util import (
23
24
  ensure_str,
24
25
  random_seed,
25
26
  snake_to_camel,
26
27
  try_parse_float,
27
28
  try_parse_int,
28
29
  )
29
- from .config_dict import ConfigValue, ConfigKey, ConfigDict
30
+ from .config_dict import ConfigDict, ConfigKey, ConfigValue
30
31
  from .types import (
31
32
  ControlDict,
32
33
  LoraDict,
@@ -524,7 +525,14 @@ class StrEnumProp(ConfigPropBase[str, int], Generic[V]):
524
525
  return self._default
525
526
 
526
527
 
528
+ _config_props: dict[ConfigKey, ConfigProp] | None = None
529
+
530
+
527
531
  def load_props() -> dict[ConfigKey, ConfigProp]:
532
+ global _config_props
533
+ if _config_props:
534
+ return _config_props
535
+
528
536
  props: dict[ConfigKey, ConfigProp] = {}
529
537
 
530
538
  definitions = cast(dict[ConfigKey, PropDefinition], load_definitions())
@@ -550,4 +558,27 @@ def load_props() -> dict[ConfigKey, ConfigProp]:
550
558
  else:
551
559
  print(f"Unknown config property type in YAML: {key}: {prop_type}")
552
560
 
561
+ _config_props = props
553
562
  return props
563
+
564
+
565
+ def config_dict_from_json(json_data: str | dict[str, object]) -> ConfigDict:
566
+ if isinstance(json_data, str):
567
+ data = cast(dict[str, object], json.loads(json_data))
568
+ else:
569
+ data = json_data
570
+ config = ConfigDict()
571
+ for prop in load_props().values():
572
+ if value := prop.from_json(data):
573
+ config[prop.name] = value # pyright: ignore[reportGeneralTypeIssues]
574
+ return config
575
+
576
+
577
+ def config_dict_from_fbs(data: bytes) -> ConfigDict:
578
+ config_t = GenerationConfigurationT.InitFromPackedBuf(data)
579
+ config = ConfigDict()
580
+ for prop in load_props().values():
581
+ value = prop.from_fbs(config_t)
582
+ if value is not None:
583
+ config[prop.name] = value # pyright: ignore[reportGeneralTypeIssues]
584
+ return config
@@ -5,13 +5,11 @@ including JSON presets, JSON strings, and Python dictionaries.
5
5
  """
6
6
 
7
7
  import copy
8
- import json
9
- from importlib.resources import files
10
- from typing import Unpack, cast
8
+ from typing import Unpack
11
9
 
12
10
  from .gen_config import GenConfig
13
11
  from .config_dict import ConfigDict
14
- from .presets import PresetDefinition, PresetName, Presets
12
+ from .presets import PresetName, Presets, load_preset_config
15
13
 
16
14
 
17
15
  class Configs:
@@ -35,7 +33,7 @@ class Configs:
35
33
  ValueError: If the preset file does not exist.
36
34
  """
37
35
  try:
38
- json_str = _load_preset_json(name)
36
+ json_str = load_preset_config(name)
39
37
  return GenConfig.from_json(json_str)
40
38
  except FileNotFoundError:
41
39
  raise ValueError(f"Unknown preset: {name}")
@@ -89,22 +87,3 @@ class Configs:
89
87
  for config_copy in reversed(copies):
90
88
  d.update(config_copy)
91
89
  return GenConfig(**d)
92
-
93
-
94
- def _load_preset_json(name: PresetName | Presets) -> str:
95
- """Get the JSON string for a named preset.
96
-
97
- Args:
98
- name: The name of the preset to get.
99
-
100
- Returns:
101
- A JSON string containing the preset data.
102
- """
103
- filename = name + ".json"
104
- path = files("drawthings_py.resources.configs") / filename
105
- preset: PresetDefinition | None = None
106
- with path.open("r", encoding="utf-8") as f:
107
- preset = cast(PresetDefinition | None, json.load(f))
108
- if preset is None:
109
- raise ValueError(f"Unknown preset: {name}")
110
- return json.dumps(preset["configuration"])
@@ -2,10 +2,18 @@ from __future__ import annotations
2
2
  from collections.abc import MutableMapping, Iterator
3
3
  from typing import Unpack, overload, Literal, cast
4
4
  from typing_extensions import override
5
- import json
6
5
  import flatbuffers # pyright: ignore[reportMissingTypeStubs]
7
- from drawthings_py.configs.config_dict import ConfigDict, ConfigKey, ConfigValue
8
- from drawthings_py.configs.config_prop import load_props
6
+ from drawthings_py.configs.config_dict import (
7
+ ConfigDict,
8
+ ConfigKey,
9
+ ConfigValue,
10
+ is_config_key,
11
+ )
12
+ from drawthings_py.configs.config_prop import (
13
+ config_dict_from_fbs,
14
+ config_dict_from_json,
15
+ load_props,
16
+ )
9
17
  from drawthings_py.generated.dt_grpc.config_generated import GenerationConfigurationT
10
18
  from drawthings_py.configs.types import (
11
19
  CompressionMethod,
@@ -203,7 +211,9 @@ class GenConfig(MutableMapping[ConfigKey, ConfigValue]):
203
211
  config: A dictionary of configuration values to set.
204
212
  **kwargs: Keyword arguments of configuration values to set.
205
213
  """
206
- self._d.update(config or kwargs)
214
+ for key, value in (config or kwargs).items():
215
+ if is_config_key(key) and value is not None:
216
+ self._d[key] = cast(ConfigValue, value) # pyright: ignore[reportGeneralTypeIssues]
207
217
 
208
218
  @overload
209
219
  def set_hires_fix(self, value: None, /) -> None: ...
@@ -233,6 +243,50 @@ class GenConfig(MutableMapping[ConfigKey, ConfigValue]):
233
243
  "Invalid arguments for set_hires_fix. Either provide no arguments to disable, or width, height, and optionally strength to enable."
234
244
  )
235
245
 
246
+ @overload
247
+ def set_tiled_diffusion(self, value: None, /) -> None: ...
248
+ @overload
249
+ def set_tiled_diffusion(self, width: int, height: int, overlap: int, /) -> None: ...
250
+ def set_tiled_diffusion(
251
+ self,
252
+ arg1: None | int = None,
253
+ height: int | None = None,
254
+ overlap: int | None = None,
255
+ /,
256
+ ) -> None:
257
+ if arg1 is None:
258
+ self["tiled_diffusion"] = False
259
+ del self["diffusion_tile_width"]
260
+ del self["diffusion_tile_height"]
261
+ del self["diffusion_tile_overlap"]
262
+ elif height is not None and overlap is not None:
263
+ self["tiled_diffusion"] = True
264
+ self["diffusion_tile_width"] = arg1
265
+ self["diffusion_tile_height"] = height
266
+ self["diffusion_tile_overlap"] = overlap
267
+
268
+ @overload
269
+ def set_tiled_decoding(self, value: None, /) -> None: ...
270
+ @overload
271
+ def set_tiled_decoding(self, width: int, height: int, overlap: int, /) -> None: ...
272
+ def set_tiled_decoding(
273
+ self,
274
+ arg1: None | int = None,
275
+ height: int | None = None,
276
+ overlap: int | None = None,
277
+ /,
278
+ ) -> None:
279
+ if arg1 is None:
280
+ self["tiled_decoding"] = False
281
+ del self["decoding_tile_width"]
282
+ del self["decoding_tile_height"]
283
+ del self["decoding_tile_overlap"]
284
+ elif height is not None and overlap is not None:
285
+ self["tiled_decoding"] = True
286
+ self["decoding_tile_width"] = arg1
287
+ self["decoding_tile_height"] = height
288
+ self["decoding_tile_overlap"] = overlap
289
+
236
290
  def add_lora(self, file: str, weight: float = 1.0, mode: LoraMode = "All"):
237
291
  """Add a LoRA to the configuration."""
238
292
  # checking for existence is not necessaary - __getitem__ assigns the default
@@ -378,20 +432,11 @@ class GenConfig(MutableMapping[ConfigKey, ConfigValue]):
378
432
  return bytes(builder.Output())
379
433
 
380
434
  @classmethod
381
- def from_json(cls, json_str: str) -> GenConfig:
382
- data = cast(dict[str, object], json.loads(json_str))
383
- config = ConfigDict()
384
- for prop in CONFIG_PROPS.values():
385
- if value := prop.from_json(data):
386
- config[prop.name] = value # pyright: ignore[reportGeneralTypeIssues]
435
+ def from_json(cls, json_data: str | dict[str, object]) -> GenConfig:
436
+ config = config_dict_from_json(json_data)
387
437
  return cls(**config)
388
438
 
389
439
  @classmethod
390
440
  def from_fbs(cls, data: bytes) -> GenConfig:
391
- config_t = GenerationConfigurationT.InitFromPackedBuf(data)
392
- config = ConfigDict()
393
- for prop in CONFIG_PROPS.values():
394
- value = prop.from_fbs(config_t)
395
- if value is not None:
396
- config[prop.name] = value # pyright: ignore[reportGeneralTypeIssues]
441
+ config = config_dict_from_fbs(data)
397
442
  return cls(**config)
@@ -1,5 +1,7 @@
1
1
  from enum import StrEnum
2
- from typing import Literal, Required, TypedDict
2
+ from importlib.resources import files
3
+ import json
4
+ from typing import Literal, Required, TypedDict, cast
3
5
 
4
6
  from .config_dict import ConfigDict
5
7
 
@@ -56,3 +58,33 @@ class PresetDefinition(TypedDict, total=False):
56
58
  configuration: Required[ConfigDict]
57
59
  name: Required[str]
58
60
  version: str
61
+
62
+ def load_preset_data(name: PresetName | Presets | str) -> PresetDefinition:
63
+ """Get the JSON string for a named preset.
64
+
65
+ Args:
66
+ name: The name of the preset to get.
67
+
68
+ Returns:
69
+ A JSON string containing the preset data.
70
+ """
71
+ filename = name + ".json"
72
+ path = files("drawthings_py.resources.configs") / filename
73
+ preset: PresetDefinition | None = None
74
+ with path.open("r", encoding="utf-8") as f:
75
+ preset = cast(PresetDefinition | None, json.load(f))
76
+ if preset is None:
77
+ raise ValueError(f"Unknown preset: {name}")
78
+ return preset
79
+
80
+
81
+ def load_preset_config(name: PresetName | Presets) -> str:
82
+ """Get the JSON string for a named preset.
83
+
84
+ Args:
85
+ name: The name of the preset to get.
86
+
87
+ Returns:
88
+ A JSON string containing the preset data.
89
+ """
90
+ return json.dumps(load_preset_data(name)["configuration"])
@@ -14,7 +14,7 @@ from .enums import (
14
14
  )
15
15
 
16
16
 
17
- from drawthings_py._util import ensure_str
17
+ from drawthings_py.util._util import ensure_str
18
18
 
19
19
 
20
20
  class UpscalerModel(StrEnum):
@@ -27,7 +27,7 @@ def grpc(
27
27
  port: int - the port of the gRPC server
28
28
  return: GrpcService - the gRPC service
29
29
  """
30
- from .grpc_service import GrpcService # pylint: disable=import-outside-toplevel
30
+ from .grpc.grpc_service import GrpcService # pylint: disable=import-outside-toplevel
31
31
 
32
32
  return GrpcService(
33
33
  host=host, port=port, progressbar=progressbar, disable_messages=disable_messages
@@ -19,15 +19,22 @@ import tqdm
19
19
  from grpclib import GRPCError
20
20
  from grpclib.client import Channel
21
21
 
22
- from ._dt_service import DrawThingsService
23
- from ._errors import raise_grpc_error
24
- from ._metadata import ImageMetadata, copy_with_seed, create_metadata
25
- from ._preview_decoders import decode_preview
26
- from ._util import pluralize, seeds_from_batch
27
- from .generated.dt_grpc.config_generated import GenerationConfiguration
28
- from .generated.dt_grpc import image_service
29
- from .image_buffer import ImageBuffer
30
- from .request_builder import ProgressCallback, RequestBuilder, build_grpc_message
22
+ from drawthings_py.models import GrpcModelsSource
23
+ from drawthings_py.models.types import ModelsInfo
24
+
25
+ from drawthings_py._dt_service import DrawThingsService
26
+ from drawthings_py.util._errors import raise_grpc_error
27
+ from drawthings_py.image._metadata import ImageMetadata, copy_with_seed, create_metadata
28
+ from drawthings_py.image._preview_decoders import decode_preview
29
+ from drawthings_py.util._util import pluralize, seeds_from_batch
30
+ from drawthings_py.generated.dt_grpc.config_generated import GenerationConfiguration
31
+ from drawthings_py.generated.dt_grpc import image_service
32
+ from drawthings_py.image.image_buffer import ImageBuffer
33
+ from drawthings_py.request_builder import (
34
+ ProgressCallback,
35
+ RequestBuilder,
36
+ build_grpc_message,
37
+ )
31
38
 
32
39
  cert_path = files("drawthings_py.resources").joinpath("root_ca.crt")
33
40
  ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
@@ -87,6 +94,7 @@ class GrpcService(DrawThingsService):
87
94
 
88
95
  _channel: Channel
89
96
  _service: image_service.ImageGenerationServiceStub
97
+ _models: GrpcModelsSource
90
98
 
91
99
  _progressbar: bool
92
100
  _disable_messages: bool
@@ -111,6 +119,12 @@ class GrpcService(DrawThingsService):
111
119
  self._service = image_service.ImageGenerationServiceStub(self._channel)
112
120
  self._progressbar = progressbar
113
121
  self._disable_messages = disable_messages
122
+ self._models = GrpcModelsSource(self._service)
123
+
124
+ @override
125
+ async def get_models(self) -> ModelsInfo:
126
+ await self._models.load()
127
+ return self._models._models # pyright: ignore[reportPrivateUsage]
114
128
 
115
129
  @override
116
130
  async def generate_image(self, request: RequestBuilder) -> list[ImageBuffer]:
@@ -273,6 +287,7 @@ def _get_batch_metadata(
273
287
  metadata_batch = [copy_with_seed(metadata, seed) for seed in seeds]
274
288
  return metadata_batch
275
289
 
290
+
276
291
  def _estimate_signposts(
277
292
  config: GenerationConfiguration, request: image_service.ImageGenerationRequest
278
293
  ) -> int:
@@ -301,3 +316,7 @@ def _estimate_signposts(
301
316
  est += 1
302
317
 
303
318
  return est
319
+
320
+
321
+ # def _get_service_stub(host: str, port: int) -> image_service.ImageGenerationServiceStub:
322
+ # pass
@@ -6,7 +6,7 @@ from collections.abc import Callable
6
6
  from pathlib import Path
7
7
  from typing import Any, Required, TypedDict, cast
8
8
 
9
- from .generated.dt_grpc.config_generated import GenerationConfiguration
9
+ from drawthings_py.generated.dt_grpc.config_generated import GenerationConfiguration
10
10
 
11
11
  SAMPLER_NAMES: dict[Any, str] = {
12
12
  0: "DPM++ 2M Karras",
@@ -11,7 +11,7 @@ from typing_extensions import override
11
11
 
12
12
  from PIL import Image
13
13
 
14
- from ._metadata import ImageMetadata
14
+ from drawthings_py.image._metadata import ImageMetadata
15
15
 
16
16
 
17
17
  class BytesEncoder(json.JSONEncoder):
@@ -1,6 +1,6 @@
1
1
  import fpzip
2
2
  import numpy as np
3
- from .image_buffer import ImageBuffer
3
+ from drawthings_py.image.image_buffer import ImageBuffer
4
4
 
5
5
 
6
6
  def clamp(value: int) -> int:
@@ -8,8 +8,8 @@ import fpzip
8
8
  import numpy as np
9
9
  from PIL import Image
10
10
 
11
- from ._metadata import ImageMetadata
12
- from ._png_writer import write_png_with_usercomment
11
+ from drawthings_py.image._metadata import ImageMetadata
12
+ from drawthings_py.image._png_writer import write_png_with_usercomment
13
13
 
14
14
  _TENSOR_HEADER_SIZE = 68
15
15
  _FPZIP_MAGIC = 1012247
@@ -0,0 +1,6 @@
1
+ from .grpc_models_source import ModelsSource, GrpcModelsSource
2
+
3
+ __all__ = [
4
+ "ModelsSource",
5
+ "GrpcModelsSource",
6
+ ]
@@ -0,0 +1,46 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from typing_extensions import override
4
+
5
+ from drawthings_py.generated.dt_grpc import image_service
6
+ from drawthings_py.models.types import ModelsInfo
7
+
8
+
9
+ class ModelsSource(ABC): # noqa: F821
10
+ _models: ModelsInfo
11
+ _files: list[str]
12
+
13
+ def __init__(self):
14
+ self._models = ModelsInfo()
15
+ self._files = []
16
+
17
+ @abstractmethod
18
+ async def load(self) -> None:
19
+ pass
20
+
21
+ pass
22
+
23
+
24
+ class GrpcModelsSource(ModelsSource):
25
+ _service: image_service.ImageGenerationServiceStub
26
+
27
+ def __init__(self, service: image_service.ImageGenerationServiceStub):
28
+ super().__init__()
29
+ self._service = service
30
+
31
+ @override
32
+ async def load(self) -> None:
33
+ req = image_service.EchoRequest(
34
+ "drawthings-py",
35
+ )
36
+ reply = await self._service.echo(req)
37
+
38
+ models = reply.override.models if reply.override is not None else None
39
+ print(f"{len(reply.files)} files. {len(models) if models else 0} models")
40
+
41
+ if reply.override is None:
42
+ return
43
+
44
+ self._models: ModelsInfo = ModelsInfo.from_echo_reply(reply)
45
+ self._files: list[str] = reply.files
46
+ return
@@ -0,0 +1,121 @@
1
+ import json
2
+ from typing import Any, Literal, TypeAlias, cast
3
+
4
+ from drawthings_py.generated.dt_grpc import image_service
5
+ from drawthings_py.util.mixins import ReprMixin
6
+
7
+ ModelTypes = Literal[
8
+ "models", "controlnets", "loras", "upscalers", "textual_inversions"
9
+ ]
10
+
11
+
12
+ class ModelBase(ReprMixin):
13
+ file: str = ""
14
+ name: str = ""
15
+
16
+ def __init__(self, **kwargs: object):
17
+ for key, value in kwargs.items():
18
+ setattr(self, key, value)
19
+
20
+
21
+ class ModelInfo(ModelBase):
22
+ prefix: str = ""
23
+ version: str = ""
24
+
25
+
26
+ class ControlNetInfo(ModelBase):
27
+ modifier: str = ""
28
+ type: str = ""
29
+ global_average_pooling: bool = False
30
+ version: str = ""
31
+
32
+
33
+ class LoRAInfo(ModelBase):
34
+ prefix: str = ""
35
+ mode: str = ""
36
+ version: str = ""
37
+
38
+
39
+ class UpscalerInfo(ModelBase):
40
+ pass
41
+
42
+
43
+ class TextualInversionInfo(ModelBase):
44
+ keyword: str = ""
45
+
46
+
47
+ _jl: TypeAlias = list[dict[str, object]]
48
+
49
+
50
+ def _cjl(data: Any) -> _jl: # pyright: ignore[reportExplicitAny, reportAny]
51
+ return cast(_jl, data)
52
+
53
+
54
+ class ModelsInfo(ReprMixin):
55
+ models: list[ModelInfo]
56
+ controlNets: list[ControlNetInfo]
57
+ loras: list[LoRAInfo]
58
+ upscalers: list[UpscalerInfo]
59
+ textualInversions: list[TextualInversionInfo]
60
+ files: list[str]
61
+
62
+ def __init__(
63
+ self,
64
+ models: list[ModelInfo] | None = None,
65
+ controlNets: list[ControlNetInfo] | None = None,
66
+ loras: list[LoRAInfo] | None = None,
67
+ upscalers: list[UpscalerInfo] | None = None,
68
+ textualInversions: list[TextualInversionInfo] | None = None,
69
+ files: list[str] | None = None,
70
+ ):
71
+ self.models = models or []
72
+ self.controlNets = controlNets or []
73
+ self.loras = loras or []
74
+ self.upscalers = upscalers or []
75
+ self.textualInversions = textualInversions or []
76
+ self.files = files or []
77
+
78
+ @classmethod
79
+ def from_echo_reply(cls, reply: image_service.EchoReply) -> "ModelsInfo":
80
+ data = reply.override
81
+ files = reply.files or []
82
+
83
+ if data is None:
84
+ return cls(files=files)
85
+
86
+ models = (
87
+ [ModelInfo(**item) for item in _cjl(json.loads(data.models))]
88
+ if data.models
89
+ else []
90
+ )
91
+ control_nets = (
92
+ [ControlNetInfo(**item) for item in _cjl(json.loads(data.control_nets))]
93
+ if data.control_nets
94
+ else []
95
+ )
96
+ loras = (
97
+ [LoRAInfo(**item) for item in _cjl(json.loads(data.loras))]
98
+ if data.loras
99
+ else []
100
+ )
101
+ upscalers = (
102
+ [UpscalerInfo(**item) for item in _cjl(json.loads(data.upscalers))]
103
+ if data.upscalers
104
+ else []
105
+ )
106
+ textual_inversions = (
107
+ [
108
+ TextualInversionInfo(**item)
109
+ for item in _cjl(json.loads(data.textual_inversions))
110
+ ]
111
+ if data.textual_inversions
112
+ else []
113
+ )
114
+ return cls(
115
+ models=models,
116
+ controlNets=control_nets,
117
+ loras=loras,
118
+ upscalers=upscalers,
119
+ textualInversions=textual_inversions,
120
+ files=reply.files,
121
+ )
@@ -9,11 +9,11 @@ import os
9
9
  from dataclasses import dataclass
10
10
  from typing import Callable, Literal, TypeAlias, cast
11
11
 
12
- from .configs.config_dict import ConfigDict, ConfigKey, ConfigValue
13
- from .configs.gen_config import GenConfig
14
- from .generated.dt_grpc import image_service
15
- from .image_buffer import ImageBuffer
16
- from .seed_provider import SeedProvider
12
+ from drawthings_py.configs.config_dict import ConfigDict, ConfigKey, ConfigValue
13
+ from drawthings_py.configs.gen_config import GenConfig
14
+ from drawthings_py.generated.dt_grpc import image_service
15
+ from drawthings_py.image.image_buffer import ImageBuffer
16
+ from drawthings_py.util.seed_provider import SeedProvider
17
17
 
18
18
  ImageSource: TypeAlias = str | os.PathLike[str] | ImageBuffer
19
19
  """Type alias for image sources, which can be file paths or ImageBuffer instances."""
@@ -0,0 +1,10 @@
1
+ from typing_extensions import override
2
+
3
+
4
+ class ReprMixin:
5
+ @override
6
+ def __repr__(self):
7
+
8
+ fields = ", ".join(f"{k}={v!r}" for k, v in vars(self).items()) # pyright: ignore[reportAny]
9
+
10
+ return f"{type(self).__name__}({fields})"
File without changes
File without changes