From 0fdd34388a4764b5270a52c9197c6c5854e37086 Mon Sep 17 00:00:00 2001 From: db0 Date: Fri, 13 Sep 2024 12:44:44 +0200 Subject: [PATCH 1/4] feat: adds extra_slow_workers and limit_max_steps --- horde_sdk/ai_horde_api/apimodels/alchemy/_async.py | 2 ++ horde_sdk/ai_horde_api/apimodels/generate/_pop.py | 4 ++++ horde_sdk/ai_horde_worker/bridge_data.py | 6 ++++++ horde_sdk/generic_api/apimodels.py | 2 ++ tests/ai_horde_api/test_ai_horde_api_models.py | 2 ++ 5 files changed, 16 insertions(+) diff --git a/horde_sdk/ai_horde_api/apimodels/alchemy/_async.py b/horde_sdk/ai_horde_api/apimodels/alchemy/_async.py index f2973ed..cab6329 100644 --- a/horde_sdk/ai_horde_api/apimodels/alchemy/_async.py +++ b/horde_sdk/ai_horde_api/apimodels/alchemy/_async.py @@ -86,6 +86,8 @@ class AlchemyAsyncRequest( """The public URL of the source image or a base64 string to use.""" slow_workers: bool = True """Whether to use the slower workers. Costs additional kudos if `False`.""" + extra_slow_workers: bool = False + """Whether to use the super slow workers.""" @field_validator("forms") def check_at_least_one_form(cls, v: list[AlchemyAsyncRequestFormItem]) -> list[AlchemyAsyncRequestFormItem]: diff --git a/horde_sdk/ai_horde_api/apimodels/generate/_pop.py b/horde_sdk/ai_horde_api/apimodels/generate/_pop.py index 7de8309..ee3cebf 100644 --- a/horde_sdk/ai_horde_api/apimodels/generate/_pop.py +++ b/horde_sdk/ai_horde_api/apimodels/generate/_pop.py @@ -492,6 +492,10 @@ class ImageGenerateJobPopRequest(BaseAIHordeRequest, APIKeyAllowedInRequestMixin """Whether this worker can generate using SDXL controlnets.""" allow_lora: bool = False """Whether this worker can generate using Loras.""" + extra_slow_worker: bool = False + """Marks the worker as extra slow.""" + limit_max_steps: bool = False + """Prevents the worker picking up jobs with more steps than the model average.""" @override @classmethod diff --git a/horde_sdk/ai_horde_worker/bridge_data.py b/horde_sdk/ai_horde_worker/bridge_data.py index 3b8771a..7e25905 100644 --- a/horde_sdk/ai_horde_worker/bridge_data.py +++ b/horde_sdk/ai_horde_worker/bridge_data.py @@ -152,6 +152,12 @@ class ImageWorkerBridgeData(SharedHordeBridgeData): allow_lora: bool = False """Whether to allow the use of LoRA.""" + extra_slow_worker: bool = False + """Marks the worker as extra slow.""" + + limit_max_steps: bool = False + """Prevents the worker picking up jobs with more steps than the model average.""" + max_lora_cache_size: int = Field( default=10, ge=10, diff --git a/horde_sdk/generic_api/apimodels.py b/horde_sdk/generic_api/apimodels.py index 377f03a..e54df4d 100644 --- a/horde_sdk/generic_api/apimodels.py +++ b/horde_sdk/generic_api/apimodels.py @@ -460,6 +460,8 @@ class RequestUsesWorkerMixin(HordeAPIDataObject): which can increase speed but adds more risk!""" slow_workers: bool = True """When True, allows slower workers to pick up this request. Disabling this incurs an extra kudos cost.""" + extra_slow_workers: bool = False + """When True, allows extra slow workers to pick up this request.""" workers: list[str] = Field(default_factory=list) """A list of worker IDs to use for this request. If empty, any worker can pick up the request. Using this incurs and extra kudos cost.""" diff --git a/tests/ai_horde_api/test_ai_horde_api_models.py b/tests/ai_horde_api/test_ai_horde_api_models.py index 83b9c3b..4081acf 100644 --- a/tests/ai_horde_api/test_ai_horde_api_models.py +++ b/tests/ai_horde_api/test_ai_horde_api_models.py @@ -86,6 +86,7 @@ def test_ImageGenerateAsyncRequest(ai_horde_api_key: str) -> None: nsfw=True, trusted_workers=False, slow_workers=False, + extra_slow_workers=False, workers=[], censor_nsfw=False, source_image="test source image (usually base64)", @@ -167,6 +168,7 @@ def test_ImageGenerateAsyncRequest_unknown_sampler(ai_horde_api_key: str) -> Non nsfw=True, trusted_workers=False, slow_workers=False, + extra_slow_workers=False, workers=[], censor_nsfw=False, source_image="test source image (usually base64)", From 02dd7ea521de3b5e91ceec59bdb3cf033df537c3 Mon Sep 17 00:00:00 2001 From: tazlin Date: Fri, 13 Sep 2024 06:50:04 -0400 Subject: [PATCH 2/4] tests: assert `extra_slow_workers` is set as intended --- tests/ai_horde_api/test_ai_horde_api_models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ai_horde_api/test_ai_horde_api_models.py b/tests/ai_horde_api/test_ai_horde_api_models.py index 4081acf..88f0789 100644 --- a/tests/ai_horde_api/test_ai_horde_api_models.py +++ b/tests/ai_horde_api/test_ai_horde_api_models.py @@ -126,6 +126,7 @@ def test_ImageGenerateAsyncRequest(ai_horde_api_key: str) -> None: assert test_async_request.nsfw is True assert test_async_request.trusted_workers is False assert test_async_request.slow_workers is False + assert test_async_request.extra_slow_workers is False assert test_async_request.workers == [] assert test_async_request.censor_nsfw is False assert test_async_request.source_image == "test source image (usually base64)" From e0e2f34e78faa7e5853aea1571077650f733fd20 Mon Sep 17 00:00:00 2001 From: tazlin Date: Fri, 13 Sep 2024 07:09:40 -0400 Subject: [PATCH 3/4] docs: indicate how to update `tests/test_data` --- CONTRIBUTING.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 736de44..d349644 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,3 +24,14 @@ * The `AI_HORDE_DEV_URL` environment variable overrides `AI_HORDE_URL`. This is useful for testing changes locally. * pytest files which end in `_api_calls.py` run last, and never run during the CI. It is currently incumbent on individual developers to confirm that these tests run successfully locally. In the future, part of the CI will be to spawn an AI-Horde and worker instances and test it there. + + +## When the API adds an endpoint or changes a model +With the top level directory (the one that contains `pyproject.toml`) as your working directory: +```python +python horde_sdk/scripts/write_all_payload_examples_for_tests.py +python horde_sdk/scripts/write_all_response_examples_for_tests.py +``` +This will update the data found in `tests/test_data/` from the default horde URL, or if any of the override environment variables are set, from there. + +Be sure to run the test suite (without any `*_api_calls.py` tests) after. From 372400b6c8a84f8f23e94c821a070097f9f8f065 Mon Sep 17 00:00:00 2001 From: tazlin Date: Fri, 13 Sep 2024 07:13:15 -0400 Subject: [PATCH 4/4] feat: support new api models as of sept 13 2024 This includes: - `active_generations` for user details (see also the new object `ActiveGenerations`) - `hires_fix_denoising_strength` for image gen payload - `transparent` for image gen payload - `extra_slow_workers` for generation payloads - `limit_max_steps` for image worker pop payloads - `validated_backends` for generation payloads --- .../request_field_names_and_descriptions.json | 42 +++++++++++++++---- ...response_field_names_and_descriptions.json | 7 ++++ horde_sdk/ai_horde_api/apimodels/__init__.py | 2 + horde_sdk/ai_horde_api/apimodels/_users.py | 19 +++++++++ .../_v2_generate_async_post.json | 6 ++- .../_v2_generate_pop_post.json | 4 +- .../_v2_generate_text_async_post.json | 4 +- .../_v2_generate_text_pop_post.json | 1 + .../_v2_find_user_get_200.json | 11 +++++ .../_v2_generate_pop_post_200.json | 5 ++- .../_v2_status_news_get_200.json | 9 +++- .../example_responses/_v2_users_get_200.json | 13 +++++- .../_v2_users_user_id_get_200.json | 11 +++++ 13 files changed, 120 insertions(+), 14 deletions(-) diff --git a/docs/request_field_names_and_descriptions.json b/docs/request_field_names_and_descriptions.json index 5465e95..bbcd9f9 100644 --- a/docs/request_field_names_and_descriptions.json +++ b/docs/request_field_names_and_descriptions.json @@ -113,6 +113,12 @@ "types": [ "bool" ] + }, + "extra_slow_workers": { + "description": "Whether to use the super slow workers.", + "types": [ + "bool" + ] } }, "AlchemyDeleteRequest": { @@ -443,6 +449,12 @@ "bool" ] }, + "extra_slow_workers": { + "description": "When True, allows extra slow workers to pick up this request.", + "types": [ + "bool" + ] + }, "workers": { "description": "A list of worker IDs to use for this request. If empty, any worker can pick up the request. Using this incurs\nand extra kudos cost.", "types": [ @@ -596,15 +608,13 @@ "models": { "description": "The models this worker can generate.", "types": [ - "list[str]", - "None" + "list[str]" ] }, "name": { "description": "The Name of the Worker.", "types": [ - "str", - "None" + "str" ] }, "nsfw": { @@ -714,6 +724,18 @@ "types": [ "bool" ] + }, + "extra_slow_worker": { + "description": "Marks the worker as extra slow.", + "types": [ + "bool" + ] + }, + "limit_max_steps": { + "description": "Prevents the worker picking up jobs with more steps than the model average.", + "types": [ + "bool" + ] } }, "ImageGenerateStatusRequest": { @@ -1192,6 +1214,12 @@ "bool" ] }, + "extra_slow_workers": { + "description": "When True, allows extra slow workers to pick up this request.", + "types": [ + "bool" + ] + }, "workers": { "description": "A list of worker IDs to use for this request. If empty, any worker can pick up the request. Using this incurs\nand extra kudos cost.", "types": [ @@ -1310,15 +1338,13 @@ "models": { "description": "The models this worker can generate.", "types": [ - "list[str]", - "None" + "list[str]" ] }, "name": { "description": "The Name of the Worker.", "types": [ - "str", - "None" + "str" ] }, "nsfw": { diff --git a/docs/response_field_names_and_descriptions.json b/docs/response_field_names_and_descriptions.json index 207d5fe..815603f 100644 --- a/docs/response_field_names_and_descriptions.json +++ b/docs/response_field_names_and_descriptions.json @@ -249,6 +249,13 @@ } }, "UserDetailsResponse": { + "active_generations": { + "description": "The active generations this user has requested.", + "types": [ + "horde_sdk.ai_horde_api.apimodels._users.ActiveGenerations", + "None" + ] + }, "admin_comment": { "description": "(Privileged) Comments from the horde admins about this user.", "types": [ diff --git a/horde_sdk/ai_horde_api/apimodels/__init__.py b/horde_sdk/ai_horde_api/apimodels/__init__.py index 8c9e52e..bb51a23 100644 --- a/horde_sdk/ai_horde_api/apimodels/__init__.py +++ b/horde_sdk/ai_horde_api/apimodels/__init__.py @@ -45,6 +45,7 @@ NewsResponse, ) from horde_sdk.ai_horde_api.apimodels._users import ( + ActiveGenerations, ContributionsDetails, ListUsersDetailsRequest, ListUsersDetailsResponse, @@ -181,6 +182,7 @@ "AIHordeGetTermsRequest", "DocumentFormat", "HordeDocument", + "ActiveGenerations", "ContributionsDetails", "FindUserRequest", "KudosTransferRequest", diff --git a/horde_sdk/ai_horde_api/apimodels/_users.py b/horde_sdk/ai_horde_api/apimodels/_users.py index ca72139..f730db3 100644 --- a/horde_sdk/ai_horde_api/apimodels/_users.py +++ b/horde_sdk/ai_horde_api/apimodels/_users.py @@ -5,6 +5,7 @@ from horde_sdk.ai_horde_api.apimodels.base import BaseAIHordeRequest from horde_sdk.ai_horde_api.endpoints import AI_HORDE_API_ENDPOINT_SUBPATH +from horde_sdk.ai_horde_api.fields import UUID_Identifier from horde_sdk.consts import _ANONYMOUS_MODEL, HTTPMethod from horde_sdk.generic_api.apimodels import ( APIKeyAllowedInRequestMixin, @@ -94,6 +95,21 @@ class UsageDetails(HordeAPIDataObject): """How many images this user has requested.""" +@Unhashable +@Unequatable +class ActiveGenerations(HordeAPIDataObject): + """A list of generations that are currently active for this user.""" + + text: list[UUID_Identifier] | None = None + """The IDs of the text generations that are currently active for this user.""" + + image: list[UUID_Identifier] | None = None + """The IDs of the image generations that are currently active for this user.""" + + alchemy: list[UUID_Identifier] | None = None + """The IDs of the alchemy generations that are currently active for this user.""" + + @Unhashable @Unequatable class UserDetailsResponse(HordeResponseBaseModel): @@ -102,6 +118,9 @@ class UserDetailsResponse(HordeResponseBaseModel): def get_api_model_name(cls) -> str | None: return "UserDetails" + active_generations: ActiveGenerations | None = None + """The active generations this user has requested.""" + admin_comment: str | None = Field( default=None, ) diff --git a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_async_post.json b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_async_post.json index 4730732..95f76a2 100644 --- a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_async_post.json +++ b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_async_post.json @@ -1,9 +1,10 @@ { "prompt": "a", "params": { - "sampler_name": "k_dpm_fast", + "sampler_name": "dpmsolver", "cfg_scale": 7.5, "denoising_strength": 0.75, + "hires_fix_denoising_strength": 0.75, "seed": "The little seed that could", "height": 512, "width": 512, @@ -47,12 +48,15 @@ } ], "workflow": "qr_code", + "transparent": false, "steps": 30, "n": 1 }, "nsfw": false, "trusted_workers": false, + "validated_backends": true, "slow_workers": true, + "extra_slow_workers": false, "censor_nsfw": false, "workers": [ "" diff --git a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_pop_post.json b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_pop_post.json index 80f94de..71b6369 100644 --- a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_pop_post.json +++ b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_pop_post.json @@ -11,6 +11,7 @@ "threads": 1, "require_upfront_kudos": false, "amount": 1, + "extra_slow_worker": true, "max_pixels": 262144, "blacklist": [ "" @@ -21,5 +22,6 @@ "allow_post_processing": true, "allow_controlnet": true, "allow_sdxl_controlnet": true, - "allow_lora": true + "allow_lora": true, + "limit_max_steps": true } diff --git a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_async_post.json b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_async_post.json index 7220284..7c75502 100644 --- a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_async_post.json +++ b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_async_post.json @@ -32,6 +32,7 @@ }, "softprompt": "a", "trusted_workers": false, + "validated_backends": true, "slow_workers": true, "workers": [ "" @@ -50,5 +51,6 @@ ], "disable_batching": false, "allow_downgrade": false, - "webhook": "" + "webhook": "", + "extra_slow_workers": false } diff --git a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_pop_post.json b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_pop_post.json index 2bb3a82..6efdbff 100644 --- a/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_pop_post.json +++ b/tests/test_data/ai_horde_api/example_payloads/_v2_generate_text_pop_post.json @@ -11,6 +11,7 @@ "threads": 1, "require_upfront_kudos": false, "amount": 1, + "extra_slow_worker": true, "max_length": 512, "max_context_length": 2048, "softprompts": [ diff --git a/tests/test_data/ai_horde_api/example_responses/_v2_find_user_get_200.json b/tests/test_data/ai_horde_api/example_responses/_v2_find_user_get_200.json index 704497b..6e6b43a 100644 --- a/tests/test_data/ai_horde_api/example_responses/_v2_find_user_get_200.json +++ b/tests/test_data/ai_horde_api/example_responses/_v2_find_user_get_200.json @@ -22,6 +22,17 @@ "sharedkey_ids": [ "00000000-0000-0000-0000-000000000000" ], + "active_generations": { + "text": [ + "00000000-0000-0000-0000-000000000000" + ], + "image": [ + "00000000-0000-0000-0000-000000000000" + ], + "alchemy": [ + "00000000-0000-0000-0000-000000000000" + ] + }, "monthly_kudos": { "amount": 0, "last_received": "2021-01-01T00:00:00Z" diff --git a/tests/test_data/ai_horde_api/example_responses/_v2_generate_pop_post_200.json b/tests/test_data/ai_horde_api/example_responses/_v2_generate_pop_post_200.json index d6044ba..b064f08 100644 --- a/tests/test_data/ai_horde_api/example_responses/_v2_generate_pop_post_200.json +++ b/tests/test_data/ai_horde_api/example_responses/_v2_generate_pop_post_200.json @@ -1,8 +1,9 @@ { "payload": { - "sampler_name": "k_dpmpp_2m", + "sampler_name": "k_dpm_adaptive", "cfg_scale": 7.5, "denoising_strength": 0.75, + "hires_fix_denoising_strength": 0.75, "seed": "The little seed that could", "height": 512, "width": 512, @@ -46,6 +47,7 @@ } ], "workflow": "qr_code", + "transparent": false, "prompt": "", "ddim_steps": 30, "n_iter": 1, @@ -65,6 +67,7 @@ "bridge_version": 0, "kudos": 0, "max_pixels": 0, + "step_count": 0, "unsafe_ip": 0, "img2img": 0, "painting": 0, diff --git a/tests/test_data/ai_horde_api/example_responses/_v2_status_news_get_200.json b/tests/test_data/ai_horde_api/example_responses/_v2_status_news_get_200.json index ba53384..28208f8 100644 --- a/tests/test_data/ai_horde_api/example_responses/_v2_status_news_get_200.json +++ b/tests/test_data/ai_horde_api/example_responses/_v2_status_news_get_200.json @@ -2,6 +2,13 @@ { "date_published": "", "newspiece": "", - "importance": "Information" + "importance": "Information", + "tags": [ + "" + ], + "title": "", + "more_info_urls": [ + "" + ] } ] diff --git a/tests/test_data/ai_horde_api/example_responses/_v2_users_get_200.json b/tests/test_data/ai_horde_api/example_responses/_v2_users_get_200.json index f2ac293..d21fd60 100644 --- a/tests/test_data/ai_horde_api/example_responses/_v2_users_get_200.json +++ b/tests/test_data/ai_horde_api/example_responses/_v2_users_get_200.json @@ -1,5 +1,5 @@ [ - { + { "username": "", "id": 0, "kudos": 0.0, @@ -23,6 +23,17 @@ "sharedkey_ids": [ "00000000-0000-0000-0000-000000000000" ], + "active_generations": { + "text": [ + "00000000-0000-0000-0000-000000000000" + ], + "image": [ + "00000000-0000-0000-0000-000000000000" + ], + "alchemy": [ + "00000000-0000-0000-0000-000000000000" + ] + }, "monthly_kudos": { "amount": 0, "last_received": "2021-01-01T00:00:00Z" diff --git a/tests/test_data/ai_horde_api/example_responses/_v2_users_user_id_get_200.json b/tests/test_data/ai_horde_api/example_responses/_v2_users_user_id_get_200.json index 704497b..6e6b43a 100644 --- a/tests/test_data/ai_horde_api/example_responses/_v2_users_user_id_get_200.json +++ b/tests/test_data/ai_horde_api/example_responses/_v2_users_user_id_get_200.json @@ -22,6 +22,17 @@ "sharedkey_ids": [ "00000000-0000-0000-0000-000000000000" ], + "active_generations": { + "text": [ + "00000000-0000-0000-0000-000000000000" + ], + "image": [ + "00000000-0000-0000-0000-000000000000" + ], + "alchemy": [ + "00000000-0000-0000-0000-000000000000" + ] + }, "monthly_kudos": { "amount": 0, "last_received": "2021-01-01T00:00:00Z"