anyscale 0.24.88__py3-none-any.whl → 0.25.0__py3-none-any.whl
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.
- anyscale/__init__.py +46 -0
- anyscale/_private/anyscale_client/anyscale_client.py +148 -28
- anyscale/_private/anyscale_client/common.py +74 -1
- anyscale/_private/anyscale_client/fake_anyscale_client.py +165 -1
- anyscale/_private/docgen/README.md +1 -1
- anyscale/_private/docgen/__main__.py +62 -19
- anyscale/_private/docgen/api.md +0 -20
- anyscale/_private/docgen/generator.py +3 -2
- anyscale/_private/docgen/models.md +1 -46
- anyscale/_private/workload/workload_config.py +1 -1
- anyscale/aggregated_instance_usage/__init__.py +1 -1
- anyscale/aggregated_instance_usage/commands.py +2 -4
- anyscale/aggregated_instance_usage/models.py +8 -8
- anyscale/client/README.md +15 -22
- anyscale/client/openapi_client/__init__.py +10 -14
- anyscale/client/openapi_client/api/default_api.py +634 -957
- anyscale/client/openapi_client/models/__init__.py +10 -14
- anyscale/client/openapi_client/models/{session_event_types.py → cloud_deployment_config.py} +35 -24
- anyscale/client/openapi_client/models/{platformfinetuningjob_response.py → clouddeploymentconfig_response.py} +11 -11
- anyscale/client/openapi_client/models/{company_size.py → cluster_size.py} +10 -10
- anyscale/client/openapi_client/models/cluster_status_details.py +2 -1
- anyscale/client/openapi_client/models/create_experimental_workspace.py +29 -1
- anyscale/client/openapi_client/models/{resubmit_ft_job_request.py → describe_machine_pool_request.py} +21 -20
- anyscale/client/openapi_client/models/describe_machine_pool_response.py +123 -0
- anyscale/client/openapi_client/models/{fine_tuning_job_status.py → describemachinepoolresponse_response.py} +34 -16
- anyscale/client/openapi_client/models/machine_allocation_state.py +3 -1
- anyscale/client/openapi_client/models/machine_state_info.py +326 -0
- anyscale/client/openapi_client/models/organization_marketing_questions.py +80 -54
- anyscale/client/openapi_client/models/request_state_info.py +210 -0
- anyscale/client/openapi_client/models/{sessionevent_list_response.py → scheduler_info.py} +43 -38
- anyscale/client/openapi_client/models/usage_by_cluster.py +28 -1
- anyscale/client/openapi_client/models/usage_by_user.py +30 -3
- anyscale/client/openapi_client/models/workload_info.py +210 -0
- anyscale/cloud/__init__.py +83 -0
- anyscale/cloud/_private/cloud_sdk.py +25 -0
- anyscale/cloud/commands.py +45 -0
- anyscale/cloud/models.py +91 -0
- anyscale/cluster_compute.py +1 -1
- anyscale/commands/aggregated_instance_usage_commands.py +4 -4
- anyscale/commands/cloud_commands.py +38 -2
- anyscale/commands/command_examples.py +61 -0
- anyscale/commands/job_commands.py +15 -3
- anyscale/commands/machine_pool_commands.py +113 -1
- anyscale/commands/organization_invitation_commands.py +98 -0
- anyscale/commands/project_commands.py +52 -2
- anyscale/commands/resource_quota_commands.py +98 -11
- anyscale/commands/service_commands.py +1 -1
- anyscale/commands/session_commands_hidden.py +5 -1
- anyscale/commands/user_commands.py +1 -1
- anyscale/commands/util.py +2 -2
- anyscale/commands/workspace_commands.py +1 -1
- anyscale/connect.py +1 -1
- anyscale/connect_utils/project.py +7 -4
- anyscale/controllers/cloud_controller.py +6 -6
- anyscale/controllers/cloud_functional_verification_controller.py +1 -1
- anyscale/controllers/cluster_controller.py +2 -2
- anyscale/controllers/compute_config_controller.py +1 -1
- anyscale/controllers/experimental_integrations_controller.py +1 -1
- anyscale/controllers/job_controller.py +8 -3
- anyscale/controllers/list_controller.py +2 -2
- anyscale/controllers/machine_pool_controller.py +12 -1
- anyscale/controllers/project_controller.py +4 -3
- anyscale/controllers/schedule_controller.py +1 -1
- anyscale/controllers/service_controller.py +1 -1
- anyscale/controllers/workspace_controller.py +1 -1
- anyscale/models/job_model.py +1 -1
- anyscale/organization_invitation/__init__.py +61 -0
- anyscale/organization_invitation/_private/organization_invitation_sdk.py +24 -0
- anyscale/organization_invitation/commands.py +84 -0
- anyscale/organization_invitation/models.py +45 -0
- anyscale/project/__init__.py +35 -0
- anyscale/project/_private/project_sdk.py +27 -0
- anyscale/project/commands.py +56 -0
- anyscale/project/models.py +91 -0
- anyscale/{project.py → project_utils.py} +3 -4
- anyscale/resource_quota/__init__.py +99 -0
- anyscale/resource_quota/_private/resource_quota_sdk.py +111 -0
- anyscale/resource_quota/commands.py +150 -0
- anyscale/resource_quota/models.py +303 -0
- anyscale/scripts.py +4 -0
- anyscale/sdk/anyscale_client/__init__.py +0 -5
- anyscale/sdk/anyscale_client/api/default_api.py +0 -150
- anyscale/sdk/anyscale_client/models/__init__.py +0 -5
- anyscale/sdk/anyscale_client/models/cluster_status_details.py +2 -1
- anyscale/sdk/anyscale_client/sdk.py +1 -1
- anyscale/user/__init__.py +1 -1
- anyscale/user/commands.py +1 -1
- anyscale/user/models.py +25 -15
- anyscale/util.py +15 -0
- anyscale/utils/cloud_utils.py +1 -1
- anyscale/version.py +1 -1
- anyscale/workspace_utils.py +1 -1
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/METADATA +1 -5
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/RECORD +100 -94
- anyscale/client/openapi_client/models/create_fine_tuning_hyperparameters.py +0 -156
- anyscale/client/openapi_client/models/create_fine_tuning_job_product_request.py +0 -353
- anyscale/client/openapi_client/models/finish_ft_job_request.py +0 -204
- anyscale/client/openapi_client/models/log_level_types.py +0 -100
- anyscale/client/openapi_client/models/platform_fine_tuning_job.py +0 -577
- anyscale/client/openapi_client/models/platformfinetuningjob_list_response.py +0 -147
- anyscale/client/openapi_client/models/session_event.py +0 -267
- anyscale/client/openapi_client/models/session_event_cause.py +0 -150
- anyscale/controllers/resource_quota_controller.py +0 -183
- anyscale/sdk/anyscale_client/models/log_level_types.py +0 -100
- anyscale/sdk/anyscale_client/models/session_event.py +0 -267
- anyscale/sdk/anyscale_client/models/session_event_cause.py +0 -150
- anyscale/sdk/anyscale_client/models/session_event_types.py +0 -111
- anyscale/sdk/anyscale_client/models/sessionevent_list_response.py +0 -147
- anyscale/utils/imports/azure.py +0 -14
- /anyscale/{cloud.py → cloud_utils.py} +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/LICENSE +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/NOTICE +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/WHEEL +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/entry_points.txt +0 -0
- {anyscale-0.24.88.dist-info → anyscale-0.25.0.dist-info}/top_level.txt +0 -0
anyscale/__init__.py
CHANGED
|
@@ -20,11 +20,15 @@ path.append(os.path.join(anyscale_dir, "sdk"))
|
|
|
20
20
|
import anyscale
|
|
21
21
|
from anyscale import (
|
|
22
22
|
aggregated_instance_usage,
|
|
23
|
+
cloud,
|
|
23
24
|
compute_config,
|
|
24
25
|
image,
|
|
25
26
|
integrations,
|
|
26
27
|
job,
|
|
27
28
|
llm,
|
|
29
|
+
organization_invitation,
|
|
30
|
+
project,
|
|
31
|
+
resource_quota,
|
|
28
32
|
schedule,
|
|
29
33
|
service,
|
|
30
34
|
user,
|
|
@@ -34,6 +38,7 @@ from anyscale._private.sdk.base_sdk import Timer
|
|
|
34
38
|
from anyscale.aggregated_instance_usage import AggregatedInstanceUsageSDK
|
|
35
39
|
from anyscale.authenticate import AuthenticationBlock
|
|
36
40
|
from anyscale.cli_logger import BlockLogger
|
|
41
|
+
from anyscale.cloud import CloudSDK
|
|
37
42
|
from anyscale.cluster import get_job_submission_client_cluster_info
|
|
38
43
|
from anyscale.cluster_compute import get_cluster_compute_from_name
|
|
39
44
|
from anyscale.compute_config import ComputeConfigSDK
|
|
@@ -41,6 +46,9 @@ from anyscale.connect import ClientBuilder
|
|
|
41
46
|
from anyscale.image import ImageSDK
|
|
42
47
|
from anyscale.job import JobSDK
|
|
43
48
|
from anyscale.llm import LLMSDK
|
|
49
|
+
from anyscale.organization_invitation import OrganizationInvitationSDK
|
|
50
|
+
from anyscale.project import ProjectSDK
|
|
51
|
+
from anyscale.resource_quota import ResourceQuotaSDK
|
|
44
52
|
from anyscale.schedule import ScheduleSDK
|
|
45
53
|
from anyscale.sdk.anyscale_client.sdk import AnyscaleSDK
|
|
46
54
|
from anyscale.service import ServiceSDK
|
|
@@ -58,6 +66,10 @@ for attr, _ in inspect.getmembers(ClientBuilder, inspect.isfunction):
|
|
|
58
66
|
if attr.startswith("_"):
|
|
59
67
|
continue
|
|
60
68
|
|
|
69
|
+
# This is exposed in anyscale/cloud/__init__.py since anyscale.cloud is used as the SDK module too.
|
|
70
|
+
if attr == "cloud":
|
|
71
|
+
continue
|
|
72
|
+
|
|
61
73
|
def _new_builder(attr: str) -> Any:
|
|
62
74
|
target = getattr(ClientBuilder, attr)
|
|
63
75
|
|
|
@@ -107,9 +119,15 @@ class Anyscale:
|
|
|
107
119
|
self._job_sdk = JobSDK(client=self._anyscale_client)
|
|
108
120
|
self._service_sdk = ServiceSDK(client=self._anyscale_client)
|
|
109
121
|
self._compute_config_sdk = ComputeConfigSDK(client=self._anyscale_client)
|
|
122
|
+
self._cloud_sdk = CloudSDK(client=self._anyscale_client)
|
|
110
123
|
self._schedule_sdk = ScheduleSDK(client=self._anyscale_client)
|
|
111
124
|
self._image_sdk = ImageSDK(client=self._anyscale_client)
|
|
112
125
|
self._llm_sdk = LLMSDK(client=self._anyscale_client)
|
|
126
|
+
self._organization_invitation_sdk = OrganizationInvitationSDK(
|
|
127
|
+
client=self._anyscale_client
|
|
128
|
+
)
|
|
129
|
+
self._project_sdk = ProjectSDK(client=self._anyscale_client)
|
|
130
|
+
self._resource_quota_sdk = ResourceQuotaSDK(client=self._anyscale_client)
|
|
113
131
|
self._user_sdk = UserSDK(client=self._anyscale_client)
|
|
114
132
|
self._workspace_sdk = WorkspaceSDK(client=self._anyscale_client)
|
|
115
133
|
|
|
@@ -131,13 +149,25 @@ class Anyscale:
|
|
|
131
149
|
obj._compute_config_sdk = ComputeConfigSDK( # noqa: SLF001
|
|
132
150
|
client=client, logger=logger, timer=timer
|
|
133
151
|
)
|
|
152
|
+
obj._cloud_sdk = CloudSDK( # noqa: SLF001
|
|
153
|
+
client=client, logger=logger, timer=timer
|
|
154
|
+
)
|
|
134
155
|
obj._schedule_sdk = ScheduleSDK( # noqa: SLF001
|
|
135
156
|
client=client, logger=logger, timer=timer,
|
|
136
157
|
)
|
|
137
158
|
obj._image_sdk = ImageSDK(client=client, logger=logger) # noqa: SLF001
|
|
159
|
+
obj._organization_invitation_sdk = OrganizationInvitationSDK( # noqa: SLF001
|
|
160
|
+
client=client, logger=logger
|
|
161
|
+
)
|
|
162
|
+
obj._project_sdk = ProjectSDK( # noqa: SLF001
|
|
163
|
+
client=client, logger=logger, timer=timer
|
|
164
|
+
)
|
|
138
165
|
obj._user_sdk = UserSDK( # noqa: SLF001
|
|
139
166
|
client=client, logger=logger, timer=timer
|
|
140
167
|
)
|
|
168
|
+
obj._resource_quota_sdk = ResourceQuotaSDK( # noqa: SLF001
|
|
169
|
+
client=client, logger=logger, timer=timer
|
|
170
|
+
)
|
|
141
171
|
obj._workspace_sdk = WorkspaceSDK( # noqa: SLF001
|
|
142
172
|
client=client, logger=logger, timer=timer,
|
|
143
173
|
)
|
|
@@ -159,6 +189,10 @@ class Anyscale:
|
|
|
159
189
|
def compute_config(self) -> ComputeConfigSDK: # noqa: F811
|
|
160
190
|
return self._compute_config_sdk
|
|
161
191
|
|
|
192
|
+
@property
|
|
193
|
+
def cloud(self) -> CloudSDK: # noqa: F811
|
|
194
|
+
return self._cloud_sdk
|
|
195
|
+
|
|
162
196
|
@property
|
|
163
197
|
def schedule(self) -> ScheduleSDK: # noqa: F811
|
|
164
198
|
return self._schedule_sdk
|
|
@@ -171,6 +205,18 @@ class Anyscale:
|
|
|
171
205
|
def llm(self) -> LLMSDK: # noqa: F811
|
|
172
206
|
return self._llm_sdk
|
|
173
207
|
|
|
208
|
+
@property
|
|
209
|
+
def organization_invitation(self) -> OrganizationInvitationSDK: # noqa: F811
|
|
210
|
+
return self._organization_invitation_sdk
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def project(self) -> ProjectSDK: # noqa: F811
|
|
214
|
+
return self._project_sdk
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def resource_quota(self) -> ResourceQuotaSDK: # noqa: F811
|
|
218
|
+
return self._resource_quota_sdk
|
|
219
|
+
|
|
174
220
|
@property
|
|
175
221
|
def user(self) -> UserSDK: # noqa: F811
|
|
176
222
|
return self._user_sdk
|
|
@@ -47,10 +47,14 @@ from anyscale.client.openapi_client.models import (
|
|
|
47
47
|
ComputeTemplate,
|
|
48
48
|
ComputeTemplateConfig,
|
|
49
49
|
ComputeTemplateQuery,
|
|
50
|
+
CreateCloudCollaborator,
|
|
50
51
|
CreateComputeTemplate,
|
|
51
52
|
CreateDataset,
|
|
52
53
|
CreateExperimentalWorkspace,
|
|
53
54
|
CreateInternalProductionJob,
|
|
55
|
+
CreateOrganizationInvitation,
|
|
56
|
+
CreateResourceQuota,
|
|
57
|
+
CreateUserProjectCollaborator,
|
|
54
58
|
Dataset as InternalDataset,
|
|
55
59
|
DatasetUpload,
|
|
56
60
|
DecoratedComputeTemplate,
|
|
@@ -60,6 +64,11 @@ from anyscale.client.openapi_client.models import (
|
|
|
60
64
|
FineTunedModel,
|
|
61
65
|
FinetunedmodelListResponse,
|
|
62
66
|
InternalProductionJob,
|
|
67
|
+
ListResourceQuotasQuery,
|
|
68
|
+
OrganizationCollaborator,
|
|
69
|
+
OrganizationInvitation,
|
|
70
|
+
ResourceQuota,
|
|
71
|
+
ResourceQuotaStatus,
|
|
63
72
|
SessionSshKey,
|
|
64
73
|
StartSessionOptions,
|
|
65
74
|
StopSessionOptions,
|
|
@@ -544,6 +553,14 @@ class AnyscaleClient(AnyscaleClientInterface):
|
|
|
544
553
|
|
|
545
554
|
raise e from None
|
|
546
555
|
|
|
556
|
+
@handle_api_exceptions
|
|
557
|
+
def add_cloud_collaborators(
|
|
558
|
+
self, cloud_id: str, collaborators: List[CreateCloudCollaborator]
|
|
559
|
+
) -> None:
|
|
560
|
+
self._internal_api_client.batch_create_cloud_collaborators_api_v2_clouds_cloud_id_collaborators_users_batch_create_post(
|
|
561
|
+
cloud_id, collaborators
|
|
562
|
+
)
|
|
563
|
+
|
|
547
564
|
@handle_api_exceptions
|
|
548
565
|
def create_compute_config(
|
|
549
566
|
self, config: ComputeTemplateConfig, *, name: Optional[str] = None
|
|
@@ -986,6 +1003,14 @@ class AnyscaleClient(AnyscaleClientInterface):
|
|
|
986
1003
|
project_id
|
|
987
1004
|
).result
|
|
988
1005
|
|
|
1006
|
+
@handle_api_exceptions
|
|
1007
|
+
def add_project_collaborators(
|
|
1008
|
+
self, project_id: str, collaborators: List[CreateUserProjectCollaborator]
|
|
1009
|
+
) -> None:
|
|
1010
|
+
self._internal_api_client.batch_create_project_collaborators_api_v2_projects_project_id_collaborators_users_batch_create_post(
|
|
1011
|
+
project_id, collaborators
|
|
1012
|
+
)
|
|
1013
|
+
|
|
989
1014
|
@handle_api_exceptions
|
|
990
1015
|
def get_job(
|
|
991
1016
|
self,
|
|
@@ -1122,39 +1147,26 @@ class AnyscaleClient(AnyscaleClientInterface):
|
|
|
1122
1147
|
# If the presigned URL scheme is SMART_OPEN, upload to cloud storage using the provided bucket name, path, & environment, and the smart_open library.
|
|
1123
1148
|
bucket_name = info.bucket_name
|
|
1124
1149
|
bucket_path = info.bucket_path
|
|
1125
|
-
file_uri = info.file_uri
|
|
1126
1150
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
blob_service_client = (
|
|
1135
|
-
try_import_azure_storage_blob_BlobServiceClient()
|
|
1136
|
-
)
|
|
1137
|
-
|
|
1138
|
-
transport_params = {
|
|
1139
|
-
"client": blob_service_client.from_connection_string(info.url),
|
|
1140
|
-
}
|
|
1141
|
-
with smart_open.open(
|
|
1142
|
-
f"{file_uri}", "wb", transport_params=transport_params,
|
|
1143
|
-
) as fout:
|
|
1144
|
-
fout.write(zip_file_bytes)
|
|
1145
|
-
else:
|
|
1146
|
-
env_vars: Dict[str, str] = {
|
|
1147
|
-
"AWS_ENDPOINT_URL": info.url,
|
|
1148
|
-
}
|
|
1149
|
-
with set_env(**env_vars), smart_open.open(
|
|
1150
|
-
f"{bucket_name}/{bucket_path}", "wb",
|
|
1151
|
-
) as fout:
|
|
1152
|
-
fout.write(zip_file_bytes)
|
|
1151
|
+
env_vars: Dict[str, str] = {
|
|
1152
|
+
"AWS_ENDPOINT_URL": info.url,
|
|
1153
|
+
}
|
|
1154
|
+
with set_env(**env_vars), smart_open.open(
|
|
1155
|
+
f"{bucket_name}/{bucket_path}", "wb",
|
|
1156
|
+
) as fout:
|
|
1157
|
+
fout.write(zip_file_bytes)
|
|
1153
1158
|
|
|
1154
1159
|
else:
|
|
1155
1160
|
# Default to HTTP PUT.
|
|
1156
1161
|
internal_logger.debug(f"Uploading file '{file_name}' to cloud storage.")
|
|
1157
|
-
|
|
1162
|
+
headers = (
|
|
1163
|
+
{"x-ms-blob-type": "BlockBlob"}
|
|
1164
|
+
if info.file_uri.startswith("azure")
|
|
1165
|
+
else None
|
|
1166
|
+
)
|
|
1167
|
+
requests.put(
|
|
1168
|
+
info.url, data=zip_file_bytes, headers=headers
|
|
1169
|
+
).raise_for_status()
|
|
1158
1170
|
|
|
1159
1171
|
return info.file_uri
|
|
1160
1172
|
|
|
@@ -1855,3 +1867,111 @@ class AnyscaleClient(AnyscaleClientInterface):
|
|
|
1855
1867
|
return self._internal_api_client.admin_batch_create_users_api_v2_users_admin_batch_create_post(
|
|
1856
1868
|
admin_create_users
|
|
1857
1869
|
).results
|
|
1870
|
+
|
|
1871
|
+
@handle_api_exceptions
|
|
1872
|
+
def create_organization_invitations(
|
|
1873
|
+
self, emails: List[str]
|
|
1874
|
+
) -> Tuple[List[str], List[str]]:
|
|
1875
|
+
results = self._internal_api_client.batch_create_invitations_api_v2_organization_invitations_batch_create_post(
|
|
1876
|
+
[CreateOrganizationInvitation(email=email) for email in emails]
|
|
1877
|
+
).results
|
|
1878
|
+
|
|
1879
|
+
success_emails = []
|
|
1880
|
+
error_messages = []
|
|
1881
|
+
|
|
1882
|
+
for idx, result in enumerate(results):
|
|
1883
|
+
if result.data:
|
|
1884
|
+
success_emails.append(emails[idx])
|
|
1885
|
+
else:
|
|
1886
|
+
error_messages.append(result.error.detail)
|
|
1887
|
+
|
|
1888
|
+
return success_emails, error_messages
|
|
1889
|
+
|
|
1890
|
+
@handle_api_exceptions
|
|
1891
|
+
def list_organization_invitations(self) -> List[OrganizationInvitation]:
|
|
1892
|
+
results = (
|
|
1893
|
+
self._internal_api_client.list_invitations_api_v2_organization_invitations_get().results
|
|
1894
|
+
)
|
|
1895
|
+
|
|
1896
|
+
return results
|
|
1897
|
+
|
|
1898
|
+
@handle_api_exceptions
|
|
1899
|
+
def delete_organization_invitation(self, email: str) -> OrganizationInvitation:
|
|
1900
|
+
invitation = self._internal_api_client.list_invitations_api_v2_organization_invitations_get(
|
|
1901
|
+
email=email
|
|
1902
|
+
).results
|
|
1903
|
+
|
|
1904
|
+
if len(invitation) == 0:
|
|
1905
|
+
raise ValueError(f"Invitation for email '{email}' not found.")
|
|
1906
|
+
elif len(invitation) > 1:
|
|
1907
|
+
raise ValueError(
|
|
1908
|
+
f"Multiple invitations found for email '{email}'. Please contact Anyscale support."
|
|
1909
|
+
)
|
|
1910
|
+
|
|
1911
|
+
invitation_id = invitation[0].id
|
|
1912
|
+
|
|
1913
|
+
return self._internal_api_client.invalidate_invitation_api_v2_organization_invitations_invitation_id_invalidate_post(
|
|
1914
|
+
invitation_id
|
|
1915
|
+
).result
|
|
1916
|
+
|
|
1917
|
+
@handle_api_exceptions
|
|
1918
|
+
def get_organization_collaborator(self, email: str) -> OrganizationCollaborator:
|
|
1919
|
+
results = self._internal_api_client.list_organization_collaborators_api_v2_organization_collaborators_get(
|
|
1920
|
+
email=email
|
|
1921
|
+
).results
|
|
1922
|
+
|
|
1923
|
+
if len(results) == 0:
|
|
1924
|
+
raise ValueError(f"User with email '{email}' not found.")
|
|
1925
|
+
|
|
1926
|
+
if len(results) > 1:
|
|
1927
|
+
raise ValueError(
|
|
1928
|
+
f"Multiple users found for email '{email}'. Please contact Anyscale support."
|
|
1929
|
+
)
|
|
1930
|
+
|
|
1931
|
+
return results[0]
|
|
1932
|
+
|
|
1933
|
+
@handle_api_exceptions
|
|
1934
|
+
def create_resource_quota(
|
|
1935
|
+
self, create_resource_quota: CreateResourceQuota
|
|
1936
|
+
) -> ResourceQuota:
|
|
1937
|
+
return self._internal_api_client.create_resource_quota_api_v2_resource_quotas_post(
|
|
1938
|
+
create_resource_quota
|
|
1939
|
+
).result
|
|
1940
|
+
|
|
1941
|
+
@handle_api_exceptions
|
|
1942
|
+
def list_resource_quotas(
|
|
1943
|
+
self,
|
|
1944
|
+
name: Optional[str] = None,
|
|
1945
|
+
cloud_id: Optional[str] = None,
|
|
1946
|
+
creator_id: Optional[str] = None,
|
|
1947
|
+
is_enabled: Optional[bool] = None,
|
|
1948
|
+
max_items: int = 20,
|
|
1949
|
+
) -> List[ResourceQuota]:
|
|
1950
|
+
|
|
1951
|
+
query = ListResourceQuotasQuery(
|
|
1952
|
+
name=TextQuery(equals=name) if name else None,
|
|
1953
|
+
cloud_id=cloud_id,
|
|
1954
|
+
creator_id=creator_id,
|
|
1955
|
+
is_enabled=is_enabled,
|
|
1956
|
+
paging=PageQuery(count=max_items),
|
|
1957
|
+
)
|
|
1958
|
+
|
|
1959
|
+
resource_quotas = self._internal_api_client.search_resource_quotas_api_v2_resource_quotas_search_post(
|
|
1960
|
+
query
|
|
1961
|
+
).results
|
|
1962
|
+
|
|
1963
|
+
return resource_quotas
|
|
1964
|
+
|
|
1965
|
+
@handle_api_exceptions
|
|
1966
|
+
def delete_resource_quota(self, resource_quota_id: str) -> None:
|
|
1967
|
+
self._internal_api_client.delete_resource_quota_api_v2_resource_quotas_resource_quota_id_delete(
|
|
1968
|
+
resource_quota_id
|
|
1969
|
+
)
|
|
1970
|
+
|
|
1971
|
+
@handle_api_exceptions
|
|
1972
|
+
def set_resource_quota_status(
|
|
1973
|
+
self, resource_quota_id: str, is_enabled: bool
|
|
1974
|
+
) -> None:
|
|
1975
|
+
self._internal_api_client.set_resource_quota_status_api_v2_resource_quotas_resource_quota_id_status_patch(
|
|
1976
|
+
resource_quota_id, ResourceQuotaStatus(is_enabled=is_enabled)
|
|
1977
|
+
).result
|
|
@@ -8,13 +8,19 @@ from anyscale.client.openapi_client.models import (
|
|
|
8
8
|
AdminCreateUser,
|
|
9
9
|
Cloud,
|
|
10
10
|
ComputeTemplateConfig,
|
|
11
|
+
CreateCloudCollaborator,
|
|
11
12
|
CreateExperimentalWorkspace,
|
|
12
13
|
CreateInternalProductionJob,
|
|
14
|
+
CreateResourceQuota,
|
|
15
|
+
CreateUserProjectCollaborator,
|
|
13
16
|
DecoratedComputeTemplate,
|
|
14
17
|
DeletedPlatformFineTunedModel,
|
|
15
18
|
FineTunedModel,
|
|
16
19
|
InternalProductionJob,
|
|
20
|
+
OrganizationCollaborator,
|
|
21
|
+
OrganizationInvitation,
|
|
17
22
|
Project,
|
|
23
|
+
ResourceQuota,
|
|
18
24
|
WorkspaceDataplaneProxiedArtifacts,
|
|
19
25
|
)
|
|
20
26
|
from anyscale.client.openapi_client.models.create_schedule import CreateSchedule
|
|
@@ -142,6 +148,13 @@ class AnyscaleClientInterface(ABC):
|
|
|
142
148
|
"""
|
|
143
149
|
raise NotImplementedError
|
|
144
150
|
|
|
151
|
+
@abstractmethod
|
|
152
|
+
def add_cloud_collaborators(
|
|
153
|
+
self, cloud_id: str, collaborators: List[CreateCloudCollaborator]
|
|
154
|
+
) -> None:
|
|
155
|
+
"""Batch add collaborators to a cloud."""
|
|
156
|
+
raise NotImplementedError
|
|
157
|
+
|
|
145
158
|
@abstractmethod
|
|
146
159
|
def create_compute_config(
|
|
147
160
|
self, config: ComputeTemplateConfig, *, name: Optional[str] = None
|
|
@@ -285,6 +298,13 @@ class AnyscaleClientInterface(ABC):
|
|
|
285
298
|
"""
|
|
286
299
|
raise NotImplementedError
|
|
287
300
|
|
|
301
|
+
@abstractmethod
|
|
302
|
+
def add_project_collaborators(
|
|
303
|
+
self, project_id: str, collaborators: List[CreateUserProjectCollaborator]
|
|
304
|
+
) -> None:
|
|
305
|
+
"""Batch add collaborators to a project."""
|
|
306
|
+
raise NotImplementedError
|
|
307
|
+
|
|
288
308
|
@abstractmethod
|
|
289
309
|
def get_job(
|
|
290
310
|
self,
|
|
@@ -598,5 +618,58 @@ class AnyscaleClientInterface(ABC):
|
|
|
598
618
|
def admin_batch_create_users(
|
|
599
619
|
self, admin_create_users: List[AdminCreateUser]
|
|
600
620
|
) -> List[AdminCreatedUser]:
|
|
601
|
-
"""Batch create users without email verification
|
|
621
|
+
"""Batch create, as an admin, users without email verification."""
|
|
622
|
+
raise NotImplementedError
|
|
623
|
+
|
|
624
|
+
@abstractmethod
|
|
625
|
+
def create_organization_invitations(
|
|
626
|
+
self, emails: List[str]
|
|
627
|
+
) -> Tuple[List[str], List[str]]:
|
|
628
|
+
"""Create organization invitations."""
|
|
629
|
+
raise NotImplementedError
|
|
630
|
+
|
|
631
|
+
@abstractmethod
|
|
632
|
+
def list_organization_invitations(self) -> List[OrganizationInvitation]:
|
|
633
|
+
"""List organization invitations."""
|
|
634
|
+
raise NotImplementedError
|
|
635
|
+
|
|
636
|
+
@abstractmethod
|
|
637
|
+
def delete_organization_invitation(self, email: str) -> OrganizationInvitation:
|
|
638
|
+
"""Delete organization invitation."""
|
|
639
|
+
raise NotImplementedError
|
|
640
|
+
|
|
641
|
+
@abstractmethod
|
|
642
|
+
def get_organization_collaborator(self, email: str) -> OrganizationCollaborator:
|
|
643
|
+
"""Get organization collaborator."""
|
|
644
|
+
raise NotImplementedError
|
|
645
|
+
|
|
646
|
+
@abstractmethod
|
|
647
|
+
def create_resource_quota(
|
|
648
|
+
self, create_resource_quota: CreateResourceQuota
|
|
649
|
+
) -> ResourceQuota:
|
|
650
|
+
"""Create a resource quota."""
|
|
651
|
+
raise NotImplementedError
|
|
652
|
+
|
|
653
|
+
@abstractmethod
|
|
654
|
+
def list_resource_quotas(
|
|
655
|
+
self,
|
|
656
|
+
name: Optional[str] = None,
|
|
657
|
+
cloud_id: Optional[str] = None,
|
|
658
|
+
creator_id: Optional[str] = None,
|
|
659
|
+
is_enabled: Optional[bool] = None,
|
|
660
|
+
max_items: int = 20,
|
|
661
|
+
) -> List[ResourceQuota]:
|
|
662
|
+
"""List resource quotas."""
|
|
663
|
+
raise NotImplementedError
|
|
664
|
+
|
|
665
|
+
@abstractmethod
|
|
666
|
+
def delete_resource_quota(self, resource_quota_id: str) -> None:
|
|
667
|
+
"""Delete a resource quota."""
|
|
668
|
+
raise NotImplementedError
|
|
669
|
+
|
|
670
|
+
@abstractmethod
|
|
671
|
+
def set_resource_quota_status(
|
|
672
|
+
self, resource_quota_id: str, is_enabled: bool
|
|
673
|
+
) -> None:
|
|
674
|
+
"""Set the status of a resource quota."""
|
|
602
675
|
raise NotImplementedError
|
|
@@ -17,9 +17,13 @@ from anyscale.client.openapi_client.models import (
|
|
|
17
17
|
AdminCreatedUser,
|
|
18
18
|
AdminCreateUser,
|
|
19
19
|
Cloud,
|
|
20
|
+
CloudProviders,
|
|
20
21
|
ComputeTemplateConfig,
|
|
22
|
+
CreateCloudCollaborator,
|
|
21
23
|
CreateExperimentalWorkspace,
|
|
22
24
|
CreateInternalProductionJob,
|
|
25
|
+
CreateResourceQuota,
|
|
26
|
+
CreateUserProjectCollaborator,
|
|
23
27
|
DecoratedComputeTemplate,
|
|
24
28
|
DeletedPlatformFineTunedModel,
|
|
25
29
|
ExperimentalWorkspace,
|
|
@@ -28,9 +32,15 @@ from anyscale.client.openapi_client.models import (
|
|
|
28
32
|
HaJobGoalStates,
|
|
29
33
|
HaJobStates,
|
|
30
34
|
InternalProductionJob,
|
|
35
|
+
MiniCloud,
|
|
36
|
+
MiniUser,
|
|
37
|
+
OrganizationCollaborator,
|
|
38
|
+
OrganizationInvitation,
|
|
39
|
+
OrganizationPermissionLevel,
|
|
31
40
|
ProductionJob,
|
|
32
41
|
ProductionJobStateTransition,
|
|
33
42
|
Project,
|
|
43
|
+
ResourceQuota,
|
|
34
44
|
WorkspaceDataplaneProxiedArtifacts,
|
|
35
45
|
)
|
|
36
46
|
from anyscale.client.openapi_client.models.create_schedule import CreateSchedule
|
|
@@ -135,6 +145,7 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
|
135
145
|
self._jobs: Dict[str, ProductionJob] = {}
|
|
136
146
|
self._job_runs: Dict[str, List[APIJobRun]] = defaultdict(list)
|
|
137
147
|
self._project_to_id: Dict[Optional[str] : Dict[Optional[str], str]] = {}
|
|
148
|
+
self._project_collaborators: Dict[str, List[CreateUserProjectCollaborator]] = {}
|
|
138
149
|
self._rolled_out_model: Optional[ApplyServiceModel] = None
|
|
139
150
|
self._sent_workspace_notifications: List[WorkspaceNotification] = []
|
|
140
151
|
self._rolled_back_service: Optional[Tuple[str, Optional[int]]] = None
|
|
@@ -154,6 +165,8 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
|
154
165
|
self._workspaces_env_vars: Dict[str, Dict[str, str]] = {}
|
|
155
166
|
self._clusters_headnode_ip: Dict[str, str] = {}
|
|
156
167
|
self._clusters_ssh_key: Dict[str, SessionSshKey] = {}
|
|
168
|
+
self._organization_invitations: Dict[str, OrganizationInvitation] = {}
|
|
169
|
+
self._resource_quotas: Dict[str, ResourceQuota] = {}
|
|
157
170
|
|
|
158
171
|
# Cloud ID -> Cloud.
|
|
159
172
|
self._clouds: Dict[str, Cloud] = {
|
|
@@ -164,6 +177,9 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
|
164
177
|
),
|
|
165
178
|
}
|
|
166
179
|
|
|
180
|
+
# Cloud ID -> collaborators
|
|
181
|
+
self._cloud_collaborators: Dict[str, List[CreateCloudCollaborator]] = {}
|
|
182
|
+
|
|
167
183
|
# Cloud ID -> default ClusterCompute.
|
|
168
184
|
compute_config = ClusterCompute(
|
|
169
185
|
id=self.DEFAULT_CLUSTER_COMPUTE_ID,
|
|
@@ -347,6 +363,25 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
|
347
363
|
def get_cloud(self, *, cloud_id: str) -> Optional[Cloud]:
|
|
348
364
|
return self._clouds.get(cloud_id, None)
|
|
349
365
|
|
|
366
|
+
def add_cloud_collaborators(
|
|
367
|
+
self, cloud_id: str, collaborators: List[CreateCloudCollaborator]
|
|
368
|
+
) -> None:
|
|
369
|
+
existing_collaborators = [
|
|
370
|
+
collaborator.email
|
|
371
|
+
for collaborator in self._cloud_collaborators.get(cloud_id, [])
|
|
372
|
+
]
|
|
373
|
+
|
|
374
|
+
for collaborator in collaborators:
|
|
375
|
+
if collaborator.email in existing_collaborators:
|
|
376
|
+
raise ValueError(
|
|
377
|
+
f"Collaborator with email '{collaborator.email}' already exists in cloud '{cloud_id}'."
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
if cloud_id not in self._cloud_collaborators:
|
|
381
|
+
self._cloud_collaborators[cloud_id] = collaborators
|
|
382
|
+
else:
|
|
383
|
+
self._cloud_collaborators[cloud_id].extend(collaborators)
|
|
384
|
+
|
|
350
385
|
def add_compute_config(self, compute_config: DecoratedComputeTemplate) -> int:
|
|
351
386
|
compute_config.version = (
|
|
352
387
|
len(self._compute_config_name_to_ids[compute_config.name]) + 1
|
|
@@ -579,6 +614,28 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
|
579
614
|
)
|
|
580
615
|
return None
|
|
581
616
|
|
|
617
|
+
def add_project_collaborators(
|
|
618
|
+
self, project_id: str, collaborators: List[CreateUserProjectCollaborator]
|
|
619
|
+
):
|
|
620
|
+
existing_collaborators = [
|
|
621
|
+
collaborator.value.email if collaborator.value else None
|
|
622
|
+
for collaborator in self._project_collaborators.get(project_id, [])
|
|
623
|
+
]
|
|
624
|
+
|
|
625
|
+
for collaborator in collaborators:
|
|
626
|
+
if (
|
|
627
|
+
collaborator.value
|
|
628
|
+
and collaborator.value.email in existing_collaborators
|
|
629
|
+
):
|
|
630
|
+
raise ValueError(
|
|
631
|
+
f"Collaborator with email '{collaborator.value.email}' already exists in project '{project_id}'."
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
if project_id not in self._project_collaborators:
|
|
635
|
+
self._project_collaborators[project_id] = collaborators
|
|
636
|
+
else:
|
|
637
|
+
self._project_collaborators[project_id].extend(collaborators)
|
|
638
|
+
|
|
582
639
|
def get_job(
|
|
583
640
|
self,
|
|
584
641
|
*,
|
|
@@ -908,7 +965,7 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
|
908
965
|
compute_config_id=model.compute_config_id,
|
|
909
966
|
environment_id=model.cluster_environment_build_id,
|
|
910
967
|
cloud_id=model.cloud_id or self.get_cloud_id(),
|
|
911
|
-
created_at=datetime.
|
|
968
|
+
created_at=datetime.utcnow(),
|
|
912
969
|
creator_id=self.DEFAULT_USER_ID,
|
|
913
970
|
creator_email=self.DEFAULT_USER_EMAIL,
|
|
914
971
|
organization_id=self.DEFAULT_ORGANIZATION_ID,
|
|
@@ -1106,3 +1163,110 @@ class FakeAnyscaleClient(AnyscaleClientInterface):
|
|
|
1106
1163
|
self._users[admin_create_user.email] = created_user
|
|
1107
1164
|
|
|
1108
1165
|
return created_users
|
|
1166
|
+
|
|
1167
|
+
def create_organization_invitations(
|
|
1168
|
+
self, emails: List[str]
|
|
1169
|
+
) -> Tuple[List[str], List[str]]:
|
|
1170
|
+
success_emails, error_messages = [], []
|
|
1171
|
+
for email in emails:
|
|
1172
|
+
if email in self._users:
|
|
1173
|
+
error_messages.append(
|
|
1174
|
+
f"User with email {email} is already a member of your organization."
|
|
1175
|
+
)
|
|
1176
|
+
else:
|
|
1177
|
+
orginv_id = f"orginv_{uuid.uuid4()!s}"
|
|
1178
|
+
self._organization_invitations[email] = OrganizationInvitation(
|
|
1179
|
+
email=email,
|
|
1180
|
+
id=orginv_id,
|
|
1181
|
+
organization_id="org_1",
|
|
1182
|
+
created_at=datetime.utcnow(),
|
|
1183
|
+
expires_at=datetime.utcnow(),
|
|
1184
|
+
)
|
|
1185
|
+
success_emails.append(email)
|
|
1186
|
+
|
|
1187
|
+
return success_emails, error_messages
|
|
1188
|
+
|
|
1189
|
+
def list_organization_invitations(self) -> List[OrganizationInvitation]:
|
|
1190
|
+
return list(self._organization_invitations.values())
|
|
1191
|
+
|
|
1192
|
+
def delete_organization_invitation(self, email: str) -> OrganizationInvitation:
|
|
1193
|
+
if email not in self._organization_invitations:
|
|
1194
|
+
raise ValueError(f"Invitation for email '{email}' not found.")
|
|
1195
|
+
|
|
1196
|
+
return self._organization_invitations.pop(email)
|
|
1197
|
+
|
|
1198
|
+
def get_organization_collaborator(self, email):
|
|
1199
|
+
return OrganizationCollaborator(
|
|
1200
|
+
id="identity_1",
|
|
1201
|
+
permission_level=OrganizationPermissionLevel.COLLABORATOR,
|
|
1202
|
+
name="Test User",
|
|
1203
|
+
created_at=datetime.utcnow(),
|
|
1204
|
+
email=email,
|
|
1205
|
+
user_id="usr_1",
|
|
1206
|
+
)
|
|
1207
|
+
|
|
1208
|
+
def create_resource_quota(
|
|
1209
|
+
self, create_resource_quota: CreateResourceQuota
|
|
1210
|
+
) -> ResourceQuota:
|
|
1211
|
+
resource_quota_id = f"rq_{uuid.uuid4()!s}"
|
|
1212
|
+
resource_quota = ResourceQuota(
|
|
1213
|
+
id=resource_quota_id,
|
|
1214
|
+
name=create_resource_quota.name,
|
|
1215
|
+
cloud_id=create_resource_quota.cloud_id,
|
|
1216
|
+
project_id=create_resource_quota.project_id,
|
|
1217
|
+
quota=create_resource_quota.quota,
|
|
1218
|
+
is_enabled=True,
|
|
1219
|
+
created_at=datetime.utcnow(),
|
|
1220
|
+
creator=MiniUser(
|
|
1221
|
+
id="user_1",
|
|
1222
|
+
email="test@anyscale.com",
|
|
1223
|
+
name="Test User",
|
|
1224
|
+
username="testuser",
|
|
1225
|
+
),
|
|
1226
|
+
cloud=MiniCloud(
|
|
1227
|
+
id=create_resource_quota.cloud_id,
|
|
1228
|
+
name="Test Cloud",
|
|
1229
|
+
provider=CloudProviders.AWS,
|
|
1230
|
+
),
|
|
1231
|
+
)
|
|
1232
|
+
|
|
1233
|
+
self._resource_quotas[resource_quota_id] = resource_quota
|
|
1234
|
+
|
|
1235
|
+
return self._resource_quotas[resource_quota_id]
|
|
1236
|
+
|
|
1237
|
+
def list_resource_quotas(
|
|
1238
|
+
self,
|
|
1239
|
+
name: Optional[str] = None,
|
|
1240
|
+
cloud_id: Optional[str] = None,
|
|
1241
|
+
creator_id: Optional[str] = None,
|
|
1242
|
+
is_enabled: Optional[bool] = None,
|
|
1243
|
+
max_items: int = 20,
|
|
1244
|
+
) -> List[ResourceQuota]:
|
|
1245
|
+
results = []
|
|
1246
|
+
for resource_quota in self._resource_quotas.values():
|
|
1247
|
+
if name and resource_quota.name != name:
|
|
1248
|
+
continue
|
|
1249
|
+
if cloud_id and resource_quota.cloud_id != cloud_id:
|
|
1250
|
+
continue
|
|
1251
|
+
if creator_id and resource_quota.creator.id != creator_id:
|
|
1252
|
+
continue
|
|
1253
|
+
if is_enabled is not None and resource_quota.is_enabled != is_enabled:
|
|
1254
|
+
continue
|
|
1255
|
+
results.append(resource_quota)
|
|
1256
|
+
|
|
1257
|
+
return results[:max_items]
|
|
1258
|
+
|
|
1259
|
+
def delete_resource_quota(self, resource_quota_id: str) -> None:
|
|
1260
|
+
if resource_quota_id not in self._resource_quotas:
|
|
1261
|
+
raise ValueError(f"Resource Quota with id '{resource_quota_id}' not found.")
|
|
1262
|
+
|
|
1263
|
+
self._resource_quotas.pop(resource_quota_id)
|
|
1264
|
+
|
|
1265
|
+
def set_resource_quota_status(
|
|
1266
|
+
self, resource_quota_id: str, is_enabled: bool
|
|
1267
|
+
) -> None:
|
|
1268
|
+
resource_quota = self._resource_quotas.get(resource_quota_id)
|
|
1269
|
+
if resource_quota is None:
|
|
1270
|
+
raise ValueError(f"Resource Quota with id '{resource_quota_id}' not found.")
|
|
1271
|
+
|
|
1272
|
+
resource_quota.is_enabled = is_enabled
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Tool to autogenerate documentation for the Anyscale SDK/CLI that can be embedded in a [Docusarus](https://docusaurus.io/) site.
|
|
1
|
+
Tool to autogenerate documentation for the Anyscale SDK/CLI that can be embedded in a [Docusarus](https://docusaurus.io/) site. If you're adding a new API reference in anyscale/product, follow this guide to see what changes you need to make: https://www.notion.so/anyscale-hq/How-to-document-new-anyscale-API-references-173027c809cb8080b8e3e53fc244d70b.
|
|
2
2
|
|
|
3
3
|
This is used by: https://github.com/anyscale/docs/tree/master/docs/reference.
|
|
4
4
|
|