skypilot-nightly 1.0.0.dev20250804__py3-none-any.whl → 1.0.0.dev20250806__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.

Potentially problematic release.


This version of skypilot-nightly might be problematic. Click here for more details.

Files changed (103) hide show
  1. sky/__init__.py +2 -2
  2. sky/catalog/kubernetes_catalog.py +8 -0
  3. sky/catalog/nebius_catalog.py +0 -1
  4. sky/client/cli/command.py +26 -7
  5. sky/client/sdk.py +16 -8
  6. sky/client/sdk.pyi +6 -5
  7. sky/client/sdk_async.py +811 -0
  8. sky/clouds/kubernetes.py +6 -1
  9. sky/clouds/nebius.py +1 -4
  10. sky/dashboard/out/404.html +1 -1
  11. sky/dashboard/out/_next/static/Gelsd19kVxXcX7aQQGsGu/_buildManifest.js +1 -0
  12. sky/dashboard/out/_next/static/chunks/1043-75af48ca5d5aaf57.js +1 -0
  13. sky/dashboard/out/_next/static/chunks/1141-8678a9102cc5f67e.js +11 -0
  14. sky/dashboard/out/_next/static/chunks/2622-951867535095b0eb.js +1 -0
  15. sky/dashboard/out/_next/static/chunks/3785.0a173cd4393f0fef.js +1 -0
  16. sky/dashboard/out/_next/static/chunks/9025.99f29acb7617963e.js +6 -0
  17. sky/dashboard/out/_next/static/chunks/{9984.78ee6d2c6fa4b0e8.js → 9984.c5564679e467d245.js} +1 -1
  18. sky/dashboard/out/_next/static/chunks/pages/{_app-a67ae198457b9886.js → _app-2a43ea3241bbdacd.js} +1 -1
  19. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/{[job]-fa63e8b1d203f298.js → [job]-7cb24da04ca00956.js} +1 -1
  20. sky/dashboard/out/_next/static/chunks/pages/clusters/{[cluster]-9e7df5fc761c95a7.js → [cluster]-1e95993124dbfc57.js} +1 -1
  21. sky/dashboard/out/_next/static/chunks/pages/clusters-47f1ddae13a2f8e4.js +1 -0
  22. sky/dashboard/out/_next/static/chunks/pages/config-d56e64f30db7b42e.js +1 -0
  23. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-2a44e70b500b6b70.js +1 -0
  24. sky/dashboard/out/_next/static/chunks/pages/infra-22faac9325016d83.js +1 -0
  25. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-90693cb88b5599a7.js +11 -0
  26. sky/dashboard/out/_next/static/chunks/pages/jobs-ab318e52eb4424a7.js +1 -0
  27. sky/dashboard/out/_next/static/chunks/pages/users-b90c865a690bfe84.js +1 -0
  28. sky/dashboard/out/_next/static/chunks/pages/volumes-7af733f5d7b6ed1c.js +1 -0
  29. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-4d41c9023287f59a.js → [name]-35e0de5bca55e594.js} +1 -1
  30. sky/dashboard/out/_next/static/chunks/pages/workspaces-062525fb5462acb6.js +1 -0
  31. sky/dashboard/out/_next/static/chunks/webpack-387626669badf82e.js +1 -0
  32. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  33. sky/dashboard/out/clusters/[cluster].html +1 -1
  34. sky/dashboard/out/clusters.html +1 -1
  35. sky/dashboard/out/config.html +1 -1
  36. sky/dashboard/out/index.html +1 -1
  37. sky/dashboard/out/infra/[context].html +1 -1
  38. sky/dashboard/out/infra.html +1 -1
  39. sky/dashboard/out/jobs/[job].html +1 -1
  40. sky/dashboard/out/jobs.html +1 -1
  41. sky/dashboard/out/users.html +1 -1
  42. sky/dashboard/out/volumes.html +1 -1
  43. sky/dashboard/out/workspace/new.html +1 -1
  44. sky/dashboard/out/workspaces/[name].html +1 -1
  45. sky/dashboard/out/workspaces.html +1 -1
  46. sky/jobs/client/sdk_async.py +135 -0
  47. sky/jobs/utils.py +3 -1
  48. sky/provision/kubernetes/utils.py +30 -4
  49. sky/provision/nebius/instance.py +1 -0
  50. sky/provision/nebius/utils.py +9 -1
  51. sky/serve/client/sdk_async.py +130 -0
  52. sky/serve/constants.py +2 -1
  53. sky/serve/controller.py +2 -1
  54. sky/serve/load_balancer.py +3 -1
  55. sky/serve/serve_state.py +70 -5
  56. sky/serve/serve_utils.py +124 -22
  57. sky/serve/server/impl.py +22 -21
  58. sky/serve/service.py +8 -1
  59. sky/server/auth/__init__.py +0 -0
  60. sky/server/auth/authn.py +46 -0
  61. sky/server/auth/oauth2_proxy.py +185 -0
  62. sky/server/common.py +108 -17
  63. sky/server/constants.py +1 -1
  64. sky/server/daemons.py +60 -11
  65. sky/server/rest.py +114 -0
  66. sky/server/server.py +44 -40
  67. sky/setup_files/dependencies.py +2 -0
  68. sky/skylet/constants.py +1 -1
  69. sky/skylet/events.py +5 -1
  70. sky/skylet/skylet.py +3 -1
  71. sky/task.py +43 -10
  72. sky/templates/kubernetes-ray.yml.j2 +4 -0
  73. sky/templates/nebius-ray.yml.j2 +1 -0
  74. sky/utils/controller_utils.py +7 -0
  75. sky/utils/rich_utils.py +120 -0
  76. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250806.dist-info}/METADATA +5 -1
  77. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250806.dist-info}/RECORD +86 -81
  78. sky/dashboard/out/_next/static/KiGGm4fK0CpmN6BT17jkh/_buildManifest.js +0 -1
  79. sky/dashboard/out/_next/static/chunks/1043-928582d4860fef92.js +0 -1
  80. sky/dashboard/out/_next/static/chunks/1141-3f10a5a9f697c630.js +0 -11
  81. sky/dashboard/out/_next/static/chunks/3698-7874720877646365.js +0 -1
  82. sky/dashboard/out/_next/static/chunks/3785.95524bc443db8260.js +0 -1
  83. sky/dashboard/out/_next/static/chunks/6989-983d3ae7a874de98.js +0 -1
  84. sky/dashboard/out/_next/static/chunks/9025.7937c16bc8623516.js +0 -6
  85. sky/dashboard/out/_next/static/chunks/pages/clusters-956ad430075efee8.js +0 -1
  86. sky/dashboard/out/_next/static/chunks/pages/config-8620d099cbef8608.js +0 -1
  87. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-9cfd875eecb6eaf5.js +0 -1
  88. sky/dashboard/out/_next/static/chunks/pages/infra-0fbdc9072f19fbe2.js +0 -1
  89. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-6c5af4c86e6ab3d3.js +0 -11
  90. sky/dashboard/out/_next/static/chunks/pages/jobs-6393a9edc7322b54.js +0 -1
  91. sky/dashboard/out/_next/static/chunks/pages/users-34d6bb10c3b3ee3d.js +0 -1
  92. sky/dashboard/out/_next/static/chunks/pages/volumes-225c8dae0634eb7f.js +0 -1
  93. sky/dashboard/out/_next/static/chunks/pages/workspaces-e4cb7e97d37e93ad.js +0 -1
  94. sky/dashboard/out/_next/static/chunks/webpack-13145516b19858fb.js +0 -1
  95. /sky/dashboard/out/_next/static/{KiGGm4fK0CpmN6BT17jkh → Gelsd19kVxXcX7aQQGsGu}/_ssgManifest.js +0 -0
  96. /sky/dashboard/out/_next/static/chunks/{1871-7e17c195296e2ea9.js → 1871-ced1c14230cad6e1.js} +0 -0
  97. /sky/dashboard/out/_next/static/chunks/{6135-d0e285ac5f3f2485.js → 6135-2d7ed3350659d073.js} +0 -0
  98. /sky/dashboard/out/_next/static/chunks/{6601-234b1cf963c7280b.js → 6601-2109d22e7861861c.js} +0 -0
  99. /sky/dashboard/out/_next/static/chunks/{938-40d15b6261ec8dc1.js → 938-bda2685db5eae6cf.js} +0 -0
  100. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250806.dist-info}/WHEEL +0 -0
  101. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250806.dist-info}/entry_points.txt +0 -0
  102. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250806.dist-info}/licenses/LICENSE +0 -0
  103. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250806.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,811 @@
