Skip to content

Commit 6d44d18

Browse files
committed
refactor: rethink of states, result types, adds batch_ids, gen/job thread safety
refactor: dynamic params via `additional_params` for image gen This allows extendibility, allowing parameter types or categories either unknown the SDK or as implemented by third parties. style: catch up fix/style: missing import, lingering style issues [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci fix: remove `type` syntax for now I am unclear how to guard it from raising an exception right now and its not terribly important as it just helps my IDE color it a certain way fix: use compat version of horde_model_reference fix: using `typing_extensions` for `override` This namespace migration is only effective as of python 3.12 and later, and the old namespace appears to still be valid in 3.12 and 3.13 at this time.
1 parent de195b0 commit 6d44d18

File tree

24 files changed

+1606
-570
lines changed

24 files changed

+1606
-570
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ repos:
3131
types-urllib3,
3232
types-aiofiles,
3333
StrEnum,
34-
horde_model_reference==0.9.2,
34+
horde_model_reference==0.10.0,
3535
]
3636
- repo: https://github.com/gauge-sh/tach-pre-commit
3737
rev: v0.27.0

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
- See all rules (but not necessarily used in the project) availible in rust [here](https://beta.ruff.rs/docs/rules/).
5252
- Run with `ruff check .`
5353
- Note: When using autofixing (`ruff check . --fix`), changes may be made that require running black, which can then result in needing to run `ruff check . --fix` again.
54-
- Consider running `black . && ruff check . --fix && black . && ruff check . --fix` to avoid this.
54+
- Consider running `black . && ruff check . --fix && black . && ruff check . --fix` to avoid this.
5555
- [**mypy**](https://mypy-lang.org/)
5656
- Static type safety
5757
- I recommending using the [mypy daemon](https://mypy.readthedocs.io/en/stable/mypy_daemon.html) instead of periodically running `pre-commit` (or `mypy` directly.).
@@ -65,7 +65,7 @@
6565
- `"python.analysis.languageServerMode": "full"`
6666
- `"python.testing.pytestEnabled": true`
6767
- [**tach**](https://github.com/gauge-sh/tach)
68-
- Enforces internal namespace dependency constraints. This helps avoid circular dependencies and helps ensure implementations are in a logical place.
68+
- Enforces internal namespace dependency constraints. This helps avoid circular dependencies and helps ensure implementations are in a logical place.
6969

7070
## Code Style and System Design
7171

@@ -120,7 +120,7 @@ The Horde ecosystem is a collaborative effort made possible through volunteer ef
120120
- Update documentation for new features or changes to existing functionality
121121
- Use descriptive commit messages consistent with the project commit history, especially for medium-to-large changesets.
122122
- While it is possible we will squash commits before merging, it is still helpful to have descriptive commit messages for review and opens the possibility of rebasing instead.
123-
123+
124124
#### Don't
125125

126126
- Make large sweeping changes unrelated to your primary goal

docs/concepts/package_structure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ API support packages do the following:
1818
- Constants for API endpoints and other configurations
1919
- Some examples include the valid values for parameters which accept only certain strings.
2020
- This also includes consts for default timeout values, default anonymous API keys, etc.
21-
21+
2222
```bash
2323
ai_horde_api/
2424
├── apimodels/

docs/concepts/style_guide.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Many classes contain standardized prefixes, suffixes, or identifiers within thei
7171
- **Base** (Always a Prefix): Indicates a foundational class that provides core functionality and may be abstract or partially implemented. It is intended to be extended by more specific classes.
7272
- Examples: `BaseAIHordeClient`, `BaseAIHordeSimpleClient`
7373

74-
#### API/Client Specific
74+
#### API/Client Specific
7575

7676
- **Manual**: Refers to classes that do not have context management or automatic session handling. These classes require the user to clean up server resources manually.
7777
- Examples: `AIHordeAPIManualClient`, `GenericHordeAPIManualClient`
@@ -134,7 +134,7 @@ Many docstrings in the SDK have additional requirements when they are related to
134134
```
135135

136136
- Children classes of `HordeResponse` must have additional information, for example:
137-
137+
138138
```python
139139
class HordePerformanceResponse(HordeResponseBaseModel):
140140
"""Information about the performance of the horde, such as worker counts and queue sizes.
@@ -146,7 +146,7 @@ Many docstrings in the SDK have additional requirements when they are related to
146146
```
147147

148148
- Children classes of `HordeRequest` must have additional information, for example:
149-
149+
150150
```python
151151
class HordePerformanceRequest(BaseAIHordeRequest):
152152
"""Request performance information about the horde, such as worker counts and queue sizes.
@@ -160,14 +160,14 @@ Many docstrings in the SDK have additional requirements when they are related to
160160
- Class and method signatures should prefer keyword-only arguments especially when there are multiple arguments of the same type
161161
- This improves readability and reduces the chance of passing arguments in the wrong order. Additionally, it improves the ability to add new arguments in the future without breaking existing code.
162162
- For example, instead of:
163-
163+
164164
```python
165165
def create_user(name: str, age: int, email: str):
166166
...
167167
```
168-
168+
169169
Prefer:
170-
170+
171171
```python
172172
def create_user(*, name: str, age: int, email: str):
173173
...
@@ -189,23 +189,23 @@ Many docstrings in the SDK have additional requirements when they are related to
189189
- This is technically enforced by type hints, but it is still important to consider the implications of returning different types, especially as a consumer.
190190
- Generally, for methods which mutate or return a different type based on input, it should be clear from the method name and documentation that this is the case and which types can be expected based on different inputs.
191191
- For example, the following method signature should be considered bad practice:
192-
192+
193193
```python
194194
def get_items(self, as_list: bool = True) -> list[Item] | set[Item]:
195195
...
196196
```
197197

198198
- Methods which *can* return a list or a container should *always* return a list or container, even if it is a single item.
199199
- For example, the following method signature should be considered bad practice:
200-
200+
201201
```python
202202
def get_items(self) -> list[Item] | Item:
203203
...
204204
```
205205

206206
- Methods should avoid returning different container types *unless that is the purpose of the method*.
207207
- For example, the following method signatures should be considered bad practice:
208-
208+
209209
```python
210210
def get_items(self) -> list[Item] | set[Item]:
211211
...
@@ -221,7 +221,7 @@ Many docstrings in the SDK have additional requirements when they are related to
221221

222222
- Prefer guard clauses over deeply nested if statements.
223223
- For example, instead of:
224-
224+
225225
```python
226226
def process_item(item: Item):
227227
if item is not None:
@@ -231,7 +231,7 @@ Many docstrings in the SDK have additional requirements when they are related to
231231
```
232232

233233
Prefer:
234-
234+
235235
```python
236236
def process_item(item: Item):
237237
if item is None or not item.is_valid():
@@ -242,18 +242,18 @@ Many docstrings in the SDK have additional requirements when they are related to
242242

243243
- Prefer meaningfully named composite `bool` conditionals over complex multi-line `if` statements.
244244
- For example, instead of:
245-
245+
246246
```python
247247
def is_valid_item(item: Item) -> bool:
248-
if (item.has_name() and item.has_value()) or
248+
if (item.has_name() and item.has_value()) or
249249
(item.has_description() and item.description_valid()):
250250
if item.is_active():
251251
return True
252252
return False
253253
```
254254

255255
Prefer:
256-
256+
257257
```python
258258
def is_valid_item(item: Item) -> bool:
259259
has_name_and_value = item.has_name() and item.has_value()
@@ -299,15 +299,15 @@ Many docstrings in the SDK have additional requirements when they are related to
299299
- `Enum`s should be used to represent numbers with a specific set of valid values and regular constants can be used for isolated (unconnected) values.
300300
- If many constants relate to each other, they should be grouped into a class.
301301
- For example, instead of:
302-
302+
303303
```python
304304
MAX_RETRIES = 5
305305
TIMEOUT = 30
306306
...
307307
```
308308

309309
Prefer:
310-
310+
311311
```python
312312
class APIConfig:
313313
MAX_RETRIES = 5
@@ -317,7 +317,7 @@ Many docstrings in the SDK have additional requirements when they are related to
317317

318318
### "KNOWN" Constants
319319

320-
- For consumer convenience, parameters which have a fixed set of known values should be defined as constants in an appropriate `consts.py` file. These constants should be named with the `KNOWN_` prefix and should be defined as `StrEnum`s or `Enum`s as appropriate.
320+
- For consumer convenience, parameters which have a fixed set of known values should be defined as constants in an appropriate `consts.py` file. These constants should be named with the `KNOWN_` prefix and should be defined as `StrEnum`s or `Enum`s as appropriate.
321321
- However, these values should **always be considered optional**. Consumers of the SDK should be able to use any valid value for the parameter as long as its type is correct. It would be ideal, but not required, that classes or functions which require these parameters validate them against the live API at runtime.
322322
- **_Rationale_**: This prevents the SDK from needing to be updated every time a new value is added to an API, and allows consumers to use any valid value without needing to wait for an SDK update.
323323

horde_sdk/consts.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Constants used by the SDK."""
22

3-
import sys
43
import os
54
from enum import IntEnum
65
from uuid import UUID
@@ -14,12 +13,8 @@
1413
_OVERLOADED_MODEL = "_MODEL_OVERLOADED"
1514
"""The model is used incorrectly on the API."""
1615

17-
if sys.version_info <= (3, 12):
18-
GENERATION_ID_TYPES = str | UUID
19-
"""The types that can be used as generation IDs."""
20-
else:
21-
type GENERATION_ID_TYPES = str | UUID
22-
"""The types that can be used as generation IDs."""
16+
GENERATION_ID_TYPES = str | UUID
17+
"""The types that can be used as generation IDs."""
2318

2419

2520
def get_default_frozen_model_config_dict() -> ConfigDict:

horde_sdk/generation_parameters/__init__.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from horde_sdk.generation_parameters.generic import BasicModelGenerationParameters, ComposedParameterSetBase
2828
from horde_sdk.generation_parameters.generic.consts import KNOWN_AUX_MODEL_SOURCE
2929
from horde_sdk.generation_parameters.image.consts import (
30+
CLIP_SKIP_REPRESENTATION,
3031
KNOWN_IMAGE_CONTROLNETS,
3132
KNOWN_IMAGE_SAMPLERS,
3233
KNOWN_IMAGE_SCHEDULERS,
@@ -35,8 +36,18 @@
3536
LORA_TRIGGER_INJECT_CHOICE,
3637
TI_TRIGGER_INJECT_CHOICE,
3738
)
38-
from horde_sdk.generation_parameters.image.object_models import ImageGenerationParameters
39-
from horde_sdk.generation_parameters.text.object_models import TextGenerationParameters
39+
from horde_sdk.generation_parameters.image.object_models import (
40+
BasicImageGenerationParameters,
41+
BasicImageGenerationParametersTemplate,
42+
ImageGenerationParameters,
43+
ImageGenerationParametersTemplate,
44+
)
45+
from horde_sdk.generation_parameters.text.object_models import (
46+
BasicTextGenerationFormatParameters,
47+
BasicTextGenerationParameters,
48+
KoboldAITextGenerationParameters,
49+
TextGenerationParameters,
50+
)
4051

4152
__all__ = [
4253
"AlchemyParameters",
@@ -65,6 +76,13 @@
6576
"KNOWN_IMAGE_WORKFLOWS",
6677
"LORA_TRIGGER_INJECT_CHOICE",
6778
"TI_TRIGGER_INJECT_CHOICE",
79+
"CLIP_SKIP_REPRESENTATION",
6880
"ImageGenerationParameters",
81+
"BasicImageGenerationParameters",
82+
"ImageGenerationParametersTemplate",
83+
"BasicImageGenerationParametersTemplate",
6984
"TextGenerationParameters",
85+
"BasicTextGenerationFormatParameters",
86+
"BasicTextGenerationParameters",
87+
"KoboldAITextGenerationParameters",
7088
]

horde_sdk/generation_parameters/alchemy/object_models.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from pydantic import BaseModel, Field
1+
from pydantic import Field
2+
from typing_extensions import override
23

34
from horde_sdk.generation_parameters.alchemy.consts import (
45
KNOWN_ALCHEMY_FORMS,
@@ -10,16 +11,17 @@
1011
KNOWN_UPSCALERS,
1112
)
1213
from horde_sdk.generation_parameters.generic import ComposedParameterSetBase
14+
from horde_sdk.generation_parameters.generic.object_models import GenerationFeatureFlags
1315

1416

15-
class AlchemyFeatureFlags(BaseModel):
17+
class AlchemyFeatureFlags(GenerationFeatureFlags):
1618
"""Feature flags for an alchemy worker."""
1719

1820
alchemy_types: list[KNOWN_ALCHEMY_TYPES] = Field(default_factory=list)
1921
"""The alchemy types supported by the worker."""
2022

2123

22-
class SingleAlchemyParameters(BaseModel):
24+
class SingleAlchemyParameters(ComposedParameterSetBase):
2325
"""Represents the common bare minimum parameters for any alchemy generation."""
2426

2527
generation_id: str
@@ -31,6 +33,11 @@ class SingleAlchemyParameters(BaseModel):
3133
source_image: bytes | str | None
3234
"""The source image to use for the generation."""
3335

36+
@override
37+
def get_number_expected_results(self) -> int:
38+
"""Get the number of expected results."""
39+
return 1
40+
3441

3542
class UpscaleAlchemyParameters(SingleAlchemyParameters):
3643
"""Represents the parameters for an upscale alchemy generation."""
@@ -86,7 +93,7 @@ class AlchemyParameters(ComposedParameterSetBase):
8693
def all_alchemy_operations(self) -> list[SingleAlchemyParameters]:
8794
"""Get all operations."""
8895
if self._all_alchemy_operations is not None:
89-
return self._all_alchemy_operations
96+
return self._all_alchemy_operations.copy()
9097

9198
all_operations: list[SingleAlchemyParameters] = []
9299
if self.upscalers:
@@ -104,4 +111,9 @@ def all_alchemy_operations(self) -> list[SingleAlchemyParameters]:
104111

105112
self._all_alchemy_operations = all_operations
106113

107-
return all_operations
114+
return all_operations.copy()
115+
116+
@override
117+
def get_number_expected_results(self) -> int:
118+
"""Get the number of expected results."""
119+
return len(self.all_alchemy_operations)

horde_sdk/generation_parameters/generic/__init__.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,50 @@
44
base model for generation parameters.
55
"""
66

7+
from abc import ABC, abstractmethod
8+
from typing import Self, Union
9+
710
from horde_model_reference.meta_consts import KNOWN_IMAGE_GENERATION_BASELINE
8-
from pydantic import BaseModel
11+
from pydantic import BaseModel, ConfigDict, Field, create_model
912

1013

11-
class ComposedParameterSetBase(BaseModel):
14+
class ComposedParameterSetBase(ABC, BaseModel):
1215
"""Base class for all combined (composed) parameter sets.
1316
1417
The top level classes which contain BasicModelGenerationParameters instance and/or other specific parameters
15-
should inherit from this class.
18+
should inherit from this class. These classes should always represent complete parameter sets that can be used
19+
for generation.
1620
"""
1721

22+
model_config = ConfigDict(
23+
use_attribute_docstrings=True,
24+
)
25+
26+
@abstractmethod
27+
def get_number_expected_results(self) -> int:
28+
"""Return the number of expected results for this parameter set.
29+
30+
Returns:
31+
int: The number of expected results.
32+
"""
33+
34+
35+
class GenerationParameterComponentBase(BaseModel):
36+
"""Base class for all generation parameters components.
37+
38+
These classes represent components of generation parameters that can be combined to form a complete
39+
parameter set. This includes the use of specific auxiliary features such as controlnets, LoRAs, etc.
40+
"""
41+
42+
model_config = ConfigDict(
43+
use_attribute_docstrings=True,
44+
)
45+
1846

19-
class BasicModelGenerationParameters(BaseModel):
47+
class BasicModelGenerationParameters(GenerationParameterComponentBase):
2048
"""Represents the common bare minimum parameters for any model-based generative inference or similar."""
2149

22-
model: str
50+
model: str | None = None
2351
"""The model to use for the generation."""
2452

2553
model_baseline: str | KNOWN_IMAGE_GENERATION_BASELINE | None = None

horde_sdk/generation_parameters/generic/object_models.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pydantic import BaseModel, Field
1+
from pydantic import BaseModel, ConfigDict, Field
22

33

44
class GenerationFeatureFlags(BaseModel):
@@ -12,4 +12,3 @@ class GenerationFeatureFlags(BaseModel):
1212

1313
extra_source_images: bool = Field(default=False)
1414
"""Whether there is support for extra source images."""
15-

0 commit comments

Comments
 (0)