Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: workers by name #248

Merged
merged 5 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,31 @@
* 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.


## Verifying the horde SDK API surface

You can run the following:

```bash
pytest -m "object_verify"
```

This will run the tests which validate the objects defined in the SDK are:
- In the appropriate place
- Match the live API (or if `AI_HORDE_DEV_URL` that version of the API)
- That the models are exposed via `__init__.py`
- And will run any other tests which ensure internal consistency.
- This generally does not include specific object validation beyond what can be automatically derived directly from the API docs or from general conventions from the SDK itself.
- If adding objects, you should add tests more specific to the expected functionality of that endpoint and the `object_verify` tests should only be treated as the bare-minimum.

## 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
```bash
python horde_sdk/scripts/write_all_payload_examples_for_tests.py
python horde_sdk/scripts/write_all_response_examples_for_tests.py
python docs/build_docs.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.
Running `build_docs.py` will update any automatically generated mkdocs documentation stubs or resources (such as the API Model <-> SDK Model map).

Be sure to run the test suite (without any `*_api_calls.py` tests) after. You may also may want to just start with `pytest -m "object_verify"` (see also the section on verifying the horde SDK API surface).
2 changes: 2 additions & 0 deletions docs/api_to_sdk_map.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ This is a mapping of the AI-Horde API models (defined at [https://stablehorde.ne
| /v2/users/{user_id} | PUT | [ModifyUserRequest][horde_sdk.ai_horde_api.apimodels._users.ModifyUserRequest] |
| /v2/users/{user_id} | GET | [SingleUserDetailsRequest][horde_sdk.ai_horde_api.apimodels._users.SingleUserDetailsRequest] |
| /v2/workers | GET | [AllWorkersDetailsRequest][horde_sdk.ai_horde_api.apimodels.workers._workers.AllWorkersDetailsRequest] |
| /v2/workers/name/{worker_name} | GET | [SingleWorkerNameDetailsRequest][horde_sdk.ai_horde_api.apimodels.workers._workers.SingleWorkerNameDetailsRequest] |
| /v2/workers/{worker_id} | DELETE | [DeleteWorkerRequest][horde_sdk.ai_horde_api.apimodels.workers._workers.DeleteWorkerRequest] |
| /v2/workers/{worker_id} | PUT | [ModifyWorkerRequest][horde_sdk.ai_horde_api.apimodels.workers._workers.ModifyWorkerRequest] |
| /v2/workers/{worker_id} | GET | [SingleWorkerDetailsRequest][horde_sdk.ai_horde_api.apimodels.workers._workers.SingleWorkerDetailsRequest] |
Expand Down Expand Up @@ -78,4 +79,5 @@ This is a mapping of the AI-Horde API models (defined at [https://stablehorde.ne
| /v2/users | 200 | [ListUsersDetailsResponse][horde_sdk.ai_horde_api.apimodels._users.ListUsersDetailsResponse] |
| /v2/users/{user_id} | 200 | [UserDetailsResponse][horde_sdk.ai_horde_api.apimodels._users.UserDetailsResponse] |
| /v2/workers | 200 | [AllWorkersDetailsResponse][horde_sdk.ai_horde_api.apimodels.workers._workers.AllWorkersDetailsResponse] |
| /v2/workers/name/{worker_name} | 200 | [SingleWorkerDetailsResponse][horde_sdk.ai_horde_api.apimodels.workers._workers.SingleWorkerDetailsResponse] |
| /v2/workers/{worker_id} | 200 | [SingleWorkerDetailsResponse][horde_sdk.ai_horde_api.apimodels.workers._workers.SingleWorkerDetailsResponse] |
3 changes: 3 additions & 0 deletions docs/api_to_sdk_payload_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@
"/v2/status/news": {
"GET": "horde_sdk.ai_horde_api.apimodels._status.NewsRequest"
},
"/v2/workers/name/{worker_name}": {
"GET": "horde_sdk.ai_horde_api.apimodels.workers._workers.SingleWorkerNameDetailsRequest"
},
"/v2/generate/text/async": {
"POST": "horde_sdk.ai_horde_api.apimodels.generate.text._async.TextGenerateAsyncRequest"
},
Expand Down
3 changes: 3 additions & 0 deletions docs/api_to_sdk_response_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
"/v2/status/news": {
"200": "horde_sdk.ai_horde_api.apimodels._status.NewsResponse"
},
"/v2/workers/name/{worker_name}": {
"200": "horde_sdk.ai_horde_api.apimodels.workers._workers.SingleWorkerDetailsResponse"
},
"/v2/generate/text/async": {
"200": "horde_sdk.ai_horde_api.apimodels.generate.text._async.TextGenerateAsyncDryRunResponse",
"202": "horde_sdk.ai_horde_api.apimodels.generate.text._async.TextGenerateAsyncResponse"
Expand Down
34 changes: 34 additions & 0 deletions docs/request_field_names_and_descriptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,13 @@
"types": [
"horde_sdk.ai_horde_api.consts.WORKER_TYPE"
]
},
"name": {
"description": "Returns a worker matching the exact name provided. Case insensitive.",
"types": [
"str",
"None"
]
}
},
"DeleteImageGenerateRequest": {
Expand Down Expand Up @@ -1201,6 +1208,33 @@
]
}
},
"SingleWorkerNameDetailsRequest": {
"apikey": {
"description": "Defaults to `ANON_API_KEY`. See also `.is_api_key_required()`",
"types": [
"str",
"None"
]
},
"worker_name": {
"description": "The name of the worker in question for this request.",
"types": [
"str"
]
},
"accept": {
"description": "The 'accept' header field.",
"types": [
"horde_sdk.generic_api.metadata.GenericAcceptTypes"
]
},
"client_agent": {
"description": "The requesting client's agent. You should set this to reflect the name, version and contact information\nfor your client.",
"types": [
"str"
]
}
},
"TextGenerateAsyncRequest": {
"trusted_workers": {
"description": "When true, only trusted workers will serve this request. When False, Evaluating workers will also be used\nwhich can increase speed but adds more risk!",
Expand Down
69 changes: 48 additions & 21 deletions examples/ai_horde_client/workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def all_workers(
) -> None:
all_workers_response: AllWorkersDetailsResponse

all_workers_response = simple_client.workers_all_details(worker_name=worker_name)
all_workers_response = simple_client.workers_all_details(worker_name=worker_name, api_key=api_key)

if worker_name is None:
logger.info("Getting details for all workers.")
Expand All @@ -33,23 +33,49 @@ def all_workers(

logger.info(f"Number of workers: {len(all_workers_response)}")

with open(filename, "w") as f:
with open(filename, "w", encoding="utf-8") as f:
f.write(all_workers_response.model_dump_json(indent=4))

logger.info(f"Workers written to {filename}")


def single_worker(api_key: str, simple_client: AIHordeAPISimpleClient, worker_id: str, filename: str) -> None:
def single_worker(
api_key: str,
simple_client: AIHordeAPISimpleClient,
worker_id: str,
filename: str,
) -> None:
single_worker_response: SingleWorkerDetailsResponse

single_worker_response = simple_client.worker_details(worker_id=worker_id, api_key=api_key)

if single_worker_response is None:
raise ValueError("No worker returned in the response.")

logger.info(f"Worker: {single_worker_response}")

with open(filename, "w", encoding="utf-8") as f:
f.write(f"{single_worker_response}\n")

logger.info(f"Worker details written to {filename}")


def single_worker_name(
api_key: str,
simple_client: AIHordeAPISimpleClient,
worker_name: str,
filename: str,
) -> None:
single_worker_response: SingleWorkerDetailsResponse

single_worker_response = simple_client.worker_details(worker_id=worker_id)
single_worker_response = simple_client.worker_details_by_name(worker_name=worker_name, api_key=api_key)

if single_worker_response is None:
raise ValueError("No worker returned in the response.")

logger.info(f"Worker: {single_worker_response}")

with open(filename, "w") as f:
with open(filename, "w", encoding="utf-8") as f:
f.write(f"{single_worker_response}\n")

logger.info(f"Worker details written to {filename}")
Expand Down Expand Up @@ -96,8 +122,16 @@ def set_maintenance_mode(
help="The filename to write the workers to.",
)

parser.add_argument(
"--worker_name",
"-n",
type=str,
help="The worker name to get details for.",
default=None,
)

# Either all or worker_id must be specified.
group = parser.add_mutually_exclusive_group(required=True)
group = parser.add_mutually_exclusive_group()

group.add_argument(
"--all",
Expand All @@ -112,13 +146,6 @@ def set_maintenance_mode(
help="The worker ID to get details for.",
)

group.add_argument(
"--worker_name",
"-n",
type=str,
help="The worker name to get details for.",
)

group2 = parser.add_mutually_exclusive_group()
group2.add_argument(
"--maintenance-mode-on",
Expand All @@ -141,14 +168,7 @@ def set_maintenance_mode(

simple_client = AIHordeAPISimpleClient()

if args.worker_name:
all_workers(
api_key=args.apikey,
simple_client=simple_client,
filename=args.filename,
worker_name=args.worker_name,
)
elif args.all:
if args.all:
all_workers(
api_key=args.apikey,
simple_client=simple_client,
Expand Down Expand Up @@ -176,3 +196,10 @@ def set_maintenance_mode(
worker_id=args.worker_id,
filename=args.filename,
)
elif args.worker_name:
single_worker_name(
api_key=args.apikey,
simple_client=simple_client,
filename=args.filename,
worker_name=args.worker_name,
)
Loading
Loading