1
+ """Async client-side Python SDK for SkyPilot.
2
+
3
+ All functions will return a future that can be awaited on.
4
+
5
+ Usage example:
6
+
7
+ .. code-block:: python
8
+
9
+ request_id = await sky.status()
10
+ statuses = await sky.get(request_id)
11
+
12
+ """
13
+ import dataclasses
14
+ import logging
15
+ import typing
16
+ from typing import Any, Dict, List, Optional, Tuple, Union
17
+
18
+ import aiohttp
19
+ import colorama
20
+
21
+ from sky import admin_policy
22
+ from sky import backends
23
+ from sky import exceptions
24
+ from sky import models
25
+ from sky import sky_logging
26
+ import sky.catalog
27
+ from sky.client import common as client_common
28
+ from sky.client import sdk
29
+ from sky.provision.kubernetes import utils as kubernetes_utils
30
+ from sky.server import common as server_common
31
+ from sky.server import rest
32
+ from sky.server.requests import payloads
33
+ from sky.server.requests import requests as requests_lib
34
+ from sky.skylet import job_lib
35
+ from sky.usage import usage_lib
36
+ from sky.utils import annotations
37
+ from sky.utils import common
38
+ from sky.utils import context_utils
39
+ from sky.utils import env_options
40
+ from sky.utils import rich_utils
41
+ from sky.utils import ux_utils
42
+
43
+ if typing.TYPE_CHECKING:
44
+ import io
45
+
46
+ import sky
47
+
48
+ logger = sky_logging.init_logger(__name__)
49
+ logging.getLogger('httpx').setLevel(logging.CRITICAL)
50
+
51
+
52
+ @dataclasses.dataclass
53
+ class StreamConfig:
54
+ """Configuration class for stream_and_get behavior.
55
+
56
+ Attributes:
57
+ log_path: The path to the log file to stream.
58
+ tail: The number of lines to show from the end of the logs.
59
+ If None, show all logs.
60
+ follow: Whether to follow the logs.
61
+ output_stream: The output stream to write to. If None, print to the
62
+ console.
63
+ """
64
+ log_path: Optional[str] = None
65
+ tail: Optional[int] = None
66
+ follow: bool = True
67
+ output_stream: Optional['io.TextIOBase'] = None
68
+
69
+
70
+ DEFAULT_STREAM_CONFIG = StreamConfig()
71
+
72
+
73
+ @usage_lib.entrypoint
74
+ @server_common.check_server_healthy_or_start
75
+ @annotations.client_api
76
+ async def get(request_id: str) -> Any:
77
+ """Async version of get() that waits for and gets the result of a request.
78
+
79
+ Args:
80
+ request_id: The request ID of the request to get.
81
+
82
+ Returns:
83
+ The ``Request Returns`` of the specified request. See the documentation
84
+ of the specific requests above for more details.
85
+
86
+ Raises:
87
+ Exception: It raises the same exceptions as the specific requests,
88
+ see ``Request Raises`` in the documentation of the specific requests
89
+ above.
90
+ """
91
+ async with aiohttp.ClientSession() as session:
92
+ response = await server_common.make_authenticated_request_async(
93
+ session,
94
+ 'GET',
95
+ f'/api/get?request_id={request_id}',
96
+ retry=False,
97
+ timeout=aiohttp.ClientTimeout(
98
+ total=None,
99
+ connect=client_common.
100
+ API_SERVER_REQUEST_CONNECTION_TIMEOUT_SECONDS))
101
+
102
+ try:
103
+ request_task = None
104
+ if response.status == 200:
105
+ request_task = requests_lib.Request.decode(
106
+ payloads.RequestPayload(**await response.json()))
107
+ elif response.status == 500:
108
+ try:
109
+ request_task = requests_lib.Request.decode(
110
+ payloads.RequestPayload(**await response.json()))
111
+ logger.debug(f'Got request with error: {request_task.name}')
112
+ except Exception: # pylint: disable=broad-except
113
+ request_task = None
114
+ if request_task is None:
115
+ with ux_utils.print_exception_no_traceback():
116
+ raise RuntimeError(
117
+ f'Failed to get request {request_id}: '
118
+ f'{response.status} {await response.text()}')
119
+ error = request_task.get_error()
120
+ if error is not None:
121
+ error_obj = error['object']
122
+ if env_options.Options.SHOW_DEBUG_INFO.get():
123
+ stacktrace = getattr(error_obj, 'stacktrace',
124
+ str(error_obj))
125
+ logger.error('=== Traceback on SkyPilot API Server ===\n'
126
+ f'{stacktrace}')
127
+ with ux_utils.print_exception_no_traceback():
128
+ raise error_obj
129
+ if request_task.status == requests_lib.RequestStatus.CANCELLED:
130
+ with ux_utils.print_exception_no_traceback():
131
+ raise exceptions.RequestCancelled(
132
+ f'{colorama.Fore.YELLOW}Current {request_task.name!r} '
133
+ f'request ({request_task.request_id}) is cancelled by '
134
+ f'another process. {colorama.Style.RESET_ALL}')
135
+ return request_task.get_return_value()
136
+ finally:
137
+ response.close()
138
+
139
+
140
+ @usage_lib.entrypoint
141
+ @server_common.check_server_healthy_or_start
142
+ @annotations.client_api
143
+ async def stream_response_async(request_id: Optional[str],
144
+ response: 'aiohttp.ClientResponse',
145
+ output_stream: Optional['io.TextIOBase'] = None,
146
+ resumable: bool = False) -> Any:
147
+ """Async version of stream_response that streams the response to the
148
+ console.
149
+
150
+ Args:
151
+ request_id: The request ID.
152
+ response: The aiohttp response.
153
+ output_stream: The output stream to write to. If None, print to the
154
+ console.
155
+ resumable: Whether the response is resumable on retry. If True, the
156
+ streaming will start from the previous failure point on retry.
157
+ """
158
+
159
+ retry_context: Optional[rest.RetryContext] = None
160
+ if resumable:
161
+ retry_context = rest.get_retry_context()
162
+ try:
163
+ line_count = 0
164
+ async for line in rich_utils.decode_rich_status_async(response):
165
+ if line is not None:
166
+ line_count += 1
167
+ if retry_context is None:
168
+ print(line, flush=True, end='', file=output_stream)
169
+ elif line_count > retry_context.line_processed:
170
+ print(line, flush=True, end='', file=output_stream)
171
+ retry_context.line_processed = line_count
172
+ if request_id is not None:
173
+ return await get(request_id)
174
+ except Exception: # pylint: disable=broad-except
175
+ logger.debug(f'To stream request logs: sky api logs {request_id}')
176
+ raise
177
+
178
+
179
+ async def _stream_and_get(
180
+ request_id: Optional[str] = None,
181
+ config: StreamConfig = DEFAULT_STREAM_CONFIG,
182
+ ) -> Any:
183
+ """Streams the logs of a request or a log file and gets the final result.
184
+ """
185
+ return await stream_and_get(
186
+ request_id,
187
+ config.log_path,
188
+ config.tail,
189
+ config.follow,
190
+ config.output_stream,
191
+ )
192
+
193
+
194
+ async def stream_and_get(
195
+ request_id: Optional[str] = None,
196
+ log_path: Optional[str] = None,
197
+ tail: Optional[int] = None,
198
+ follow: bool = True,
199
+ output_stream: Optional['io.TextIOBase'] = None,
200
+ ) -> Any:
201
+ """Streams the logs of a request or a log file and gets the final result.
202
+
203
+ This will block until the request is finished. The request id can be a
204
+ prefix of the full request id.
205
+
206
+ Args:
207
+ request_id: The prefix of the request ID of the request to stream.
208
+ config: Configuration for streaming behavior.
209
+
210
+ Returns:
211
+ The ``Request Returns`` of the specified request. See the documentation
212
+ of the specific requests above for more details.
213
+
214
+ Raises:
215
+ Exception: It raises the same exceptions as the specific requests,
216
+ see ``Request Raises`` in the documentation of the specific requests
217
+ above.
218
+ """
219
+ params = {
220
+ 'request_id': request_id,
221
+ 'log_path': log_path,
222
+ 'tail': str(tail) if tail is not None else None,
223
+ 'follow': str(follow).lower(), # Convert boolean to string for aiohttp
224
+ 'format': 'console',
225
+ }
226
+ # Filter out None values
227
+ params = {k: v for k, v in params.items() if v is not None}
228
+
229
+ async with aiohttp.ClientSession() as session:
230
+ response = await server_common.make_authenticated_request_async(
231
+ session,
232
+ 'GET',
233
+ '/api/stream',
234
+ params=params,
235
+ retry=False,
236
+ timeout=aiohttp.ClientTimeout(
237
+ total=None,
238
+ connect=client_common.
239
+ API_SERVER_REQUEST_CONNECTION_TIMEOUT_SECONDS))
240
+
241
+ try:
242
+ if response.status in [404, 400]:
243
+ detail = (await response.json()).get('detail')
244
+ with ux_utils.print_exception_no_traceback():
245
+ raise RuntimeError(f'Failed to stream logs: {detail}')
246
+ elif response.status != 200:
247
+ return await get(request_id)
248
+
249
+ return await stream_response_async(request_id, response,
250
+ output_stream)
251
+ finally:
252
+ response.close()
253
+
254
+
255
+ @usage_lib.entrypoint
256
+ @annotations.client_api
257
+ async def check(
258
+ infra_list: Optional[Tuple[str, ...]],
259
+ verbose: bool,
260
+ workspace: Optional[str] = None,
261
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
262
+ ) -> Dict[str, List[str]]:
263
+ """Async version of check() that checks the credentials to enable clouds."""
264
+ request_id = await context_utils.to_thread(sdk.check, infra_list, verbose,
265
+ workspace)
266
+ if stream_logs is not None:
267
+ return await _stream_and_get(request_id, stream_logs)
268
+ else:
269
+ return await get(request_id)
270
+
271
+
272
+ @usage_lib.entrypoint
273
+ @annotations.client_api
274
+ async def enabled_clouds(
275
+ workspace: Optional[str] = None,
276
+ expand: bool = False,
277
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
278
+ ) -> List[str]:
279
+ """Async version of enabled_clouds() that gets the enabled clouds."""
280
+ request_id = await context_utils.to_thread(sdk.enabled_clouds, workspace,
281
+ expand)
282
+ if stream_logs is not None:
283
+ return await _stream_and_get(request_id, stream_logs)
284
+ else:
285
+ return await get(request_id)
286
+
287
+
288
+ @usage_lib.entrypoint
289
+ @annotations.client_api
290
+ async def list_accelerators(
291
+ gpus_only: bool = True,
292
+ name_filter: Optional[str] = None,
293
+ region_filter: Optional[str] = None,
294
+ quantity_filter: Optional[int] = None,
295
+ clouds: Optional[Union[List[str], str]] = None,
296
+ all_regions: bool = False,
297
+ require_price: bool = True,
298
+ case_sensitive: bool = True,
299
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
300
+ ) -> Dict[str, List[sky.catalog.common.InstanceTypeInfo]]:
301
+ """Async version of list_accelerators() that lists the names of all
302
+ accelerators offered by Sky."""
303
+ request_id = await context_utils.to_thread(sdk.list_accelerators, gpus_only,
304
+ name_filter, region_filter,
305
+ quantity_filter, clouds,
306
+ all_regions, require_price,
307
+ case_sensitive)
308
+ if stream_logs is not None:
309
+ return await _stream_and_get(request_id, stream_logs)
310
+ else:
311
+ return await get(request_id)
312
+
313
+
314
+ @usage_lib.entrypoint
315
+ @annotations.client_api
316
+ async def list_accelerator_counts(
317
+ gpus_only: bool = True,
318
+ name_filter: Optional[str] = None,
319
+ region_filter: Optional[str] = None,
320
+ quantity_filter: Optional[int] = None,
321
+ clouds: Optional[Union[List[str], str]] = None,
322
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
323
+ ) -> Dict[str, List[int]]:
324
+ """Async version of list_accelerator_counts() that lists all accelerators
325
+ offered by Sky and available counts."""
326
+ request_id = await context_utils.to_thread(sdk.list_accelerator_counts,
327
+ gpus_only, name_filter,
328
+ region_filter, quantity_filter,
329
+ clouds)
330
+ if stream_logs is not None:
331
+ return await _stream_and_get(request_id, stream_logs)
332
+ else:
333
+ return await get(request_id)
334
+
335
+
336
+ @usage_lib.entrypoint
337
+ @annotations.client_api
338
+ async def optimize(
339
+ dag: 'sky.Dag',
340
+ minimize: common.OptimizeTarget = common.OptimizeTarget.COST,
341
+ admin_policy_request_options: Optional[
342
+ admin_policy.RequestOptions] = None,
343
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
344
+ ) -> sky.dag.Dag:
345
+ """Async version of optimize() that finds the best execution plan for the
346
+ given DAG."""
347
+ request_id = await context_utils.to_thread(sdk.optimize, dag, minimize,
348
+ admin_policy_request_options)
349
+ if stream_logs is not None:
350
+ return await _stream_and_get(request_id, stream_logs)
351
+ else:
352
+ return await get(request_id)
353
+
354
+
355
+ @usage_lib.entrypoint
356
+ @annotations.client_api
357
+ async def workspaces(
358
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
359
+ ) -> Dict[str, Any]:
360
+ """Async version of workspaces() that gets the workspaces."""
361
+ request_id = await context_utils.to_thread(sdk.workspaces)
362
+ if stream_logs is not None:
363
+ return await _stream_and_get(request_id, stream_logs)
364
+ else:
365
+ return await get(request_id)
366
+
367
+
368
+ @usage_lib.entrypoint
369
+ @annotations.client_api
370
+ async def launch(
371
+ task: Union['sky.Task', 'sky.Dag'],
372
+ cluster_name: Optional[str] = None,
373
+ retry_until_up: bool = False,
374
+ idle_minutes_to_autostop: Optional[int] = None,
375
+ dryrun: bool = False,
376
+ down: bool = False, # pylint: disable=redefined-outer-name
377
+ backend: Optional[backends.Backend] = None,
378
+ optimize_target: common.OptimizeTarget = common.OptimizeTarget.COST,
379
+ no_setup: bool = False,
380
+ clone_disk_from: Optional[str] = None,
381
+ fast: bool = False,
382
+ # Internal only:
383
+ # pylint: disable=invalid-name
384
+ _need_confirmation: bool = False,
385
+ _is_launched_by_jobs_controller: bool = False,
386
+ _is_launched_by_sky_serve_controller: bool = False,
387
+ _disable_controller_check: bool = False,
388
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
389
+ ) -> Tuple[Optional[int], Optional[backends.ResourceHandle]]:
390
+ """Async version of launch() that launches a cluster or task."""
391
+ request_id = await context_utils.to_thread(
392
+ sdk.launch, task, cluster_name, retry_until_up,
393
+ idle_minutes_to_autostop, dryrun, down, backend, optimize_target,
394
+ no_setup, clone_disk_from, fast, _need_confirmation,
395
+ _is_launched_by_jobs_controller, _is_launched_by_sky_serve_controller,
396
+ _disable_controller_check)
397
+ if stream_logs is not None:
398
+ return await _stream_and_get(request_id, stream_logs)
399
+ else:
400
+ return await get(request_id)
401
+
402
+
403
+ @usage_lib.entrypoint
404
+ @annotations.client_api
405
+ async def exec( # pylint: disable=redefined-builtin
406
+ task: Union['sky.Task', 'sky.Dag'],
407
+ cluster_name: Optional[str] = None,
408
+ dryrun: bool = False,
409
+ down: bool = False, # pylint: disable=redefined-outer-name
410
+ backend: Optional[backends.Backend] = None,
411
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
412
+ ) -> Tuple[Optional[int], Optional[backends.ResourceHandle]]:
413
+ """Async version of exec() that executes a task on an existing cluster."""
414
+ request_id = await context_utils.to_thread(sdk.exec, task, cluster_name,
415
+ dryrun, down, backend)
416
+ if stream_logs is not None:
417
+ return await _stream_and_get(request_id, stream_logs)
418
+ else:
419
+ return await get(request_id)
420
+
421
+
422
+ @usage_lib.entrypoint
423
+ @annotations.client_api
424
+ async def tail_logs(cluster_name: str,
425
+ job_id: Optional[int],
426
+ follow: bool,
427
+ tail: int = 0,
428
+ output_stream: Optional['io.TextIOBase'] = None) -> int:
429
+ """Async version of tail_logs() that tails the logs of a job."""
430
+ return await context_utils.to_thread(sdk.tail_logs, cluster_name, job_id,
431
+ follow, tail, output_stream)
432
+
433
+
434
+ @usage_lib.entrypoint
435
+ @annotations.client_api
436
+ async def download_logs(cluster_name: str,
437
+ job_ids: Optional[List[str]]) -> Dict[str, str]:
438
+ """Async version of download_logs() that downloads the logs of jobs."""
439
+ return await context_utils.to_thread(sdk.download_logs, cluster_name,
440
+ job_ids)
441
+
442
+
443
+ @usage_lib.entrypoint
444
+ @annotations.client_api
445
+ async def start(
446
+ cluster_name: str,
447
+ idle_minutes_to_autostop: Optional[int] = None,
448
+ retry_until_up: bool = False,
449
+ down: bool = False, # pylint: disable=redefined-outer-name
450
+ force: bool = False,
451
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
452
+ ) -> backends.CloudVmRayResourceHandle:
453
+ """Async version of start() that restarts a cluster."""
454
+ request_id = await context_utils.to_thread(sdk.start, cluster_name,
455
+ idle_minutes_to_autostop,
456
+ retry_until_up, down, force)
457
+ if stream_logs is not None:
458
+ return await _stream_and_get(request_id, stream_logs)
459
+ else:
460
+ return await get(request_id)
461
+
462
+
463
+ @usage_lib.entrypoint
464
+ @annotations.client_api
465
+ async def down(
466
+ cluster_name: str,
467
+ purge: bool = False,
468
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
469
+ """Async version of down() that tears down a cluster."""
470
+ request_id = await context_utils.to_thread(sdk.down, cluster_name, purge)
471
+ if stream_logs is not None:
472
+ return await _stream_and_get(request_id, stream_logs)
473
+ else:
474
+ return await get(request_id)
475
+
476
+
477
+ @usage_lib.entrypoint
478
+ @annotations.client_api
479
+ async def stop(
480
+ cluster_name: str,
481
+ purge: bool = False,
482
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
483
+ """Async version of stop() that stops a cluster."""
484
+ request_id = await context_utils.to_thread(sdk.stop, cluster_name, purge)
485
+ if stream_logs is not None:
486
+ return await _stream_and_get(request_id, stream_logs)
487
+ else:
488
+ return await get(request_id)
489
+
490
+
491
+ @usage_lib.entrypoint
492
+ @annotations.client_api
493
+ async def autostop(
494
+ cluster_name: str,
495
+ idle_minutes: int,
496
+ down: bool = False, # pylint: disable=redefined-outer-name
497
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
498
+ ) -> None:
499
+ """Async version of autostop() that schedules an autostop/autodown for a
500
+ cluster."""
501
+ request_id = await context_utils.to_thread(sdk.autostop, cluster_name,
502
+ idle_minutes, down)
503
+ if stream_logs is not None:
504
+ return await _stream_and_get(request_id, stream_logs)
505
+ else:
506
+ return await get(request_id)
507
+
508
+
509
+ @usage_lib.entrypoint
510
+ @annotations.client_api
511
+ async def queue(
512
+ cluster_name: str,
513
+ skip_finished: bool = False,
514
+ all_users: bool = False,
515
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
516
+ ) -> List[dict]:
517
+ """Async version of queue() that gets the job queue of a cluster."""
518
+ request_id = await context_utils.to_thread(sdk.queue, cluster_name,
519
+ skip_finished, all_users)
520
+ if stream_logs is not None:
521
+ return await _stream_and_get(request_id, stream_logs)
522
+ else:
523
+ return await get(request_id)
524
+
525
+
526
+ @usage_lib.entrypoint
527
+ @annotations.client_api
528
+ async def job_status(
529
+ cluster_name: str,
530
+ job_ids: Optional[List[int]] = None,
531
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
532
+ ) -> Dict[Optional[int], Optional[job_lib.JobStatus]]:
533
+ """Async version of job_status() that gets the status of jobs on a
534
+ cluster."""
535
+ request_id = await context_utils.to_thread(sdk.job_status, cluster_name,
536
+ job_ids)
537
+ if stream_logs is not None:
538
+ return await _stream_and_get(request_id, stream_logs)
539
+ else:
540
+ return await get(request_id)
541
+
542
+
543
+ @usage_lib.entrypoint
544
+ @annotations.client_api
545
+ async def cancel(
546
+ cluster_name: str,
547
+ all: bool = False, # pylint: disable=redefined-builtin
548
+ all_users: bool = False,
549
+ job_ids: Optional[List[int]] = None,
550
+ # pylint: disable=invalid-name
551
+ _try_cancel_if_cluster_is_init: bool = False,
552
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
553
+ """Async version of cancel() that cancels jobs on a cluster."""
554
+ request_id = await context_utils.to_thread(sdk.cancel, cluster_name, all,
555
+ all_users, job_ids,
556
+ _try_cancel_if_cluster_is_init)
557
+ if stream_logs is not None:
558
+ return await _stream_and_get(request_id, stream_logs)
559
+ else:
560
+ return await get(request_id)
561
+
562
+
563
+ @usage_lib.entrypoint
564
+ @annotations.client_api
565
+ async def status(
566
+ cluster_names: Optional[List[str]] = None,
567
+ refresh: common.StatusRefreshMode = common.StatusRefreshMode.NONE,
568
+ all_users: bool = False,
569
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
570
+ ) -> List[Dict[str, Any]]:
571
+ """Async version of status() that gets cluster statuses."""
572
+ request_id = await context_utils.to_thread(sdk.status, cluster_names,
573
+ refresh, all_users)
574
+ if stream_logs is not None:
575
+ return await _stream_and_get(request_id, stream_logs)
576
+ else:
577
+ return await get(request_id)
578
+
579
+
580
+ @usage_lib.entrypoint
581
+ @annotations.client_api
582
+ async def endpoints(
583
+ cluster: str,
584
+ port: Optional[Union[int, str]] = None,
585
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
586
+ ) -> Dict[int, str]:
587
+ """Async version of endpoints() that gets the endpoint for a given cluster
588
+ and port number."""
589
+ request_id = await context_utils.to_thread(sdk.endpoints, cluster, port)
590
+ if stream_logs is not None:
591
+ return await _stream_and_get(request_id, stream_logs)
592
+ else:
593
+ return await get(request_id)
594
+
595
+
596
+ @usage_lib.entrypoint
597
+ @annotations.client_api
598
+ async def cost_report(
599
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
600
+ ) -> List[Dict[str, Any]]:
601
+ """Async version of cost_report() that gets all cluster cost reports."""
602
+ request_id = await context_utils.to_thread(sdk.cost_report)
603
+ if stream_logs is not None:
604
+ return await _stream_and_get(request_id, stream_logs)
605
+ else:
606
+ return await get(request_id)
607
+
608
+
609
+ @usage_lib.entrypoint
610
+ @annotations.client_api
611
+ async def storage_ls(
612
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
613
+ ) -> List[Dict[str, Any]]:
614
+ """Async version of storage_ls() that gets the storages."""
615
+ request_id = await context_utils.to_thread(sdk.storage_ls)
616
+ if stream_logs is not None:
617
+ return await _stream_and_get(request_id, stream_logs)
618
+ else:
619
+ return await get(request_id)
620
+
621
+
622
+ @usage_lib.entrypoint
623
+ @annotations.client_api
624
+ async def storage_delete(
625
+ name: str,
626
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
627
+ """Async version of storage_delete() that deletes a storage."""
628
+ request_id = await context_utils.to_thread(sdk.storage_delete, name)
629
+ if stream_logs is not None:
630
+ return await _stream_and_get(request_id, stream_logs)
631
+ else:
632
+ return await get(request_id)
633
+
634
+
635
+ @usage_lib.entrypoint
636
+ @annotations.client_api
637
+ async def local_up(
638
+ gpus: bool,
639
+ ips: Optional[List[str]],
640
+ ssh_user: Optional[str],
641
+ ssh_key: Optional[str],
642
+ cleanup: bool,
643
+ context_name: Optional[str] = None,
644
+ password: Optional[str] = None,
645
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
646
+ """Async version of local_up() that launches a Kubernetes cluster on
647
+ local machines."""
648
+ request_id = await context_utils.to_thread(sdk.local_up, gpus, ips,
649
+ ssh_user, ssh_key, cleanup,
650
+ context_name, password)
651
+ if stream_logs is not None:
652
+ return await _stream_and_get(request_id, stream_logs)
653
+ else:
654
+ return await get(request_id)
655
+
656
+
657
+ @usage_lib.entrypoint
658
+ @annotations.client_api
659
+ async def local_down(
660
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
661
+ """Async version of local_down() that tears down the Kubernetes cluster
662
+ started by local_up."""
663
+ request_id = await context_utils.to_thread(sdk.local_down)
664
+ if stream_logs is not None:
665
+ return await _stream_and_get(request_id, stream_logs)
666
+ else:
667
+ return await get(request_id)
668
+
669
+
670
+ @usage_lib.entrypoint
671
+ @annotations.client_api
672
+ async def ssh_up(
673
+ infra: Optional[str] = None,
674
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
675
+ """Async version of ssh_up() that deploys the SSH Node Pools defined in
676
+ ~/.sky/ssh_targets.yaml."""
677
+ request_id = await context_utils.to_thread(sdk.ssh_up, infra)
678
+ if stream_logs is not None:
679
+ return await _stream_and_get(request_id, stream_logs)
680
+ else:
681
+ return await get(request_id)
682
+
683
+
684
+ @usage_lib.entrypoint
685
+ @annotations.client_api
686
+ async def ssh_down(
687
+ infra: Optional[str] = None,
688
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
689
+ """Async version of ssh_down() that tears down a Kubernetes cluster on SSH
690
+ targets."""
691
+ request_id = await context_utils.to_thread(sdk.ssh_down, infra)
692
+ if stream_logs is not None:
693
+ return await _stream_and_get(request_id, stream_logs)
694
+ else:
695
+ return await get(request_id)
696
+
697
+
698
+ @usage_lib.entrypoint
699
+ @annotations.client_api
700
+ async def realtime_kubernetes_gpu_availability(
701
+ context: Optional[str] = None,
702
+ name_filter: Optional[str] = None,
703
+ quantity_filter: Optional[int] = None,
704
+ is_ssh: Optional[bool] = None,
705
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
706
+ ) -> List[Tuple[str, List[models.RealtimeGpuAvailability]]]:
707
+ """Async version of realtime_kubernetes_gpu_availability() that gets the
708
+ real-time Kubernetes GPU availability."""
709
+ request_id = await context_utils.to_thread(
710
+ sdk.realtime_kubernetes_gpu_availability, context, name_filter,
711
+ quantity_filter, is_ssh)
712
+ if stream_logs is not None:
713
+ return await _stream_and_get(request_id, stream_logs)
714
+ else:
715
+ return await get(request_id)
716
+
717
+
718
+ @usage_lib.entrypoint
719
+ @annotations.client_api
720
+ async def kubernetes_node_info(
721
+ context: Optional[str] = None,
722
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
723
+ ) -> models.KubernetesNodesInfo:
724
+ """Async version of kubernetes_node_info() that gets the resource
725
+ information for all the nodes in the cluster."""
726
+ request_id = await context_utils.to_thread(sdk.kubernetes_node_info,
727
+ context)
728
+ if stream_logs is not None:
729
+ return await _stream_and_get(request_id, stream_logs)
730
+ else:
731
+ return await get(request_id)
732
+
733
+
734
+ @usage_lib.entrypoint
735
+ @annotations.client_api
736
+ async def status_kubernetes(
737
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
738
+ ) -> Tuple[List[kubernetes_utils.KubernetesSkyPilotClusterInfoPayload],
739
+ List[kubernetes_utils.KubernetesSkyPilotClusterInfoPayload],
740
+ List[Dict[str, Any]], Optional[str]]:
741
+ """Async version of status_kubernetes() that gets all SkyPilot clusters
742
+ and jobs in the Kubernetes cluster."""
743
+ request_id = await context_utils.to_thread(sdk.status_kubernetes)
744
+ if stream_logs is not None:
745
+ return await _stream_and_get(request_id, stream_logs)
746
+ else:
747
+ return await get(request_id)
748
+
749
+
750
+ @usage_lib.entrypoint
751
+ @annotations.client_api
752
+ async def api_cancel(
753
+ request_ids: Optional[Union[str, List[str]]] = None,
754
+ all_users: bool = False,
755
+ silent: bool = False,
756
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
757
+ ) -> List[str]:
758
+ """Async version of api_cancel() that aborts a request or all requests."""
759
+ request_id = await context_utils.to_thread(sdk.api_cancel, request_ids,
760
+ all_users, silent)
761
+ if stream_logs is not None:
762
+ return await _stream_and_get(request_id, stream_logs)
763
+ else:
764
+ return await get(request_id)
765
+
766
+
767
+ @usage_lib.entrypoint
768
+ @annotations.client_api
769
+ async def api_status(request_ids: Optional[List[str]] = None,
770
+ all_status: bool = False) -> List[payloads.RequestPayload]:
771
+ """Async version of api_status() that lists all requests."""
772
+ return await context_utils.to_thread(sdk.api_status, request_ids,
773
+ all_status)
774
+
775
+
776
+ @usage_lib.entrypoint
777
+ @annotations.client_api
778
+ async def dashboard(starting_page: Optional[str] = None) -> None:
779
+ """Async version of dashboard() that starts the dashboard for SkyPilot."""
780
+ return await context_utils.to_thread(sdk.dashboard, starting_page)
781
+
782
+
783
+ @usage_lib.entrypoint
784
+ @annotations.client_api
785
+ async def api_info() -> Dict[str, Any]:
786
+ """Async version of api_info() that gets the server's status, commit and
787
+ version."""
788
+ return await context_utils.to_thread(sdk.api_info)
789
+
790
+
791
+ @usage_lib.entrypoint
792
+ @annotations.client_api
793
+ async def api_stop() -> None:
794
+ """Async version of api_stop() that stops the API server."""
795
+ return await context_utils.to_thread(sdk.api_stop)
796
+
797
+
798
+ @usage_lib.entrypoint
799
+ @annotations.client_api
800
+ async def api_server_logs(follow: bool = True,
801
+ tail: Optional[int] = None) -> None:
802
+ """Async version of api_server_logs() that streams the API server logs."""
803
+ return await context_utils.to_thread(sdk.api_server_logs, follow, tail)
804
+
805
+
806
+ @usage_lib.entrypoint
807
+ @annotations.client_api
808
+ async def api_login(endpoint: Optional[str] = None,
809
+ get_token: bool = False) -> None:
810
+ """Async version of api_login() that logs into a SkyPilot API server."""
811
+ return await context_utils.to_thread(sdk.api_login, endpoint, get_token)